1 //===-- TraceIntelPT.cpp --------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "TraceIntelPT.h" 10 11 #include "../common/ThreadPostMortemTrace.h" 12 #include "CommandObjectTraceStartIntelPT.h" 13 #include "DecodedThread.h" 14 #include "TraceIntelPTConstants.h" 15 #include "TraceIntelPTBundleLoader.h" 16 #include "TraceIntelPTBundleSaver.h" 17 #include "lldb/Core/PluginManager.h" 18 #include "lldb/Target/Process.h" 19 #include "lldb/Target/Target.h" 20 #include "llvm/ADT/None.h" 21 22 using namespace lldb; 23 using namespace lldb_private; 24 using namespace lldb_private::trace_intel_pt; 25 using namespace llvm; 26 27 LLDB_PLUGIN_DEFINE(TraceIntelPT) 28 29 lldb::CommandObjectSP 30 TraceIntelPT::GetProcessTraceStartCommand(CommandInterpreter &interpreter) { 31 return CommandObjectSP( 32 new CommandObjectProcessTraceStartIntelPT(*this, interpreter)); 33 } 34 35 lldb::CommandObjectSP 36 TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) { 37 return CommandObjectSP( 38 new CommandObjectThreadTraceStartIntelPT(*this, interpreter)); 39 } 40 41 void TraceIntelPT::Initialize() { 42 PluginManager::RegisterPlugin(GetPluginNameStatic(), "Intel Processor Trace", 43 CreateInstanceForTraceBundle, 44 CreateInstanceForLiveProcess, 45 TraceIntelPTBundleLoader::GetSchema()); 46 } 47 48 void TraceIntelPT::Terminate() { 49 PluginManager::UnregisterPlugin(CreateInstanceForTraceBundle); 50 } 51 52 StringRef TraceIntelPT::GetSchema() { 53 return TraceIntelPTBundleLoader::GetSchema(); 54 } 55 56 void TraceIntelPT::Dump(Stream *s) const {} 57 58 llvm::Error TraceIntelPT::SaveLiveTraceToDisk(FileSpec directory) { 59 RefreshLiveProcessState(); 60 return TraceIntelPTBundleSaver().SaveToDisk(*this, directory); 61 } 62 63 Expected<TraceSP> TraceIntelPT::CreateInstanceForTraceBundle( 64 const json::Value &bundle_description, StringRef bundle_dir, 65 Debugger &debugger) { 66 return TraceIntelPTBundleLoader(debugger, bundle_description, 67 bundle_dir) 68 .Load(); 69 } 70 71 Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) { 72 TraceSP instance(new TraceIntelPT(process)); 73 process.GetTarget().SetTrace(instance); 74 return instance; 75 } 76 77 TraceIntelPTSP TraceIntelPT::GetSharedPtr() { 78 return std::static_pointer_cast<TraceIntelPT>(shared_from_this()); 79 } 80 81 TraceIntelPTSP TraceIntelPT::CreateInstanceForPostmortemTrace( 82 JSONTraceBundleDescription &bundle_description, ArrayRef<ProcessSP> traced_processes, 83 ArrayRef<ThreadPostMortemTraceSP> traced_threads) { 84 TraceIntelPTSP trace_sp(new TraceIntelPT(bundle_description, traced_processes)); 85 trace_sp->m_storage.tsc_conversion = bundle_description.tsc_perf_zero_conversion; 86 87 if (bundle_description.cpus) { 88 std::vector<cpu_id_t> cpus; 89 90 for (const JSONCpu &cpu : *bundle_description.cpus) { 91 trace_sp->SetPostMortemCpuDataFile(cpu.id, IntelPTDataKinds::kIptTrace, 92 FileSpec(cpu.ipt_trace)); 93 94 trace_sp->SetPostMortemCpuDataFile( 95 cpu.id, IntelPTDataKinds::kPerfContextSwitchTrace, 96 FileSpec(cpu.context_switch_trace)); 97 cpus.push_back(cpu.id); 98 } 99 100 std::vector<tid_t> tids; 101 for (const JSONProcess &process : bundle_description.processes) 102 for (const JSONThread &thread : process.threads) 103 tids.push_back(thread.tid); 104 105 trace_sp->m_storage.multicpu_decoder.emplace(trace_sp); 106 } else { 107 for (const ThreadPostMortemTraceSP &thread : traced_threads) { 108 trace_sp->m_storage.thread_decoders.try_emplace( 109 thread->GetID(), std::make_unique<ThreadDecoder>(thread, *trace_sp)); 110 if (const Optional<FileSpec> &trace_file = thread->GetTraceFile()) { 111 trace_sp->SetPostMortemThreadDataFile( 112 thread->GetID(), IntelPTDataKinds::kIptTrace, *trace_file); 113 } 114 } 115 } 116 117 for (const ProcessSP &process_sp : traced_processes) 118 process_sp->GetTarget().SetTrace(trace_sp); 119 return trace_sp; 120 } 121 122 TraceIntelPT::TraceIntelPT(JSONTraceBundleDescription &bundle_description, 123 ArrayRef<ProcessSP> traced_processes) 124 : Trace(traced_processes, bundle_description.GetCpuIds()), 125 m_cpu_info(bundle_description.cpu_info) {} 126 127 DecodedThreadSP TraceIntelPT::Decode(Thread &thread) { 128 if (const char *error = RefreshLiveProcessState()) 129 return std::make_shared<DecodedThread>( 130 thread.shared_from_this(), 131 createStringError(inconvertibleErrorCode(), error)); 132 133 Storage &storage = GetUpdatedStorage(); 134 if (storage.multicpu_decoder) 135 return storage.multicpu_decoder->Decode(thread); 136 137 auto it = storage.thread_decoders.find(thread.GetID()); 138 if (it == storage.thread_decoders.end()) 139 return std::make_shared<DecodedThread>( 140 thread.shared_from_this(), 141 createStringError(inconvertibleErrorCode(), "thread not traced")); 142 return it->second->Decode(); 143 } 144 145 lldb::TraceCursorUP TraceIntelPT::GetCursor(Thread &thread) { 146 return Decode(thread)->GetCursor(); 147 } 148 149 void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { 150 Storage &storage = GetUpdatedStorage(); 151 152 lldb::tid_t tid = thread.GetID(); 153 s.Format("\nthread #{0}: tid = {1}", thread.GetIndexID(), thread.GetID()); 154 if (!IsTraced(tid)) { 155 s << ", not traced\n"; 156 return; 157 } 158 s << "\n"; 159 160 Expected<Optional<uint64_t>> raw_size_or_error = GetRawTraceSize(thread); 161 if (!raw_size_or_error) { 162 s.Format(" {0}\n", toString(raw_size_or_error.takeError())); 163 return; 164 } 165 Optional<uint64_t> raw_size = *raw_size_or_error; 166 167 DecodedThreadSP decoded_trace_sp = Decode(thread); 168 169 /// Instruction stats 170 { 171 uint64_t insn_len = decoded_trace_sp->GetInstructionsCount(); 172 uint64_t mem_used = decoded_trace_sp->CalculateApproximateMemoryUsage(); 173 174 s.Format(" Total number of instructions: {0}\n", insn_len); 175 176 s << "\n Memory usage:\n"; 177 if (raw_size) 178 s.Format(" Raw trace size: {0} KiB\n", *raw_size / 1024); 179 180 s.Format( 181 " Total approximate memory usage (excluding raw trace): {0:2} KiB\n", 182 (double)mem_used / 1024); 183 if (insn_len != 0) 184 s.Format( 185 " Average memory usage per instruction (excluding raw trace): " 186 "{0:2} bytes\n", 187 (double)mem_used / insn_len); 188 } 189 190 // Timing 191 { 192 s << "\n Timing for this thread:\n"; 193 auto print_duration = [&](const std::string &name, 194 std::chrono::milliseconds duration) { 195 s.Format(" {0}: {1:2}s\n", name, duration.count() / 1000.0); 196 }; 197 GetTimer().ForThread(tid).ForEachTimedTask(print_duration); 198 199 s << "\n Timing for global tasks:\n"; 200 GetTimer().ForGlobal().ForEachTimedTask(print_duration); 201 } 202 203 // Instruction events stats 204 { 205 const DecodedThread::EventsStats &events_stats = 206 decoded_trace_sp->GetEventsStats(); 207 s << "\n Events:\n"; 208 s.Format(" Number of instructions with events: {0}\n", 209 events_stats.total_instructions_with_events); 210 s.Format(" Number of individual events: {0}\n", 211 events_stats.total_count); 212 for (const auto &event_to_count : events_stats.events_counts) { 213 s.Format(" {0}: {1}\n", 214 trace_event_utils::EventToDisplayString(event_to_count.first), 215 event_to_count.second); 216 } 217 } 218 219 if (storage.multicpu_decoder) { 220 s << "\n Multi-cpu decoding:\n"; 221 s.Format(" Total number of continuous executions found: {0}\n", 222 storage.multicpu_decoder->GetTotalContinuousExecutionsCount()); 223 s.Format( 224 " Number of continuous executions for this thread: {0}\n", 225 storage.multicpu_decoder->GetNumContinuousExecutionsForThread(tid)); 226 } 227 228 // Errors 229 { 230 s << "\n Errors:\n"; 231 const DecodedThread::LibiptErrorsStats &tsc_errors_stats = 232 decoded_trace_sp->GetTscErrorsStats(); 233 s.Format(" Number of TSC decoding errors: {0}\n", 234 tsc_errors_stats.total_count); 235 for (const auto &error_message_to_count : 236 tsc_errors_stats.libipt_errors_counts) { 237 s.Format(" {0}: {1}\n", error_message_to_count.first, 238 error_message_to_count.second); 239 } 240 } 241 } 242 243 llvm::Expected<Optional<uint64_t>> 244 TraceIntelPT::GetRawTraceSize(Thread &thread) { 245 if (GetUpdatedStorage().multicpu_decoder) 246 return None; // TODO: calculate the amount of intel pt raw trace associated 247 // with the given thread. 248 if (GetLiveProcess()) 249 return GetLiveThreadBinaryDataSize(thread.GetID(), 250 IntelPTDataKinds::kIptTrace); 251 uint64_t size; 252 auto callback = [&](llvm::ArrayRef<uint8_t> data) { 253 size = data.size(); 254 return Error::success(); 255 }; 256 if (Error err = OnThreadBufferRead(thread.GetID(), callback)) 257 return std::move(err); 258 259 return size; 260 } 261 262 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() { 263 Expected<std::vector<uint8_t>> cpu_info = 264 GetLiveProcessBinaryData(IntelPTDataKinds::kProcFsCpuInfo); 265 if (!cpu_info) 266 return cpu_info.takeError(); 267 268 int64_t cpu_family = -1; 269 int64_t model = -1; 270 int64_t stepping = -1; 271 std::string vendor_id; 272 273 StringRef rest(reinterpret_cast<const char *>(cpu_info->data()), 274 cpu_info->size()); 275 while (!rest.empty()) { 276 StringRef line; 277 std::tie(line, rest) = rest.split('\n'); 278 279 SmallVector<StringRef, 2> columns; 280 line.split(columns, StringRef(":"), -1, false); 281 282 if (columns.size() < 2) 283 continue; // continue searching 284 285 columns[1] = columns[1].trim(" "); 286 if (columns[0].contains("cpu family") && 287 columns[1].getAsInteger(10, cpu_family)) 288 continue; 289 290 else if (columns[0].contains("model") && columns[1].getAsInteger(10, model)) 291 continue; 292 293 else if (columns[0].contains("stepping") && 294 columns[1].getAsInteger(10, stepping)) 295 continue; 296 297 else if (columns[0].contains("vendor_id")) { 298 vendor_id = columns[1].str(); 299 if (!vendor_id.empty()) 300 continue; 301 } 302 303 if ((cpu_family != -1) && (model != -1) && (stepping != -1) && 304 (!vendor_id.empty())) { 305 return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown, 306 static_cast<uint16_t>(cpu_family), 307 static_cast<uint8_t>(model), 308 static_cast<uint8_t>(stepping)}; 309 } 310 } 311 return createStringError(inconvertibleErrorCode(), 312 "Failed parsing the target's /proc/cpuinfo file"); 313 } 314 315 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() { 316 if (!m_cpu_info) { 317 if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess()) 318 m_cpu_info = *cpu_info; 319 else 320 return cpu_info.takeError(); 321 } 322 return *m_cpu_info; 323 } 324 325 llvm::Optional<LinuxPerfZeroTscConversion> 326 TraceIntelPT::GetPerfZeroTscConversion() { 327 return GetUpdatedStorage().tsc_conversion; 328 } 329 330 TraceIntelPT::Storage &TraceIntelPT::GetUpdatedStorage() { 331 RefreshLiveProcessState(); 332 return m_storage; 333 } 334 335 Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state, 336 StringRef json_response) { 337 m_storage = Storage(); 338 339 Expected<TraceIntelPTGetStateResponse> intelpt_state = 340 json::parse<TraceIntelPTGetStateResponse>(json_response, 341 "TraceIntelPTGetStateResponse"); 342 if (!intelpt_state) 343 return intelpt_state.takeError(); 344 345 m_storage.tsc_conversion = intelpt_state->tsc_perf_zero_conversion; 346 347 if (!intelpt_state->cpus) { 348 for (const TraceThreadState &thread_state : state.traced_threads) { 349 ThreadSP thread_sp = 350 GetLiveProcess()->GetThreadList().FindThreadByID(thread_state.tid); 351 m_storage.thread_decoders.try_emplace( 352 thread_state.tid, std::make_unique<ThreadDecoder>(thread_sp, *this)); 353 } 354 } else { 355 std::vector<cpu_id_t> cpus; 356 for (const TraceCpuState &cpu : *intelpt_state->cpus) 357 cpus.push_back(cpu.id); 358 359 std::vector<tid_t> tids; 360 for (const TraceThreadState &thread : intelpt_state->traced_threads) 361 tids.push_back(thread.tid); 362 363 if (!intelpt_state->tsc_perf_zero_conversion) 364 return createStringError(inconvertibleErrorCode(), 365 "Missing perf time_zero conversion values"); 366 m_storage.multicpu_decoder.emplace(GetSharedPtr()); 367 } 368 369 if (m_storage.tsc_conversion) { 370 Log *log = GetLog(LLDBLog::Target); 371 LLDB_LOG(log, "TraceIntelPT found TSC conversion information"); 372 } 373 return Error::success(); 374 } 375 376 bool TraceIntelPT::IsTraced(lldb::tid_t tid) { 377 Storage &storage = GetUpdatedStorage(); 378 if (storage.multicpu_decoder) 379 return storage.multicpu_decoder->TracesThread(tid); 380 return storage.thread_decoders.count(tid); 381 } 382 383 // The information here should match the description of the intel-pt section 384 // of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt 385 // documentation file. Similarly, it should match the CLI help messages of the 386 // TraceIntelPTOptions.td file. 387 const char *TraceIntelPT::GetStartConfigurationHelp() { 388 static Optional<std::string> message; 389 if (!message) { 390 message.emplace(formatv(R"(Parameters: 391 392 See the jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt for a 393 description of each parameter below. 394 395 - int iptTraceSize (defaults to {0} bytes): 396 [process and thread tracing] 397 398 - boolean enableTsc (default to {1}): 399 [process and thread tracing] 400 401 - int psbPeriod (defaults to {2}): 402 [process and thread tracing] 403 404 - boolean perCpuTracing (default to {3}): 405 [process tracing only] 406 407 - int processBufferSizeLimit (defaults to {4} MiB): 408 [process tracing only])", 409 kDefaultIptTraceSize, kDefaultEnableTscValue, 410 kDefaultPsbPeriod, kDefaultPerCpuTracing, 411 kDefaultProcessBufferSizeLimit / 1024 / 1024)); 412 } 413 return message->c_str(); 414 } 415 416 Error TraceIntelPT::Start(uint64_t ipt_trace_size, 417 uint64_t total_buffer_size_limit, bool enable_tsc, 418 Optional<uint64_t> psb_period, bool per_cpu_tracing) { 419 TraceIntelPTStartRequest request; 420 request.ipt_trace_size = ipt_trace_size; 421 request.process_buffer_size_limit = total_buffer_size_limit; 422 request.enable_tsc = enable_tsc; 423 request.psb_period = psb_period; 424 request.type = GetPluginName().str(); 425 request.per_cpu_tracing = per_cpu_tracing; 426 return Trace::Start(toJSON(request)); 427 } 428 429 Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) { 430 uint64_t ipt_trace_size = kDefaultIptTraceSize; 431 uint64_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit; 432 bool enable_tsc = kDefaultEnableTscValue; 433 Optional<uint64_t> psb_period = kDefaultPsbPeriod; 434 bool per_cpu_tracing = kDefaultPerCpuTracing; 435 436 if (configuration) { 437 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { 438 dict->GetValueForKeyAsInteger("iptTraceSize", ipt_trace_size); 439 dict->GetValueForKeyAsInteger("processBufferSizeLimit", 440 process_buffer_size_limit); 441 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); 442 dict->GetValueForKeyAsInteger("psbPeriod", psb_period); 443 dict->GetValueForKeyAsBoolean("perCpuTracing", per_cpu_tracing); 444 } else { 445 return createStringError(inconvertibleErrorCode(), 446 "configuration object is not a dictionary"); 447 } 448 } 449 450 return Start(ipt_trace_size, process_buffer_size_limit, enable_tsc, 451 psb_period, per_cpu_tracing); 452 } 453 454 llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, 455 uint64_t ipt_trace_size, bool enable_tsc, 456 Optional<uint64_t> psb_period) { 457 TraceIntelPTStartRequest request; 458 request.ipt_trace_size = ipt_trace_size; 459 request.enable_tsc = enable_tsc; 460 request.psb_period = psb_period; 461 request.type = GetPluginName().str(); 462 request.tids.emplace(); 463 for (lldb::tid_t tid : tids) 464 request.tids->push_back(tid); 465 return Trace::Start(toJSON(request)); 466 } 467 468 Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, 469 StructuredData::ObjectSP configuration) { 470 uint64_t ipt_trace_size = kDefaultIptTraceSize; 471 bool enable_tsc = kDefaultEnableTscValue; 472 Optional<uint64_t> psb_period = kDefaultPsbPeriod; 473 474 if (configuration) { 475 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { 476 dict->GetValueForKeyAsInteger("iptTraceSize", ipt_trace_size); 477 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); 478 dict->GetValueForKeyAsInteger("psbPeriod", psb_period); 479 } else { 480 return createStringError(inconvertibleErrorCode(), 481 "configuration object is not a dictionary"); 482 } 483 } 484 485 return Start(tids, ipt_trace_size, enable_tsc, psb_period); 486 } 487 488 Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid, 489 OnBinaryDataReadCallback callback) { 490 return OnThreadBinaryDataRead(tid, IntelPTDataKinds::kIptTrace, callback); 491 } 492 493 TaskTimer &TraceIntelPT::GetTimer() { return GetUpdatedStorage().task_timer; } 494