diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp')
-rw-r--r-- | contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp | 744 |
1 files changed, 744 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp new file mode 100644 index 000000000000..72e9948ffe81 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -0,0 +1,744 @@ +//===-- TraceIntelPT.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TraceIntelPT.h" + +#include "../common/ThreadPostMortemTrace.h" +#include "CommandObjectTraceStartIntelPT.h" +#include "DecodedThread.h" +#include "TraceCursorIntelPT.h" +#include "TraceIntelPTBundleLoader.h" +#include "TraceIntelPTBundleSaver.h" +#include "TraceIntelPTConstants.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::trace_intel_pt; +using namespace llvm; + +LLDB_PLUGIN_DEFINE(TraceIntelPT) + +lldb::CommandObjectSP +TraceIntelPT::GetProcessTraceStartCommand(CommandInterpreter &interpreter) { + return CommandObjectSP( + new CommandObjectProcessTraceStartIntelPT(*this, interpreter)); +} + +lldb::CommandObjectSP +TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) { + return CommandObjectSP( + new CommandObjectThreadTraceStartIntelPT(*this, interpreter)); +} + +#define LLDB_PROPERTIES_traceintelpt +#include "TraceIntelPTProperties.inc" + +enum { +#define LLDB_PROPERTIES_traceintelpt +#include "TraceIntelPTPropertiesEnum.inc" +}; + +llvm::StringRef TraceIntelPT::PluginProperties::GetSettingName() { + return TraceIntelPT::GetPluginNameStatic(); +} + +TraceIntelPT::PluginProperties::PluginProperties() : Properties() { + m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName()); + m_collection_sp->Initialize(g_traceintelpt_properties); +} + +uint64_t +TraceIntelPT::PluginProperties::GetInfiniteDecodingLoopVerificationThreshold() { + const uint32_t idx = ePropertyInfiniteDecodingLoopVerificationThreshold; + return GetPropertyAtIndexAs<uint64_t>( + idx, g_traceintelpt_properties[idx].default_uint_value); +} + +uint64_t TraceIntelPT::PluginProperties::GetExtremelyLargeDecodingThreshold() { + const uint32_t idx = ePropertyExtremelyLargeDecodingThreshold; + return GetPropertyAtIndexAs<uint64_t>( + idx, g_traceintelpt_properties[idx].default_uint_value); +} + +TraceIntelPT::PluginProperties &TraceIntelPT::GetGlobalProperties() { + static TraceIntelPT::PluginProperties g_settings; + return g_settings; +} + +void TraceIntelPT::Initialize() { + PluginManager::RegisterPlugin( + GetPluginNameStatic(), "Intel Processor Trace", + CreateInstanceForTraceBundle, CreateInstanceForLiveProcess, + TraceIntelPTBundleLoader::GetSchema(), DebuggerInitialize); +} + +void TraceIntelPT::DebuggerInitialize(Debugger &debugger) { + if (!PluginManager::GetSettingForProcessPlugin( + debugger, PluginProperties::GetSettingName())) { + const bool is_global_setting = true; + PluginManager::CreateSettingForTracePlugin( + debugger, GetGlobalProperties().GetValueProperties(), + "Properties for the intel-pt trace plug-in.", is_global_setting); + } +} + +void TraceIntelPT::Terminate() { + PluginManager::UnregisterPlugin(CreateInstanceForTraceBundle); +} + +StringRef TraceIntelPT::GetSchema() { + return TraceIntelPTBundleLoader::GetSchema(); +} + +void TraceIntelPT::Dump(Stream *s) const {} + +Expected<FileSpec> TraceIntelPT::SaveToDisk(FileSpec directory, bool compact) { + RefreshLiveProcessState(); + return TraceIntelPTBundleSaver().SaveToDisk(*this, directory, compact); +} + +Expected<TraceSP> TraceIntelPT::CreateInstanceForTraceBundle( + const json::Value &bundle_description, StringRef bundle_dir, + Debugger &debugger) { + return TraceIntelPTBundleLoader(debugger, bundle_description, bundle_dir) + .Load(); +} + +Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) { + TraceSP instance(new TraceIntelPT(process)); + process.GetTarget().SetTrace(instance); + return instance; +} + +TraceIntelPTSP TraceIntelPT::GetSharedPtr() { + return std::static_pointer_cast<TraceIntelPT>(shared_from_this()); +} + +TraceIntelPT::TraceMode TraceIntelPT::GetTraceMode() { return trace_mode; } + +TraceIntelPTSP TraceIntelPT::CreateInstanceForPostmortemTrace( + JSONTraceBundleDescription &bundle_description, + ArrayRef<ProcessSP> traced_processes, + ArrayRef<ThreadPostMortemTraceSP> traced_threads, TraceMode trace_mode) { + TraceIntelPTSP trace_sp( + new TraceIntelPT(bundle_description, traced_processes, trace_mode)); + trace_sp->m_storage.tsc_conversion = + bundle_description.tsc_perf_zero_conversion; + + if (bundle_description.cpus) { + std::vector<cpu_id_t> cpus; + + for (const JSONCpu &cpu : *bundle_description.cpus) { + trace_sp->SetPostMortemCpuDataFile(cpu.id, IntelPTDataKinds::kIptTrace, + FileSpec(cpu.ipt_trace)); + + trace_sp->SetPostMortemCpuDataFile( + cpu.id, IntelPTDataKinds::kPerfContextSwitchTrace, + FileSpec(cpu.context_switch_trace)); + cpus.push_back(cpu.id); + } + + if (trace_mode == TraceMode::UserMode) { + trace_sp->m_storage.multicpu_decoder.emplace(trace_sp); + } + } + + if (!bundle_description.cpus || trace_mode == TraceMode::KernelMode) { + for (const ThreadPostMortemTraceSP &thread : traced_threads) { + trace_sp->m_storage.thread_decoders.try_emplace( + thread->GetID(), std::make_unique<ThreadDecoder>(thread, *trace_sp)); + if (const std::optional<FileSpec> &trace_file = thread->GetTraceFile()) { + trace_sp->SetPostMortemThreadDataFile( + thread->GetID(), IntelPTDataKinds::kIptTrace, *trace_file); + } + } + } + + for (const ProcessSP &process_sp : traced_processes) + process_sp->GetTarget().SetTrace(trace_sp); + return trace_sp; +} + +TraceIntelPT::TraceIntelPT(JSONTraceBundleDescription &bundle_description, + ArrayRef<ProcessSP> traced_processes, + TraceMode trace_mode) + : Trace(traced_processes, bundle_description.GetCpuIds()), + m_cpu_info(bundle_description.cpu_info), trace_mode(trace_mode) {} + +Expected<DecodedThreadSP> TraceIntelPT::Decode(Thread &thread) { + if (const char *error = RefreshLiveProcessState()) + return createStringError(inconvertibleErrorCode(), error); + + Storage &storage = GetUpdatedStorage(); + if (storage.multicpu_decoder) + return storage.multicpu_decoder->Decode(thread); + + auto it = storage.thread_decoders.find(thread.GetID()); + if (it == storage.thread_decoders.end()) + return createStringError(inconvertibleErrorCode(), "thread not traced"); + return it->second->Decode(); +} + +Expected<std::optional<uint64_t>> TraceIntelPT::FindBeginningOfTimeNanos() { + Storage &storage = GetUpdatedStorage(); + if (storage.beginning_of_time_nanos_calculated) + return storage.beginning_of_time_nanos; + storage.beginning_of_time_nanos_calculated = true; + + if (!storage.tsc_conversion) + return std::nullopt; + + std::optional<uint64_t> lowest_tsc; + + if (storage.multicpu_decoder) { + if (Expected<std::optional<uint64_t>> tsc = + storage.multicpu_decoder->FindLowestTSC()) { + lowest_tsc = *tsc; + } else { + return tsc.takeError(); + } + } + + for (auto &decoder : storage.thread_decoders) { + Expected<std::optional<uint64_t>> tsc = decoder.second->FindLowestTSC(); + if (!tsc) + return tsc.takeError(); + + if (*tsc && (!lowest_tsc || *lowest_tsc > **tsc)) + lowest_tsc = **tsc; + } + + if (lowest_tsc) { + storage.beginning_of_time_nanos = + storage.tsc_conversion->ToNanos(*lowest_tsc); + } + return storage.beginning_of_time_nanos; +} + +llvm::Expected<lldb::TraceCursorSP> +TraceIntelPT::CreateNewCursor(Thread &thread) { + if (Expected<DecodedThreadSP> decoded_thread = Decode(thread)) { + if (Expected<std::optional<uint64_t>> beginning_of_time = + FindBeginningOfTimeNanos()) + return std::make_shared<TraceCursorIntelPT>( + thread.shared_from_this(), *decoded_thread, m_storage.tsc_conversion, + *beginning_of_time); + else + return beginning_of_time.takeError(); + } else + return decoded_thread.takeError(); +} + +void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose, + bool json) { + Storage &storage = GetUpdatedStorage(); + + lldb::tid_t tid = thread.GetID(); + if (json) { + DumpTraceInfoAsJson(thread, s, verbose); + return; + } + + s.Format("\nthread #{0}: tid = {1}", thread.GetIndexID(), thread.GetID()); + if (!IsTraced(tid)) { + s << ", not traced\n"; + return; + } + s << "\n"; + + Expected<DecodedThreadSP> decoded_thread_sp_or_err = Decode(thread); + if (!decoded_thread_sp_or_err) { + s << toString(decoded_thread_sp_or_err.takeError()) << "\n"; + return; + } + + DecodedThreadSP &decoded_thread_sp = *decoded_thread_sp_or_err; + + Expected<std::optional<uint64_t>> raw_size_or_error = GetRawTraceSize(thread); + if (!raw_size_or_error) { + s.Format(" {0}\n", toString(raw_size_or_error.takeError())); + return; + } + std::optional<uint64_t> raw_size = *raw_size_or_error; + + s.Format("\n Trace technology: {0}\n", GetPluginName()); + + /// Instruction stats + { + uint64_t items_count = decoded_thread_sp->GetItemsCount(); + uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage(); + + s.Format("\n Total number of trace items: {0}\n", items_count); + + s << "\n Memory usage:\n"; + if (raw_size) + s.Format(" Raw trace size: {0} KiB\n", *raw_size / 1024); + + s.Format( + " Total approximate memory usage (excluding raw trace): {0:2} KiB\n", + (double)mem_used / 1024); + if (items_count != 0) + s.Format(" Average memory usage per item (excluding raw trace): " + "{0:2} bytes\n", + (double)mem_used / items_count); + } + + // Timing + { + s << "\n Timing for this thread:\n"; + auto print_duration = [&](const std::string &name, + std::chrono::milliseconds duration) { + s.Format(" {0}: {1:2}s\n", name, duration.count() / 1000.0); + }; + GetThreadTimer(tid).ForEachTimedTask(print_duration); + + s << "\n Timing for global tasks:\n"; + GetGlobalTimer().ForEachTimedTask(print_duration); + } + + // Instruction events stats + { + const DecodedThread::EventsStats &events_stats = + decoded_thread_sp->GetEventsStats(); + s << "\n Events:\n"; + s.Format(" Number of individual events: {0}\n", + events_stats.total_count); + for (const auto &event_to_count : events_stats.events_counts) { + s.Format(" {0}: {1}\n", + TraceCursor::EventKindToString(event_to_count.first), + event_to_count.second); + } + } + // Trace error stats + { + const DecodedThread::ErrorStats &error_stats = + decoded_thread_sp->GetErrorStats(); + s << "\n Errors:\n"; + s.Format(" Number of individual errors: {0}\n", + error_stats.GetTotalCount()); + s.Format(" Number of fatal errors: {0}\n", error_stats.fatal_errors); + for (const auto &[kind, count] : error_stats.libipt_errors) { + s.Format(" Number of libipt errors of kind [{0}]: {1}\n", kind, + count); + } + s.Format(" Number of other errors: {0}\n", error_stats.other_errors); + } + + if (storage.multicpu_decoder) { + s << "\n Multi-cpu decoding:\n"; + s.Format(" Total number of continuous executions found: {0}\n", + storage.multicpu_decoder->GetTotalContinuousExecutionsCount()); + s.Format( + " Number of continuous executions for this thread: {0}\n", + storage.multicpu_decoder->GetNumContinuousExecutionsForThread(tid)); + s.Format(" Total number of PSB blocks found: {0}\n", + storage.multicpu_decoder->GetTotalPSBBlocksCount()); + s.Format(" Number of PSB blocks for this thread: {0}\n", + storage.multicpu_decoder->GePSBBlocksCountForThread(tid)); + s.Format(" Total number of unattributed PSB blocks found: {0}\n", + storage.multicpu_decoder->GetUnattributedPSBBlocksCount()); + } +} + +void TraceIntelPT::DumpTraceInfoAsJson(Thread &thread, Stream &s, + bool verbose) { + Storage &storage = GetUpdatedStorage(); + + lldb::tid_t tid = thread.GetID(); + json::OStream json_str(s.AsRawOstream(), 2); + if (!IsTraced(tid)) { + s << "error: thread not traced\n"; + return; + } + + Expected<std::optional<uint64_t>> raw_size_or_error = GetRawTraceSize(thread); + if (!raw_size_or_error) { + s << "error: " << toString(raw_size_or_error.takeError()) << "\n"; + return; + } + + Expected<DecodedThreadSP> decoded_thread_sp_or_err = Decode(thread); + if (!decoded_thread_sp_or_err) { + s << "error: " << toString(decoded_thread_sp_or_err.takeError()) << "\n"; + return; + } + DecodedThreadSP &decoded_thread_sp = *decoded_thread_sp_or_err; + + json_str.object([&] { + json_str.attribute("traceTechnology", "intel-pt"); + json_str.attributeObject("threadStats", [&] { + json_str.attribute("tid", tid); + + uint64_t insn_len = decoded_thread_sp->GetItemsCount(); + json_str.attribute("traceItemsCount", insn_len); + + // Instruction stats + uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage(); + json_str.attributeObject("memoryUsage", [&] { + json_str.attribute("totalInBytes", std::to_string(mem_used)); + std::optional<double> avg; + if (insn_len != 0) + avg = double(mem_used) / insn_len; + json_str.attribute("avgPerItemInBytes", avg); + }); + + // Timing + json_str.attributeObject("timingInSeconds", [&] { + GetTimer().ForThread(tid).ForEachTimedTask( + [&](const std::string &name, std::chrono::milliseconds duration) { + json_str.attribute(name, duration.count() / 1000.0); + }); + }); + + // Instruction events stats + const DecodedThread::EventsStats &events_stats = + decoded_thread_sp->GetEventsStats(); + json_str.attributeObject("events", [&] { + json_str.attribute("totalCount", events_stats.total_count); + json_str.attributeObject("individualCounts", [&] { + for (const auto &event_to_count : events_stats.events_counts) { + json_str.attribute( + TraceCursor::EventKindToString(event_to_count.first), + event_to_count.second); + } + }); + }); + // Trace error stats + const DecodedThread::ErrorStats &error_stats = + decoded_thread_sp->GetErrorStats(); + json_str.attributeObject("errors", [&] { + json_str.attribute("totalCount", error_stats.GetTotalCount()); + json_str.attributeObject("libiptErrors", [&] { + for (const auto &[kind, count] : error_stats.libipt_errors) { + json_str.attribute(kind, count); + } + }); + json_str.attribute("fatalErrors", error_stats.fatal_errors); + json_str.attribute("otherErrors", error_stats.other_errors); + }); + + if (storage.multicpu_decoder) { + json_str.attribute( + "continuousExecutions", + storage.multicpu_decoder->GetNumContinuousExecutionsForThread(tid)); + json_str.attribute( + "PSBBlocks", + storage.multicpu_decoder->GePSBBlocksCountForThread(tid)); + } + }); + + json_str.attributeObject("globalStats", [&] { + json_str.attributeObject("timingInSeconds", [&] { + GetTimer().ForGlobal().ForEachTimedTask( + [&](const std::string &name, std::chrono::milliseconds duration) { + json_str.attribute(name, duration.count() / 1000.0); + }); + }); + if (storage.multicpu_decoder) { + json_str.attribute( + "totalUnattributedPSBBlocks", + storage.multicpu_decoder->GetUnattributedPSBBlocksCount()); + json_str.attribute( + "totalCountinuosExecutions", + storage.multicpu_decoder->GetTotalContinuousExecutionsCount()); + json_str.attribute("totalPSBBlocks", + storage.multicpu_decoder->GetTotalPSBBlocksCount()); + json_str.attribute( + "totalContinuousExecutions", + storage.multicpu_decoder->GetTotalContinuousExecutionsCount()); + } + }); + }); +} + +llvm::Expected<std::optional<uint64_t>> +TraceIntelPT::GetRawTraceSize(Thread &thread) { + if (GetUpdatedStorage().multicpu_decoder) + return std::nullopt; // TODO: calculate the amount of intel pt raw trace associated + // with the given thread. + if (GetLiveProcess()) + return GetLiveThreadBinaryDataSize(thread.GetID(), + IntelPTDataKinds::kIptTrace); + uint64_t size; + auto callback = [&](llvm::ArrayRef<uint8_t> data) { + size = data.size(); + return Error::success(); + }; + if (Error err = OnThreadBufferRead(thread.GetID(), callback)) + return std::move(err); + + return size; +} + +Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() { + Expected<std::vector<uint8_t>> cpu_info = + GetLiveProcessBinaryData(IntelPTDataKinds::kProcFsCpuInfo); + if (!cpu_info) + return cpu_info.takeError(); + + int64_t cpu_family = -1; + int64_t model = -1; + int64_t stepping = -1; + std::string vendor_id; + + StringRef rest(reinterpret_cast<const char *>(cpu_info->data()), + cpu_info->size()); + while (!rest.empty()) { + StringRef line; + std::tie(line, rest) = rest.split('\n'); + + SmallVector<StringRef, 2> columns; + line.split(columns, StringRef(":"), -1, false); + + if (columns.size() < 2) + continue; // continue searching + + columns[1] = columns[1].trim(" "); + if (columns[0].contains("cpu family") && + columns[1].getAsInteger(10, cpu_family)) + continue; + + else if (columns[0].contains("model") && columns[1].getAsInteger(10, model)) + continue; + + else if (columns[0].contains("stepping") && + columns[1].getAsInteger(10, stepping)) + continue; + + else if (columns[0].contains("vendor_id")) { + vendor_id = columns[1].str(); + if (!vendor_id.empty()) + continue; + } + + if ((cpu_family != -1) && (model != -1) && (stepping != -1) && + (!vendor_id.empty())) { + return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown, + static_cast<uint16_t>(cpu_family), + static_cast<uint8_t>(model), + static_cast<uint8_t>(stepping)}; + } + } + return createStringError(inconvertibleErrorCode(), + "Failed parsing the target's /proc/cpuinfo file"); +} + +Expected<pt_cpu> TraceIntelPT::GetCPUInfo() { + if (!m_cpu_info) { + if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess()) + m_cpu_info = *cpu_info; + else + return cpu_info.takeError(); + } + return *m_cpu_info; +} + +std::optional<LinuxPerfZeroTscConversion> +TraceIntelPT::GetPerfZeroTscConversion() { + return GetUpdatedStorage().tsc_conversion; +} + +TraceIntelPT::Storage &TraceIntelPT::GetUpdatedStorage() { + RefreshLiveProcessState(); + return m_storage; +} + +Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state, + StringRef json_response) { + m_storage = Storage(); + + Expected<TraceIntelPTGetStateResponse> intelpt_state = + json::parse<TraceIntelPTGetStateResponse>(json_response, + "TraceIntelPTGetStateResponse"); + if (!intelpt_state) + return intelpt_state.takeError(); + + m_storage.tsc_conversion = intelpt_state->tsc_perf_zero_conversion; + + if (!intelpt_state->cpus) { + for (const TraceThreadState &thread_state : state.traced_threads) { + ThreadSP thread_sp = + GetLiveProcess()->GetThreadList().FindThreadByID(thread_state.tid); + m_storage.thread_decoders.try_emplace( + thread_state.tid, std::make_unique<ThreadDecoder>(thread_sp, *this)); + } + } else { + std::vector<cpu_id_t> cpus; + for (const TraceCpuState &cpu : *intelpt_state->cpus) + cpus.push_back(cpu.id); + + std::vector<tid_t> tids; + for (const TraceThreadState &thread : intelpt_state->traced_threads) + tids.push_back(thread.tid); + + if (!intelpt_state->tsc_perf_zero_conversion) + return createStringError(inconvertibleErrorCode(), + "Missing perf time_zero conversion values"); + m_storage.multicpu_decoder.emplace(GetSharedPtr()); + } + + if (m_storage.tsc_conversion) { + Log *log = GetLog(LLDBLog::Target); + LLDB_LOG(log, "TraceIntelPT found TSC conversion information"); + } + return Error::success(); +} + +bool TraceIntelPT::IsTraced(lldb::tid_t tid) { + Storage &storage = GetUpdatedStorage(); + if (storage.multicpu_decoder) + return storage.multicpu_decoder->TracesThread(tid); + return storage.thread_decoders.count(tid); +} + +// The information here should match the description of the intel-pt section +// of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt +// documentation file. Similarly, it should match the CLI help messages of the +// TraceIntelPTOptions.td file. +const char *TraceIntelPT::GetStartConfigurationHelp() { + static std::optional<std::string> message; + if (!message) { + message.emplace(formatv(R"(Parameters: + + See the jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt for a + description of each parameter below. + + - int iptTraceSize (defaults to {0} bytes): + [process and thread tracing] + + - boolean enableTsc (default to {1}): + [process and thread tracing] + + - int psbPeriod (defaults to {2}): + [process and thread tracing] + + - boolean perCpuTracing (default to {3}): + [process tracing only] + + - int processBufferSizeLimit (defaults to {4} MiB): + [process tracing only] + + - boolean disableCgroupFiltering (default to {5}): + [process tracing only])", + kDefaultIptTraceSize, kDefaultEnableTscValue, + kDefaultPsbPeriod, kDefaultPerCpuTracing, + kDefaultProcessBufferSizeLimit / 1024 / 1024, + kDefaultDisableCgroupFiltering)); + } + return message->c_str(); +} + +Error TraceIntelPT::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) { + TraceIntelPTStartRequest request; + request.ipt_trace_size = ipt_trace_size; + request.process_buffer_size_limit = total_buffer_size_limit; + request.enable_tsc = enable_tsc; + request.psb_period = psb_period; + request.type = GetPluginName().str(); + request.per_cpu_tracing = per_cpu_tracing; + request.disable_cgroup_filtering = disable_cgroup_filtering; + return Trace::Start(toJSON(request)); +} + +Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) { + uint64_t ipt_trace_size = kDefaultIptTraceSize; + uint64_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit; + bool enable_tsc = kDefaultEnableTscValue; + std::optional<uint64_t> psb_period = kDefaultPsbPeriod; + bool per_cpu_tracing = kDefaultPerCpuTracing; + bool disable_cgroup_filtering = kDefaultDisableCgroupFiltering; + + if (configuration) { + if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { + dict->GetValueForKeyAsInteger("iptTraceSize", ipt_trace_size); + dict->GetValueForKeyAsInteger("processBufferSizeLimit", + process_buffer_size_limit); + dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); + dict->GetValueForKeyAsInteger("psbPeriod", psb_period); + dict->GetValueForKeyAsBoolean("perCpuTracing", per_cpu_tracing); + dict->GetValueForKeyAsBoolean("disableCgroupFiltering", + disable_cgroup_filtering); + } else { + return createStringError(inconvertibleErrorCode(), + "configuration object is not a dictionary"); + } + } + + return Start(ipt_trace_size, process_buffer_size_limit, enable_tsc, + psb_period, per_cpu_tracing, disable_cgroup_filtering); +} + +llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, + uint64_t ipt_trace_size, bool enable_tsc, + std::optional<uint64_t> psb_period) { + TraceIntelPTStartRequest request; + request.ipt_trace_size = ipt_trace_size; + request.enable_tsc = enable_tsc; + request.psb_period = psb_period; + request.type = GetPluginName().str(); + request.tids.emplace(); + for (lldb::tid_t tid : tids) + request.tids->push_back(tid); + return Trace::Start(toJSON(request)); +} + +Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, + StructuredData::ObjectSP configuration) { + uint64_t ipt_trace_size = kDefaultIptTraceSize; + bool enable_tsc = kDefaultEnableTscValue; + std::optional<uint64_t> psb_period = kDefaultPsbPeriod; + + if (configuration) { + if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { + llvm::StringRef ipt_trace_size_not_parsed; + if (dict->GetValueForKeyAsString("iptTraceSize", + ipt_trace_size_not_parsed)) { + if (std::optional<uint64_t> bytes = + ParsingUtils::ParseUserFriendlySizeExpression( + ipt_trace_size_not_parsed)) + ipt_trace_size = *bytes; + else + return createStringError(inconvertibleErrorCode(), + "iptTraceSize is wrong bytes expression"); + } else { + dict->GetValueForKeyAsInteger("iptTraceSize", ipt_trace_size); + } + + dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); + dict->GetValueForKeyAsInteger("psbPeriod", psb_period); + } else { + return createStringError(inconvertibleErrorCode(), + "configuration object is not a dictionary"); + } + } + + return Start(tids, ipt_trace_size, enable_tsc, psb_period); +} + +Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid, + OnBinaryDataReadCallback callback) { + return OnThreadBinaryDataRead(tid, IntelPTDataKinds::kIptTrace, callback); +} + +TaskTimer &TraceIntelPT::GetTimer() { return GetUpdatedStorage().task_timer; } + +ScopedTaskTimer &TraceIntelPT::GetThreadTimer(lldb::tid_t tid) { + return GetTimer().ForThread(tid); +} + +ScopedTaskTimer &TraceIntelPT::GetGlobalTimer() { + return GetTimer().ForGlobal(); +} |