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