11#include "../common/ThreadPostMortemTrace.h"
43#define LLDB_PROPERTIES_traceintelpt
44#include "TraceIntelPTProperties.inc"
47#define LLDB_PROPERTIES_traceintelpt
48#include "TraceIntelPTPropertiesEnum.inc"
62 const uint32_t idx = ePropertyInfiniteDecodingLoopVerificationThreshold;
63 return GetPropertyAtIndexAs<uint64_t>(
64 idx, g_traceintelpt_properties[idx].default_uint_value);
68 const uint32_t idx = ePropertyExtremelyLargeDecodingThreshold;
69 return GetPropertyAtIndexAs<uint64_t>(
70 idx, g_traceintelpt_properties[idx].default_uint_value);
88 const bool is_global_setting =
true;
91 "Properties for the intel-pt trace plug-in.", is_global_setting);
111 const json::Value &bundle_description, StringRef bundle_dir,
124 return std::static_pointer_cast<TraceIntelPT>(shared_from_this());
131 ArrayRef<ProcessSP> traced_processes,
135 trace_sp->m_storage.tsc_conversion =
138 if (bundle_description.
cpus) {
139 std::vector<cpu_id_t> cpus;
141 for (
const JSONCpu &cpu : *bundle_description.
cpus) {
145 trace_sp->SetPostMortemCpuDataFile(
148 cpus.push_back(cpu.
id);
152 trace_sp->m_storage.multicpu_decoder.emplace(trace_sp);
158 trace_sp->m_storage.thread_decoders.try_emplace(
159 thread->GetID(), std::make_unique<ThreadDecoder>(thread, *trace_sp));
160 if (
const std::optional<FileSpec> &trace_file = thread->GetTraceFile()) {
161 trace_sp->SetPostMortemThreadDataFile(
167 for (
const ProcessSP &process_sp : traced_processes)
168 process_sp->GetTarget().SetTrace(trace_sp);
173 ArrayRef<ProcessSP> traced_processes,
175 :
Trace(traced_processes, bundle_description.GetCpuIds()),
180 return createStringError(inconvertibleErrorCode(),
error);
188 return createStringError(inconvertibleErrorCode(),
"thread not traced");
189 return it->second->Decode();
201 std::optional<uint64_t> lowest_tsc;
204 if (Expected<std::optional<uint64_t>> tsc =
208 return tsc.takeError();
213 Expected<std::optional<uint64_t>> tsc = decoder.second->FindLowestTSC();
215 return tsc.takeError();
217 if (*tsc && (!lowest_tsc || *lowest_tsc > **tsc))
228llvm::Expected<lldb::TraceCursorSP>
230 if (Expected<DecodedThreadSP> decoded_thread =
Decode(thread)) {
231 if (Expected<std::optional<uint64_t>> beginning_of_time =
233 return std::make_shared<TraceCursorIntelPT>(
237 return beginning_of_time.takeError();
239 return decoded_thread.takeError();
254 s <<
", not traced\n";
259 Expected<DecodedThreadSP> decoded_thread_sp_or_err =
Decode(thread);
260 if (!decoded_thread_sp_or_err) {
261 s <<
toString(decoded_thread_sp_or_err.takeError()) <<
"\n";
267 Expected<std::optional<uint64_t>> raw_size_or_error =
GetRawTraceSize(thread);
268 if (!raw_size_or_error) {
272 std::optional<uint64_t> raw_size = *raw_size_or_error;
278 uint64_t items_count = decoded_thread_sp->GetItemsCount();
279 uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage();
281 s.
Format(
"\n Total number of trace items: {0}\n", items_count);
283 s <<
"\n Memory usage:\n";
285 s.
Format(
" Raw trace size: {0} KiB\n", *raw_size / 1024);
288 " Total approximate memory usage (excluding raw trace): {0:2} KiB\n",
289 (
double)mem_used / 1024);
290 if (items_count != 0)
291 s.
Format(
" Average memory usage per item (excluding raw trace): "
293 (
double)mem_used / items_count);
298 s <<
"\n Timing for this thread:\n";
299 auto print_duration = [&](
const std::string &name,
300 std::chrono::milliseconds duration) {
301 s.
Format(
" {0}: {1:2}s\n", name, duration.count() / 1000.0);
305 s <<
"\n Timing for global tasks:\n";
312 decoded_thread_sp->GetEventsStats();
314 s.
Format(
" Number of individual events: {0}\n",
316 for (
const auto &event_to_count : events_stats.
events_counts) {
319 event_to_count.second);
325 decoded_thread_sp->GetErrorStats();
327 s.
Format(
" Number of individual errors: {0}\n",
330 for (
const auto &[kind, count] : error_stats.
libipt_errors) {
331 s.
Format(
" Number of libipt errors of kind [{0}]: {1}\n", kind,
338 s <<
"\n Multi-cpu decoding:\n";
339 s.
Format(
" Total number of continuous executions found: {0}\n",
342 " Number of continuous executions for this thread: {0}\n",
344 s.
Format(
" Total number of PSB blocks found: {0}\n",
346 s.
Format(
" Number of PSB blocks for this thread: {0}\n",
348 s.
Format(
" Total number of unattributed PSB blocks found: {0}\n",
360 s <<
"error: thread not traced\n";
364 Expected<std::optional<uint64_t>> raw_size_or_error =
GetRawTraceSize(thread);
365 if (!raw_size_or_error) {
366 s <<
"error: " <<
toString(raw_size_or_error.takeError()) <<
"\n";
370 Expected<DecodedThreadSP> decoded_thread_sp_or_err =
Decode(thread);
371 if (!decoded_thread_sp_or_err) {
372 s <<
"error: " <<
toString(decoded_thread_sp_or_err.takeError()) <<
"\n";
377 json_str.object([&] {
378 json_str.attribute(
"traceTechnology",
"intel-pt");
379 json_str.attributeObject(
"threadStats", [&] {
380 json_str.attribute(
"tid", tid);
382 uint64_t insn_len = decoded_thread_sp->GetItemsCount();
383 json_str.attribute(
"traceItemsCount", insn_len);
386 uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage();
387 json_str.attributeObject(
"memoryUsage", [&] {
388 json_str.attribute(
"totalInBytes", std::to_string(mem_used));
389 std::optional<double> avg;
391 avg =
double(mem_used) / insn_len;
392 json_str.attribute(
"avgPerItemInBytes", avg);
396 json_str.attributeObject(
"timingInSeconds", [&] {
398 [&](
const std::string &name, std::chrono::milliseconds duration) {
399 json_str.attribute(name, duration.count() / 1000.0);
405 decoded_thread_sp->GetEventsStats();
406 json_str.attributeObject(
"events", [&] {
407 json_str.attribute(
"totalCount", events_stats.
total_count);
408 json_str.attributeObject(
"individualCounts", [&] {
409 for (
const auto &event_to_count : events_stats.
events_counts) {
411 TraceCursor::EventKindToString(event_to_count.first),
412 event_to_count.second);
418 decoded_thread_sp->GetErrorStats();
419 json_str.attributeObject(
"errors", [&] {
420 json_str.attribute(
"totalCount", error_stats.
GetTotalCount());
421 json_str.attributeObject(
"libiptErrors", [&] {
422 for (
const auto &[kind, count] : error_stats.
libipt_errors) {
423 json_str.attribute(kind, count);
426 json_str.attribute(
"fatalErrors", error_stats.
fatal_errors);
427 json_str.attribute(
"otherErrors", error_stats.
other_errors);
432 "continuousExecutions",
440 json_str.attributeObject(
"globalStats", [&] {
441 json_str.attributeObject(
"timingInSeconds", [&] {
443 [&](
const std::string &name, std::chrono::milliseconds duration) {
444 json_str.attribute(name, duration.count() / 1000.0);
449 "totalUnattributedPSBBlocks",
452 "totalCountinuosExecutions",
454 json_str.attribute(
"totalPSBBlocks",
457 "totalContinuousExecutions",
464llvm::Expected<std::optional<uint64_t>>
473 auto callback = [&](llvm::ArrayRef<uint8_t> data) {
475 return Error::success();
478 return std::move(err);
484 Expected<std::vector<uint8_t>> cpu_info =
487 return cpu_info.takeError();
489 int64_t cpu_family = -1;
491 int64_t stepping = -1;
492 std::string vendor_id;
494 StringRef rest(
reinterpret_cast<const char *
>(cpu_info->data()),
496 while (!rest.empty()) {
498 std::tie(line, rest) = rest.split(
'\n');
500 SmallVector<StringRef, 2> columns;
501 line.split(columns, StringRef(
":"), -1,
false);
503 if (columns.size() < 2)
506 columns[1] = columns[1].trim(
" ");
507 if (columns[0].contains(
"cpu family") &&
508 columns[1].getAsInteger(10, cpu_family))
511 else if (columns[0].contains(
"model") && columns[1].getAsInteger(10, model))
514 else if (columns[0].contains(
"stepping") &&
515 columns[1].getAsInteger(10, stepping))
518 else if (columns[0].contains(
"vendor_id")) {
519 vendor_id = columns[1].str();
520 if (!vendor_id.empty())
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)};
532 return createStringError(inconvertibleErrorCode(),
533 "Failed parsing the target's /proc/cpuinfo file");
541 return cpu_info.takeError();
546std::optional<LinuxPerfZeroTscConversion>
557 StringRef json_response) {
560 Expected<TraceIntelPTGetStateResponse> intelpt_state =
561 json::parse<TraceIntelPTGetStateResponse>(json_response,
562 "TraceIntelPTGetStateResponse");
564 return intelpt_state.takeError();
568 if (!intelpt_state->cpus) {
573 thread_state.
tid, std::make_unique<ThreadDecoder>(thread_sp, *
this));
576 std::vector<cpu_id_t> cpus;
578 cpus.push_back(cpu.
id);
580 std::vector<tid_t> tids;
582 tids.push_back(thread.
tid);
584 if (!intelpt_state->tsc_perf_zero_conversion)
585 return createStringError(inconvertibleErrorCode(),
586 "Missing perf time_zero conversion values");
592 LLDB_LOG(log,
"TraceIntelPT found TSC conversion information");
594 return Error::success();
609 static std::optional<std::string> message;
611 message.emplace(formatv(R
"(Parameters:
613 See the jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt for a
614 description of each parameter below.
616 - int iptTraceSize (defaults to {0} bytes):
617 [process and thread tracing]
619 - boolean enableTsc (default to {1}):
620 [process and thread tracing]
622 - int psbPeriod (defaults to {2}):
623 [process and thread tracing]
625 - boolean perCpuTracing (default to {3}):
626 [process tracing only]
628 - int processBufferSizeLimit (defaults to {4} MiB):
629 [process tracing only]
631 - boolean disableCgroupFiltering (default to {5}):
632 [process tracing only])",
638 return message->c_str();
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) {
666 dict->GetValueForKeyAsInteger(
"iptTraceSize", ipt_trace_size);
667 dict->GetValueForKeyAsInteger(
"processBufferSizeLimit",
668 process_buffer_size_limit);
669 dict->GetValueForKeyAsBoolean(
"enableTsc", enable_tsc);
670 dict->GetValueForKeyAsInteger(
"psbPeriod", psb_period);
671 dict->GetValueForKeyAsBoolean(
"perCpuTracing", per_cpu_tracing);
672 dict->GetValueForKeyAsBoolean(
"disableCgroupFiltering",
673 disable_cgroup_filtering);
675 return createStringError(inconvertibleErrorCode(),
676 "configuration object is not a dictionary");
680 return Start(ipt_trace_size, process_buffer_size_limit, enable_tsc,
681 psb_period, per_cpu_tracing, disable_cgroup_filtering);
685 uint64_t ipt_trace_size,
bool enable_tsc,
686 std::optional<uint64_t> psb_period) {
692 request.
tids.emplace();
694 request.
tids->push_back(tid);
706 llvm::StringRef ipt_trace_size_not_parsed;
707 if (dict->GetValueForKeyAsString(
"iptTraceSize",
708 ipt_trace_size_not_parsed)) {
709 if (std::optional<uint64_t> bytes =
711 ipt_trace_size_not_parsed))
712 ipt_trace_size = *bytes;
714 return createStringError(inconvertibleErrorCode(),
715 "iptTraceSize is wrong bytes expression");
717 dict->GetValueForKeyAsInteger(
"iptTraceSize", ipt_trace_size);
720 dict->GetValueForKeyAsBoolean(
"enableTsc", enable_tsc);
721 dict->GetValueForKeyAsInteger(
"psbPeriod", psb_period);
723 return createStringError(inconvertibleErrorCode(),
724 "configuration object is not a dictionary");
728 return Start(tids, ipt_trace_size, enable_tsc, psb_period);
static llvm::raw_ostream & error(Stream &strm)
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
#define LLDB_PLUGIN_DEFINE(PluginName)
A class to manage flag bits.
static bool CreateSettingForTracePlugin(Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, llvm::StringRef description, bool is_global_property)
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static lldb::OptionValuePropertiesSP GetSettingForProcessPlugin(Debugger &debugger, llvm::StringRef setting_name)
static bool UnregisterPlugin(ABICreateInstance create_callback)
A plug-in interface definition class for debugging a process.
ThreadList & GetThreadList()
Target & GetTarget()
Get the target object pointer for this module.
lldb::OptionValuePropertiesSP m_collection_sp
A stream class that can stream formatted output to a file.
void Format(const char *format, Args &&... args)
llvm::raw_ostream & AsRawOstream()
Returns a raw_ostream that forwards the data to this Stream object.
Dictionary * GetAsDictionary()
std::shared_ptr< Object > ObjectSP
void SetTrace(const lldb::TraceSP &trace_sp)
Set the Trace object containing processor trace information of this target.
lldb::ThreadSP FindThreadByID(lldb::tid_t tid, bool can_update=true)
uint32_t GetIndexID() const
static const char * EventKindToString(lldb::TraceEvent event_kind)
A plug-in interface definition class for trace information.
virtual llvm::Error Start(StructuredData::ObjectSP configuration=StructuredData::ObjectSP())=0
Start tracing a live process.
std::function< llvm::Error(llvm::ArrayRef< uint8_t > data)> OnBinaryDataReadCallback
llvm::Error OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, OnBinaryDataReadCallback callback)
Fetch binary data associated with a thread, either live or postmortem, and pass it to the given callb...
llvm::Expected< std::vector< uint8_t > > GetLiveProcessBinaryData(llvm::StringRef kind)
Get binary data of the current process given a data identifier.
const char * RefreshLiveProcessState()
Method to be invoked by the plug-in to refresh the live process state.
Process * GetLiveProcess()
Get the currently traced live process.
std::optional< uint64_t > GetLiveThreadBinaryDataSize(lldb::tid_t tid, llvm::StringRef kind)
Get the size of the data returned by GetLiveThreadBinaryData.
Class used to track the duration of long running tasks related to a single scope for reporting.
void ForEachTimedTask(std::function< void(const std::string &name, std::chrono::milliseconds duration)> callback)
Executive the given callback on each recorded task.
Class used to track the duration of long running tasks for reporting.
ScopedTaskTimer & ForGlobal()
ScopedTaskTimer & ForThread(lldb::tid_t tid)
static llvm::StringRef GetSchema()
llvm::Expected< FileSpec > SaveToDisk(TraceIntelPT &trace_ipt, FileSpec directory, bool compact)
Save the Intel PT trace of a live process to the specified directory, which will be created if needed...
Properties to be used with the settings command.
uint64_t GetExtremelyLargeDecodingThreshold()
static llvm::StringRef GetSettingName()
uint64_t GetInfiniteDecodingLoopVerificationThreshold()
static TraceIntelPTSP CreateInstanceForPostmortemTrace(JSONTraceBundleDescription &bundle_description, llvm::ArrayRef< lldb::ProcessSP > traced_processes, llvm::ArrayRef< lldb::ThreadPostMortemTraceSP > traced_threads, TraceMode trace_mode)
Postmortem trace constructor.
void DumpTraceInfo(Thread &thread, Stream &s, bool verbose, bool json) override
Dump general info about a given thread's trace.
llvm::StringRef GetSchema() override
llvm::Error DoRefreshLiveProcessState(TraceGetStateResponse state, llvm::StringRef json_response) override
Method to be overriden by the plug-in to refresh its own state.
static llvm::Expected< lldb::TraceSP > CreateInstanceForTraceBundle(const llvm::json::Value &trace_bundle_description, llvm::StringRef bundle_dir, Debugger &debugger)
Create an instance of this class from a trace bundle.
static void DebuggerInitialize(Debugger &debugger)
llvm::Error OnThreadBufferRead(lldb::tid_t tid, OnBinaryDataReadCallback callback)
See Trace::OnThreadBinaryDataRead().
void Dump(Stream *s) const override
Dump the trace data that this plug-in has access to.
static llvm::Expected< lldb::TraceSP > CreateInstanceForLiveProcess(Process &process)
ScopedTaskTimer & GetGlobalTimer()
std::optional< pt_cpu > m_cpu_info
It is provided by either a trace bundle or a live process' "cpuInfo" binary data.
TraceMode trace_mode
The tracing mode of post mortem trace.
struct lldb_private::trace_intel_pt::TraceIntelPT::Storage m_storage
Storage & GetUpdatedStorage()
Get the storage after refreshing the data in the case of a live process.
llvm::Expected< lldb::TraceCursorSP > CreateNewCursor(Thread &thread) override
Get a TraceCursor for the given thread's trace.
const char * GetStartConfigurationHelp() override
void DumpTraceInfoAsJson(Thread &thread, Stream &s, bool verbose)
llvm::Expected< pt_cpu > GetCPUInfoForLiveProcess()
friend class TraceIntelPTBundleLoader
llvm::Error Start(uint64_t ipt_trace_size, uint64_t total_buffer_size_limit, bool enable_tsc, std::optional< uint64_t > psb_period, bool m_per_cpu_tracing, bool disable_cgroup_filtering)
Start tracing a live process.
static llvm::StringRef GetPluginNameStatic()
static PluginProperties & GetGlobalProperties()
Return the global properties for this trace plug-in.
lldb::CommandObjectSP GetThreadTraceStartCommand(CommandInterpreter &interpreter) override
Get the command handle for the "thread trace start" command.
TraceIntelPTSP GetSharedPtr()
llvm::Expected< DecodedThreadSP > Decode(Thread &thread)
Decode the trace of the given thread that, i.e.
llvm::Expected< std::optional< uint64_t > > GetRawTraceSize(Thread &thread)
ScopedTaskTimer & GetThreadTimer(lldb::tid_t tid)
lldb::CommandObjectSP GetProcessTraceStartCommand(CommandInterpreter &interpreter) override
Get the command handle for the "process trace start" command.
llvm::Expected< pt_cpu > GetCPUInfo()
Get or fetch the cpu information from, for example, /proc/cpuinfo.
llvm::StringRef GetPluginName() override
PluginInterface protocol.
llvm::Expected< std::optional< uint64_t > > FindBeginningOfTimeNanos()
std::optional< LinuxPerfZeroTscConversion > GetPerfZeroTscConversion()
Get or fetch the values used to convert to and from TSCs and nanos.
bool IsTraced(lldb::tid_t tid) override
Check if a thread is currently traced by this object.
TraceIntelPT(JSONTraceBundleDescription &bundle_description, llvm::ArrayRef< lldb::ProcessSP > traced_processes, TraceMode trace_mode)
This constructor is used by CreateInstanceForPostmortemTrace to get the instance ready before using s...
llvm::Expected< FileSpec > SaveToDisk(FileSpec directory, bool compact) override
Save the trace to the specified directory, which will be created if needed.
std::optional< uint64_t > ParseUserFriendlySizeExpression(llvm::StringRef size_expression)
Convert an integral size expression like 12KiB or 4MB into bytes.
const bool kDefaultPerCpuTracing
const std::optional< size_t > kDefaultPsbPeriod
std::shared_ptr< DecodedThread > DecodedThreadSP
json::Value toJSON(const JSONModule &module)
std::shared_ptr< TraceIntelPT > TraceIntelPTSP
const bool kDefaultDisableCgroupFiltering
const size_t kDefaultIptTraceSize
const bool kDefaultEnableTscValue
const size_t kDefaultProcessBufferSizeLimit
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
const char * toString(AppleArm64ExceptionClass EC)
std::shared_ptr< lldb_private::Trace > TraceSP
std::shared_ptr< lldb_private::ThreadPostMortemTrace > ThreadPostMortemTraceSP
std::shared_ptr< lldb_private::Thread > ThreadSP
std::shared_ptr< lldb_private::CommandObject > CommandObjectSP
std::shared_ptr< lldb_private::Process > ProcessSP
static const char * kIptTrace
static const char * kProcFsCpuInfo
static const char * kPerfContextSwitchTrace
std::vector< TraceThreadState > traced_threads
jLLDBTraceStart gdb-remote packet
bool enable_tsc
Whether to enable TSC.
std::optional< bool > disable_cgroup_filtering
Disable the cgroup filtering that is automatically applied in per cpu mode.
std::optional< bool > per_cpu_tracing
Whether to have a trace buffer per thread or per cpu cpu.
std::optional< uint64_t > psb_period
PSB packet period.
std::optional< uint64_t > process_buffer_size_limit
Required when doing "process tracing".
uint64_t ipt_trace_size
Size in bytes to use for each thread's trace buffer.
std::optional< std::vector< lldb::tid_t > > tids
If std::nullopt, then this starts tracing the whole process.
std::string type
Tracing technology name, e.g. intel-pt, arm-coresight.
lldb::user_id_t GetID() const
Get accessor for the user ID.
llvm::DenseMap< const char *, uint64_t > libipt_errors
uint64_t other_errors
The following counters are mutually exclusive.
uint64_t GetTotalCount() const
std::unordered_map< lldb::TraceEvent, uint64_t > events_counts
A count for each individual event kind.
std::string context_switch_trace
std::optional< std::vector< JSONCpu > > cpus
std::optional< LinuxPerfZeroTscConversion > tsc_perf_zero_conversion
We package all the data that can change upon process stops to make sure this contract is very visible...
std::optional< LinuxPerfZeroTscConversion > tsc_conversion
It is provided by either a trace bundle or a live process to convert TSC counters to and from nanos.
std::optional< TraceIntelPTMultiCpuDecoder > multicpu_decoder
bool beginning_of_time_nanos_calculated
std::optional< uint64_t > beginning_of_time_nanos
llvm::DenseMap< lldb::tid_t, std::unique_ptr< ThreadDecoder > > thread_decoders
These decoders are used for the non-per-cpu case.
TaskTimer task_timer
Helper variable used to track long running operations for telemetry.