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
24using namespace lldb;
25using namespace lldb_private;
26using namespace lldb_private::trace_intel_pt;
27using namespace llvm;
28
29LLDB_PLUGIN_DEFINE(TraceIntelPT)
30
31lldb::CommandObjectSP
32TraceIntelPT::GetProcessTraceStartCommand(CommandInterpreter &interpreter) {
33 return CommandObjectSP(
34 new CommandObjectProcessTraceStartIntelPT(*this, interpreter));
35}
36
37lldb::CommandObjectSP
38TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) {
39 return CommandObjectSP(
40 new CommandObjectThreadTraceStartIntelPT(*this, interpreter));
41}
42
43#define LLDB_PROPERTIES_traceintelpt
44#include "TraceIntelPTProperties.inc"
45
46enum {
47#define LLDB_PROPERTIES_traceintelpt
48#include "TraceIntelPTPropertiesEnum.inc"
49};
50
51llvm::StringRef TraceIntelPT::PluginProperties::GetSettingName() {
52 return TraceIntelPT::GetPluginNameStatic();
53}
54
55TraceIntelPT::PluginProperties::PluginProperties() : Properties() {
56 m_collection_sp = std::make_shared<OptionValueProperties>(args: GetSettingName());
57 m_collection_sp->Initialize(setting_definitions: g_traceintelpt_properties);
58}
59
60uint64_t
61TraceIntelPT::PluginProperties::GetInfiniteDecodingLoopVerificationThreshold() {
62 const uint32_t idx = ePropertyInfiniteDecodingLoopVerificationThreshold;
63 return GetPropertyAtIndexAs<uint64_t>(
64 idx, g_traceintelpt_properties[idx].default_uint_value);
65}
66
67uint64_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
73TraceIntelPT::PluginProperties &TraceIntelPT::GetGlobalProperties() {
74 static TraceIntelPT::PluginProperties g_settings;
75 return g_settings;
76}
77
78void TraceIntelPT::Initialize() {
79 PluginManager::RegisterPlugin(
80 name: GetPluginNameStatic(), description: "Intel Processor Trace",
81 create_callback_from_bundle: CreateInstanceForTraceBundle, create_callback_for_live_process: CreateInstanceForLiveProcess,
82 schema: TraceIntelPTBundleLoader::GetSchema(), debugger_init_callback: DebuggerInitialize);
83}
84
85void TraceIntelPT::DebuggerInitialize(Debugger &debugger) {
86 if (!PluginManager::GetSettingForProcessPlugin(
87 debugger, setting_name: PluginProperties::GetSettingName())) {
88 const bool is_global_setting = true;
89 PluginManager::CreateSettingForTracePlugin(
90 debugger, properties_sp: GetGlobalProperties().GetValueProperties(),
91 description: "Properties for the intel-pt trace plug-in.", is_global_property: is_global_setting);
92 }
93}
94
95void TraceIntelPT::Terminate() {
96 PluginManager::UnregisterPlugin(create_callback: CreateInstanceForTraceBundle);
97}
98
99StringRef TraceIntelPT::GetSchema() {
100 return TraceIntelPTBundleLoader::GetSchema();
101}
102
103void TraceIntelPT::Dump(Stream *s) const {}
104
105Expected<FileSpec> TraceIntelPT::SaveToDisk(FileSpec directory, bool compact) {
106 RefreshLiveProcessState();
107 return TraceIntelPTBundleSaver().SaveToDisk(trace_ipt&: *this, directory, compact);
108}
109
110Expected<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
117Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) {
118 TraceSP instance(new TraceIntelPT(process));
119 process.GetTarget().SetTrace(instance);
120 return instance;
121}
122
123TraceIntelPTSP TraceIntelPT::GetSharedPtr() {
124 return std::static_pointer_cast<TraceIntelPT>(shared_from_this());
125}
126
127TraceIntelPT::TraceMode TraceIntelPT::GetTraceMode() { return trace_mode; }
128
129TraceIntelPTSP 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(x: cpu.id);
149 }
150
151 if (trace_mode == TraceMode::UserMode) {
152 trace_sp->m_storage.multicpu_decoder.emplace(args&: 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 Key: thread->GetID(), Args: std::make_unique<ThreadDecoder>(args: thread, args&: *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
172TraceIntelPT::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
178Expected<DecodedThreadSP> TraceIntelPT::Decode(Thread &thread) {
179 if (const char *error = RefreshLiveProcessState())
180 return createStringError(EC: inconvertibleErrorCode(), Msg: 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(Val: thread.GetID());
187 if (it == storage.thread_decoders.end())
188 return createStringError(EC: inconvertibleErrorCode(), Msg: "thread not traced");
189 return it->second->Decode();
190}
191
192Expected<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(tsc: *lowest_tsc);
224 }
225 return storage.beginning_of_time_nanos;
226}
227
228llvm::Expected<lldb::TraceCursorSP>
229TraceIntelPT::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 args: thread.shared_from_this(), args&: *decoded_thread, args&: m_storage.tsc_conversion,
235 args&: *beginning_of_time);
236 else
237 return beginning_of_time.takeError();
238 } else
239 return decoded_thread.takeError();
240}
241
242void 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(format: "\nthread #{0}: tid = {1}", args: thread.GetIndexID(), args: 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(E: 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(format: " {0}\n", args: toString(E: raw_size_or_error.takeError()));
270 return;
271 }
272 std::optional<uint64_t> raw_size = *raw_size_or_error;
273
274 s.Format(format: "\n Trace technology: {0}\n", args: 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(format: "\n Total number of trace items: {0}\n", args&: items_count);
282
283 s << "\n Memory usage:\n";
284 if (raw_size)
285 s.Format(format: " Raw trace size: {0} KiB\n", args: *raw_size / 1024);
286
287 s.Format(
288 format: " Total approximate memory usage (excluding raw trace): {0:2} KiB\n",
289 args: (double)mem_used / 1024);
290 if (items_count != 0)
291 s.Format(format: " Average memory usage per item (excluding raw trace): "
292 "{0:2} bytes\n",
293 args: (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(format: " {0}: {1:2}s\n", args: name, args: duration.count() / 1000.0);
302 };
303 GetThreadTimer(tid).ForEachTimedTask(callback: print_duration);
304
305 s << "\n Timing for global tasks:\n";
306 GetGlobalTimer().ForEachTimedTask(callback: 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(format: " Number of individual events: {0}\n",
315 args: events_stats.total_count);
316 for (const auto &event_to_count : events_stats.events_counts) {
317 s.Format(format: " {0}: {1}\n",
318 args: TraceCursor::EventKindToString(event_kind: event_to_count.first),
319 args: 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(format: " Number of individual errors: {0}\n",
328 args: error_stats.GetTotalCount());
329 s.Format(format: " Number of fatal errors: {0}\n", args: error_stats.fatal_errors);
330 for (const auto &[kind, count] : error_stats.libipt_errors) {
331 s.Format(format: " Number of libipt errors of kind [{0}]: {1}\n", args: kind,
332 args: count);
333 }
334 s.Format(format: " Number of other errors: {0}\n", args: error_stats.other_errors);
335 }
336
337 if (storage.multicpu_decoder) {
338 s << "\n Multi-cpu decoding:\n";
339 s.Format(format: " Total number of continuous executions found: {0}\n",
340 args: storage.multicpu_decoder->GetTotalContinuousExecutionsCount());
341 s.Format(
342 format: " Number of continuous executions for this thread: {0}\n",
343 args: storage.multicpu_decoder->GetNumContinuousExecutionsForThread(tid));
344 s.Format(format: " Total number of PSB blocks found: {0}\n",
345 args: storage.multicpu_decoder->GetTotalPSBBlocksCount());
346 s.Format(format: " Number of PSB blocks for this thread: {0}\n",
347 args: storage.multicpu_decoder->GePSBBlocksCountForThread(tid));
348 s.Format(format: " Total number of unattributed PSB blocks found: {0}\n",
349 args: storage.multicpu_decoder->GetUnattributedPSBBlocksCount());
350 }
351}
352
353void 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(E: 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(E: 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(Contents: [&] {
378 json_str.attribute(Key: "traceTechnology", Contents: "intel-pt");
379 json_str.attributeObject(Key: "threadStats", Contents: [&] {
380 json_str.attribute(Key: "tid", Contents: tid);
381
382 uint64_t insn_len = decoded_thread_sp->GetItemsCount();
383 json_str.attribute(Key: "traceItemsCount", Contents: insn_len);
384
385 // Instruction stats
386 uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage();
387 json_str.attributeObject(Key: "memoryUsage", Contents: [&] {
388 json_str.attribute(Key: "totalInBytes", Contents: std::to_string(val: mem_used));
389 std::optional<double> avg;
390 if (insn_len != 0)
391 avg = double(mem_used) / insn_len;
392 json_str.attribute(Key: "avgPerItemInBytes", Contents: avg);
393 });
394
395 // Timing
396 json_str.attributeObject(Key: "timingInSeconds", Contents: [&] {
397 GetTimer().ForThread(tid).ForEachTimedTask(
398 callback: [&](const std::string &name, std::chrono::milliseconds duration) {
399 json_str.attribute(Key: name, Contents: 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(Key: "events", Contents: [&] {
407 json_str.attribute(Key: "totalCount", Contents: events_stats.total_count);
408 json_str.attributeObject(Key: "individualCounts", Contents: [&] {
409 for (const auto &event_to_count : events_stats.events_counts) {
410 json_str.attribute(
411 Key: TraceCursor::EventKindToString(event_kind: event_to_count.first),
412 Contents: 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(Key: "errors", Contents: [&] {
420 json_str.attribute(Key: "totalCount", Contents: error_stats.GetTotalCount());
421 json_str.attributeObject(Key: "libiptErrors", Contents: [&] {
422 for (const auto &[kind, count] : error_stats.libipt_errors) {
423 json_str.attribute(Key: kind, Contents: count);
424 }
425 });
426 json_str.attribute(Key: "fatalErrors", Contents: error_stats.fatal_errors);
427 json_str.attribute(Key: "otherErrors", Contents: error_stats.other_errors);
428 });
429
430 if (storage.multicpu_decoder) {
431 json_str.attribute(
432 Key: "continuousExecutions",
433 Contents: storage.multicpu_decoder->GetNumContinuousExecutionsForThread(tid));
434 json_str.attribute(
435 Key: "PSBBlocks",
436 Contents: storage.multicpu_decoder->GePSBBlocksCountForThread(tid));
437 }
438 });
439
440 json_str.attributeObject(Key: "globalStats", Contents: [&] {
441 json_str.attributeObject(Key: "timingInSeconds", Contents: [&] {
442 GetTimer().ForGlobal().ForEachTimedTask(
443 callback: [&](const std::string &name, std::chrono::milliseconds duration) {
444 json_str.attribute(Key: name, Contents: duration.count() / 1000.0);
445 });
446 });
447 if (storage.multicpu_decoder) {
448 json_str.attribute(
449 Key: "totalUnattributedPSBBlocks",
450 Contents: storage.multicpu_decoder->GetUnattributedPSBBlocksCount());
451 json_str.attribute(
452 Key: "totalCountinuosExecutions",
453 Contents: storage.multicpu_decoder->GetTotalContinuousExecutionsCount());
454 json_str.attribute(Key: "totalPSBBlocks",
455 Contents: storage.multicpu_decoder->GetTotalPSBBlocksCount());
456 json_str.attribute(
457 Key: "totalContinuousExecutions",
458 Contents: storage.multicpu_decoder->GetTotalContinuousExecutionsCount());
459 }
460 });
461 });
462}
463
464llvm::Expected<std::optional<uint64_t>>
465TraceIntelPT::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(tid: thread.GetID(), callback))
478 return std::move(err);
479
480 return size;
481}
482
483Expected<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(args&: line, args&: rest) = rest.split(Separator: '\n');
499
500 SmallVector<StringRef, 2> columns;
501 line.split(A&: columns, Separator: StringRef(":"), MaxSplit: -1, KeepEmpty: false);
502
503 if (columns.size() < 2)
504 continue; // continue searching
505
506 columns[1] = columns[1].trim(Chars: " ");
507 if (columns[0].contains(Other: "cpu family") &&
508 columns[1].getAsInteger(Radix: 10, Result&: cpu_family))
509 continue;
510
511 else if (columns[0].contains(Other: "model") && columns[1].getAsInteger(Radix: 10, Result&: model))
512 continue;
513
514 else if (columns[0].contains(Other: "stepping") &&
515 columns[1].getAsInteger(Radix: 10, Result&: stepping))
516 continue;
517
518 else if (columns[0].contains(Other: "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(EC: inconvertibleErrorCode(),
533 Msg: "Failed parsing the target's /proc/cpuinfo file");
534}
535
536Expected<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
546std::optional<LinuxPerfZeroTscConversion>
547TraceIntelPT::GetPerfZeroTscConversion() {
548 return GetUpdatedStorage().tsc_conversion;
549}
550
551TraceIntelPT::Storage &TraceIntelPT::GetUpdatedStorage() {
552 RefreshLiveProcessState();
553 return m_storage;
554}
555
556Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state,
557 StringRef json_response) {
558 m_storage = Storage();
559
560 Expected<TraceIntelPTGetStateResponse> intelpt_state =
561 json::parse<TraceIntelPTGetStateResponse>(JSON: json_response,
562 RootName: "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 Key: thread_state.tid, Args: std::make_unique<ThreadDecoder>(args&: thread_sp, args&: *this));
574 }
575 } else {
576 std::vector<cpu_id_t> cpus;
577 for (const TraceCpuState &cpu : *intelpt_state->cpus)
578 cpus.push_back(x: cpu.id);
579
580 std::vector<tid_t> tids;
581 for (const TraceThreadState &thread : intelpt_state->traced_threads)
582 tids.push_back(x: thread.tid);
583
584 if (!intelpt_state->tsc_perf_zero_conversion)
585 return createStringError(EC: inconvertibleErrorCode(),
586 Msg: "Missing perf time_zero conversion values");
587 m_storage.multicpu_decoder.emplace(args: GetSharedPtr());
588 }
589
590 if (m_storage.tsc_conversion) {
591 Log *log = GetLog(mask: LLDBLog::Target);
592 LLDB_LOG(log, "TraceIntelPT found TSC conversion information");
593 }
594 return Error::success();
595}
596
597bool 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(Val: 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.
608const char *TraceIntelPT::GetStartConfigurationHelp() {
609 static std::optional<std::string> message;
610 if (!message) {
611 message.emplace(args: formatv(Fmt: 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 Vals: kDefaultIptTraceSize, Vals: kDefaultEnableTscValue,
634 Vals: kDefaultPsbPeriod, Vals: kDefaultPerCpuTracing,
635 Vals: kDefaultProcessBufferSizeLimit / 1024 / 1024,
636 Vals: kDefaultDisableCgroupFiltering));
637 }
638 return message->c_str();
639}
640
641Error 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(packet: request));
654}
655
656Error 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(key: "iptTraceSize", result&: ipt_trace_size);
667 dict->GetValueForKeyAsInteger(key: "processBufferSizeLimit",
668 result&: process_buffer_size_limit);
669 dict->GetValueForKeyAsBoolean(key: "enableTsc", result&: enable_tsc);
670 dict->GetValueForKeyAsInteger(key: "psbPeriod", result&: psb_period);
671 dict->GetValueForKeyAsBoolean(key: "perCpuTracing", result&: per_cpu_tracing);
672 dict->GetValueForKeyAsBoolean(key: "disableCgroupFiltering",
673 result&: disable_cgroup_filtering);
674 } else {
675 return createStringError(EC: inconvertibleErrorCode(),
676 Msg: "configuration object is not a dictionary");
677 }
678 }
679
680 return Start(ipt_trace_size, total_buffer_size_limit: process_buffer_size_limit, enable_tsc,
681 psb_period, per_cpu_tracing, disable_cgroup_filtering);
682}
683
684llvm::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(x: tid);
695 return Trace::Start(toJSON(packet: request));
696}
697
698Error 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(key: "iptTraceSize",
708 result&: ipt_trace_size_not_parsed)) {
709 if (std::optional<uint64_t> bytes =
710 ParsingUtils::ParseUserFriendlySizeExpression(
711 size_expression: ipt_trace_size_not_parsed))
712 ipt_trace_size = *bytes;
713 else
714 return createStringError(EC: inconvertibleErrorCode(),
715 Msg: "iptTraceSize is wrong bytes expression");
716 } else {
717 dict->GetValueForKeyAsInteger(key: "iptTraceSize", result&: ipt_trace_size);
718 }
719
720 dict->GetValueForKeyAsBoolean(key: "enableTsc", result&: enable_tsc);
721 dict->GetValueForKeyAsInteger(key: "psbPeriod", result&: psb_period);
722 } else {
723 return createStringError(EC: inconvertibleErrorCode(),
724 Msg: "configuration object is not a dictionary");
725 }
726 }
727
728 return Start(tids, ipt_trace_size, enable_tsc, psb_period);
729}
730
731Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid,
732 OnBinaryDataReadCallback callback) {
733 return OnThreadBinaryDataRead(tid, IntelPTDataKinds::kIptTrace, callback);
734}
735
736TaskTimer &TraceIntelPT::GetTimer() { return GetUpdatedStorage().task_timer; }
737
738ScopedTaskTimer &TraceIntelPT::GetThreadTimer(lldb::tid_t tid) {
739 return GetTimer().ForThread(tid);
740}
741
742ScopedTaskTimer &TraceIntelPT::GetGlobalTimer() {
743 return GetTimer().ForGlobal();
744}
745

source code of lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp