11#include "../common/ThreadPostMortemTrace.h"
33 return CommandObjectSP(
39 return CommandObjectSP(
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 m_collection_sp->GetPropertyAtIndexAsUInt64(
64 nullptr, idx, g_traceintelpt_properties[idx].default_uint_value);
68 const uint32_t idx = ePropertyExtremelyLargeDecodingThreshold;
69 return m_collection_sp->GetPropertyAtIndexAsUInt64(
70 nullptr, idx, g_traceintelpt_properties[idx].default_uint_value);
88 const bool is_global_setting =
true;
91 ConstString(
"Properties for the intel-pt trace plug-in."),
112 const json::Value &bundle_description, StringRef bundle_dir,
125 return std::static_pointer_cast<TraceIntelPT>(shared_from_this());
132 ArrayRef<ProcessSP> traced_processes,
136 trace_sp->m_storage.tsc_conversion =
139 if (bundle_description.
cpus) {
140 std::vector<cpu_id_t> cpus;
142 for (
const JSONCpu &cpu : *bundle_description.
cpus) {
146 trace_sp->SetPostMortemCpuDataFile(
149 cpus.push_back(cpu.
id);
153 trace_sp->m_storage.multicpu_decoder.emplace(trace_sp);
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(
168 for (
const ProcessSP &process_sp : traced_processes)
169 process_sp->GetTarget().SetTrace(trace_sp);
174 ArrayRef<ProcessSP> traced_processes,
176 :
Trace(traced_processes, bundle_description.GetCpuIds()),
181 return createStringError(inconvertibleErrorCode(),
error);
189 return createStringError(inconvertibleErrorCode(),
"thread not traced");
190 return it->second->Decode();
202 std::optional<uint64_t> lowest_tsc;
205 if (Expected<std::optional<uint64_t>> tsc =
209 return tsc.takeError();
214 Expected<std::optional<uint64_t>> tsc = decoder.second->FindLowestTSC();
216 return tsc.takeError();
218 if (*tsc && (!lowest_tsc || *lowest_tsc > **tsc))
229llvm::Expected<lldb::TraceCursorSP>
231 if (Expected<DecodedThreadSP> decoded_thread =
Decode(thread)) {
232 if (Expected<std::optional<uint64_t>> beginning_of_time =
234 return std::make_shared<TraceCursorIntelPT>(
238 return beginning_of_time.takeError();
240 return decoded_thread.takeError();
255 s <<
", not traced\n";
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";
268 Expected<std::optional<uint64_t>> raw_size_or_error =
GetRawTraceSize(thread);
269 if (!raw_size_or_error) {
273 std::optional<uint64_t> raw_size = *raw_size_or_error;
279 uint64_t items_count = decoded_thread_sp->GetItemsCount();
280 uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage();
282 s.
Format(
"\n Total number of trace items: {0}\n", items_count);
284 s <<
"\n Memory usage:\n";
286 s.
Format(
" Raw trace size: {0} KiB\n", *raw_size / 1024);
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): "
294 (
double)mem_used / items_count);
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);
306 s <<
"\n Timing for global tasks:\n";
313 decoded_thread_sp->GetEventsStats();
315 s.
Format(
" Number of individual events: {0}\n",
317 for (
const auto &event_to_count : events_stats.
events_counts) {
320 event_to_count.second);
326 decoded_thread_sp->GetErrorStats();
328 s.
Format(
" Number of individual errors: {0}\n",
331 for (
const auto &[kind, count] : error_stats.
libipt_errors) {
332 s.
Format(
" Number of libipt errors of kind [{0}]: {1}\n", kind,
339 s <<
"\n Multi-cpu decoding:\n";
340 s.
Format(
" Total number of continuous executions found: {0}\n",
343 " Number of continuous executions for this thread: {0}\n",
345 s.
Format(
" Total number of PSB blocks found: {0}\n",
347 s.
Format(
" Number of PSB blocks for this thread: {0}\n",
349 s.
Format(
" Total number of unattributed PSB blocks found: {0}\n",
361 s <<
"error: thread not traced\n";
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";
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";
378 json_str.object([&] {
379 json_str.attribute(
"traceTechnology",
"intel-pt");
380 json_str.attributeObject(
"threadStats", [&] {
381 json_str.attribute(
"tid", tid);
383 uint64_t insn_len = decoded_thread_sp->GetItemsCount();
384 json_str.attribute(
"traceItemsCount", insn_len);
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;
392 avg =
double(mem_used) / insn_len;
393 json_str.attribute(
"avgPerItemInBytes", avg);
397 json_str.attributeObject(
"timingInSeconds", [&] {
399 [&](
const std::string &name, std::chrono::milliseconds duration) {
400 json_str.attribute(name, duration.count() / 1000.0);
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) {
412 TraceCursor::EventKindToString(event_to_count.first),
413 event_to_count.second);
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);
427 json_str.attribute(
"fatalErrors", error_stats.
fatal_errors);
428 json_str.attribute(
"otherErrors", error_stats.
other_errors);
433 "continuousExecutions",
441 json_str.attributeObject(
"globalStats", [&] {
442 json_str.attributeObject(
"timingInSeconds", [&] {
444 [&](
const std::string &name, std::chrono::milliseconds duration) {
445 json_str.attribute(name, duration.count() / 1000.0);
450 "totalUnattributedPSBBlocks",
453 "totalCountinuosExecutions",
455 json_str.attribute(
"totalPSBBlocks",
458 "totalContinuousExecutions",
465llvm::Expected<std::optional<uint64_t>>
474 auto callback = [&](llvm::ArrayRef<uint8_t> data) {
476 return Error::success();
479 return std::move(err);
485 Expected<std::vector<uint8_t>> cpu_info =
488 return cpu_info.takeError();
490 int64_t cpu_family = -1;
492 int64_t stepping = -1;
493 std::string vendor_id;
495 StringRef rest(
reinterpret_cast<const char *
>(cpu_info->data()),
497 while (!rest.empty()) {
499 std::tie(line, rest) = rest.split(
'\n');
501 SmallVector<StringRef, 2> columns;
502 line.split(columns, StringRef(
":"), -1,
false);
504 if (columns.size() < 2)
507 columns[1] = columns[1].trim(
" ");
508 if (columns[0].contains(
"cpu family") &&
509 columns[1].getAsInteger(10, cpu_family))
512 else if (columns[0].contains(
"model") && columns[1].getAsInteger(10, model))
515 else if (columns[0].contains(
"stepping") &&
516 columns[1].getAsInteger(10, stepping))
519 else if (columns[0].contains(
"vendor_id")) {
520 vendor_id = columns[1].str();
521 if (!vendor_id.empty())
525 if ((cpu_family != -1) && (model != -1) && (stepping != -1) &&
526 (!vendor_id.empty())) {
527 return pt_cpu{vendor_id ==
"GenuineIntel" ? pcv_intel : pcv_unknown,
529 static_cast<uint8_t
>(model),
530 static_cast<uint8_t
>(stepping)};
533 return createStringError(inconvertibleErrorCode(),
534 "Failed parsing the target's /proc/cpuinfo file");
542 return cpu_info.takeError();
547std::optional<LinuxPerfZeroTscConversion>
558 StringRef json_response) {
561 Expected<TraceIntelPTGetStateResponse> intelpt_state =
562 json::parse<TraceIntelPTGetStateResponse>(json_response,
563 "TraceIntelPTGetStateResponse");
565 return intelpt_state.takeError();
569 if (!intelpt_state->cpus) {
574 thread_state.
tid, std::make_unique<ThreadDecoder>(thread_sp, *
this));
577 std::vector<cpu_id_t> cpus;
579 cpus.push_back(cpu.
id);
581 std::vector<tid_t> tids;
583 tids.push_back(thread.
tid);
585 if (!intelpt_state->tsc_perf_zero_conversion)
586 return createStringError(inconvertibleErrorCode(),
587 "Missing perf time_zero conversion values");
593 LLDB_LOG(log,
"TraceIntelPT found TSC conversion information");
595 return Error::success();
610 static std::optional<std::string> message;
612 message.emplace(formatv(R
"(Parameters:
614 See the jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt for a
615 description of each parameter below.
617 - int iptTraceSize (defaults to {0} bytes):
618 [process and thread tracing]
620 - boolean enableTsc (default to {1}):
621 [process and thread tracing]
623 - int psbPeriod (defaults to {2}):
624 [process and thread tracing]
626 - boolean perCpuTracing (default to {3}):
627 [process tracing only]
629 - int processBufferSizeLimit (defaults to {4} MiB):
630 [process tracing only]
632 - boolean disableCgroupFiltering (default to {5}):
633 [process tracing only])",
639 return message->c_str();
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) {
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);
676 return createStringError(inconvertibleErrorCode(),
677 "configuration object is not a dictionary");
681 return Start(ipt_trace_size, process_buffer_size_limit, enable_tsc,
682 psb_period, per_cpu_tracing, disable_cgroup_filtering);
686 uint64_t ipt_trace_size,
bool enable_tsc,
687 std::optional<uint64_t> psb_period) {
693 request.
tids.emplace();
695 request.
tids->push_back(tid);
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 =
712 ipt_trace_size_not_parsed))
713 ipt_trace_size = *bytes;
715 return createStringError(inconvertibleErrorCode(),
716 "iptTraceSize is wrong bytes expression");
718 dict->GetValueForKeyAsInteger(
"iptTraceSize", ipt_trace_size);
721 dict->GetValueForKeyAsBoolean(
"enableTsc", enable_tsc);
722 dict->GetValueForKeyAsInteger(
"psbPeriod", psb_period);
724 return createStringError(inconvertibleErrorCode(),
725 "configuration object is not a dictionary");
729 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 uniqued constant string class.
A class to manage flag bits.
static bool CreateSettingForTracePlugin(Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, ConstString description, bool is_global_property)
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static lldb::OptionValuePropertiesSP GetSettingForProcessPlugin(Debugger &debugger, ConstString 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 ConstString 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)
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.