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
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 // This should instead try to directly create an instance of ProcessTrace.
107 // ProcessSP process_sp = target_sp->CreateProcess(
108 // /*listener*/ nullptr, "trace",
109 // /*crash_file*/ nullptr,
110 // /*can_connect*/ false);
111
112 process_sp->SetID(static_cast<lldb::pid_t>(pid));
113
114 return parsed_process;
115}
116
117Expected<TraceIntelPTBundleLoader::ParsedProcess>
119 Expected<ParsedProcess> parsed_process =
120 CreateEmptyProcess(process.pid, process.triple.value_or(""));
121
122 if (!parsed_process)
123 return parsed_process.takeError();
124
125 ProcessSP process_sp = parsed_process->target_sp->GetProcessSP();
126
127 for (const JSONThread &thread : process.threads)
128 parsed_process->threads.push_back(ParseThread(*process_sp, thread));
129
130 for (const JSONModule &module : process.modules)
131 if (Error err = ParseModule(*parsed_process->target_sp, module))
132 return std::move(err);
133
134 if (!process.threads.empty())
135 process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
136
137 // We invoke DidAttach to create a correct stopped state for the process and
138 // its threads.
139 ArchSpec process_arch;
140 process_sp->DidAttach(process_arch);
141
142 return parsed_process;
143}
144
145Expected<TraceIntelPTBundleLoader::ParsedProcess>
147 const JSONTraceBundleDescription &bundle_description) {
148 Expected<ParsedProcess> parsed_process =
150
151 if (!parsed_process)
152 return parsed_process.takeError();
153
154 ProcessSP process_sp = parsed_process->target_sp->GetProcessSP();
155
156 // Add cpus as fake threads
157 for (const JSONCpu &cpu : *bundle_description.cpus) {
158 ThreadPostMortemTraceSP thread_sp = std::make_shared<ThreadPostMortemTrace>(
159 *process_sp, static_cast<lldb::tid_t>(cpu.id), FileSpec(cpu.ipt_trace));
160 thread_sp->SetName(formatv("kernel_cpu_{0}", cpu.id).str().c_str());
161 process_sp->GetThreadList().AddThread(thread_sp);
162 parsed_process->threads.push_back(thread_sp);
163 }
164
165 // Add kernel image
166 FileSpec file_spec(bundle_description.kernel->file);
167 ModuleSpec module_spec;
168 module_spec.GetFileSpec() = file_spec;
169
171 ModuleSP module_sp =
172 parsed_process->target_sp->GetOrCreateModule(module_spec, false, &error);
173
174 if (error.Fail())
175 return error.ToError();
176
177 lldb::addr_t load_address =
178 bundle_description.kernel->load_address
179 ? bundle_description.kernel->load_address->value
181
182 bool load_addr_changed = false;
183 module_sp->SetLoadAddress(*parsed_process->target_sp, load_address, false,
184 load_addr_changed);
185
186 process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
187
188 // We invoke DidAttach to create a correct stopped state for the process and
189 // its threads.
190 ArchSpec process_arch;
191 process_sp->DidAttach(process_arch);
192
193 return parsed_process;
194}
195
196Expected<std::vector<TraceIntelPTBundleLoader::ParsedProcess>>
198 const JSONTraceBundleDescription &bundle_description) {
199 std::vector<ParsedProcess> parsed_processes;
200
201 auto HandleError = [&](Error &&err) {
202 // Delete all targets that were created so far in case of failures
203 for (ParsedProcess &parsed_process : parsed_processes)
204 m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);
205 return std::move(err);
206 };
207
208 if (bundle_description.processes) {
209 for (const JSONProcess &process : *bundle_description.processes) {
210 if (Expected<ParsedProcess> parsed_process = ParseProcess(process))
211 parsed_processes.push_back(std::move(*parsed_process));
212 else
213 return HandleError(parsed_process.takeError());
214 }
215 }
216
217 if (bundle_description.kernel) {
218 if (Expected<ParsedProcess> kernel_process =
219 ParseKernel(bundle_description))
220 parsed_processes.push_back(std::move(*kernel_process));
221 else
222 return HandleError(kernel_process.takeError());
223 }
224
225 return parsed_processes;
226}
227
229 static std::string schema;
230 if (schema.empty()) {
231 schema = R"({
232 "type": "intel-pt",
233 "cpuInfo": {
234 // CPU information gotten from, for example, /proc/cpuinfo.
235
236 "vendor": "GenuineIntel" | "unknown",
237 "family": integer,
238 "model": integer,
239 "stepping": integer
240 },
241 "processes?": [
242 {
243 "pid": integer,
244 "triple"?: string,
245 // Optional clang/llvm target triple.
246 // This must be provided if the trace will be created not using the
247 // CLI or on a machine other than where the target was traced.
248 "threads": [
249 // A list of known threads for the given process. When context switch
250 // data is provided, LLDB will automatically create threads for the
251 // this process whenever it finds new threads when traversing the
252 // context switches, so passing values to this list in this case is
253 // optional.
254 {
255 "tid": integer,
256 "iptTrace"?: string
257 // Path to the raw Intel PT buffer file for this thread.
258 }
259 ],
260 "modules": [
261 {
262 "systemPath": string,
263 // Original path of the module at runtime.
264 "file"?: string,
265 // Path to a copy of the file if not available at "systemPath".
266 "loadAddress": integer | string decimal | hex string,
267 // Lowest address of the sections of the module loaded on memory.
268 "uuid"?: string,
269 // Build UUID for the file for sanity checks.
270 }
271 ]
272 }
273 ],
274 "cpus"?: [
275 {
276 "id": integer,
277 // Id of this CPU core.
278 "iptTrace": string,
279 // Path to the raw Intel PT buffer for this cpu core.
280 "contextSwitchTrace": string,
281 // Path to the raw perf_event_open context switch trace file for this cpu core.
282 // The perf_event must have been configured with PERF_SAMPLE_TID and
283 // PERF_SAMPLE_TIME, as well as sample_id_all = 1.
284 }
285 ],
286 "tscPerfZeroConversion"?: {
287 // Values used to convert between TSCs and nanoseconds. See the time_zero
288 // section in https://man7.org/linux/man-pages/man2/perf_event_open.2.html
289 // for information.
290
291 "timeMult": integer,
292 "timeShift": integer,
293 "timeZero": integer | string decimal | hex string,
294 },
295 "kernel"?: {
296 "loadAddress"?: integer | string decimal | hex string,
297 // Kernel's image load address. Defaults to 0xffffffff81000000, which
298 // is a load address of x86 architecture if KASLR is not enabled.
299 "file": string,
300 // Path to the kernel image.
301 }
302}
303
304Notes:
305
306- All paths are either absolute or relative to folder containing the bundle
307 description file.
308- "cpus" is provided if and only if processes[].threads[].iptTrace is not provided.
309- "tscPerfZeroConversion" must be provided if "cpus" is provided.
310- If "kernel" is provided, then the "processes" section must be empty or not
311 passed at all, and the "cpus" section must be provided. This configuration
312 indicates that the kernel was traced and user processes weren't. Besides
313 that, the kernel is treated as a single process with one thread per CPU
314 core. This doesn't handle actual kernel threads, but instead treats
315 all the instructions executed by the kernel on each core as an
316 individual thread.})";
317 }
318 return schema;
319}
320
322 JSONTraceBundleDescription &bundle_description) {
323 if (!bundle_description.cpus || !bundle_description.processes)
324 return Error::success();
325
326 if (!bundle_description.tsc_perf_zero_conversion)
327 return createStringError(inconvertibleErrorCode(),
328 "TSC to nanos conversion values are needed when "
329 "context switch information is provided.");
330
331 DenseMap<lldb::pid_t, JSONProcess *> indexed_processes;
332 DenseMap<JSONProcess *, DenseSet<tid_t>> indexed_threads;
333
334 for (JSONProcess &process : *bundle_description.processes) {
335 indexed_processes[process.pid] = &process;
336 for (JSONThread &thread : process.threads)
337 indexed_threads[&process].insert(thread.tid);
338 }
339
340 auto on_thread_seen = [&](lldb::pid_t pid, tid_t tid) {
341 auto proc = indexed_processes.find(pid);
342 if (proc == indexed_processes.end())
343 return;
344 if (indexed_threads[proc->second].count(tid))
345 return;
346 indexed_threads[proc->second].insert(tid);
347 proc->second->threads.push_back({tid, /*ipt_trace=*/None});
348 };
349
350 for (const JSONCpu &cpu : *bundle_description.cpus) {
353 [&](ArrayRef<uint8_t> data) -> Error {
354 Expected<std::vector<ThreadContinuousExecution>> executions =
355 DecodePerfContextSwitchTrace(
356 data, cpu.id, *bundle_description.tsc_perf_zero_conversion);
357 if (!executions)
358 return executions.takeError();
359 for (const ThreadContinuousExecution &execution : *executions)
360 on_thread_seen(execution.pid, execution.tid);
361 return Error::success();
362 });
363 if (err)
364 return err;
365 }
366 return Error::success();
367}
368
370 JSONTraceBundleDescription &bundle_description,
371 std::vector<ParsedProcess> &parsed_processes) {
372 std::vector<ThreadPostMortemTraceSP> threads;
373 std::vector<ProcessSP> processes;
374 for (const ParsedProcess &parsed_process : parsed_processes) {
375 processes.push_back(parsed_process.target_sp->GetProcessSP());
376 threads.insert(threads.end(), parsed_process.threads.begin(),
377 parsed_process.threads.end());
378 }
379
380 TraceIntelPT::TraceMode trace_mode = bundle_description.kernel
383
385 bundle_description, processes, threads, trace_mode);
386 for (const ParsedProcess &parsed_process : parsed_processes)
387 parsed_process.target_sp->SetTrace(trace_instance);
388
389 return trace_instance;
390}
391
393 JSONTraceBundleDescription &bundle_description) {
394 if (bundle_description.processes) {
395 for (JSONProcess &process : *bundle_description.processes) {
396 for (JSONModule &module : process.modules) {
397 module.system_path = NormalizePath(module.system_path).GetPath();
398 if (module.file)
399 module.file = NormalizePath(*module.file).GetPath();
400 }
401 for (JSONThread &thread : process.threads) {
402 if (thread.ipt_trace)
403 thread.ipt_trace = NormalizePath(*thread.ipt_trace).GetPath();
404 }
405 }
406 }
407 if (bundle_description.cpus) {
408 for (JSONCpu &cpu : *bundle_description.cpus) {
412 }
413 }
414 if (bundle_description.kernel) {
415 bundle_description.kernel->file =
416 NormalizePath(bundle_description.kernel->file).GetPath();
417 }
418}
419
421 json::Path::Root root("traceBundle");
422 JSONTraceBundleDescription bundle_description;
423 if (!fromJSON(m_bundle_description, bundle_description, root))
425
426 NormalizeAllPaths(bundle_description);
427
428 if (Error err = AugmentThreadsFromContextSwitches(bundle_description))
429 return std::move(err);
430
431 if (Expected<std::vector<ParsedProcess>> parsed_processes =
432 LoadBundle(bundle_description))
433 return CreateTraceIntelPTInstance(bundle_description, *parsed_processes);
434 else
435 return parsed_processes.takeError();
436}
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:205
A file utility class.
Definition: FileSpec.h:57
bool IsRelative() const
Returns true if the filespec represents a relative path.
Definition: FileSpec.cpp:510
void PrependPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:436
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:370
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:339
ThreadList & GetThreadList()
Definition: Process.h:2187
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:2160
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.
Definition: SBAttachInfo.h:14
const char * toString(AppleArm64ExceptionClass EC)
Definition: SBAddress.h:15
std::shared_ptr< lldb_private::Trace > TraceSP
Definition: lldb-forward.h:442
std::shared_ptr< lldb_private::ThreadPostMortemTrace > ThreadPostMortemTraceSP
Definition: lldb-forward.h:439
std::shared_ptr< lldb_private::Process > ProcessSP
Definition: lldb-forward.h:377
uint64_t pid_t
Definition: lldb-types.h:81
uint64_t addr_t
Definition: lldb-types.h:79
std::shared_ptr< lldb_private::Target > TargetSP
Definition: lldb-forward.h:432
uint64_t tid_t
Definition: lldb-types.h:82
std::shared_ptr< lldb_private::Module > ModuleSP
Definition: lldb-forward.h:361
Definition: Debugger.h:53
std::optional< LinuxPerfZeroTscConversion > tsc_perf_zero_conversion
std::optional< std::vector< JSONProcess > > processes
Helper struct holding the objects created when parsing a process.