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