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"
19#include "lldb/Target/Target.h"
20#include <optional>
21
22using namespace lldb;
23using namespace lldb_private;
24using namespace lldb_private::trace_intel_pt;
25using namespace llvm;
26
28 FileSpec file_spec(path);
29 if (file_spec.IsRelative())
31 return file_spec;
32}
33
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
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
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
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
93Expected<TraceIntelPTBundleLoader::ParsedProcess>
95 llvm::StringRef triple) {
96 TargetSP target_sp;
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
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
118Expected<TraceIntelPTBundleLoader::ParsedProcess>
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
146Expected<TraceIntelPTBundleLoader::ParsedProcess>
148 const JSONTraceBundleDescription &bundle_description) {
149 Expected<ParsedProcess> parsed_process =
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
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
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
197Expected<std::vector<TraceIntelPTBundleLoader::ParsedProcess>>
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
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
305Notes:
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
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) {
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
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
384
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
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) {
413 }
414 }
415 if (bundle_description.kernel) {
416 bundle_description.kernel->file =
417 NormalizePath(bundle_description.kernel->file).GetPath();
418 }
419}
420
422 json::Path::Root root("traceBundle");
423 JSONTraceBundleDescription bundle_description;
424 if (!fromJSON(m_bundle_description, bundle_description, root))
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}
static llvm::raw_ostream & error(Stream &strm)
llvm::Error Error
An architecture specification class.
Definition: ArchSpec.h:31
TargetList & GetTargetList()
Get accessor for the target list.
Definition: Debugger.h:198
A file utility class.
Definition: FileSpec.h:56
bool IsRelative() const
Returns true if the filespec represents a relative path.
Definition: FileSpec.cpp:507
void PrependPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:433
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:367
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:341
ThreadList & GetThreadList()
Definition: Process.h:2227
An error handling class.
Definition: Status.h:44
bool DeleteTarget(lldb::TargetSP &target_sp)
Delete a Target object from the list.
Definition: TargetList.cpp:371
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.
Definition: TargetList.cpp:45
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:2159
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
std::shared_ptr< lldb_private::Trace > TraceSP
Definition: lldb-forward.h:454
std::shared_ptr< lldb_private::ThreadPostMortemTrace > ThreadPostMortemTraceSP
Definition: lldb-forward.h:451
std::shared_ptr< lldb_private::Process > ProcessSP
Definition: lldb-forward.h:387
uint64_t pid_t
Definition: lldb-types.h:83
uint64_t addr_t
Definition: lldb-types.h:80
std::shared_ptr< lldb_private::Target > TargetSP
Definition: lldb-forward.h:444
uint64_t tid_t
Definition: lldb-types.h:84
std::shared_ptr< lldb_private::Module > ModuleSP
Definition: lldb-forward.h:371
Definition: Debugger.h:54
std::optional< LinuxPerfZeroTscConversion > tsc_perf_zero_conversion
std::optional< std::vector< JSONProcess > > processes
Helper struct holding the objects created when parsing a process.