181ad6265SDimitry Andric //===-- TraceIntelPTBundleLoader.cpp --------------------------------------===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric 
981ad6265SDimitry Andric #include "TraceIntelPTBundleLoader.h"
1081ad6265SDimitry Andric 
1181ad6265SDimitry Andric #include "../common/ThreadPostMortemTrace.h"
1281ad6265SDimitry Andric #include "TraceIntelPT.h"
13bdd1243dSDimitry Andric #include "TraceIntelPTConstants.h"
1481ad6265SDimitry Andric #include "TraceIntelPTJSONStructs.h"
1581ad6265SDimitry Andric #include "lldb/Core/Debugger.h"
1681ad6265SDimitry Andric #include "lldb/Core/Module.h"
1781ad6265SDimitry Andric #include "lldb/Target/Process.h"
18*297eecfbSDimitry Andric #include "lldb/Target/ProcessTrace.h"
1981ad6265SDimitry Andric #include "lldb/Target/Target.h"
20bdd1243dSDimitry Andric #include <optional>
2181ad6265SDimitry Andric 
2281ad6265SDimitry Andric using namespace lldb;
2381ad6265SDimitry Andric using namespace lldb_private;
2481ad6265SDimitry Andric using namespace lldb_private::trace_intel_pt;
2581ad6265SDimitry Andric using namespace llvm;
2681ad6265SDimitry Andric 
NormalizePath(const std::string & path)2781ad6265SDimitry Andric FileSpec TraceIntelPTBundleLoader::NormalizePath(const std::string &path) {
2881ad6265SDimitry Andric   FileSpec file_spec(path);
2981ad6265SDimitry Andric   if (file_spec.IsRelative())
3081ad6265SDimitry Andric     file_spec.PrependPathComponent(m_bundle_dir);
3181ad6265SDimitry Andric   return file_spec;
3281ad6265SDimitry Andric }
3381ad6265SDimitry Andric 
ParseModule(Target & target,const JSONModule & module)3481ad6265SDimitry Andric Error TraceIntelPTBundleLoader::ParseModule(Target &target,
3581ad6265SDimitry Andric                                             const JSONModule &module) {
3681ad6265SDimitry Andric   auto do_parse = [&]() -> Error {
3781ad6265SDimitry Andric     FileSpec system_file_spec(module.system_path);
3881ad6265SDimitry Andric 
39bdd1243dSDimitry Andric     FileSpec local_file_spec(module.file.has_value() ? *module.file
4081ad6265SDimitry Andric                                                      : module.system_path);
4181ad6265SDimitry Andric 
4281ad6265SDimitry Andric     ModuleSpec module_spec;
4381ad6265SDimitry Andric     module_spec.GetFileSpec() = local_file_spec;
4481ad6265SDimitry Andric     module_spec.GetPlatformFileSpec() = system_file_spec;
4581ad6265SDimitry Andric 
46bdd1243dSDimitry Andric     if (module.uuid.has_value())
4781ad6265SDimitry Andric       module_spec.GetUUID().SetFromStringRef(*module.uuid);
4881ad6265SDimitry Andric 
4981ad6265SDimitry Andric     Status error;
5081ad6265SDimitry Andric     ModuleSP module_sp =
5181ad6265SDimitry Andric         target.GetOrCreateModule(module_spec, /*notify*/ false, &error);
5281ad6265SDimitry Andric 
5381ad6265SDimitry Andric     if (error.Fail())
5481ad6265SDimitry Andric       return error.ToError();
5581ad6265SDimitry Andric 
5681ad6265SDimitry Andric     bool load_addr_changed = false;
5781ad6265SDimitry Andric     module_sp->SetLoadAddress(target, module.load_address.value, false,
5881ad6265SDimitry Andric                               load_addr_changed);
5981ad6265SDimitry Andric     return Error::success();
6081ad6265SDimitry Andric   };
6181ad6265SDimitry Andric   if (Error err = do_parse())
6281ad6265SDimitry Andric     return createStringError(
6381ad6265SDimitry Andric         inconvertibleErrorCode(), "Error when parsing module %s. %s",
6481ad6265SDimitry Andric         module.system_path.c_str(), toString(std::move(err)).c_str());
6581ad6265SDimitry Andric   return Error::success();
6681ad6265SDimitry Andric }
6781ad6265SDimitry Andric 
CreateJSONError(json::Path::Root & root,const json::Value & value)6881ad6265SDimitry Andric Error TraceIntelPTBundleLoader::CreateJSONError(json::Path::Root &root,
6981ad6265SDimitry Andric                                                 const json::Value &value) {
7081ad6265SDimitry Andric   std::string err;
7181ad6265SDimitry Andric   raw_string_ostream os(err);
7281ad6265SDimitry Andric   root.printErrorContext(value, os);
7381ad6265SDimitry Andric   return createStringError(
7481ad6265SDimitry Andric       std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s",
7581ad6265SDimitry Andric       toString(root.getError()).c_str(), os.str().c_str(), GetSchema().data());
7681ad6265SDimitry Andric }
7781ad6265SDimitry Andric 
7881ad6265SDimitry Andric ThreadPostMortemTraceSP
ParseThread(Process & process,const JSONThread & thread)7981ad6265SDimitry Andric TraceIntelPTBundleLoader::ParseThread(Process &process,
8081ad6265SDimitry Andric                                       const JSONThread &thread) {
8181ad6265SDimitry Andric   lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
8281ad6265SDimitry Andric 
83bdd1243dSDimitry Andric   std::optional<FileSpec> trace_file;
8481ad6265SDimitry Andric   if (thread.ipt_trace)
8581ad6265SDimitry Andric     trace_file = FileSpec(*thread.ipt_trace);
8681ad6265SDimitry Andric 
8781ad6265SDimitry Andric   ThreadPostMortemTraceSP thread_sp =
8881ad6265SDimitry Andric       std::make_shared<ThreadPostMortemTrace>(process, tid, trace_file);
8981ad6265SDimitry Andric   process.GetThreadList().AddThread(thread_sp);
9081ad6265SDimitry Andric   return thread_sp;
9181ad6265SDimitry Andric }
9281ad6265SDimitry Andric 
9381ad6265SDimitry Andric Expected<TraceIntelPTBundleLoader::ParsedProcess>
CreateEmptyProcess(lldb::pid_t pid,llvm::StringRef triple)94bdd1243dSDimitry Andric TraceIntelPTBundleLoader::CreateEmptyProcess(lldb::pid_t pid,
95bdd1243dSDimitry Andric                                              llvm::StringRef triple) {
9681ad6265SDimitry Andric   TargetSP target_sp;
9781ad6265SDimitry Andric   Status error = m_debugger.GetTargetList().CreateTarget(
98bdd1243dSDimitry Andric       m_debugger, /*user_exe_path*/ StringRef(), triple, eLoadDependentsNo,
9981ad6265SDimitry Andric       /*platform_options*/ nullptr, target_sp);
10081ad6265SDimitry Andric 
10181ad6265SDimitry Andric   if (!target_sp)
10281ad6265SDimitry Andric     return error.ToError();
10381ad6265SDimitry Andric 
10481ad6265SDimitry Andric   ParsedProcess parsed_process;
10581ad6265SDimitry Andric   parsed_process.target_sp = target_sp;
10681ad6265SDimitry Andric 
107*297eecfbSDimitry Andric   ProcessTrace::Initialize();
108*297eecfbSDimitry Andric   ProcessSP process_sp = target_sp->CreateProcess(
109*297eecfbSDimitry Andric       /*listener*/ nullptr, "trace",
110*297eecfbSDimitry Andric       /*crash_file*/ nullptr,
111*297eecfbSDimitry Andric       /*can_connect*/ false);
11281ad6265SDimitry Andric 
113bdd1243dSDimitry Andric   process_sp->SetID(static_cast<lldb::pid_t>(pid));
114bdd1243dSDimitry Andric 
115bdd1243dSDimitry Andric   return parsed_process;
116bdd1243dSDimitry Andric }
117bdd1243dSDimitry Andric 
118bdd1243dSDimitry Andric Expected<TraceIntelPTBundleLoader::ParsedProcess>
ParseProcess(const JSONProcess & process)119bdd1243dSDimitry Andric TraceIntelPTBundleLoader::ParseProcess(const JSONProcess &process) {
120bdd1243dSDimitry Andric   Expected<ParsedProcess> parsed_process =
121bdd1243dSDimitry Andric       CreateEmptyProcess(process.pid, process.triple.value_or(""));
122bdd1243dSDimitry Andric 
123bdd1243dSDimitry Andric   if (!parsed_process)
124bdd1243dSDimitry Andric     return parsed_process.takeError();
125bdd1243dSDimitry Andric 
126bdd1243dSDimitry Andric   ProcessSP process_sp = parsed_process->target_sp->GetProcessSP();
12781ad6265SDimitry Andric 
12881ad6265SDimitry Andric   for (const JSONThread &thread : process.threads)
129bdd1243dSDimitry Andric     parsed_process->threads.push_back(ParseThread(*process_sp, thread));
13081ad6265SDimitry Andric 
13181ad6265SDimitry Andric   for (const JSONModule &module : process.modules)
132bdd1243dSDimitry Andric     if (Error err = ParseModule(*parsed_process->target_sp, module))
13381ad6265SDimitry Andric       return std::move(err);
13481ad6265SDimitry Andric 
13581ad6265SDimitry Andric   if (!process.threads.empty())
13681ad6265SDimitry Andric     process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
13781ad6265SDimitry Andric 
13881ad6265SDimitry Andric   // We invoke DidAttach to create a correct stopped state for the process and
13981ad6265SDimitry Andric   // its threads.
14081ad6265SDimitry Andric   ArchSpec process_arch;
14181ad6265SDimitry Andric   process_sp->DidAttach(process_arch);
14281ad6265SDimitry Andric 
14381ad6265SDimitry Andric   return parsed_process;
14481ad6265SDimitry Andric }
14581ad6265SDimitry Andric 
146bdd1243dSDimitry Andric Expected<TraceIntelPTBundleLoader::ParsedProcess>
ParseKernel(const JSONTraceBundleDescription & bundle_description)147bdd1243dSDimitry Andric TraceIntelPTBundleLoader::ParseKernel(
148bdd1243dSDimitry Andric     const JSONTraceBundleDescription &bundle_description) {
149bdd1243dSDimitry Andric   Expected<ParsedProcess> parsed_process =
150bdd1243dSDimitry Andric       CreateEmptyProcess(kDefaultKernelProcessID, "");
151bdd1243dSDimitry Andric 
152bdd1243dSDimitry Andric   if (!parsed_process)
153bdd1243dSDimitry Andric     return parsed_process.takeError();
154bdd1243dSDimitry Andric 
155bdd1243dSDimitry Andric   ProcessSP process_sp = parsed_process->target_sp->GetProcessSP();
156bdd1243dSDimitry Andric 
157bdd1243dSDimitry Andric   // Add cpus as fake threads
158bdd1243dSDimitry Andric   for (const JSONCpu &cpu : *bundle_description.cpus) {
159bdd1243dSDimitry Andric     ThreadPostMortemTraceSP thread_sp = std::make_shared<ThreadPostMortemTrace>(
160bdd1243dSDimitry Andric         *process_sp, static_cast<lldb::tid_t>(cpu.id), FileSpec(cpu.ipt_trace));
161bdd1243dSDimitry Andric     thread_sp->SetName(formatv("kernel_cpu_{0}", cpu.id).str().c_str());
162bdd1243dSDimitry Andric     process_sp->GetThreadList().AddThread(thread_sp);
163bdd1243dSDimitry Andric     parsed_process->threads.push_back(thread_sp);
164bdd1243dSDimitry Andric   }
165bdd1243dSDimitry Andric 
166bdd1243dSDimitry Andric   // Add kernel image
167bdd1243dSDimitry Andric   FileSpec file_spec(bundle_description.kernel->file);
168bdd1243dSDimitry Andric   ModuleSpec module_spec;
169bdd1243dSDimitry Andric   module_spec.GetFileSpec() = file_spec;
170bdd1243dSDimitry Andric 
171bdd1243dSDimitry Andric   Status error;
172bdd1243dSDimitry Andric   ModuleSP module_sp =
173bdd1243dSDimitry Andric       parsed_process->target_sp->GetOrCreateModule(module_spec, false, &error);
174bdd1243dSDimitry Andric 
175bdd1243dSDimitry Andric   if (error.Fail())
176bdd1243dSDimitry Andric     return error.ToError();
177bdd1243dSDimitry Andric 
178bdd1243dSDimitry Andric   lldb::addr_t load_address =
179bdd1243dSDimitry Andric       bundle_description.kernel->load_address
180bdd1243dSDimitry Andric           ? bundle_description.kernel->load_address->value
181bdd1243dSDimitry Andric           : kDefaultKernelLoadAddress;
182bdd1243dSDimitry Andric 
183bdd1243dSDimitry Andric   bool load_addr_changed = false;
184bdd1243dSDimitry Andric   module_sp->SetLoadAddress(*parsed_process->target_sp, load_address, false,
185bdd1243dSDimitry Andric                             load_addr_changed);
186bdd1243dSDimitry Andric 
187bdd1243dSDimitry Andric   process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
188bdd1243dSDimitry Andric 
189bdd1243dSDimitry Andric   // We invoke DidAttach to create a correct stopped state for the process and
190bdd1243dSDimitry Andric   // its threads.
191bdd1243dSDimitry Andric   ArchSpec process_arch;
192bdd1243dSDimitry Andric   process_sp->DidAttach(process_arch);
193bdd1243dSDimitry Andric 
194bdd1243dSDimitry Andric   return parsed_process;
195bdd1243dSDimitry Andric }
196bdd1243dSDimitry Andric 
19781ad6265SDimitry Andric Expected<std::vector<TraceIntelPTBundleLoader::ParsedProcess>>
LoadBundle(const JSONTraceBundleDescription & bundle_description)19881ad6265SDimitry Andric TraceIntelPTBundleLoader::LoadBundle(
19981ad6265SDimitry Andric     const JSONTraceBundleDescription &bundle_description) {
20081ad6265SDimitry Andric   std::vector<ParsedProcess> parsed_processes;
20181ad6265SDimitry Andric 
20281ad6265SDimitry Andric   auto HandleError = [&](Error &&err) {
20381ad6265SDimitry Andric     // Delete all targets that were created so far in case of failures
20481ad6265SDimitry Andric     for (ParsedProcess &parsed_process : parsed_processes)
20581ad6265SDimitry Andric       m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);
20681ad6265SDimitry Andric     return std::move(err);
20781ad6265SDimitry Andric   };
20881ad6265SDimitry Andric 
209bdd1243dSDimitry Andric   if (bundle_description.processes) {
210bdd1243dSDimitry Andric     for (const JSONProcess &process : *bundle_description.processes) {
21181ad6265SDimitry Andric       if (Expected<ParsedProcess> parsed_process = ParseProcess(process))
21281ad6265SDimitry Andric         parsed_processes.push_back(std::move(*parsed_process));
21381ad6265SDimitry Andric       else
21481ad6265SDimitry Andric         return HandleError(parsed_process.takeError());
21581ad6265SDimitry Andric     }
216bdd1243dSDimitry Andric   }
217bdd1243dSDimitry Andric 
218bdd1243dSDimitry Andric   if (bundle_description.kernel) {
219bdd1243dSDimitry Andric     if (Expected<ParsedProcess> kernel_process =
220bdd1243dSDimitry Andric             ParseKernel(bundle_description))
221bdd1243dSDimitry Andric       parsed_processes.push_back(std::move(*kernel_process));
222bdd1243dSDimitry Andric     else
223bdd1243dSDimitry Andric       return HandleError(kernel_process.takeError());
224bdd1243dSDimitry Andric   }
22581ad6265SDimitry Andric 
22681ad6265SDimitry Andric   return parsed_processes;
22781ad6265SDimitry Andric }
22881ad6265SDimitry Andric 
GetSchema()22981ad6265SDimitry Andric StringRef TraceIntelPTBundleLoader::GetSchema() {
23081ad6265SDimitry Andric   static std::string schema;
23181ad6265SDimitry Andric   if (schema.empty()) {
23281ad6265SDimitry Andric     schema = R"({
23381ad6265SDimitry Andric   "type": "intel-pt",
23481ad6265SDimitry Andric   "cpuInfo": {
23581ad6265SDimitry Andric     // CPU information gotten from, for example, /proc/cpuinfo.
23681ad6265SDimitry Andric 
23781ad6265SDimitry Andric     "vendor": "GenuineIntel" | "unknown",
23881ad6265SDimitry Andric     "family": integer,
23981ad6265SDimitry Andric     "model": integer,
24081ad6265SDimitry Andric     "stepping": integer
24181ad6265SDimitry Andric   },
242bdd1243dSDimitry Andric   "processes?": [
24381ad6265SDimitry Andric     {
24481ad6265SDimitry Andric       "pid": integer,
24581ad6265SDimitry Andric       "triple"?: string,
24681ad6265SDimitry Andric           // Optional clang/llvm target triple.
247bdd1243dSDimitry Andric           // This must be provided if the trace will be created not using the
248bdd1243dSDimitry Andric           // CLI or on a machine other than where the target was traced.
24981ad6265SDimitry Andric       "threads": [
25081ad6265SDimitry Andric           // A list of known threads for the given process. When context switch
25181ad6265SDimitry Andric           // data is provided, LLDB will automatically create threads for the
25281ad6265SDimitry Andric           // this process whenever it finds new threads when traversing the
25381ad6265SDimitry Andric           // context switches, so passing values to this list in this case is
25481ad6265SDimitry Andric           // optional.
25581ad6265SDimitry Andric         {
25681ad6265SDimitry Andric           "tid": integer,
25781ad6265SDimitry Andric           "iptTrace"?: string
25881ad6265SDimitry Andric               // Path to the raw Intel PT buffer file for this thread.
25981ad6265SDimitry Andric         }
26081ad6265SDimitry Andric       ],
26181ad6265SDimitry Andric       "modules": [
26281ad6265SDimitry Andric         {
26381ad6265SDimitry Andric           "systemPath": string,
26481ad6265SDimitry Andric               // Original path of the module at runtime.
26581ad6265SDimitry Andric           "file"?: string,
26681ad6265SDimitry Andric               // Path to a copy of the file if not available at "systemPath".
26781ad6265SDimitry Andric           "loadAddress": integer | string decimal | hex string,
26881ad6265SDimitry Andric               // Lowest address of the sections of the module loaded on memory.
26981ad6265SDimitry Andric           "uuid"?: string,
27081ad6265SDimitry Andric               // Build UUID for the file for sanity checks.
27181ad6265SDimitry Andric         }
27281ad6265SDimitry Andric       ]
27381ad6265SDimitry Andric     }
27481ad6265SDimitry Andric   ],
27581ad6265SDimitry Andric   "cpus"?: [
27681ad6265SDimitry Andric     {
27781ad6265SDimitry Andric       "id": integer,
27881ad6265SDimitry Andric           // Id of this CPU core.
27981ad6265SDimitry Andric       "iptTrace": string,
28081ad6265SDimitry Andric           // Path to the raw Intel PT buffer for this cpu core.
28181ad6265SDimitry Andric       "contextSwitchTrace": string,
28281ad6265SDimitry Andric           // Path to the raw perf_event_open context switch trace file for this cpu core.
28381ad6265SDimitry Andric           // The perf_event must have been configured with PERF_SAMPLE_TID and
28481ad6265SDimitry Andric           // PERF_SAMPLE_TIME, as well as sample_id_all = 1.
28581ad6265SDimitry Andric     }
28681ad6265SDimitry Andric   ],
28781ad6265SDimitry Andric   "tscPerfZeroConversion"?: {
28881ad6265SDimitry Andric     // Values used to convert between TSCs and nanoseconds. See the time_zero
28981ad6265SDimitry Andric     // section in https://man7.org/linux/man-pages/man2/perf_event_open.2.html
2905f757f3fSDimitry Andric     // for information.
29181ad6265SDimitry Andric 
29281ad6265SDimitry Andric     "timeMult": integer,
29381ad6265SDimitry Andric     "timeShift": integer,
29481ad6265SDimitry Andric     "timeZero": integer | string decimal | hex string,
295bdd1243dSDimitry Andric   },
296bdd1243dSDimitry Andric   "kernel"?: {
297bdd1243dSDimitry Andric     "loadAddress"?: integer | string decimal | hex string,
298bdd1243dSDimitry Andric         // Kernel's image load address. Defaults to 0xffffffff81000000, which
299bdd1243dSDimitry Andric         // is a load address of x86 architecture if KASLR is not enabled.
300bdd1243dSDimitry Andric     "file": string,
301bdd1243dSDimitry Andric         // Path to the kernel image.
30281ad6265SDimitry Andric   }
30381ad6265SDimitry Andric }
30481ad6265SDimitry Andric 
30581ad6265SDimitry Andric Notes:
30681ad6265SDimitry Andric 
307bdd1243dSDimitry Andric - All paths are either absolute or relative to folder containing the bundle
308bdd1243dSDimitry Andric   description file.
30981ad6265SDimitry Andric - "cpus" is provided if and only if processes[].threads[].iptTrace is not provided.
31081ad6265SDimitry Andric - "tscPerfZeroConversion" must be provided if "cpus" is provided.
311bdd1243dSDimitry Andric - If "kernel" is provided, then the "processes" section must be empty or not
312bdd1243dSDimitry Andric   passed at all, and the "cpus" section must be provided. This configuration
313bdd1243dSDimitry Andric   indicates that the kernel was traced and user processes weren't. Besides
314bdd1243dSDimitry Andric   that, the kernel is treated as a single process with one thread per CPU
315bdd1243dSDimitry Andric   core. This doesn't handle actual kernel threads, but instead treats
316bdd1243dSDimitry Andric   all the instructions executed by the kernel on each core as an
317bdd1243dSDimitry Andric   individual thread.})";
31881ad6265SDimitry Andric   }
31981ad6265SDimitry Andric   return schema;
32081ad6265SDimitry Andric }
32181ad6265SDimitry Andric 
AugmentThreadsFromContextSwitches(JSONTraceBundleDescription & bundle_description)32281ad6265SDimitry Andric Error TraceIntelPTBundleLoader::AugmentThreadsFromContextSwitches(
32381ad6265SDimitry Andric     JSONTraceBundleDescription &bundle_description) {
324bdd1243dSDimitry Andric   if (!bundle_description.cpus || !bundle_description.processes)
32581ad6265SDimitry Andric     return Error::success();
32681ad6265SDimitry Andric 
32781ad6265SDimitry Andric   if (!bundle_description.tsc_perf_zero_conversion)
32881ad6265SDimitry Andric     return createStringError(inconvertibleErrorCode(),
32981ad6265SDimitry Andric                              "TSC to nanos conversion values are needed when "
33081ad6265SDimitry Andric                              "context switch information is provided.");
33181ad6265SDimitry Andric 
33281ad6265SDimitry Andric   DenseMap<lldb::pid_t, JSONProcess *> indexed_processes;
33381ad6265SDimitry Andric   DenseMap<JSONProcess *, DenseSet<tid_t>> indexed_threads;
33481ad6265SDimitry Andric 
335bdd1243dSDimitry Andric   for (JSONProcess &process : *bundle_description.processes) {
33681ad6265SDimitry Andric     indexed_processes[process.pid] = &process;
33781ad6265SDimitry Andric     for (JSONThread &thread : process.threads)
33881ad6265SDimitry Andric       indexed_threads[&process].insert(thread.tid);
33981ad6265SDimitry Andric   }
34081ad6265SDimitry Andric 
34181ad6265SDimitry Andric   auto on_thread_seen = [&](lldb::pid_t pid, tid_t tid) {
34281ad6265SDimitry Andric     auto proc = indexed_processes.find(pid);
34381ad6265SDimitry Andric     if (proc == indexed_processes.end())
34481ad6265SDimitry Andric       return;
34581ad6265SDimitry Andric     if (indexed_threads[proc->second].count(tid))
34681ad6265SDimitry Andric       return;
34781ad6265SDimitry Andric     indexed_threads[proc->second].insert(tid);
348*297eecfbSDimitry Andric     proc->second->threads.push_back({tid, /*ipt_trace=*/std::nullopt});
34981ad6265SDimitry Andric   };
35081ad6265SDimitry Andric 
35181ad6265SDimitry Andric   for (const JSONCpu &cpu : *bundle_description.cpus) {
35281ad6265SDimitry Andric     Error err = Trace::OnDataFileRead(
35381ad6265SDimitry Andric         FileSpec(cpu.context_switch_trace),
35481ad6265SDimitry Andric         [&](ArrayRef<uint8_t> data) -> Error {
35581ad6265SDimitry Andric           Expected<std::vector<ThreadContinuousExecution>> executions =
356bdd1243dSDimitry Andric               DecodePerfContextSwitchTrace(
357bdd1243dSDimitry Andric                   data, cpu.id, *bundle_description.tsc_perf_zero_conversion);
35881ad6265SDimitry Andric           if (!executions)
35981ad6265SDimitry Andric             return executions.takeError();
36081ad6265SDimitry Andric           for (const ThreadContinuousExecution &execution : *executions)
36181ad6265SDimitry Andric             on_thread_seen(execution.pid, execution.tid);
36281ad6265SDimitry Andric           return Error::success();
36381ad6265SDimitry Andric         });
36481ad6265SDimitry Andric     if (err)
36581ad6265SDimitry Andric       return err;
36681ad6265SDimitry Andric   }
36781ad6265SDimitry Andric   return Error::success();
36881ad6265SDimitry Andric }
36981ad6265SDimitry Andric 
CreateTraceIntelPTInstance(JSONTraceBundleDescription & bundle_description,std::vector<ParsedProcess> & parsed_processes)37081ad6265SDimitry Andric Expected<TraceSP> TraceIntelPTBundleLoader::CreateTraceIntelPTInstance(
371bdd1243dSDimitry Andric     JSONTraceBundleDescription &bundle_description,
372bdd1243dSDimitry Andric     std::vector<ParsedProcess> &parsed_processes) {
37381ad6265SDimitry Andric   std::vector<ThreadPostMortemTraceSP> threads;
37481ad6265SDimitry Andric   std::vector<ProcessSP> processes;
37581ad6265SDimitry Andric   for (const ParsedProcess &parsed_process : parsed_processes) {
37681ad6265SDimitry Andric     processes.push_back(parsed_process.target_sp->GetProcessSP());
37781ad6265SDimitry Andric     threads.insert(threads.end(), parsed_process.threads.begin(),
37881ad6265SDimitry Andric                    parsed_process.threads.end());
37981ad6265SDimitry Andric   }
38081ad6265SDimitry Andric 
381bdd1243dSDimitry Andric   TraceIntelPT::TraceMode trace_mode = bundle_description.kernel
382bdd1243dSDimitry Andric                                            ? TraceIntelPT::TraceMode::KernelMode
383bdd1243dSDimitry Andric                                            : TraceIntelPT::TraceMode::UserMode;
384bdd1243dSDimitry Andric 
38581ad6265SDimitry Andric   TraceSP trace_instance = TraceIntelPT::CreateInstanceForPostmortemTrace(
386bdd1243dSDimitry Andric       bundle_description, processes, threads, trace_mode);
38781ad6265SDimitry Andric   for (const ParsedProcess &parsed_process : parsed_processes)
38881ad6265SDimitry Andric     parsed_process.target_sp->SetTrace(trace_instance);
38981ad6265SDimitry Andric 
39081ad6265SDimitry Andric   return trace_instance;
39181ad6265SDimitry Andric }
39281ad6265SDimitry Andric 
NormalizeAllPaths(JSONTraceBundleDescription & bundle_description)39381ad6265SDimitry Andric void TraceIntelPTBundleLoader::NormalizeAllPaths(
39481ad6265SDimitry Andric     JSONTraceBundleDescription &bundle_description) {
395bdd1243dSDimitry Andric   if (bundle_description.processes) {
396bdd1243dSDimitry Andric     for (JSONProcess &process : *bundle_description.processes) {
39781ad6265SDimitry Andric       for (JSONModule &module : process.modules) {
39881ad6265SDimitry Andric         module.system_path = NormalizePath(module.system_path).GetPath();
39981ad6265SDimitry Andric         if (module.file)
40081ad6265SDimitry Andric           module.file = NormalizePath(*module.file).GetPath();
40181ad6265SDimitry Andric       }
40281ad6265SDimitry Andric       for (JSONThread &thread : process.threads) {
40381ad6265SDimitry Andric         if (thread.ipt_trace)
40481ad6265SDimitry Andric           thread.ipt_trace = NormalizePath(*thread.ipt_trace).GetPath();
40581ad6265SDimitry Andric       }
40681ad6265SDimitry Andric     }
407bdd1243dSDimitry Andric   }
40881ad6265SDimitry Andric   if (bundle_description.cpus) {
40981ad6265SDimitry Andric     for (JSONCpu &cpu : *bundle_description.cpus) {
41081ad6265SDimitry Andric       cpu.context_switch_trace =
41181ad6265SDimitry Andric           NormalizePath(cpu.context_switch_trace).GetPath();
41281ad6265SDimitry Andric       cpu.ipt_trace = NormalizePath(cpu.ipt_trace).GetPath();
41381ad6265SDimitry Andric     }
41481ad6265SDimitry Andric   }
415bdd1243dSDimitry Andric   if (bundle_description.kernel) {
416bdd1243dSDimitry Andric     bundle_description.kernel->file =
417bdd1243dSDimitry Andric         NormalizePath(bundle_description.kernel->file).GetPath();
418bdd1243dSDimitry Andric   }
41981ad6265SDimitry Andric }
42081ad6265SDimitry Andric 
Load()42181ad6265SDimitry Andric Expected<TraceSP> TraceIntelPTBundleLoader::Load() {
42281ad6265SDimitry Andric   json::Path::Root root("traceBundle");
42381ad6265SDimitry Andric   JSONTraceBundleDescription bundle_description;
42481ad6265SDimitry Andric   if (!fromJSON(m_bundle_description, bundle_description, root))
42581ad6265SDimitry Andric     return CreateJSONError(root, m_bundle_description);
42681ad6265SDimitry Andric 
42781ad6265SDimitry Andric   NormalizeAllPaths(bundle_description);
42881ad6265SDimitry Andric 
42981ad6265SDimitry Andric   if (Error err = AugmentThreadsFromContextSwitches(bundle_description))
43081ad6265SDimitry Andric     return std::move(err);
43181ad6265SDimitry Andric 
43281ad6265SDimitry Andric   if (Expected<std::vector<ParsedProcess>> parsed_processes =
43381ad6265SDimitry Andric           LoadBundle(bundle_description))
43481ad6265SDimitry Andric     return CreateTraceIntelPTInstance(bundle_description, *parsed_processes);
43581ad6265SDimitry Andric   else
43681ad6265SDimitry Andric     return parsed_processes.takeError();
43781ad6265SDimitry Andric }
438