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