LLDB mainline
TraceIntelPTBundleLoader.cpp
Go to the documentation of this file.
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
10
11#include "../common/ThreadPostMortemTrace.h"
12#include "TraceIntelPT.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
21using namespace lldb;
22using namespace lldb_private;
23using namespace lldb_private::trace_intel_pt;
24using namespace llvm;
25
27 FileSpec file_spec(path);
28 if (file_spec.IsRelative())
30 return file_spec;
31}
32
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
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
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
77ThreadPostMortemTraceSP
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
92Expected<TraceIntelPTBundleLoader::ParsedProcess>
94 llvm::StringRef triple) {
95 TargetSP target_sp;
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
116Expected<TraceIntelPTBundleLoader::ParsedProcess>
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
144Expected<TraceIntelPTBundleLoader::ParsedProcess>
146 const JSONTraceBundleDescription &bundle_description) {
147 Expected<ParsedProcess> parsed_process =
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
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
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
195Expected<std::vector<TraceIntelPTBundleLoader::ParsedProcess>>
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
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
303Notes:
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
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) {
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
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
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
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) {
411 }
412 }
413 if (bundle_description.kernel) {
414 bundle_description.kernel->file =
415 NormalizePath(bundle_description.kernel->file).GetPath();
416 }
417}
418
420 json::Path::Root root("traceBundle");
421 JSONTraceBundleDescription bundle_description;
422 if (!fromJSON(m_bundle_description, bundle_description, root))
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}
static llvm::raw_ostream & error(Stream &strm)
llvm::Error Error
An architecture specification class.
Definition: ArchSpec.h:32
TargetList & GetTargetList()
Get accessor for the target list.
Definition: Debugger.h:200
A file utility class.
Definition: FileSpec.h:56
bool IsRelative() const
Returns true if the filespec represents a relative path.
Definition: FileSpec.cpp:493
void PrependPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:439
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:366
FileSpec & GetPlatformFileSpec()
Definition: ModuleSpec.h:65
FileSpec & GetFileSpec()
Definition: ModuleSpec.h:53
A plug-in interface definition class for debugging a process.
Definition: Process.h:342
ThreadList & GetThreadList()
Definition: Process.h:2095
An error handling class.
Definition: Status.h:44
Status CreateTarget(Debugger &debugger, llvm::StringRef user_exe_path, llvm::StringRef triple_str, LoadDependentFiles get_dependent_modules, const OptionGroupPlatform *platform_options, lldb::TargetSP &target_sp)
Create a new Target.
bool DeleteTarget(lldb::TargetSP &target_sp)
Delete a Target object from the list.
Definition: TargetList.cpp:369
lldb::ModuleSP GetOrCreateModule(const ModuleSpec &module_spec, bool notify, Status *error_ptr=nullptr)
Find a binary on the system and return its Module, or return an existing Module that is already in th...
Definition: Target.cpp:2111
void AddThread(const lldb::ThreadSP &thread_sp)
static llvm::Error OnDataFileRead(FileSpec file, OnBinaryDataReadCallback callback)
Helper method for reading a data file and passing its data to the given callback.
Definition: Trace.cpp:437
bool SetFromStringRef(llvm::StringRef str)
Definition: UUID.cpp:97
llvm::Error AugmentThreadsFromContextSwitches(JSONTraceBundleDescription &bundle_description)
When applicable, augment the list of threads in the trace bundle by inspecting the context switch tra...
llvm::Expected< lldb::TraceSP > CreateTraceIntelPTInstance(JSONTraceBundleDescription &bundle_description, std::vector< ParsedProcess > &parsed_processes)
Given a bundle description and a list of fully parsed processes, create an actual Trace instance that...
void NormalizeAllPaths(JSONTraceBundleDescription &bundle_description)
Modifiy the bundle description by normalizing all the paths relative to the session file directory.
llvm::Expected< std::vector< ParsedProcess > > LoadBundle(const JSONTraceBundleDescription &bundle_description)
Create the corresponding Process, Thread and Module objects given this bundle description.
lldb::ThreadPostMortemTraceSP ParseThread(Process &process, const JSONThread &thread)
Create a post-mortem thread associated with the given process using the definition from thread.
llvm::Error CreateJSONError(llvm::json::Path::Root &root, const llvm::json::Value &value)
Create a user-friendly error message upon a JSON-parsing failure using the json::ObjectMapper functio...
llvm::Expected< lldb::TraceSP > Load()
Parse the trace bundle description and create the corresponding Target objects.
FileSpec NormalizePath(const std::string &path)
Resolve non-absolute paths relative to the bundle folder.
llvm::Expected< ParsedProcess > CreateEmptyProcess(lldb::pid_t pid, llvm::StringRef triple)
Create an empty Process object with given pid and target.
llvm::Expected< ParsedProcess > ParseProcess(const JSONProcess &process)
Create the corresponding Threads and Process objects given the JSON process definition.
llvm::Expected< ParsedProcess > ParseKernel(const JSONTraceBundleDescription &bundle_description)
Create a kernel process and cpu threads given the JSON kernel definition.
llvm::Error ParseModule(Target &target, const JSONModule &module)
Create a module associated with the given target using the definition from module.
static TraceIntelPTSP CreateInstanceForPostmortemTrace(JSONTraceBundleDescription &bundle_description, llvm::ArrayRef< lldb::ProcessSP > traced_processes, llvm::ArrayRef< lldb::ThreadPostMortemTraceSP > traced_threads, TraceMode trace_mode)
Postmortem trace constructor.
const lldb::pid_t kDefaultKernelProcessID
bool fromJSON(const json::Value &value, JSONModule &module, Path path)
const lldb::addr_t kDefaultKernelLoadAddress
A class that represents a running process on the host machine.
const char * toString(AppleArm64ExceptionClass EC)
Definition: SBAddress.h:15
uint64_t pid_t
Definition: lldb-types.h:85
uint64_t addr_t
Definition: lldb-types.h:83
uint64_t tid_t
Definition: lldb-types.h:86
Definition: Debugger.h:51
std::optional< LinuxPerfZeroTscConversion > tsc_perf_zero_conversion
std::optional< std::vector< JSONProcess > > processes
Helper struct holding the objects created when parsing a process.