Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp
39645 views
//===-- TraceIntelPTBundleLoader.cpp --------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "TraceIntelPTBundleLoader.h"910#include "../common/ThreadPostMortemTrace.h"11#include "TraceIntelPT.h"12#include "TraceIntelPTConstants.h"13#include "TraceIntelPTJSONStructs.h"14#include "lldb/Core/Debugger.h"15#include "lldb/Core/Module.h"16#include "lldb/Target/Process.h"17#include "lldb/Target/ProcessTrace.h"18#include "lldb/Target/Target.h"19#include <optional>2021using namespace lldb;22using namespace lldb_private;23using namespace lldb_private::trace_intel_pt;24using namespace llvm;2526FileSpec TraceIntelPTBundleLoader::NormalizePath(const std::string &path) {27FileSpec file_spec(path);28if (file_spec.IsRelative())29file_spec.PrependPathComponent(m_bundle_dir);30return file_spec;31}3233Error TraceIntelPTBundleLoader::ParseModule(Target &target,34const JSONModule &module) {35auto do_parse = [&]() -> Error {36FileSpec system_file_spec(module.system_path);3738FileSpec local_file_spec(module.file.has_value() ? *module.file39: module.system_path);4041ModuleSpec module_spec;42module_spec.GetFileSpec() = local_file_spec;43module_spec.GetPlatformFileSpec() = system_file_spec;4445if (module.uuid.has_value())46module_spec.GetUUID().SetFromStringRef(*module.uuid);4748Status error;49ModuleSP module_sp =50target.GetOrCreateModule(module_spec, /*notify*/ false, &error);5152if (error.Fail())53return error.ToError();5455bool load_addr_changed = false;56module_sp->SetLoadAddress(target, module.load_address.value, false,57load_addr_changed);58return Error::success();59};60if (Error err = do_parse())61return createStringError(62inconvertibleErrorCode(), "Error when parsing module %s. %s",63module.system_path.c_str(), toString(std::move(err)).c_str());64return Error::success();65}6667Error TraceIntelPTBundleLoader::CreateJSONError(json::Path::Root &root,68const json::Value &value) {69std::string err;70raw_string_ostream os(err);71root.printErrorContext(value, os);72return createStringError(73std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s",74toString(root.getError()).c_str(), os.str().c_str(), GetSchema().data());75}7677ThreadPostMortemTraceSP78TraceIntelPTBundleLoader::ParseThread(Process &process,79const JSONThread &thread) {80lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);8182std::optional<FileSpec> trace_file;83if (thread.ipt_trace)84trace_file = FileSpec(*thread.ipt_trace);8586ThreadPostMortemTraceSP thread_sp =87std::make_shared<ThreadPostMortemTrace>(process, tid, trace_file);88process.GetThreadList().AddThread(thread_sp);89return thread_sp;90}9192Expected<TraceIntelPTBundleLoader::ParsedProcess>93TraceIntelPTBundleLoader::CreateEmptyProcess(lldb::pid_t pid,94llvm::StringRef triple) {95TargetSP target_sp;96Status error = m_debugger.GetTargetList().CreateTarget(97m_debugger, /*user_exe_path*/ StringRef(), triple, eLoadDependentsNo,98/*platform_options*/ nullptr, target_sp);99100if (!target_sp)101return error.ToError();102103ParsedProcess parsed_process;104parsed_process.target_sp = target_sp;105106ProcessTrace::Initialize();107ProcessSP process_sp = target_sp->CreateProcess(108/*listener*/ nullptr, "trace",109/*crash_file*/ nullptr,110/*can_connect*/ false);111112process_sp->SetID(static_cast<lldb::pid_t>(pid));113114return parsed_process;115}116117Expected<TraceIntelPTBundleLoader::ParsedProcess>118TraceIntelPTBundleLoader::ParseProcess(const JSONProcess &process) {119Expected<ParsedProcess> parsed_process =120CreateEmptyProcess(process.pid, process.triple.value_or(""));121122if (!parsed_process)123return parsed_process.takeError();124125ProcessSP process_sp = parsed_process->target_sp->GetProcessSP();126127for (const JSONThread &thread : process.threads)128parsed_process->threads.push_back(ParseThread(*process_sp, thread));129130for (const JSONModule &module : process.modules)131if (Error err = ParseModule(*parsed_process->target_sp, module))132return std::move(err);133134if (!process.threads.empty())135process_sp->GetThreadList().SetSelectedThreadByIndexID(0);136137// We invoke DidAttach to create a correct stopped state for the process and138// its threads.139ArchSpec process_arch;140process_sp->DidAttach(process_arch);141142return parsed_process;143}144145Expected<TraceIntelPTBundleLoader::ParsedProcess>146TraceIntelPTBundleLoader::ParseKernel(147const JSONTraceBundleDescription &bundle_description) {148Expected<ParsedProcess> parsed_process =149CreateEmptyProcess(kDefaultKernelProcessID, "");150151if (!parsed_process)152return parsed_process.takeError();153154ProcessSP process_sp = parsed_process->target_sp->GetProcessSP();155156// Add cpus as fake threads157for (const JSONCpu &cpu : *bundle_description.cpus) {158ThreadPostMortemTraceSP thread_sp = std::make_shared<ThreadPostMortemTrace>(159*process_sp, static_cast<lldb::tid_t>(cpu.id), FileSpec(cpu.ipt_trace));160thread_sp->SetName(formatv("kernel_cpu_{0}", cpu.id).str().c_str());161process_sp->GetThreadList().AddThread(thread_sp);162parsed_process->threads.push_back(thread_sp);163}164165// Add kernel image166FileSpec file_spec(bundle_description.kernel->file);167ModuleSpec module_spec;168module_spec.GetFileSpec() = file_spec;169170Status error;171ModuleSP module_sp =172parsed_process->target_sp->GetOrCreateModule(module_spec, false, &error);173174if (error.Fail())175return error.ToError();176177lldb::addr_t load_address =178bundle_description.kernel->load_address179? bundle_description.kernel->load_address->value180: kDefaultKernelLoadAddress;181182bool load_addr_changed = false;183module_sp->SetLoadAddress(*parsed_process->target_sp, load_address, false,184load_addr_changed);185186process_sp->GetThreadList().SetSelectedThreadByIndexID(0);187188// We invoke DidAttach to create a correct stopped state for the process and189// its threads.190ArchSpec process_arch;191process_sp->DidAttach(process_arch);192193return parsed_process;194}195196Expected<std::vector<TraceIntelPTBundleLoader::ParsedProcess>>197TraceIntelPTBundleLoader::LoadBundle(198const JSONTraceBundleDescription &bundle_description) {199std::vector<ParsedProcess> parsed_processes;200201auto HandleError = [&](Error &&err) {202// Delete all targets that were created so far in case of failures203for (ParsedProcess &parsed_process : parsed_processes)204m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);205return std::move(err);206};207208if (bundle_description.processes) {209for (const JSONProcess &process : *bundle_description.processes) {210if (Expected<ParsedProcess> parsed_process = ParseProcess(process))211parsed_processes.push_back(std::move(*parsed_process));212else213return HandleError(parsed_process.takeError());214}215}216217if (bundle_description.kernel) {218if (Expected<ParsedProcess> kernel_process =219ParseKernel(bundle_description))220parsed_processes.push_back(std::move(*kernel_process));221else222return HandleError(kernel_process.takeError());223}224225return parsed_processes;226}227228StringRef TraceIntelPTBundleLoader::GetSchema() {229static std::string schema;230if (schema.empty()) {231schema = R"({232"type": "intel-pt",233"cpuInfo": {234// CPU information gotten from, for example, /proc/cpuinfo.235236"vendor": "GenuineIntel" | "unknown",237"family": integer,238"model": integer,239"stepping": integer240},241"processes?": [242{243"pid": integer,244"triple"?: string,245// Optional clang/llvm target triple.246// This must be provided if the trace will be created not using the247// CLI or on a machine other than where the target was traced.248"threads": [249// A list of known threads for the given process. When context switch250// data is provided, LLDB will automatically create threads for the251// this process whenever it finds new threads when traversing the252// context switches, so passing values to this list in this case is253// optional.254{255"tid": integer,256"iptTrace"?: string257// Path to the raw Intel PT buffer file for this thread.258}259],260"modules": [261{262"systemPath": string,263// Original path of the module at runtime.264"file"?: string,265// Path to a copy of the file if not available at "systemPath".266"loadAddress": integer | string decimal | hex string,267// Lowest address of the sections of the module loaded on memory.268"uuid"?: string,269// Build UUID for the file for sanity checks.270}271]272}273],274"cpus"?: [275{276"id": integer,277// Id of this CPU core.278"iptTrace": string,279// Path to the raw Intel PT buffer for this cpu core.280"contextSwitchTrace": string,281// Path to the raw perf_event_open context switch trace file for this cpu core.282// The perf_event must have been configured with PERF_SAMPLE_TID and283// PERF_SAMPLE_TIME, as well as sample_id_all = 1.284}285],286"tscPerfZeroConversion"?: {287// Values used to convert between TSCs and nanoseconds. See the time_zero288// section in https://man7.org/linux/man-pages/man2/perf_event_open.2.html289// for information.290291"timeMult": integer,292"timeShift": integer,293"timeZero": integer | string decimal | hex string,294},295"kernel"?: {296"loadAddress"?: integer | string decimal | hex string,297// Kernel's image load address. Defaults to 0xffffffff81000000, which298// is a load address of x86 architecture if KASLR is not enabled.299"file": string,300// Path to the kernel image.301}302}303304Notes:305306- All paths are either absolute or relative to folder containing the bundle307description file.308- "cpus" is provided if and only if processes[].threads[].iptTrace is not provided.309- "tscPerfZeroConversion" must be provided if "cpus" is provided.310- If "kernel" is provided, then the "processes" section must be empty or not311passed at all, and the "cpus" section must be provided. This configuration312indicates that the kernel was traced and user processes weren't. Besides313that, the kernel is treated as a single process with one thread per CPU314core. This doesn't handle actual kernel threads, but instead treats315all the instructions executed by the kernel on each core as an316individual thread.})";317}318return schema;319}320321Error TraceIntelPTBundleLoader::AugmentThreadsFromContextSwitches(322JSONTraceBundleDescription &bundle_description) {323if (!bundle_description.cpus || !bundle_description.processes)324return Error::success();325326if (!bundle_description.tsc_perf_zero_conversion)327return createStringError(inconvertibleErrorCode(),328"TSC to nanos conversion values are needed when "329"context switch information is provided.");330331DenseMap<lldb::pid_t, JSONProcess *> indexed_processes;332DenseMap<JSONProcess *, DenseSet<tid_t>> indexed_threads;333334for (JSONProcess &process : *bundle_description.processes) {335indexed_processes[process.pid] = &process;336for (JSONThread &thread : process.threads)337indexed_threads[&process].insert(thread.tid);338}339340auto on_thread_seen = [&](lldb::pid_t pid, tid_t tid) {341auto proc = indexed_processes.find(pid);342if (proc == indexed_processes.end())343return;344if (indexed_threads[proc->second].count(tid))345return;346indexed_threads[proc->second].insert(tid);347proc->second->threads.push_back({tid, /*ipt_trace=*/std::nullopt});348};349350for (const JSONCpu &cpu : *bundle_description.cpus) {351Error err = Trace::OnDataFileRead(352FileSpec(cpu.context_switch_trace),353[&](ArrayRef<uint8_t> data) -> Error {354Expected<std::vector<ThreadContinuousExecution>> executions =355DecodePerfContextSwitchTrace(356data, cpu.id, *bundle_description.tsc_perf_zero_conversion);357if (!executions)358return executions.takeError();359for (const ThreadContinuousExecution &execution : *executions)360on_thread_seen(execution.pid, execution.tid);361return Error::success();362});363if (err)364return err;365}366return Error::success();367}368369Expected<TraceSP> TraceIntelPTBundleLoader::CreateTraceIntelPTInstance(370JSONTraceBundleDescription &bundle_description,371std::vector<ParsedProcess> &parsed_processes) {372std::vector<ThreadPostMortemTraceSP> threads;373std::vector<ProcessSP> processes;374for (const ParsedProcess &parsed_process : parsed_processes) {375processes.push_back(parsed_process.target_sp->GetProcessSP());376threads.insert(threads.end(), parsed_process.threads.begin(),377parsed_process.threads.end());378}379380TraceIntelPT::TraceMode trace_mode = bundle_description.kernel381? TraceIntelPT::TraceMode::KernelMode382: TraceIntelPT::TraceMode::UserMode;383384TraceSP trace_instance = TraceIntelPT::CreateInstanceForPostmortemTrace(385bundle_description, processes, threads, trace_mode);386for (const ParsedProcess &parsed_process : parsed_processes)387parsed_process.target_sp->SetTrace(trace_instance);388389return trace_instance;390}391392void TraceIntelPTBundleLoader::NormalizeAllPaths(393JSONTraceBundleDescription &bundle_description) {394if (bundle_description.processes) {395for (JSONProcess &process : *bundle_description.processes) {396for (JSONModule &module : process.modules) {397module.system_path = NormalizePath(module.system_path).GetPath();398if (module.file)399module.file = NormalizePath(*module.file).GetPath();400}401for (JSONThread &thread : process.threads) {402if (thread.ipt_trace)403thread.ipt_trace = NormalizePath(*thread.ipt_trace).GetPath();404}405}406}407if (bundle_description.cpus) {408for (JSONCpu &cpu : *bundle_description.cpus) {409cpu.context_switch_trace =410NormalizePath(cpu.context_switch_trace).GetPath();411cpu.ipt_trace = NormalizePath(cpu.ipt_trace).GetPath();412}413}414if (bundle_description.kernel) {415bundle_description.kernel->file =416NormalizePath(bundle_description.kernel->file).GetPath();417}418}419420Expected<TraceSP> TraceIntelPTBundleLoader::Load() {421json::Path::Root root("traceBundle");422JSONTraceBundleDescription bundle_description;423if (!fromJSON(m_bundle_description, bundle_description, root))424return CreateJSONError(root, m_bundle_description);425426NormalizeAllPaths(bundle_description);427428if (Error err = AugmentThreadsFromContextSwitches(bundle_description))429return std::move(err);430431if (Expected<std::vector<ParsedProcess>> parsed_processes =432LoadBundle(bundle_description))433return CreateTraceIntelPTInstance(bundle_description, *parsed_processes);434else435return parsed_processes.takeError();436}437438439