xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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