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