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 "TraceCursorIntelPT.h" 15 #include "TraceIntelPTBundleLoader.h" 16 #include "TraceIntelPTBundleSaver.h" 17 #include "TraceIntelPTConstants.h" 18 #include "lldb/Core/PluginManager.h" 19 #include "lldb/Interpreter/OptionValueProperties.h" 20 #include "lldb/Target/Process.h" 21 #include "lldb/Target/Target.h" 22 #include <optional> 23 24 using namespace lldb; 25 using namespace lldb_private; 26 using namespace lldb_private::trace_intel_pt; 27 using namespace llvm; 28 29 LLDB_PLUGIN_DEFINE(TraceIntelPT) 30 31 lldb::CommandObjectSP 32 TraceIntelPT::GetProcessTraceStartCommand(CommandInterpreter &interpreter) { 33 return CommandObjectSP( 34 new CommandObjectProcessTraceStartIntelPT(*this, interpreter)); 35 } 36 37 lldb::CommandObjectSP 38 TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) { 39 return CommandObjectSP( 40 new CommandObjectThreadTraceStartIntelPT(*this, interpreter)); 41 } 42 43 #define LLDB_PROPERTIES_traceintelpt 44 #include "TraceIntelPTProperties.inc" 45 46 enum { 47 #define LLDB_PROPERTIES_traceintelpt 48 #include "TraceIntelPTPropertiesEnum.inc" 49 }; 50 51 ConstString TraceIntelPT::PluginProperties::GetSettingName() { 52 return ConstString(TraceIntelPT::GetPluginNameStatic()); 53 } 54 55 TraceIntelPT::PluginProperties::PluginProperties() : Properties() { 56 m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName()); 57 m_collection_sp->Initialize(g_traceintelpt_properties); 58 } 59 60 uint64_t 61 TraceIntelPT::PluginProperties::GetInfiniteDecodingLoopVerificationThreshold() { 62 const uint32_t idx = ePropertyInfiniteDecodingLoopVerificationThreshold; 63 return m_collection_sp->GetPropertyAtIndexAsUInt64( 64 nullptr, idx, g_traceintelpt_properties[idx].default_uint_value); 65 } 66 67 uint64_t TraceIntelPT::PluginProperties::GetExtremelyLargeDecodingThreshold() { 68 const uint32_t idx = ePropertyExtremelyLargeDecodingThreshold; 69 return m_collection_sp->GetPropertyAtIndexAsUInt64( 70 nullptr, idx, g_traceintelpt_properties[idx].default_uint_value); 71 } 72 73 TraceIntelPT::PluginProperties &TraceIntelPT::GetGlobalProperties() { 74 static TraceIntelPT::PluginProperties g_settings; 75 return g_settings; 76 } 77 78 void TraceIntelPT::Initialize() { 79 PluginManager::RegisterPlugin( 80 GetPluginNameStatic(), "Intel Processor Trace", 81 CreateInstanceForTraceBundle, CreateInstanceForLiveProcess, 82 TraceIntelPTBundleLoader::GetSchema(), DebuggerInitialize); 83 } 84 85 void TraceIntelPT::DebuggerInitialize(Debugger &debugger) { 86 if (!PluginManager::GetSettingForProcessPlugin( 87 debugger, PluginProperties::GetSettingName())) { 88 const bool is_global_setting = true; 89 PluginManager::CreateSettingForTracePlugin( 90 debugger, GetGlobalProperties().GetValueProperties(), 91 ConstString("Properties for the intel-pt trace plug-in."), 92 is_global_setting); 93 } 94 } 95 96 void TraceIntelPT::Terminate() { 97 PluginManager::UnregisterPlugin(CreateInstanceForTraceBundle); 98 } 99 100 StringRef TraceIntelPT::GetSchema() { 101 return TraceIntelPTBundleLoader::GetSchema(); 102 } 103 104 void TraceIntelPT::Dump(Stream *s) const {} 105 106 Expected<FileSpec> TraceIntelPT::SaveToDisk(FileSpec directory, bool compact) { 107 RefreshLiveProcessState(); 108 return TraceIntelPTBundleSaver().SaveToDisk(*this, directory, compact); 109 } 110 111 Expected<TraceSP> TraceIntelPT::CreateInstanceForTraceBundle( 112 const json::Value &bundle_description, StringRef bundle_dir, 113 Debugger &debugger) { 114 return TraceIntelPTBundleLoader(debugger, bundle_description, bundle_dir) 115 .Load(); 116 } 117 118 Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) { 119 TraceSP instance(new TraceIntelPT(process)); 120 process.GetTarget().SetTrace(instance); 121 return instance; 122 } 123 124 TraceIntelPTSP TraceIntelPT::GetSharedPtr() { 125 return std::static_pointer_cast<TraceIntelPT>(shared_from_this()); 126 } 127 128 TraceIntelPT::TraceMode TraceIntelPT::GetTraceMode() { return trace_mode; } 129 130 TraceIntelPTSP TraceIntelPT::CreateInstanceForPostmortemTrace( 131 JSONTraceBundleDescription &bundle_description, 132 ArrayRef<ProcessSP> traced_processes, 133 ArrayRef<ThreadPostMortemTraceSP> traced_threads, TraceMode trace_mode) { 134 TraceIntelPTSP trace_sp( 135 new TraceIntelPT(bundle_description, traced_processes, trace_mode)); 136 trace_sp->m_storage.tsc_conversion = 137 bundle_description.tsc_perf_zero_conversion; 138 139 if (bundle_description.cpus) { 140 std::vector<cpu_id_t> cpus; 141 142 for (const JSONCpu &cpu : *bundle_description.cpus) { 143 trace_sp->SetPostMortemCpuDataFile(cpu.id, IntelPTDataKinds::kIptTrace, 144 FileSpec(cpu.ipt_trace)); 145 146 trace_sp->SetPostMortemCpuDataFile( 147 cpu.id, IntelPTDataKinds::kPerfContextSwitchTrace, 148 FileSpec(cpu.context_switch_trace)); 149 cpus.push_back(cpu.id); 150 } 151 152 if (trace_mode == TraceMode::UserMode) { 153 trace_sp->m_storage.multicpu_decoder.emplace(trace_sp); 154 } 155 } 156 157 if (!bundle_description.cpus || trace_mode == TraceMode::KernelMode) { 158 for (const ThreadPostMortemTraceSP &thread : traced_threads) { 159 trace_sp->m_storage.thread_decoders.try_emplace( 160 thread->GetID(), std::make_unique<ThreadDecoder>(thread, *trace_sp)); 161 if (const std::optional<FileSpec> &trace_file = thread->GetTraceFile()) { 162 trace_sp->SetPostMortemThreadDataFile( 163 thread->GetID(), IntelPTDataKinds::kIptTrace, *trace_file); 164 } 165 } 166 } 167 168 for (const ProcessSP &process_sp : traced_processes) 169 process_sp->GetTarget().SetTrace(trace_sp); 170 return trace_sp; 171 } 172 173 TraceIntelPT::TraceIntelPT(JSONTraceBundleDescription &bundle_description, 174 ArrayRef<ProcessSP> traced_processes, 175 TraceMode trace_mode) 176 : Trace(traced_processes, bundle_description.GetCpuIds()), 177 m_cpu_info(bundle_description.cpu_info), trace_mode(trace_mode) {} 178 179 Expected<DecodedThreadSP> TraceIntelPT::Decode(Thread &thread) { 180 if (const char *error = RefreshLiveProcessState()) 181 return createStringError(inconvertibleErrorCode(), error); 182 183 Storage &storage = GetUpdatedStorage(); 184 if (storage.multicpu_decoder) 185 return storage.multicpu_decoder->Decode(thread); 186 187 auto it = storage.thread_decoders.find(thread.GetID()); 188 if (it == storage.thread_decoders.end()) 189 return createStringError(inconvertibleErrorCode(), "thread not traced"); 190 return it->second->Decode(); 191 } 192 193 Expected<std::optional<uint64_t>> TraceIntelPT::FindBeginningOfTimeNanos() { 194 Storage &storage = GetUpdatedStorage(); 195 if (storage.beginning_of_time_nanos_calculated) 196 return storage.beginning_of_time_nanos; 197 storage.beginning_of_time_nanos_calculated = true; 198 199 if (!storage.tsc_conversion) 200 return std::nullopt; 201 202 std::optional<uint64_t> lowest_tsc; 203 204 if (storage.multicpu_decoder) { 205 if (Expected<std::optional<uint64_t>> tsc = 206 storage.multicpu_decoder->FindLowestTSC()) { 207 lowest_tsc = *tsc; 208 } else { 209 return tsc.takeError(); 210 } 211 } 212 213 for (auto &decoder : storage.thread_decoders) { 214 Expected<std::optional<uint64_t>> tsc = decoder.second->FindLowestTSC(); 215 if (!tsc) 216 return tsc.takeError(); 217 218 if (*tsc && (!lowest_tsc || *lowest_tsc > **tsc)) 219 lowest_tsc = **tsc; 220 } 221 222 if (lowest_tsc) { 223 storage.beginning_of_time_nanos = 224 storage.tsc_conversion->ToNanos(*lowest_tsc); 225 } 226 return storage.beginning_of_time_nanos; 227 } 228 229 llvm::Expected<lldb::TraceCursorSP> 230 TraceIntelPT::CreateNewCursor(Thread &thread) { 231 if (Expected<DecodedThreadSP> decoded_thread = Decode(thread)) { 232 if (Expected<std::optional<uint64_t>> beginning_of_time = 233 FindBeginningOfTimeNanos()) 234 return std::make_shared<TraceCursorIntelPT>( 235 thread.shared_from_this(), *decoded_thread, m_storage.tsc_conversion, 236 *beginning_of_time); 237 else 238 return beginning_of_time.takeError(); 239 } else 240 return decoded_thread.takeError(); 241 } 242 243 void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose, 244 bool json) { 245 Storage &storage = GetUpdatedStorage(); 246 247 lldb::tid_t tid = thread.GetID(); 248 if (json) { 249 DumpTraceInfoAsJson(thread, s, verbose); 250 return; 251 } 252 253 s.Format("\nthread #{0}: tid = {1}", thread.GetIndexID(), thread.GetID()); 254 if (!IsTraced(tid)) { 255 s << ", not traced\n"; 256 return; 257 } 258 s << "\n"; 259 260 Expected<DecodedThreadSP> decoded_thread_sp_or_err = Decode(thread); 261 if (!decoded_thread_sp_or_err) { 262 s << toString(decoded_thread_sp_or_err.takeError()) << "\n"; 263 return; 264 } 265 266 DecodedThreadSP &decoded_thread_sp = *decoded_thread_sp_or_err; 267 268 Expected<std::optional<uint64_t>> raw_size_or_error = GetRawTraceSize(thread); 269 if (!raw_size_or_error) { 270 s.Format(" {0}\n", toString(raw_size_or_error.takeError())); 271 return; 272 } 273 std::optional<uint64_t> raw_size = *raw_size_or_error; 274 275 s.Format("\n Trace technology: {0}\n", GetPluginName()); 276 277 /// Instruction stats 278 { 279 uint64_t items_count = decoded_thread_sp->GetItemsCount(); 280 uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage(); 281 282 s.Format("\n Total number of trace items: {0}\n", items_count); 283 284 s << "\n Memory usage:\n"; 285 if (raw_size) 286 s.Format(" Raw trace size: {0} KiB\n", *raw_size / 1024); 287 288 s.Format( 289 " Total approximate memory usage (excluding raw trace): {0:2} KiB\n", 290 (double)mem_used / 1024); 291 if (items_count != 0) 292 s.Format(" Average memory usage per item (excluding raw trace): " 293 "{0:2} bytes\n", 294 (double)mem_used / items_count); 295 } 296 297 // Timing 298 { 299 s << "\n Timing for this thread:\n"; 300 auto print_duration = [&](const std::string &name, 301 std::chrono::milliseconds duration) { 302 s.Format(" {0}: {1:2}s\n", name, duration.count() / 1000.0); 303 }; 304 GetThreadTimer(tid).ForEachTimedTask(print_duration); 305 306 s << "\n Timing for global tasks:\n"; 307 GetGlobalTimer().ForEachTimedTask(print_duration); 308 } 309 310 // Instruction events stats 311 { 312 const DecodedThread::EventsStats &events_stats = 313 decoded_thread_sp->GetEventsStats(); 314 s << "\n Events:\n"; 315 s.Format(" Number of individual events: {0}\n", 316 events_stats.total_count); 317 for (const auto &event_to_count : events_stats.events_counts) { 318 s.Format(" {0}: {1}\n", 319 TraceCursor::EventKindToString(event_to_count.first), 320 event_to_count.second); 321 } 322 } 323 // Trace error stats 324 { 325 const DecodedThread::ErrorStats &error_stats = 326 decoded_thread_sp->GetErrorStats(); 327 s << "\n Errors:\n"; 328 s.Format(" Number of individual errors: {0}\n", 329 error_stats.GetTotalCount()); 330 s.Format(" Number of fatal errors: {0}\n", error_stats.fatal_errors); 331 for (const auto &[kind, count] : error_stats.libipt_errors) { 332 s.Format(" Number of libipt errors of kind [{0}]: {1}\n", kind, 333 count); 334 } 335 s.Format(" Number of other errors: {0}\n", error_stats.other_errors); 336 } 337 338 if (storage.multicpu_decoder) { 339 s << "\n Multi-cpu decoding:\n"; 340 s.Format(" Total number of continuous executions found: {0}\n", 341 storage.multicpu_decoder->GetTotalContinuousExecutionsCount()); 342 s.Format( 343 " Number of continuous executions for this thread: {0}\n", 344 storage.multicpu_decoder->GetNumContinuousExecutionsForThread(tid)); 345 s.Format(" Total number of PSB blocks found: {0}\n", 346 storage.multicpu_decoder->GetTotalPSBBlocksCount()); 347 s.Format(" Number of PSB blocks for this thread: {0}\n", 348 storage.multicpu_decoder->GePSBBlocksCountForThread(tid)); 349 s.Format(" Total number of unattributed PSB blocks found: {0}\n", 350 storage.multicpu_decoder->GetUnattributedPSBBlocksCount()); 351 } 352 } 353 354 void TraceIntelPT::DumpTraceInfoAsJson(Thread &thread, Stream &s, 355 bool verbose) { 356 Storage &storage = GetUpdatedStorage(); 357 358 lldb::tid_t tid = thread.GetID(); 359 json::OStream json_str(s.AsRawOstream(), 2); 360 if (!IsTraced(tid)) { 361 s << "error: thread not traced\n"; 362 return; 363 } 364 365 Expected<std::optional<uint64_t>> raw_size_or_error = GetRawTraceSize(thread); 366 if (!raw_size_or_error) { 367 s << "error: " << toString(raw_size_or_error.takeError()) << "\n"; 368 return; 369 } 370 371 Expected<DecodedThreadSP> decoded_thread_sp_or_err = Decode(thread); 372 if (!decoded_thread_sp_or_err) { 373 s << "error: " << toString(decoded_thread_sp_or_err.takeError()) << "\n"; 374 return; 375 } 376 DecodedThreadSP &decoded_thread_sp = *decoded_thread_sp_or_err; 377 378 json_str.object([&] { 379 json_str.attribute("traceTechnology", "intel-pt"); 380 json_str.attributeObject("threadStats", [&] { 381 json_str.attribute("tid", tid); 382 383 uint64_t insn_len = decoded_thread_sp->GetItemsCount(); 384 json_str.attribute("traceItemsCount", insn_len); 385 386 // Instruction stats 387 uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage(); 388 json_str.attributeObject("memoryUsage", [&] { 389 json_str.attribute("totalInBytes", std::to_string(mem_used)); 390 std::optional<double> avg; 391 if (insn_len != 0) 392 avg = double(mem_used) / insn_len; 393 json_str.attribute("avgPerItemInBytes", avg); 394 }); 395 396 // Timing 397 json_str.attributeObject("timingInSeconds", [&] { 398 GetTimer().ForThread(tid).ForEachTimedTask( 399 [&](const std::string &name, std::chrono::milliseconds duration) { 400 json_str.attribute(name, duration.count() / 1000.0); 401 }); 402 }); 403 404 // Instruction events stats 405 const DecodedThread::EventsStats &events_stats = 406 decoded_thread_sp->GetEventsStats(); 407 json_str.attributeObject("events", [&] { 408 json_str.attribute("totalCount", events_stats.total_count); 409 json_str.attributeObject("individualCounts", [&] { 410 for (const auto &event_to_count : events_stats.events_counts) { 411 json_str.attribute( 412 TraceCursor::EventKindToString(event_to_count.first), 413 event_to_count.second); 414 } 415 }); 416 }); 417 // Trace error stats 418 const DecodedThread::ErrorStats &error_stats = 419 decoded_thread_sp->GetErrorStats(); 420 json_str.attributeObject("errors", [&] { 421 json_str.attribute("totalCount", error_stats.GetTotalCount()); 422 json_str.attributeObject("libiptErrors", [&] { 423 for (const auto &[kind, count] : error_stats.libipt_errors) { 424 json_str.attribute(kind, count); 425 } 426 }); 427 json_str.attribute("fatalErrors", error_stats.fatal_errors); 428 json_str.attribute("otherErrors", error_stats.other_errors); 429 }); 430 431 if (storage.multicpu_decoder) { 432 json_str.attribute( 433 "continuousExecutions", 434 storage.multicpu_decoder->GetNumContinuousExecutionsForThread(tid)); 435 json_str.attribute( 436 "PSBBlocks", 437 storage.multicpu_decoder->GePSBBlocksCountForThread(tid)); 438 } 439 }); 440 441 json_str.attributeObject("globalStats", [&] { 442 json_str.attributeObject("timingInSeconds", [&] { 443 GetTimer().ForGlobal().ForEachTimedTask( 444 [&](const std::string &name, std::chrono::milliseconds duration) { 445 json_str.attribute(name, duration.count() / 1000.0); 446 }); 447 }); 448 if (storage.multicpu_decoder) { 449 json_str.attribute( 450 "totalUnattributedPSBBlocks", 451 storage.multicpu_decoder->GetUnattributedPSBBlocksCount()); 452 json_str.attribute( 453 "totalCountinuosExecutions", 454 storage.multicpu_decoder->GetTotalContinuousExecutionsCount()); 455 json_str.attribute("totalPSBBlocks", 456 storage.multicpu_decoder->GetTotalPSBBlocksCount()); 457 json_str.attribute( 458 "totalContinuousExecutions", 459 storage.multicpu_decoder->GetTotalContinuousExecutionsCount()); 460 } 461 }); 462 }); 463 } 464 465 llvm::Expected<std::optional<uint64_t>> 466 TraceIntelPT::GetRawTraceSize(Thread &thread) { 467 if (GetUpdatedStorage().multicpu_decoder) 468 return std::nullopt; // TODO: calculate the amount of intel pt raw trace associated 469 // with the given thread. 470 if (GetLiveProcess()) 471 return GetLiveThreadBinaryDataSize(thread.GetID(), 472 IntelPTDataKinds::kIptTrace); 473 uint64_t size; 474 auto callback = [&](llvm::ArrayRef<uint8_t> data) { 475 size = data.size(); 476 return Error::success(); 477 }; 478 if (Error err = OnThreadBufferRead(thread.GetID(), callback)) 479 return std::move(err); 480 481 return size; 482 } 483 484 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() { 485 Expected<std::vector<uint8_t>> cpu_info = 486 GetLiveProcessBinaryData(IntelPTDataKinds::kProcFsCpuInfo); 487 if (!cpu_info) 488 return cpu_info.takeError(); 489 490 int64_t cpu_family = -1; 491 int64_t model = -1; 492 int64_t stepping = -1; 493 std::string vendor_id; 494 495 StringRef rest(reinterpret_cast<const char *>(cpu_info->data()), 496 cpu_info->size()); 497 while (!rest.empty()) { 498 StringRef line; 499 std::tie(line, rest) = rest.split('\n'); 500 501 SmallVector<StringRef, 2> columns; 502 line.split(columns, StringRef(":"), -1, false); 503 504 if (columns.size() < 2) 505 continue; // continue searching 506 507 columns[1] = columns[1].trim(" "); 508 if (columns[0].contains("cpu family") && 509 columns[1].getAsInteger(10, cpu_family)) 510 continue; 511 512 else if (columns[0].contains("model") && columns[1].getAsInteger(10, model)) 513 continue; 514 515 else if (columns[0].contains("stepping") && 516 columns[1].getAsInteger(10, stepping)) 517 continue; 518 519 else if (columns[0].contains("vendor_id")) { 520 vendor_id = columns[1].str(); 521 if (!vendor_id.empty()) 522 continue; 523 } 524 525 if ((cpu_family != -1) && (model != -1) && (stepping != -1) && 526 (!vendor_id.empty())) { 527 return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown, 528 static_cast<uint16_t>(cpu_family), 529 static_cast<uint8_t>(model), 530 static_cast<uint8_t>(stepping)}; 531 } 532 } 533 return createStringError(inconvertibleErrorCode(), 534 "Failed parsing the target's /proc/cpuinfo file"); 535 } 536 537 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() { 538 if (!m_cpu_info) { 539 if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess()) 540 m_cpu_info = *cpu_info; 541 else 542 return cpu_info.takeError(); 543 } 544 return *m_cpu_info; 545 } 546 547 std::optional<LinuxPerfZeroTscConversion> 548 TraceIntelPT::GetPerfZeroTscConversion() { 549 return GetUpdatedStorage().tsc_conversion; 550 } 551 552 TraceIntelPT::Storage &TraceIntelPT::GetUpdatedStorage() { 553 RefreshLiveProcessState(); 554 return m_storage; 555 } 556 557 Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state, 558 StringRef json_response) { 559 m_storage = Storage(); 560 561 Expected<TraceIntelPTGetStateResponse> intelpt_state = 562 json::parse<TraceIntelPTGetStateResponse>(json_response, 563 "TraceIntelPTGetStateResponse"); 564 if (!intelpt_state) 565 return intelpt_state.takeError(); 566 567 m_storage.tsc_conversion = intelpt_state->tsc_perf_zero_conversion; 568 569 if (!intelpt_state->cpus) { 570 for (const TraceThreadState &thread_state : state.traced_threads) { 571 ThreadSP thread_sp = 572 GetLiveProcess()->GetThreadList().FindThreadByID(thread_state.tid); 573 m_storage.thread_decoders.try_emplace( 574 thread_state.tid, std::make_unique<ThreadDecoder>(thread_sp, *this)); 575 } 576 } else { 577 std::vector<cpu_id_t> cpus; 578 for (const TraceCpuState &cpu : *intelpt_state->cpus) 579 cpus.push_back(cpu.id); 580 581 std::vector<tid_t> tids; 582 for (const TraceThreadState &thread : intelpt_state->traced_threads) 583 tids.push_back(thread.tid); 584 585 if (!intelpt_state->tsc_perf_zero_conversion) 586 return createStringError(inconvertibleErrorCode(), 587 "Missing perf time_zero conversion values"); 588 m_storage.multicpu_decoder.emplace(GetSharedPtr()); 589 } 590 591 if (m_storage.tsc_conversion) { 592 Log *log = GetLog(LLDBLog::Target); 593 LLDB_LOG(log, "TraceIntelPT found TSC conversion information"); 594 } 595 return Error::success(); 596 } 597 598 bool TraceIntelPT::IsTraced(lldb::tid_t tid) { 599 Storage &storage = GetUpdatedStorage(); 600 if (storage.multicpu_decoder) 601 return storage.multicpu_decoder->TracesThread(tid); 602 return storage.thread_decoders.count(tid); 603 } 604 605 // The information here should match the description of the intel-pt section 606 // of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt 607 // documentation file. Similarly, it should match the CLI help messages of the 608 // TraceIntelPTOptions.td file. 609 const char *TraceIntelPT::GetStartConfigurationHelp() { 610 static std::optional<std::string> message; 611 if (!message) { 612 message.emplace(formatv(R"(Parameters: 613 614 See the jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt for a 615 description of each parameter below. 616 617 - int iptTraceSize (defaults to {0} bytes): 618 [process and thread tracing] 619 620 - boolean enableTsc (default to {1}): 621 [process and thread tracing] 622 623 - int psbPeriod (defaults to {2}): 624 [process and thread tracing] 625 626 - boolean perCpuTracing (default to {3}): 627 [process tracing only] 628 629 - int processBufferSizeLimit (defaults to {4} MiB): 630 [process tracing only] 631 632 - boolean disableCgroupFiltering (default to {5}): 633 [process tracing only])", 634 kDefaultIptTraceSize, kDefaultEnableTscValue, 635 kDefaultPsbPeriod, kDefaultPerCpuTracing, 636 kDefaultProcessBufferSizeLimit / 1024 / 1024, 637 kDefaultDisableCgroupFiltering)); 638 } 639 return message->c_str(); 640 } 641 642 Error TraceIntelPT::Start(uint64_t ipt_trace_size, 643 uint64_t total_buffer_size_limit, bool enable_tsc, 644 std::optional<uint64_t> psb_period, 645 bool per_cpu_tracing, bool disable_cgroup_filtering) { 646 TraceIntelPTStartRequest request; 647 request.ipt_trace_size = ipt_trace_size; 648 request.process_buffer_size_limit = total_buffer_size_limit; 649 request.enable_tsc = enable_tsc; 650 request.psb_period = psb_period; 651 request.type = GetPluginName().str(); 652 request.per_cpu_tracing = per_cpu_tracing; 653 request.disable_cgroup_filtering = disable_cgroup_filtering; 654 return Trace::Start(toJSON(request)); 655 } 656 657 Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) { 658 uint64_t ipt_trace_size = kDefaultIptTraceSize; 659 uint64_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit; 660 bool enable_tsc = kDefaultEnableTscValue; 661 std::optional<uint64_t> psb_period = kDefaultPsbPeriod; 662 bool per_cpu_tracing = kDefaultPerCpuTracing; 663 bool disable_cgroup_filtering = kDefaultDisableCgroupFiltering; 664 665 if (configuration) { 666 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { 667 dict->GetValueForKeyAsInteger("iptTraceSize", ipt_trace_size); 668 dict->GetValueForKeyAsInteger("processBufferSizeLimit", 669 process_buffer_size_limit); 670 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); 671 dict->GetValueForKeyAsInteger("psbPeriod", psb_period); 672 dict->GetValueForKeyAsBoolean("perCpuTracing", per_cpu_tracing); 673 dict->GetValueForKeyAsBoolean("disableCgroupFiltering", 674 disable_cgroup_filtering); 675 } else { 676 return createStringError(inconvertibleErrorCode(), 677 "configuration object is not a dictionary"); 678 } 679 } 680 681 return Start(ipt_trace_size, process_buffer_size_limit, enable_tsc, 682 psb_period, per_cpu_tracing, disable_cgroup_filtering); 683 } 684 685 llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, 686 uint64_t ipt_trace_size, bool enable_tsc, 687 std::optional<uint64_t> psb_period) { 688 TraceIntelPTStartRequest request; 689 request.ipt_trace_size = ipt_trace_size; 690 request.enable_tsc = enable_tsc; 691 request.psb_period = psb_period; 692 request.type = GetPluginName().str(); 693 request.tids.emplace(); 694 for (lldb::tid_t tid : tids) 695 request.tids->push_back(tid); 696 return Trace::Start(toJSON(request)); 697 } 698 699 Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, 700 StructuredData::ObjectSP configuration) { 701 uint64_t ipt_trace_size = kDefaultIptTraceSize; 702 bool enable_tsc = kDefaultEnableTscValue; 703 std::optional<uint64_t> psb_period = kDefaultPsbPeriod; 704 705 if (configuration) { 706 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { 707 llvm::StringRef ipt_trace_size_not_parsed; 708 if (dict->GetValueForKeyAsString("iptTraceSize", 709 ipt_trace_size_not_parsed)) { 710 if (std::optional<uint64_t> bytes = 711 ParsingUtils::ParseUserFriendlySizeExpression( 712 ipt_trace_size_not_parsed)) 713 ipt_trace_size = *bytes; 714 else 715 return createStringError(inconvertibleErrorCode(), 716 "iptTraceSize is wrong bytes expression"); 717 } else { 718 dict->GetValueForKeyAsInteger("iptTraceSize", ipt_trace_size); 719 } 720 721 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); 722 dict->GetValueForKeyAsInteger("psbPeriod", psb_period); 723 } else { 724 return createStringError(inconvertibleErrorCode(), 725 "configuration object is not a dictionary"); 726 } 727 } 728 729 return Start(tids, ipt_trace_size, enable_tsc, psb_period); 730 } 731 732 Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid, 733 OnBinaryDataReadCallback callback) { 734 return OnThreadBinaryDataRead(tid, IntelPTDataKinds::kIptTrace, callback); 735 } 736 737 TaskTimer &TraceIntelPT::GetTimer() { return GetUpdatedStorage().task_timer; } 738 739 ScopedTaskTimer &TraceIntelPT::GetThreadTimer(lldb::tid_t tid) { 740 return GetTimer().ForThread(tid); 741 } 742 743 ScopedTaskTimer &TraceIntelPT::GetGlobalTimer() { 744 return GetTimer().ForGlobal(); 745 } 746