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"
14 
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 
20 using namespace lldb;
21 using namespace lldb_private;
22 using namespace lldb_private::trace_intel_pt;
23 using namespace llvm;
24 
25 FileSpec TraceIntelPTBundleLoader::NormalizePath(const std::string &path) {
26  FileSpec file_spec(path);
27  if (file_spec.IsRelative())
28  file_spec.PrependPathComponent(m_bundle_dir);
29  return file_spec;
30 }
31 
32 Error TraceIntelPTBundleLoader::ParseModule(Target &target,
33  const JSONModule &module) {
34  auto do_parse = [&]() -> Error {
35  FileSpec system_file_spec(module.system_path);
36 
37  FileSpec local_file_spec(module.file.hasValue() ? *module.file
38  : module.system_path);
39 
40  ModuleSpec module_spec;
41  module_spec.GetFileSpec() = local_file_spec;
42  module_spec.GetPlatformFileSpec() = system_file_spec;
43 
44  if (module.uuid.hasValue())
45  module_spec.GetUUID().SetFromStringRef(*module.uuid);
46 
47  Status error;
48  ModuleSP module_sp =
49  target.GetOrCreateModule(module_spec, /*notify*/ false, &error);
50 
51  if (error.Fail())
52  return error.ToError();
53 
54  bool load_addr_changed = false;
55  module_sp->SetLoadAddress(target, module.load_address.value, false,
56  load_addr_changed);
57  return Error::success();
58  };
59  if (Error err = do_parse())
60  return createStringError(
61  inconvertibleErrorCode(), "Error when parsing module %s. %s",
62  module.system_path.c_str(), toString(std::move(err)).c_str());
63  return Error::success();
64 }
65 
66 Error TraceIntelPTBundleLoader::CreateJSONError(json::Path::Root &root,
67  const json::Value &value) {
68  std::string err;
69  raw_string_ostream os(err);
70  root.printErrorContext(value, os);
71  return createStringError(
72  std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s",
73  toString(root.getError()).c_str(), os.str().c_str(), GetSchema().data());
74 }
75 
76 ThreadPostMortemTraceSP
77 TraceIntelPTBundleLoader::ParseThread(Process &process,
78  const JSONThread &thread) {
79  lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
80 
81  Optional<FileSpec> trace_file;
82  if (thread.ipt_trace)
83  trace_file = FileSpec(*thread.ipt_trace);
84 
85  ThreadPostMortemTraceSP thread_sp =
86  std::make_shared<ThreadPostMortemTrace>(process, tid, trace_file);
87  process.GetThreadList().AddThread(thread_sp);
88  return thread_sp;
89 }
90 
91 Expected<TraceIntelPTBundleLoader::ParsedProcess>
92 TraceIntelPTBundleLoader::ParseProcess(const JSONProcess &process) {
93  TargetSP target_sp;
94  Status error = m_debugger.GetTargetList().CreateTarget(
95  m_debugger, /*user_exe_path*/ StringRef(), process.triple.value_or(""),
97  /*platform_options*/ nullptr, target_sp);
98 
99  if (!target_sp)
100  return error.ToError();
101 
102  ParsedProcess parsed_process;
103  parsed_process.target_sp = target_sp;
104 
105  ProcessSP process_sp = target_sp->CreateProcess(
106  /*listener*/ nullptr, "trace",
107  /*crash_file*/ nullptr,
108  /*can_connect*/ false);
109 
110  process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
111 
112  for (const JSONThread &thread : process.threads)
113  parsed_process.threads.push_back(ParseThread(*process_sp, thread));
114 
115  for (const JSONModule &module : process.modules)
116  if (Error err = ParseModule(*target_sp, module))
117  return std::move(err);
118 
119  if (!process.threads.empty())
120  process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
121 
122  // We invoke DidAttach to create a correct stopped state for the process and
123  // its threads.
124  ArchSpec process_arch;
125  process_sp->DidAttach(process_arch);
126 
127  return parsed_process;
128 }
129 
130 Expected<std::vector<TraceIntelPTBundleLoader::ParsedProcess>>
131 TraceIntelPTBundleLoader::LoadBundle(
132  const JSONTraceBundleDescription &bundle_description) {
133  std::vector<ParsedProcess> parsed_processes;
134 
135  auto HandleError = [&](Error &&err) {
136  // Delete all targets that were created so far in case of failures
137  for (ParsedProcess &parsed_process : parsed_processes)
138  m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);
139  return std::move(err);
140  };
141 
142  for (const JSONProcess &process : bundle_description.processes) {
143  if (Expected<ParsedProcess> parsed_process = ParseProcess(process))
144  parsed_processes.push_back(std::move(*parsed_process));
145  else
146  return HandleError(parsed_process.takeError());
147  }
148 
149  return parsed_processes;
150 }
151 
152 StringRef TraceIntelPTBundleLoader::GetSchema() {
153  static std::string schema;
154  if (schema.empty()) {
155  schema = R"({
156  "type": "intel-pt",
157  "cpuInfo": {
158  // CPU information gotten from, for example, /proc/cpuinfo.
159 
160  "vendor": "GenuineIntel" | "unknown",
161  "family": integer,
162  "model": integer,
163  "stepping": integer
164  },
165  "processes": [
166  {
167  "pid": integer,
168  "triple"?: string,
169  // Optional clang/llvm target triple.
170  "threads": [
171  // A list of known threads for the given process. When context switch
172  // data is provided, LLDB will automatically create threads for the
173  // this process whenever it finds new threads when traversing the
174  // context switches, so passing values to this list in this case is
175  // optional.
176  {
177  "tid": integer,
178  "iptTrace"?: string
179  // Path to the raw Intel PT buffer file for this thread.
180  }
181  ],
182  "modules": [
183  {
184  "systemPath": string,
185  // Original path of the module at runtime.
186  "file"?: string,
187  // Path to a copy of the file if not available at "systemPath".
188  "loadAddress": integer | string decimal | hex string,
189  // Lowest address of the sections of the module loaded on memory.
190  "uuid"?: string,
191  // Build UUID for the file for sanity checks.
192  }
193  ]
194  }
195  ],
196  "cpus"?: [
197  {
198  "id": integer,
199  // Id of this CPU core.
200  "iptTrace": string,
201  // Path to the raw Intel PT buffer for this cpu core.
202  "contextSwitchTrace": string,
203  // Path to the raw perf_event_open context switch trace file for this cpu core.
204  // The perf_event must have been configured with PERF_SAMPLE_TID and
205  // PERF_SAMPLE_TIME, as well as sample_id_all = 1.
206  }
207  ],
208  "tscPerfZeroConversion"?: {
209  // Values used to convert between TSCs and nanoseconds. See the time_zero
210  // section in https://man7.org/linux/man-pages/man2/perf_event_open.2.html
211  // for for information.
212 
213  "timeMult": integer,
214  "timeShift": integer,
215  "timeZero": integer | string decimal | hex string,
216  }
217 }
218 
219 Notes:
220 
221 - All paths are either absolute or relative to folder containing the bundle description file.
222 - "cpus" is provided if and only if processes[].threads[].iptTrace is not provided.
223 - "tscPerfZeroConversion" must be provided if "cpus" is provided.
224  })";
225  }
226  return schema;
227 }
228 
229 Error TraceIntelPTBundleLoader::AugmentThreadsFromContextSwitches(
230  JSONTraceBundleDescription &bundle_description) {
231  if (!bundle_description.cpus)
232  return Error::success();
233 
234  if (!bundle_description.tsc_perf_zero_conversion)
235  return createStringError(inconvertibleErrorCode(),
236  "TSC to nanos conversion values are needed when "
237  "context switch information is provided.");
238 
239  DenseMap<lldb::pid_t, JSONProcess *> indexed_processes;
240  DenseMap<JSONProcess *, DenseSet<tid_t>> indexed_threads;
241 
242  for (JSONProcess &process : bundle_description.processes) {
243  indexed_processes[process.pid] = &process;
244  for (JSONThread &thread : process.threads)
245  indexed_threads[&process].insert(thread.tid);
246  }
247 
248  auto on_thread_seen = [&](lldb::pid_t pid, tid_t tid) {
249  auto proc = indexed_processes.find(pid);
250  if (proc == indexed_processes.end())
251  return;
252  if (indexed_threads[proc->second].count(tid))
253  return;
254  indexed_threads[proc->second].insert(tid);
255  proc->second->threads.push_back({tid, /*ipt_trace=*/None});
256  };
257 
258  for (const JSONCpu &cpu : *bundle_description.cpus) {
259  Error err = Trace::OnDataFileRead(
261  [&](ArrayRef<uint8_t> data) -> Error {
262  Expected<std::vector<ThreadContinuousExecution>> executions =
263  DecodePerfContextSwitchTrace(data, cpu.id,
264  *bundle_description.tsc_perf_zero_conversion);
265  if (!executions)
266  return executions.takeError();
267  for (const ThreadContinuousExecution &execution : *executions)
268  on_thread_seen(execution.pid, execution.tid);
269  return Error::success();
270  });
271  if (err)
272  return err;
273  }
274  return Error::success();
275 }
276 
277 Expected<TraceSP> TraceIntelPTBundleLoader::CreateTraceIntelPTInstance(
278  JSONTraceBundleDescription &bundle_description, std::vector<ParsedProcess> &parsed_processes) {
279  std::vector<ThreadPostMortemTraceSP> threads;
280  std::vector<ProcessSP> processes;
281  for (const ParsedProcess &parsed_process : parsed_processes) {
282  processes.push_back(parsed_process.target_sp->GetProcessSP());
283  threads.insert(threads.end(), parsed_process.threads.begin(),
284  parsed_process.threads.end());
285  }
286 
287  TraceSP trace_instance = TraceIntelPT::CreateInstanceForPostmortemTrace(
288  bundle_description, processes, threads);
289  for (const ParsedProcess &parsed_process : parsed_processes)
290  parsed_process.target_sp->SetTrace(trace_instance);
291 
292  return trace_instance;
293 }
294 
295 void TraceIntelPTBundleLoader::NormalizeAllPaths(
296  JSONTraceBundleDescription &bundle_description) {
297  for (JSONProcess &process : bundle_description.processes) {
298  for (JSONModule &module : process.modules) {
299  module.system_path = NormalizePath(module.system_path).GetPath();
300  if (module.file)
301  module.file = NormalizePath(*module.file).GetPath();
302  }
303  for (JSONThread &thread : process.threads) {
304  if (thread.ipt_trace)
305  thread.ipt_trace = NormalizePath(*thread.ipt_trace).GetPath();
306  }
307  }
308  if (bundle_description.cpus) {
309  for (JSONCpu &cpu : *bundle_description.cpus) {
311  NormalizePath(cpu.context_switch_trace).GetPath();
312  cpu.ipt_trace = NormalizePath(cpu.ipt_trace).GetPath();
313  }
314  }
315 }
316 
317 Expected<TraceSP> TraceIntelPTBundleLoader::Load() {
318  json::Path::Root root("traceBundle");
319  JSONTraceBundleDescription bundle_description;
320  if (!fromJSON(m_bundle_description, bundle_description, root))
321  return CreateJSONError(root, m_bundle_description);
322 
323  NormalizeAllPaths(bundle_description);
324 
325  if (Error err = AugmentThreadsFromContextSwitches(bundle_description))
326  return std::move(err);
327 
328  if (Expected<std::vector<ParsedProcess>> parsed_processes =
329  LoadBundle(bundle_description))
330  return CreateTraceIntelPTInstance(bundle_description, *parsed_processes);
331  else
332  return parsed_processes.takeError();
333 }
lldb_private::toString
const char * toString(AppleArm64ExceptionClass EC)
Definition: AppleArm64ExceptionClass.h:38
lldb_private::trace_intel_pt::TraceIntelPTBundleLoader::ParsedProcess::target_sp
lldb::TargetSP target_sp
Definition: TraceIntelPTBundleLoader.h:24
llvm
Definition: Debugger.h:50
lldb_private::trace_intel_pt::TraceIntelPTBundleLoader::ParsedProcess::threads
std::vector< lldb::ThreadPostMortemTraceSP > threads
Definition: TraceIntelPTBundleLoader.h:25
lldb_private::ArchSpec
Definition: ArchSpec.h:33
lldb_private::ModuleSpec::GetPlatformFileSpec
FileSpec & GetPlatformFileSpec()
Definition: ModuleSpec.h:65
lldb_private::Target::GetOrCreateModule
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:2065
lldb_private::Process
Definition: Process.h:338
Module.h
lldb_private::Process::GetThreadList
ThreadList & GetThreadList()
Definition: Process.h:2073
lldb_private::trace_intel_pt::JSONProcess::pid
uint64_t pid
Definition: TraceIntelPTJSONStructs.h:38
Debugger.h
lldb_private::Target
Definition: Target.h:467
lldb_private::trace_intel_pt::JSONModule
Definition: TraceIntelPTJSONStructs.h:25
lldb_private::FileSpec::PrependPathComponent
void PrependPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:420
Process.h
lldb_private::trace_intel_pt::JSONProcess::threads
std::vector< JSONThread > threads
Definition: TraceIntelPTJSONStructs.h:40
lldb_private::trace_intel_pt::JSONTraceBundleDescription::cpus
llvm::Optional< std::vector< JSONCpu > > cpus
Definition: TraceIntelPTJSONStructs.h:54
Target.h
lldb_private::ModuleSpec::GetUUID
UUID & GetUUID()
Definition: ModuleSpec.h:99
TraceIntelPTJSONStructs.h
lldb_private::FileSpec
Definition: FileSpec.h:56
lldb_private::trace_intel_pt::JSONThread::tid
uint64_t tid
Definition: TraceIntelPTJSONStructs.h:33
error
static llvm::raw_ostream & error(Stream &strm)
Definition: CommandReturnObject.cpp:17
lldb_private::eLoadDependentsNo
@ eLoadDependentsNo
Definition: lldb-private-enumerations.h:254
lldb_private::trace_intel_pt::JSONModule::file
llvm::Optional< std::string > file
Definition: TraceIntelPTJSONStructs.h:27
lldb_private::trace_intel_pt::JSONThread
Definition: TraceIntelPTJSONStructs.h:32
lldb_private::trace_intel_pt::JSONModule::load_address
JSONUINT64 load_address
Definition: TraceIntelPTJSONStructs.h:28
lldb_private::trace_intel_pt::JSONCpu::ipt_trace
std::string ipt_trace
Definition: TraceIntelPTJSONStructs.h:46
string
string(SUBSTRING ${p} 10 -1 pStripped) if($
Definition: Plugins/CMakeLists.txt:40
lldb_private::trace_intel_pt::JSONTraceBundleDescription::processes
std::vector< JSONProcess > processes
Definition: TraceIntelPTJSONStructs.h:53
lldb_private::fromJSON
bool fromJSON(const llvm::json::Value &value, TraceSupportedResponse &info, llvm::json::Path path)
lldb_private::ModuleSpec::GetFileSpec
FileSpec & GetFileSpec()
Definition: ModuleSpec.h:53
lldb_private::ModuleSpec
Definition: ModuleSpec.h:27
lldb_private::JSONUINT64::value
uint64_t value
Definition: TraceIntelPTGDBRemotePackets.h:72
lldb_private::trace_intel_pt::JSONCpu
Definition: TraceIntelPTJSONStructs.h:44
lldb_private::trace_intel_pt::JSONCpu::context_switch_trace
std::string context_switch_trace
Definition: TraceIntelPTJSONStructs.h:47
lldb_private::Status
Definition: Status.h:44
lldb_private::FileSpec::IsRelative
bool IsRelative() const
Returns true if the filespec represents a relative path.
Definition: FileSpec.cpp:474
lldb_private::trace_intel_pt::JSONThread::ipt_trace
llvm::Optional< std::string > ipt_trace
Definition: TraceIntelPTJSONStructs.h:34
lldb_private::trace_intel_pt::JSONProcess::triple
llvm::Optional< std::string > triple
Definition: TraceIntelPTJSONStructs.h:39
lldb::pid_t
uint64_t pid_t
Definition: lldb-types.h:85
lldb_private::UUID::SetFromStringRef
bool SetFromStringRef(llvm::StringRef str)
Definition: UUID.cpp:96
lldb_private::trace_intel_pt::JSONTraceBundleDescription
Definition: TraceIntelPTJSONStructs.h:50
lldb_private::trace_intel_pt::JSONModule::uuid
llvm::Optional< std::string > uuid
Definition: TraceIntelPTJSONStructs.h:29
lldb_private::trace_intel_pt::JSONTraceBundleDescription::tsc_perf_zero_conversion
llvm::Optional< LinuxPerfZeroTscConversion > tsc_perf_zero_conversion
Definition: TraceIntelPTJSONStructs.h:55
lldb_private::trace_intel_pt::JSONProcess::modules
std::vector< JSONModule > modules
Definition: TraceIntelPTJSONStructs.h:41
lldb_private::trace_intel_pt::TraceIntelPTBundleLoader::ParsedProcess
Helper struct holding the objects created when parsing a process.
Definition: TraceIntelPTBundleLoader.h:23
lldb_private
A class that represents a running process on the host machine.
Definition: SBCommandInterpreterRunOptions.h:16
Error
llvm::Error Error
Definition: UdtRecordCompleter.cpp:30
lldb_private::trace_intel_pt
Definition: CommandObjectTraceStartIntelPT.h:18
lldb_private::ThreadCollection::AddThread
void AddThread(const lldb::ThreadSP &thread_sp)
Definition: ThreadCollection.cpp:24
lldb_private::trace_intel_pt::JSONModule::system_path
std::string system_path
Definition: TraceIntelPTJSONStructs.h:26
lldb_private::trace_intel_pt::JSONProcess
Definition: TraceIntelPTJSONStructs.h:37
lldb
Definition: SBAddress.h:15
TraceIntelPT.h
TraceIntelPTBundleLoader.h
lldb::tid_t
uint64_t tid_t
Definition: lldb-types.h:86