LLDB  mainline
TraceIntelPTBundleSaver.cpp
Go to the documentation of this file.
1 //===-- TraceIntelPTBundleSaver.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 
12 #include "TraceIntelPT.h"
14 
15 #include "lldb/Core/Module.h"
16 #include "lldb/Core/ModuleList.h"
17 #include "lldb/Target/Process.h"
19 #include "lldb/Target/Target.h"
20 #include "lldb/Target/ThreadList.h"
21 #include "lldb/lldb-types.h"
22 #include "llvm/ADT/None.h"
23 #include "llvm/Support/Error.h"
24 #include "llvm/Support/JSON.h"
25 
26 #include <fstream>
27 #include <iostream>
28 #include <sstream>
29 #include <string>
30 
31 using namespace lldb;
32 using namespace lldb_private;
33 using namespace lldb_private::trace_intel_pt;
34 using namespace llvm;
35 
36 /// Strip the \p directory component from the given \p path. It assumes that \p
37 /// directory is a prefix of \p path.
38 static std::string GetRelativePath(const FileSpec &directory,
39  const FileSpec &path) {
40  return path.GetPath().substr(directory.GetPath().size() + 1);
41 }
42 
43 /// Write a stream of bytes from \p data to the given output file.
44 /// It creates or overwrites the output file, but not append.
46  ArrayRef<uint8_t> data) {
47  std::basic_fstream<char> out_fs = std::fstream(
48  output_file.GetPath().c_str(), std::ios::out | std::ios::binary);
49  if (!data.empty())
50  out_fs.write(reinterpret_cast<const char *>(&data[0]), data.size());
51 
52  out_fs.close();
53  if (!out_fs)
54  return createStringError(inconvertibleErrorCode(),
55  formatv("couldn't write to the file {0}",
56  output_file.GetPath().c_str()));
57  return Error::success();
58 }
59 
60 /// Save the trace bundle description JSON object inside the given directory
61 /// as a file named \a trace.json.
62 ///
63 /// \param[in] trace_bundle_description
64 /// The trace bundle description as JSON Object.
65 ///
66 /// \param[in] directory
67 /// The directory where the JSON file will be saved.
68 ///
69 /// \return
70 /// A \a FileSpec pointing to the bundle description file, or an \a
71 /// llvm::Error otherwise.
72 static Expected<FileSpec>
73 SaveTraceBundleDescription(const llvm::json::Value &trace_bundle_description,
74  const FileSpec &directory) {
75  FileSpec trace_path = directory;
76  trace_path.AppendPathComponent("trace.json");
77  std::ofstream os(trace_path.GetPath());
78  os << formatv("{0:2}", trace_bundle_description).str();
79  os.close();
80  if (!os)
81  return createStringError(inconvertibleErrorCode(),
82  formatv("couldn't write to the file {0}",
83  trace_path.GetPath().c_str()));
84  return trace_path;
85 }
86 
87 /// Build the threads sub-section of the trace bundle description file.
88 /// Any associated binary files are created inside the given directory.
89 ///
90 /// \param[in] process
91 /// The process being traced.
92 ///
93 /// \param[in] directory
94 /// The directory where files will be saved when building the threads
95 /// section.
96 ///
97 /// \return
98 /// The threads section or \a llvm::Error in case of failures.
99 static llvm::Expected<std::vector<JSONThread>>
100 BuildThreadsSection(Process &process, FileSpec directory) {
101  std::vector<JSONThread> json_threads;
102  TraceSP trace_sp = process.GetTarget().GetTrace();
103 
104  FileSpec threads_dir = directory;
105  threads_dir.AppendPathComponent("threads");
106  sys::fs::create_directories(threads_dir.GetCString());
107 
108  for (ThreadSP thread_sp : process.Threads()) {
109  lldb::tid_t tid = thread_sp->GetID();
110  if (!trace_sp->IsTraced(tid))
111  continue;
112 
113  JSONThread json_thread;
114  json_thread.tid = tid;
115 
116  if (trace_sp->GetTracedCpus().empty()) {
117  FileSpec output_file = threads_dir;
118  output_file.AppendPathComponent(std::to_string(tid) + ".intelpt_trace");
119  json_thread.ipt_trace = GetRelativePath(directory, output_file);
120 
121  llvm::Error err = process.GetTarget().GetTrace()->OnThreadBinaryDataRead(
122  tid, IntelPTDataKinds::kIptTrace,
123  [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
124  return WriteBytesToDisk(output_file, data);
125  });
126  if (err)
127  return std::move(err);
128  }
129 
130  json_threads.push_back(std::move(json_thread));
131  }
132  return json_threads;
133 }
134 
135 /// \return
136 /// an \a llvm::Error in case of failures, \a None if the trace is not written
137 /// to disk because the trace is empty and the \p compact flag is present, or
138 /// the FileSpec of the trace file on disk.
139 static Expected<Optional<FileSpec>>
141  const FileSpec &cpus_dir, bool compact) {
142  FileSpec output_context_switch_trace = cpus_dir;
143  output_context_switch_trace.AppendPathComponent(std::to_string(cpu_id) +
144  ".perf_context_switch_trace");
145 
146  bool should_skip = false;
147 
148  Error err = trace_ipt.OnCpuBinaryDataRead(
149  cpu_id, IntelPTDataKinds::kPerfContextSwitchTrace,
150  [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
151  if (!compact)
152  return WriteBytesToDisk(output_context_switch_trace, data);
153 
154  std::set<lldb::pid_t> pids;
155  for (Process *process : trace_ipt.GetAllProcesses())
156  pids.insert(process->GetID());
157 
158  Expected<std::vector<uint8_t>> compact_context_switch_trace =
160  if (!compact_context_switch_trace)
161  return compact_context_switch_trace.takeError();
162 
163  if (compact_context_switch_trace->empty()) {
164  should_skip = true;
165  return Error::success();
166  }
167 
168  return WriteBytesToDisk(output_context_switch_trace,
169  *compact_context_switch_trace);
170  });
171  if (err)
172  return std::move(err);
173 
174  if (should_skip)
175  return None;
176  return output_context_switch_trace;
177 }
178 
179 static Expected<FileSpec> WriteIntelPTTrace(TraceIntelPT &trace_ipt,
180  lldb::cpu_id_t cpu_id,
181  const FileSpec &cpus_dir) {
182  FileSpec output_trace = cpus_dir;
183  output_trace.AppendPathComponent(std::to_string(cpu_id) + ".intelpt_trace");
184 
185  Error err = trace_ipt.OnCpuBinaryDataRead(
186  cpu_id, IntelPTDataKinds::kIptTrace,
187  [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
188  return WriteBytesToDisk(output_trace, data);
189  });
190  if (err)
191  return std::move(err);
192  return output_trace;
193 }
194 
195 static llvm::Expected<llvm::Optional<std::vector<JSONCpu>>>
196 BuildCpusSection(TraceIntelPT &trace_ipt, FileSpec directory, bool compact) {
197  if (trace_ipt.GetTracedCpus().empty())
198  return None;
199 
200  std::vector<JSONCpu> json_cpus;
201  FileSpec cpus_dir = directory;
202  cpus_dir.AppendPathComponent("cpus");
203  sys::fs::create_directories(cpus_dir.GetCString());
204 
205  for (lldb::cpu_id_t cpu_id : trace_ipt.GetTracedCpus()) {
206  JSONCpu json_cpu;
207  json_cpu.id = cpu_id;
208  Expected<Optional<FileSpec>> context_switch_trace_path =
209  WriteContextSwitchTrace(trace_ipt, cpu_id, cpus_dir, compact);
210  if (!context_switch_trace_path)
211  return context_switch_trace_path.takeError();
212  if (!*context_switch_trace_path)
213  continue;
214  json_cpu.context_switch_trace =
215  GetRelativePath(directory, **context_switch_trace_path);
216 
217  if (Expected<FileSpec> ipt_trace_path =
218  WriteIntelPTTrace(trace_ipt, cpu_id, cpus_dir))
219  json_cpu.ipt_trace = GetRelativePath(directory, *ipt_trace_path);
220  else
221  return ipt_trace_path.takeError();
222 
223  json_cpus.push_back(std::move(json_cpu));
224  }
225  return json_cpus;
226 }
227 
228 /// Build modules sub-section of the trace bundle. The original modules
229 /// will be copied over to the \a <directory/modules> folder. Invalid modules
230 /// are skipped.
231 /// Copying the modules has the benefit of making these
232 /// directories self-contained, as the raw traces and modules are part of the
233 /// output directory and can be sent to another machine, where lldb can load
234 /// them and replicate exactly the same trace session.
235 ///
236 /// \param[in] process
237 /// The process being traced.
238 ///
239 /// \param[in] directory
240 /// The directory where the modules files will be saved when building
241 /// the modules section.
242 /// Example: If a module \a libbar.so exists in the path
243 /// \a /usr/lib/foo/libbar.so, then it will be copied to
244 /// \a <directory>/modules/usr/lib/foo/libbar.so.
245 ///
246 /// \return
247 /// The modules section or \a llvm::Error in case of failures.
248 static llvm::Expected<std::vector<JSONModule>>
249 BuildModulesSection(Process &process, FileSpec directory) {
250  std::vector<JSONModule> json_modules;
251  ModuleList module_list = process.GetTarget().GetImages();
252  for (size_t i = 0; i < module_list.GetSize(); ++i) {
253  ModuleSP module_sp(module_list.GetModuleAtIndex(i));
254  if (!module_sp)
255  continue;
256  std::string system_path = module_sp->GetPlatformFileSpec().GetPath();
257  // TODO: support memory-only libraries like [vdso]
258  if (!module_sp->GetFileSpec().IsAbsolute())
259  continue;
260 
261  std::string file = module_sp->GetFileSpec().GetPath();
262  ObjectFile *objfile = module_sp->GetObjectFile();
263  if (objfile == nullptr)
264  continue;
265 
267  Address base_addr(objfile->GetBaseAddress());
268  if (base_addr.IsValid() &&
269  !process.GetTarget().GetSectionLoadList().IsEmpty())
270  load_addr = base_addr.GetLoadAddress(&process.GetTarget());
271 
272  if (load_addr == LLDB_INVALID_ADDRESS)
273  continue;
274 
275  FileSpec path_to_copy_module = directory;
276  path_to_copy_module.AppendPathComponent("modules");
277  path_to_copy_module.AppendPathComponent(system_path);
278  sys::fs::create_directories(path_to_copy_module.GetDirectory().AsCString());
279 
280  if (std::error_code ec =
281  llvm::sys::fs::copy_file(file, path_to_copy_module.GetPath()))
282  return createStringError(
283  inconvertibleErrorCode(),
284  formatv("couldn't write to the file. {0}", ec.message()));
285 
286  json_modules.push_back(
287  JSONModule{system_path, GetRelativePath(directory, path_to_copy_module),
288  JSONUINT64{load_addr}, module_sp->GetUUID().GetAsString()});
289  }
290  return json_modules;
291 }
292 
293 /// Build the processes section of the trace bundle description object. Besides
294 /// returning the processes information, this method saves to disk all modules
295 /// and raw traces corresponding to the traced threads of the given process.
296 ///
297 /// \param[in] process
298 /// The process being traced.
299 ///
300 /// \param[in] directory
301 /// The directory where files will be saved when building the processes
302 /// section.
303 ///
304 /// \return
305 /// The processes section or \a llvm::Error in case of failures.
306 static llvm::Expected<JSONProcess>
307 BuildProcessSection(Process &process, const FileSpec &directory) {
308  Expected<std::vector<JSONThread>> json_threads =
309  BuildThreadsSection(process, directory);
310  if (!json_threads)
311  return json_threads.takeError();
312 
313  Expected<std::vector<JSONModule>> json_modules =
314  BuildModulesSection(process, directory);
315  if (!json_modules)
316  return json_modules.takeError();
317 
318  return JSONProcess{
319  process.GetID(),
320  process.GetTarget().GetArchitecture().GetTriple().getTriple(),
321  json_threads.get(), json_modules.get()};
322 }
323 
324 /// See BuildProcessSection()
325 static llvm::Expected<std::vector<JSONProcess>>
326 BuildProcessesSection(TraceIntelPT &trace_ipt, const FileSpec &directory) {
327  std::vector<JSONProcess> processes;
328  for (Process *process : trace_ipt.GetAllProcesses()) {
329  if (llvm::Expected<JSONProcess> json_process =
330  BuildProcessSection(*process, directory))
331  processes.push_back(std::move(*json_process));
332  else
333  return json_process.takeError();
334  }
335  return processes;
336 }
337 
338 Expected<FileSpec> TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt,
339  FileSpec directory,
340  bool compact) {
341  if (std::error_code ec =
342  sys::fs::create_directories(directory.GetPath().c_str()))
343  return llvm::errorCodeToError(ec);
344 
345  Expected<pt_cpu> cpu_info = trace_ipt.GetCPUInfo();
346  if (!cpu_info)
347  return cpu_info.takeError();
348 
349  FileSystem::Instance().Resolve(directory);
350 
351  Expected<std::vector<JSONProcess>> json_processes =
352  BuildProcessesSection(trace_ipt, directory);
353 
354  if (!json_processes)
355  return json_processes.takeError();
356 
357  Expected<Optional<std::vector<JSONCpu>>> json_cpus =
358  BuildCpusSection(trace_ipt, directory, compact);
359  if (!json_cpus)
360  return json_cpus.takeError();
361 
362  JSONTraceBundleDescription json_intel_pt_bundle_desc{"intel-pt", *cpu_info, *json_processes,
363  *json_cpus,
364  trace_ipt.GetPerfZeroTscConversion()};
365 
366  return SaveTraceBundleDescription(toJSON(json_intel_pt_bundle_desc), directory);
367 }
lldb_private::toJSON
llvm::json::Value toJSON(const TraceSupportedResponse &packet)
Definition: TraceGDBRemotePackets.cpp:24
llvm
Definition: Debugger.h:50
lldb_private::Address::IsValid
bool IsValid() const
Check if the object state is valid.
Definition: Address.h:345
PerfContextSwitchDecoder.h
lldb_private::Trace::GetAllProcesses
std::vector< Process * > GetAllProcesses()
Definition: Trace.cpp:359
BuildProcessSection
static llvm::Expected< JSONProcess > BuildProcessSection(Process &process, const FileSpec &directory)
Build the processes section of the trace bundle description object.
Definition: TraceIntelPTBundleSaver.cpp:307
lldb_private::Process
Definition: Process.h:338
Module.h
lldb_private::Process::GetTarget
Target & GetTarget()
Get the target object pointer for this module.
Definition: Process.h:1206
lldb_private::trace_intel_pt::TraceIntelPT
Definition: TraceIntelPT.h:24
SectionLoadList.h
lldb_private::ConstString::AsCString
const char * AsCString(const char *value_if_empty=nullptr) const
Get the string value as a C string.
Definition: ConstString.h:193
WriteContextSwitchTrace
static Expected< Optional< FileSpec > > WriteContextSwitchTrace(TraceIntelPT &trace_ipt, lldb::cpu_id_t cpu_id, const FileSpec &cpus_dir, bool compact)
Definition: TraceIntelPTBundleSaver.cpp:140
lldb_private::ArchSpec::GetTriple
llvm::Triple & GetTriple()
Architecture triple accessor.
Definition: ArchSpec.h:455
lldb::addr_t
uint64_t addr_t
Definition: lldb-types.h:83
lldb_private::trace_intel_pt::TraceIntelPT::GetPerfZeroTscConversion
llvm::Optional< LinuxPerfZeroTscConversion > GetPerfZeroTscConversion()
Get or fetch the values used to convert to and from TSCs and nanos.
Definition: TraceIntelPT.cpp:454
lldb_private::Target::GetImages
const ModuleList & GetImages() const
Get accessor for the images for this process.
Definition: Target.h:941
lldb_private::trace_intel_pt::JSONModule
Definition: TraceIntelPTJSONStructs.h:25
Process.h
lldb_private::FileSpec::GetCString
const char * GetCString(bool denormalize=true) const
Definition: FileSpec.cpp:363
Target.h
TraceIntelPTJSONStructs.h
lldb_private::FileSpec
Definition: FileSpec.h:56
lldb_private::trace_intel_pt::JSONThread::tid
uint64_t tid
Definition: TraceIntelPTJSONStructs.h:33
lldb_private::ModuleList::GetSize
size_t GetSize() const
Gets the size of the module list.
Definition: ModuleList.cpp:630
ModuleList.h
lldb_private::Process::Threads
ThreadList::ThreadIterable Threads()
Definition: Process.h:2081
lldb_private::ModuleList
Definition: ModuleList.h:81
lldb_private::ObjectFile::GetBaseAddress
virtual lldb_private::Address GetBaseAddress()
Returns base address of this object file.
Definition: ObjectFile.h:497
lldb_private::trace_intel_pt::JSONThread
Definition: TraceIntelPTJSONStructs.h:32
BuildModulesSection
static llvm::Expected< std::vector< JSONModule > > BuildModulesSection(Process &process, FileSpec directory)
Build modules sub-section of the trace bundle.
Definition: TraceIntelPTBundleSaver.cpp:249
lldb_private::trace_intel_pt::JSONCpu::ipt_trace
std::string ipt_trace
Definition: TraceIntelPTJSONStructs.h:46
BuildProcessesSection
static llvm::Expected< std::vector< JSONProcess > > BuildProcessesSection(TraceIntelPT &trace_ipt, const FileSpec &directory)
See BuildProcessSection()
Definition: TraceIntelPTBundleSaver.cpp:326
ThreadList.h
string
string(SUBSTRING ${p} 10 -1 pStripped) if($
Definition: Plugins/CMakeLists.txt:40
lldb-types.h
TraceIntelPTBundleSaver.h
lldb_private::SectionLoadList::IsEmpty
bool IsEmpty() const
Definition: SectionLoadList.cpp:38
lldb_private::Target::GetArchitecture
const ArchSpec & GetArchitecture() const
Definition: Target.h:983
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::trace_intel_pt::JSONThread::ipt_trace
llvm::Optional< std::string > ipt_trace
Definition: TraceIntelPTJSONStructs.h:34
uint32_t
WriteBytesToDisk
static llvm::Error WriteBytesToDisk(FileSpec &output_file, ArrayRef< uint8_t > data)
Write a stream of bytes from data to the given output file.
Definition: TraceIntelPTBundleSaver.cpp:45
lldb_private::Address
Definition: Address.h:59
SaveTraceBundleDescription
static Expected< FileSpec > SaveTraceBundleDescription(const llvm::json::Value &trace_bundle_description, const FileSpec &directory)
Save the trace bundle description JSON object inside the given directory as a file named trace....
Definition: TraceIntelPTBundleSaver.cpp:73
lldb_private::trace_intel_pt::JSONTraceBundleDescription
Definition: TraceIntelPTJSONStructs.h:50
lldb_private::Address::GetLoadAddress
lldb::addr_t GetLoadAddress(Target *target) const
Get the load address.
Definition: Address.cpp:311
if
if(APPLE AND LLVM_ENABLE_LOCAL_SUBMODULE_VISIBILITY) remove_module_flags() endif() macro(add_host_subdirectory group) list(APPEND HOST_SOURCES $
Definition: Host/CMakeLists.txt:1
lldb_private::Target::GetSectionLoadList
SectionLoadList & GetSectionLoadList()
Definition: Target.h:1091
LLDB_INVALID_ADDRESS
#define LLDB_INVALID_ADDRESS
Definition: lldb-defines.h:74
lldb_private::Process::GetID
lldb::pid_t GetID() const
Returns the pid of the process or LLDB_INVALID_PROCESS_ID if there is no known pid.
Definition: Process.h:535
lldb_private::FileSpec::AppendPathComponent
void AppendPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:434
lldb_private::Trace::GetTracedCpus
llvm::ArrayRef< lldb::cpu_id_t > GetTracedCpus()
Definition: Trace.cpp:509
lldb_private
A class that represents a running process on the host machine.
Definition: SBCommandInterpreterRunOptions.h:16
lldb_private::Trace::OnCpuBinaryDataRead
llvm::Error OnCpuBinaryDataRead(lldb::cpu_id_t cpu_id, llvm::StringRef kind, OnBinaryDataReadCallback callback)
Fetch binary data associated with a cpu, either live or postmortem, and pass it to the given callback...
Definition: Trace.cpp:500
lldb_private::trace_intel_pt::JSONCpu::id
lldb::cpu_id_t id
Definition: TraceIntelPTJSONStructs.h:45
Error
llvm::Error Error
Definition: UdtRecordCompleter.cpp:30
lldb_private::ModuleList::GetModuleAtIndex
lldb::ModuleSP GetModuleAtIndex(size_t idx) const
Get the module shared pointer for the module at index idx.
Definition: ModuleList.cpp:405
lldb_private::trace_intel_pt::FilterProcessesFromContextSwitchTrace
llvm::Expected< std::vector< uint8_t > > FilterProcessesFromContextSwitchTrace(llvm::ArrayRef< uint8_t > data, const std::set< lldb::pid_t > &pids)
lldb_private::Target::GetTrace
lldb::TraceSP GetTrace()
Get the Trace object containing processor trace information of this target.
Definition: Target.cpp:3170
lldb_private::trace_intel_pt::TraceIntelPT::GetCPUInfo
llvm::Expected< pt_cpu > GetCPUInfo()
Get or fetch the cpu information from, for example, /proc/cpuinfo.
Definition: TraceIntelPT.cpp:443
lldb_private::FileSpec::GetDirectory
ConstString & GetDirectory()
Directory string get accessor.
Definition: FileSpec.cpp:334
lldb_private::trace_intel_pt
Definition: CommandObjectTraceStartIntelPT.h:18
lldb_private::JSONUINT64
Helper structure to help parse long numbers that can't be easily represented by a JSON number that is...
Definition: TraceIntelPTGDBRemotePackets.h:71
lldb_private::FileSpec::GetPath
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:347
lldb_private::trace_intel_pt::JSONProcess
Definition: TraceIntelPTJSONStructs.h:37
GetRelativePath
static std::string GetRelativePath(const FileSpec &directory, const FileSpec &path)
Strip the directory component from the given path.
Definition: TraceIntelPTBundleSaver.cpp:38
BuildThreadsSection
static llvm::Expected< std::vector< JSONThread > > BuildThreadsSection(Process &process, FileSpec directory)
Build the threads sub-section of the trace bundle description file.
Definition: TraceIntelPTBundleSaver.cpp:100
BuildCpusSection
static llvm::Expected< llvm::Optional< std::vector< JSONCpu > > > BuildCpusSection(TraceIntelPT &trace_ipt, FileSpec directory, bool compact)
Definition: TraceIntelPTBundleSaver.cpp:196
lldb
Definition: SBAddress.h:15
TraceIntelPT.h
WriteIntelPTTrace
static Expected< FileSpec > WriteIntelPTTrace(TraceIntelPT &trace_ipt, lldb::cpu_id_t cpu_id, const FileSpec &cpus_dir)
Definition: TraceIntelPTBundleSaver.cpp:179
lldb_private::ObjectFile
Definition: ObjectFile.h:60
lldb::tid_t
uint64_t tid_t
Definition: lldb-types.h:86