xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
1*81ad6265SDimitry Andric //===-- TraceIntelPTBundleLoader.cpp --------------------------------------===//
2*81ad6265SDimitry Andric //
3*81ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*81ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*81ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*81ad6265SDimitry Andric //
7*81ad6265SDimitry Andric //===----------------------------------------------------------------------===//
8*81ad6265SDimitry Andric 
9*81ad6265SDimitry Andric #include "TraceIntelPTBundleLoader.h"
10*81ad6265SDimitry Andric 
11*81ad6265SDimitry Andric #include "../common/ThreadPostMortemTrace.h"
12*81ad6265SDimitry Andric #include "TraceIntelPT.h"
13*81ad6265SDimitry Andric #include "TraceIntelPTJSONStructs.h"
14*81ad6265SDimitry Andric 
15*81ad6265SDimitry Andric #include "lldb/Core/Debugger.h"
16*81ad6265SDimitry Andric #include "lldb/Core/Module.h"
17*81ad6265SDimitry Andric #include "lldb/Target/Process.h"
18*81ad6265SDimitry Andric #include "lldb/Target/Target.h"
19*81ad6265SDimitry Andric 
20*81ad6265SDimitry Andric using namespace lldb;
21*81ad6265SDimitry Andric using namespace lldb_private;
22*81ad6265SDimitry Andric using namespace lldb_private::trace_intel_pt;
23*81ad6265SDimitry Andric using namespace llvm;
24*81ad6265SDimitry Andric 
25*81ad6265SDimitry Andric FileSpec TraceIntelPTBundleLoader::NormalizePath(const std::string &path) {
26*81ad6265SDimitry Andric   FileSpec file_spec(path);
27*81ad6265SDimitry Andric   if (file_spec.IsRelative())
28*81ad6265SDimitry Andric     file_spec.PrependPathComponent(m_bundle_dir);
29*81ad6265SDimitry Andric   return file_spec;
30*81ad6265SDimitry Andric }
31*81ad6265SDimitry Andric 
32*81ad6265SDimitry Andric Error TraceIntelPTBundleLoader::ParseModule(Target &target,
33*81ad6265SDimitry Andric                                                  const JSONModule &module) {
34*81ad6265SDimitry Andric   auto do_parse = [&]() -> Error {
35*81ad6265SDimitry Andric     FileSpec system_file_spec(module.system_path);
36*81ad6265SDimitry Andric 
37*81ad6265SDimitry Andric     FileSpec local_file_spec(module.file.hasValue() ? *module.file
38*81ad6265SDimitry Andric                                                     : module.system_path);
39*81ad6265SDimitry Andric 
40*81ad6265SDimitry Andric     ModuleSpec module_spec;
41*81ad6265SDimitry Andric     module_spec.GetFileSpec() = local_file_spec;
42*81ad6265SDimitry Andric     module_spec.GetPlatformFileSpec() = system_file_spec;
43*81ad6265SDimitry Andric 
44*81ad6265SDimitry Andric     if (module.uuid.hasValue())
45*81ad6265SDimitry Andric       module_spec.GetUUID().SetFromStringRef(*module.uuid);
46*81ad6265SDimitry Andric 
47*81ad6265SDimitry Andric     Status error;
48*81ad6265SDimitry Andric     ModuleSP module_sp =
49*81ad6265SDimitry Andric         target.GetOrCreateModule(module_spec, /*notify*/ false, &error);
50*81ad6265SDimitry Andric 
51*81ad6265SDimitry Andric     if (error.Fail())
52*81ad6265SDimitry Andric       return error.ToError();
53*81ad6265SDimitry Andric 
54*81ad6265SDimitry Andric     bool load_addr_changed = false;
55*81ad6265SDimitry Andric     module_sp->SetLoadAddress(target, module.load_address.value, false,
56*81ad6265SDimitry Andric                               load_addr_changed);
57*81ad6265SDimitry Andric     return Error::success();
58*81ad6265SDimitry Andric   };
59*81ad6265SDimitry Andric   if (Error err = do_parse())
60*81ad6265SDimitry Andric     return createStringError(
61*81ad6265SDimitry Andric         inconvertibleErrorCode(), "Error when parsing module %s. %s",
62*81ad6265SDimitry Andric         module.system_path.c_str(), toString(std::move(err)).c_str());
63*81ad6265SDimitry Andric   return Error::success();
64*81ad6265SDimitry Andric }
65*81ad6265SDimitry Andric 
66*81ad6265SDimitry Andric Error TraceIntelPTBundleLoader::CreateJSONError(json::Path::Root &root,
67*81ad6265SDimitry Andric                                                      const json::Value &value) {
68*81ad6265SDimitry Andric   std::string err;
69*81ad6265SDimitry Andric   raw_string_ostream os(err);
70*81ad6265SDimitry Andric   root.printErrorContext(value, os);
71*81ad6265SDimitry Andric   return createStringError(
72*81ad6265SDimitry Andric       std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s",
73*81ad6265SDimitry Andric       toString(root.getError()).c_str(), os.str().c_str(), GetSchema().data());
74*81ad6265SDimitry Andric }
75*81ad6265SDimitry Andric 
76*81ad6265SDimitry Andric ThreadPostMortemTraceSP
77*81ad6265SDimitry Andric TraceIntelPTBundleLoader::ParseThread(Process &process,
78*81ad6265SDimitry Andric                                            const JSONThread &thread) {
79*81ad6265SDimitry Andric   lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
80*81ad6265SDimitry Andric 
81*81ad6265SDimitry Andric   Optional<FileSpec> trace_file;
82*81ad6265SDimitry Andric   if (thread.ipt_trace)
83*81ad6265SDimitry Andric     trace_file = FileSpec(*thread.ipt_trace);
84*81ad6265SDimitry Andric 
85*81ad6265SDimitry Andric   ThreadPostMortemTraceSP thread_sp =
86*81ad6265SDimitry Andric       std::make_shared<ThreadPostMortemTrace>(process, tid, trace_file);
87*81ad6265SDimitry Andric   process.GetThreadList().AddThread(thread_sp);
88*81ad6265SDimitry Andric   return thread_sp;
89*81ad6265SDimitry Andric }
90*81ad6265SDimitry Andric 
91*81ad6265SDimitry Andric Expected<TraceIntelPTBundleLoader::ParsedProcess>
92*81ad6265SDimitry Andric TraceIntelPTBundleLoader::ParseProcess(const JSONProcess &process) {
93*81ad6265SDimitry Andric   TargetSP target_sp;
94*81ad6265SDimitry Andric   Status error = m_debugger.GetTargetList().CreateTarget(
95*81ad6265SDimitry Andric       m_debugger, /*user_exe_path*/ StringRef(), process.triple.value_or(""),
96*81ad6265SDimitry Andric       eLoadDependentsNo,
97*81ad6265SDimitry Andric       /*platform_options*/ nullptr, target_sp);
98*81ad6265SDimitry Andric 
99*81ad6265SDimitry Andric   if (!target_sp)
100*81ad6265SDimitry Andric     return error.ToError();
101*81ad6265SDimitry Andric 
102*81ad6265SDimitry Andric   ParsedProcess parsed_process;
103*81ad6265SDimitry Andric   parsed_process.target_sp = target_sp;
104*81ad6265SDimitry Andric 
105*81ad6265SDimitry Andric   ProcessSP process_sp = target_sp->CreateProcess(
106*81ad6265SDimitry Andric       /*listener*/ nullptr, "trace",
107*81ad6265SDimitry Andric       /*crash_file*/ nullptr,
108*81ad6265SDimitry Andric       /*can_connect*/ false);
109*81ad6265SDimitry Andric 
110*81ad6265SDimitry Andric   process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
111*81ad6265SDimitry Andric 
112*81ad6265SDimitry Andric   for (const JSONThread &thread : process.threads)
113*81ad6265SDimitry Andric     parsed_process.threads.push_back(ParseThread(*process_sp, thread));
114*81ad6265SDimitry Andric 
115*81ad6265SDimitry Andric   for (const JSONModule &module : process.modules)
116*81ad6265SDimitry Andric     if (Error err = ParseModule(*target_sp, module))
117*81ad6265SDimitry Andric       return std::move(err);
118*81ad6265SDimitry Andric 
119*81ad6265SDimitry Andric   if (!process.threads.empty())
120*81ad6265SDimitry Andric     process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
121*81ad6265SDimitry Andric 
122*81ad6265SDimitry Andric   // We invoke DidAttach to create a correct stopped state for the process and
123*81ad6265SDimitry Andric   // its threads.
124*81ad6265SDimitry Andric   ArchSpec process_arch;
125*81ad6265SDimitry Andric   process_sp->DidAttach(process_arch);
126*81ad6265SDimitry Andric 
127*81ad6265SDimitry Andric   return parsed_process;
128*81ad6265SDimitry Andric }
129*81ad6265SDimitry Andric 
130*81ad6265SDimitry Andric Expected<std::vector<TraceIntelPTBundleLoader::ParsedProcess>>
131*81ad6265SDimitry Andric TraceIntelPTBundleLoader::LoadBundle(
132*81ad6265SDimitry Andric     const JSONTraceBundleDescription &bundle_description) {
133*81ad6265SDimitry Andric   std::vector<ParsedProcess> parsed_processes;
134*81ad6265SDimitry Andric 
135*81ad6265SDimitry Andric   auto HandleError = [&](Error &&err) {
136*81ad6265SDimitry Andric     // Delete all targets that were created so far in case of failures
137*81ad6265SDimitry Andric     for (ParsedProcess &parsed_process : parsed_processes)
138*81ad6265SDimitry Andric       m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);
139*81ad6265SDimitry Andric     return std::move(err);
140*81ad6265SDimitry Andric   };
141*81ad6265SDimitry Andric 
142*81ad6265SDimitry Andric   for (const JSONProcess &process : bundle_description.processes) {
143*81ad6265SDimitry Andric     if (Expected<ParsedProcess> parsed_process = ParseProcess(process))
144*81ad6265SDimitry Andric       parsed_processes.push_back(std::move(*parsed_process));
145*81ad6265SDimitry Andric     else
146*81ad6265SDimitry Andric       return HandleError(parsed_process.takeError());
147*81ad6265SDimitry Andric   }
148*81ad6265SDimitry Andric 
149*81ad6265SDimitry Andric   return parsed_processes;
150*81ad6265SDimitry Andric }
151*81ad6265SDimitry Andric 
152*81ad6265SDimitry Andric StringRef TraceIntelPTBundleLoader::GetSchema() {
153*81ad6265SDimitry Andric   static std::string schema;
154*81ad6265SDimitry Andric   if (schema.empty()) {
155*81ad6265SDimitry Andric     schema = R"({
156*81ad6265SDimitry Andric   "type": "intel-pt",
157*81ad6265SDimitry Andric   "cpuInfo": {
158*81ad6265SDimitry Andric     // CPU information gotten from, for example, /proc/cpuinfo.
159*81ad6265SDimitry Andric 
160*81ad6265SDimitry Andric     "vendor": "GenuineIntel" | "unknown",
161*81ad6265SDimitry Andric     "family": integer,
162*81ad6265SDimitry Andric     "model": integer,
163*81ad6265SDimitry Andric     "stepping": integer
164*81ad6265SDimitry Andric   },
165*81ad6265SDimitry Andric   "processes": [
166*81ad6265SDimitry Andric     {
167*81ad6265SDimitry Andric       "pid": integer,
168*81ad6265SDimitry Andric       "triple"?: string,
169*81ad6265SDimitry Andric           // Optional clang/llvm target triple.
170*81ad6265SDimitry Andric       "threads": [
171*81ad6265SDimitry Andric           // A list of known threads for the given process. When context switch
172*81ad6265SDimitry Andric           // data is provided, LLDB will automatically create threads for the
173*81ad6265SDimitry Andric           // this process whenever it finds new threads when traversing the
174*81ad6265SDimitry Andric           // context switches, so passing values to this list in this case is
175*81ad6265SDimitry Andric           // optional.
176*81ad6265SDimitry Andric         {
177*81ad6265SDimitry Andric           "tid": integer,
178*81ad6265SDimitry Andric           "iptTrace"?: string
179*81ad6265SDimitry Andric               // Path to the raw Intel PT buffer file for this thread.
180*81ad6265SDimitry Andric         }
181*81ad6265SDimitry Andric       ],
182*81ad6265SDimitry Andric       "modules": [
183*81ad6265SDimitry Andric         {
184*81ad6265SDimitry Andric           "systemPath": string,
185*81ad6265SDimitry Andric               // Original path of the module at runtime.
186*81ad6265SDimitry Andric           "file"?: string,
187*81ad6265SDimitry Andric               // Path to a copy of the file if not available at "systemPath".
188*81ad6265SDimitry Andric           "loadAddress": integer | string decimal | hex string,
189*81ad6265SDimitry Andric               // Lowest address of the sections of the module loaded on memory.
190*81ad6265SDimitry Andric           "uuid"?: string,
191*81ad6265SDimitry Andric               // Build UUID for the file for sanity checks.
192*81ad6265SDimitry Andric         }
193*81ad6265SDimitry Andric       ]
194*81ad6265SDimitry Andric     }
195*81ad6265SDimitry Andric   ],
196*81ad6265SDimitry Andric   "cpus"?: [
197*81ad6265SDimitry Andric     {
198*81ad6265SDimitry Andric       "id": integer,
199*81ad6265SDimitry Andric           // Id of this CPU core.
200*81ad6265SDimitry Andric       "iptTrace": string,
201*81ad6265SDimitry Andric           // Path to the raw Intel PT buffer for this cpu core.
202*81ad6265SDimitry Andric       "contextSwitchTrace": string,
203*81ad6265SDimitry Andric           // Path to the raw perf_event_open context switch trace file for this cpu core.
204*81ad6265SDimitry Andric           // The perf_event must have been configured with PERF_SAMPLE_TID and
205*81ad6265SDimitry Andric           // PERF_SAMPLE_TIME, as well as sample_id_all = 1.
206*81ad6265SDimitry Andric     }
207*81ad6265SDimitry Andric   ],
208*81ad6265SDimitry Andric   "tscPerfZeroConversion"?: {
209*81ad6265SDimitry Andric     // Values used to convert between TSCs and nanoseconds. See the time_zero
210*81ad6265SDimitry Andric     // section in https://man7.org/linux/man-pages/man2/perf_event_open.2.html
211*81ad6265SDimitry Andric     // for for information.
212*81ad6265SDimitry Andric 
213*81ad6265SDimitry Andric     "timeMult": integer,
214*81ad6265SDimitry Andric     "timeShift": integer,
215*81ad6265SDimitry Andric     "timeZero": integer | string decimal | hex string,
216*81ad6265SDimitry Andric   }
217*81ad6265SDimitry Andric }
218*81ad6265SDimitry Andric 
219*81ad6265SDimitry Andric Notes:
220*81ad6265SDimitry Andric 
221*81ad6265SDimitry Andric - All paths are either absolute or relative to folder containing the bundle description file.
222*81ad6265SDimitry Andric - "cpus" is provided if and only if processes[].threads[].iptTrace is not provided.
223*81ad6265SDimitry Andric - "tscPerfZeroConversion" must be provided if "cpus" is provided.
224*81ad6265SDimitry Andric  })";
225*81ad6265SDimitry Andric   }
226*81ad6265SDimitry Andric   return schema;
227*81ad6265SDimitry Andric }
228*81ad6265SDimitry Andric 
229*81ad6265SDimitry Andric Error TraceIntelPTBundleLoader::AugmentThreadsFromContextSwitches(
230*81ad6265SDimitry Andric     JSONTraceBundleDescription &bundle_description) {
231*81ad6265SDimitry Andric   if (!bundle_description.cpus)
232*81ad6265SDimitry Andric     return Error::success();
233*81ad6265SDimitry Andric 
234*81ad6265SDimitry Andric   if (!bundle_description.tsc_perf_zero_conversion)
235*81ad6265SDimitry Andric     return createStringError(inconvertibleErrorCode(),
236*81ad6265SDimitry Andric                              "TSC to nanos conversion values are needed when "
237*81ad6265SDimitry Andric                              "context switch information is provided.");
238*81ad6265SDimitry Andric 
239*81ad6265SDimitry Andric   DenseMap<lldb::pid_t, JSONProcess *> indexed_processes;
240*81ad6265SDimitry Andric   DenseMap<JSONProcess *, DenseSet<tid_t>> indexed_threads;
241*81ad6265SDimitry Andric 
242*81ad6265SDimitry Andric   for (JSONProcess &process : bundle_description.processes) {
243*81ad6265SDimitry Andric     indexed_processes[process.pid] = &process;
244*81ad6265SDimitry Andric     for (JSONThread &thread : process.threads)
245*81ad6265SDimitry Andric       indexed_threads[&process].insert(thread.tid);
246*81ad6265SDimitry Andric   }
247*81ad6265SDimitry Andric 
248*81ad6265SDimitry Andric   auto on_thread_seen = [&](lldb::pid_t pid, tid_t tid) {
249*81ad6265SDimitry Andric     auto proc = indexed_processes.find(pid);
250*81ad6265SDimitry Andric     if (proc == indexed_processes.end())
251*81ad6265SDimitry Andric       return;
252*81ad6265SDimitry Andric     if (indexed_threads[proc->second].count(tid))
253*81ad6265SDimitry Andric       return;
254*81ad6265SDimitry Andric     indexed_threads[proc->second].insert(tid);
255*81ad6265SDimitry Andric     proc->second->threads.push_back({tid, /*ipt_trace=*/None});
256*81ad6265SDimitry Andric   };
257*81ad6265SDimitry Andric 
258*81ad6265SDimitry Andric   for (const JSONCpu &cpu : *bundle_description.cpus) {
259*81ad6265SDimitry Andric     Error err = Trace::OnDataFileRead(
260*81ad6265SDimitry Andric         FileSpec(cpu.context_switch_trace),
261*81ad6265SDimitry Andric         [&](ArrayRef<uint8_t> data) -> Error {
262*81ad6265SDimitry Andric           Expected<std::vector<ThreadContinuousExecution>> executions =
263*81ad6265SDimitry Andric               DecodePerfContextSwitchTrace(data, cpu.id,
264*81ad6265SDimitry Andric                                            *bundle_description.tsc_perf_zero_conversion);
265*81ad6265SDimitry Andric           if (!executions)
266*81ad6265SDimitry Andric             return executions.takeError();
267*81ad6265SDimitry Andric           for (const ThreadContinuousExecution &execution : *executions)
268*81ad6265SDimitry Andric             on_thread_seen(execution.pid, execution.tid);
269*81ad6265SDimitry Andric           return Error::success();
270*81ad6265SDimitry Andric         });
271*81ad6265SDimitry Andric     if (err)
272*81ad6265SDimitry Andric       return err;
273*81ad6265SDimitry Andric   }
274*81ad6265SDimitry Andric   return Error::success();
275*81ad6265SDimitry Andric }
276*81ad6265SDimitry Andric 
277*81ad6265SDimitry Andric Expected<TraceSP> TraceIntelPTBundleLoader::CreateTraceIntelPTInstance(
278*81ad6265SDimitry Andric     JSONTraceBundleDescription &bundle_description, std::vector<ParsedProcess> &parsed_processes) {
279*81ad6265SDimitry Andric   std::vector<ThreadPostMortemTraceSP> threads;
280*81ad6265SDimitry Andric   std::vector<ProcessSP> processes;
281*81ad6265SDimitry Andric   for (const ParsedProcess &parsed_process : parsed_processes) {
282*81ad6265SDimitry Andric     processes.push_back(parsed_process.target_sp->GetProcessSP());
283*81ad6265SDimitry Andric     threads.insert(threads.end(), parsed_process.threads.begin(),
284*81ad6265SDimitry Andric                    parsed_process.threads.end());
285*81ad6265SDimitry Andric   }
286*81ad6265SDimitry Andric 
287*81ad6265SDimitry Andric   TraceSP trace_instance = TraceIntelPT::CreateInstanceForPostmortemTrace(
288*81ad6265SDimitry Andric       bundle_description, processes, threads);
289*81ad6265SDimitry Andric   for (const ParsedProcess &parsed_process : parsed_processes)
290*81ad6265SDimitry Andric     parsed_process.target_sp->SetTrace(trace_instance);
291*81ad6265SDimitry Andric 
292*81ad6265SDimitry Andric   return trace_instance;
293*81ad6265SDimitry Andric }
294*81ad6265SDimitry Andric 
295*81ad6265SDimitry Andric void TraceIntelPTBundleLoader::NormalizeAllPaths(
296*81ad6265SDimitry Andric     JSONTraceBundleDescription &bundle_description) {
297*81ad6265SDimitry Andric   for (JSONProcess &process : bundle_description.processes) {
298*81ad6265SDimitry Andric     for (JSONModule &module : process.modules) {
299*81ad6265SDimitry Andric       module.system_path = NormalizePath(module.system_path).GetPath();
300*81ad6265SDimitry Andric       if (module.file)
301*81ad6265SDimitry Andric         module.file = NormalizePath(*module.file).GetPath();
302*81ad6265SDimitry Andric     }
303*81ad6265SDimitry Andric     for (JSONThread &thread : process.threads) {
304*81ad6265SDimitry Andric       if (thread.ipt_trace)
305*81ad6265SDimitry Andric         thread.ipt_trace = NormalizePath(*thread.ipt_trace).GetPath();
306*81ad6265SDimitry Andric     }
307*81ad6265SDimitry Andric   }
308*81ad6265SDimitry Andric   if (bundle_description.cpus) {
309*81ad6265SDimitry Andric     for (JSONCpu &cpu : *bundle_description.cpus) {
310*81ad6265SDimitry Andric       cpu.context_switch_trace =
311*81ad6265SDimitry Andric           NormalizePath(cpu.context_switch_trace).GetPath();
312*81ad6265SDimitry Andric       cpu.ipt_trace = NormalizePath(cpu.ipt_trace).GetPath();
313*81ad6265SDimitry Andric     }
314*81ad6265SDimitry Andric   }
315*81ad6265SDimitry Andric }
316*81ad6265SDimitry Andric 
317*81ad6265SDimitry Andric Expected<TraceSP> TraceIntelPTBundleLoader::Load() {
318*81ad6265SDimitry Andric   json::Path::Root root("traceBundle");
319*81ad6265SDimitry Andric   JSONTraceBundleDescription bundle_description;
320*81ad6265SDimitry Andric   if (!fromJSON(m_bundle_description, bundle_description, root))
321*81ad6265SDimitry Andric     return CreateJSONError(root, m_bundle_description);
322*81ad6265SDimitry Andric 
323*81ad6265SDimitry Andric   NormalizeAllPaths(bundle_description);
324*81ad6265SDimitry Andric 
325*81ad6265SDimitry Andric   if (Error err = AugmentThreadsFromContextSwitches(bundle_description))
326*81ad6265SDimitry Andric     return std::move(err);
327*81ad6265SDimitry Andric 
328*81ad6265SDimitry Andric   if (Expected<std::vector<ParsedProcess>> parsed_processes =
329*81ad6265SDimitry Andric           LoadBundle(bundle_description))
330*81ad6265SDimitry Andric     return CreateTraceIntelPTInstance(bundle_description, *parsed_processes);
331*81ad6265SDimitry Andric   else
332*81ad6265SDimitry Andric     return parsed_processes.takeError();
333*81ad6265SDimitry Andric }
334