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