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