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
LLDB_PLUGIN_DEFINE(TraceIntelPT)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
GetThreadTraceStartCommand(CommandInterpreter & interpreter)38 TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) {
39 return CommandObjectSP(
40 new CommandObjectThreadTraceStartIntelPT(*this, interpreter));
41 }
42
Initialize()43 void TraceIntelPT::Initialize() {
44 PluginManager::RegisterPlugin(GetPluginNameStatic(), "Intel Processor Trace",
45 CreateInstanceForTraceBundle,
46 CreateInstanceForLiveProcess,
47 TraceIntelPTBundleLoader::GetSchema());
48 }
49
Terminate()50 void TraceIntelPT::Terminate() {
51 PluginManager::UnregisterPlugin(CreateInstanceForTraceBundle);
52 }
53
GetSchema()54 StringRef TraceIntelPT::GetSchema() {
55 return TraceIntelPTBundleLoader::GetSchema();
56 }
57
Dump(Stream * s) const58 void TraceIntelPT::Dump(Stream *s) const {}
59
SaveToDisk(FileSpec directory,bool compact)60 Expected<FileSpec> TraceIntelPT::SaveToDisk(FileSpec directory, bool compact) {
61 RefreshLiveProcessState();
62 return TraceIntelPTBundleSaver().SaveToDisk(*this, directory, compact);
63 }
64
CreateInstanceForTraceBundle(const json::Value & bundle_description,StringRef bundle_dir,Debugger & debugger)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
CreateInstanceForLiveProcess(Process & process)73 Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) {
74 TraceSP instance(new TraceIntelPT(process));
75 process.GetTarget().SetTrace(instance);
76 return instance;
77 }
78
GetSharedPtr()79 TraceIntelPTSP TraceIntelPT::GetSharedPtr() {
80 return std::static_pointer_cast<TraceIntelPT>(shared_from_this());
81 }
82
CreateInstanceForPostmortemTrace(JSONTraceBundleDescription & bundle_description,ArrayRef<ProcessSP> traced_processes,ArrayRef<ThreadPostMortemTraceSP> traced_threads)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
TraceIntelPT(JSONTraceBundleDescription & bundle_description,ArrayRef<ProcessSP> traced_processes)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
Decode(Thread & thread)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
FindBeginningOfTimeNanos()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>
CreateNewCursor(Thread & thread)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
DumpTraceInfo(Thread & thread,Stream & s,bool verbose,bool json)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
DumpTraceInfoAsJson(Thread & thread,Stream & s,bool verbose)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>>
GetRawTraceSize(Thread & thread)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
GetCPUInfoForLiveProcess()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
GetCPUInfo()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>
GetPerfZeroTscConversion()498 TraceIntelPT::GetPerfZeroTscConversion() {
499 return GetUpdatedStorage().tsc_conversion;
500 }
501
GetUpdatedStorage()502 TraceIntelPT::Storage &TraceIntelPT::GetUpdatedStorage() {
503 RefreshLiveProcessState();
504 return m_storage;
505 }
506
DoRefreshLiveProcessState(TraceGetStateResponse state,StringRef json_response)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
IsTraced(lldb::tid_t tid)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.
GetStartConfigurationHelp()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
Start(uint64_t ipt_trace_size,uint64_t total_buffer_size_limit,bool enable_tsc,Optional<uint64_t> psb_period,bool per_cpu_tracing,bool disable_cgroup_filtering)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
Start(StructuredData::ObjectSP configuration)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
Start(llvm::ArrayRef<lldb::tid_t> tids,uint64_t ipt_trace_size,bool enable_tsc,Optional<uint64_t> psb_period)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
Start(llvm::ArrayRef<lldb::tid_t> tids,StructuredData::ObjectSP configuration)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
OnThreadBufferRead(lldb::tid_t tid,OnBinaryDataReadCallback callback)682 Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid,
683 OnBinaryDataReadCallback callback) {
684 return OnThreadBinaryDataRead(tid, IntelPTDataKinds::kIptTrace, callback);
685 }
686
GetTimer()687 TaskTimer &TraceIntelPT::GetTimer() { return GetUpdatedStorage().task_timer; }
688
GetThreadTimer(lldb::tid_t tid)689 ScopedTaskTimer &TraceIntelPT::GetThreadTimer(lldb::tid_t tid) {
690 return GetTimer().ForThread(tid);
691 }
692
GetGlobalTimer()693 ScopedTaskTimer &TraceIntelPT::GetGlobalTimer() {
694 return GetTimer().ForGlobal();
695 }
696