Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp
39645 views
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
439