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