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