1*f6aab3d8Srobert //===-- TraceIntelPTBundleSaver.cpp ---------------------------------------===//
2*f6aab3d8Srobert //
3*f6aab3d8Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*f6aab3d8Srobert // See https://llvm.org/LICENSE.txt for license information.
5*f6aab3d8Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*f6aab3d8Srobert //
7*f6aab3d8Srobert //===----------------------------------------------------------------------===//
8*f6aab3d8Srobert
9*f6aab3d8Srobert #include "TraceIntelPTBundleSaver.h"
10*f6aab3d8Srobert #include "PerfContextSwitchDecoder.h"
11*f6aab3d8Srobert #include "TraceIntelPT.h"
12*f6aab3d8Srobert #include "TraceIntelPTConstants.h"
13*f6aab3d8Srobert #include "TraceIntelPTJSONStructs.h"
14*f6aab3d8Srobert #include "lldb/Core/Module.h"
15*f6aab3d8Srobert #include "lldb/Core/ModuleList.h"
16*f6aab3d8Srobert #include "lldb/Target/Process.h"
17*f6aab3d8Srobert #include "lldb/Target/SectionLoadList.h"
18*f6aab3d8Srobert #include "lldb/Target/Target.h"
19*f6aab3d8Srobert #include "lldb/Target/ThreadList.h"
20*f6aab3d8Srobert #include "lldb/lldb-types.h"
21*f6aab3d8Srobert #include "llvm/Support/Error.h"
22*f6aab3d8Srobert #include "llvm/Support/JSON.h"
23*f6aab3d8Srobert #include <fstream>
24*f6aab3d8Srobert #include <iostream>
25*f6aab3d8Srobert #include <optional>
26*f6aab3d8Srobert #include <sstream>
27*f6aab3d8Srobert #include <string>
28*f6aab3d8Srobert
29*f6aab3d8Srobert using namespace lldb;
30*f6aab3d8Srobert using namespace lldb_private;
31*f6aab3d8Srobert using namespace lldb_private::trace_intel_pt;
32*f6aab3d8Srobert using namespace llvm;
33*f6aab3d8Srobert
34*f6aab3d8Srobert /// Strip the \p directory component from the given \p path. It assumes that \p
35*f6aab3d8Srobert /// directory is a prefix of \p path.
GetRelativePath(const FileSpec & directory,const FileSpec & path)36*f6aab3d8Srobert static std::string GetRelativePath(const FileSpec &directory,
37*f6aab3d8Srobert const FileSpec &path) {
38*f6aab3d8Srobert return path.GetPath().substr(directory.GetPath().size() + 1);
39*f6aab3d8Srobert }
40*f6aab3d8Srobert
41*f6aab3d8Srobert /// Write a stream of bytes from \p data to the given output file.
42*f6aab3d8Srobert /// It creates or overwrites the output file, but not append.
WriteBytesToDisk(FileSpec & output_file,ArrayRef<uint8_t> data)43*f6aab3d8Srobert static llvm::Error WriteBytesToDisk(FileSpec &output_file,
44*f6aab3d8Srobert ArrayRef<uint8_t> data) {
45*f6aab3d8Srobert std::basic_fstream<char> out_fs = std::fstream(
46*f6aab3d8Srobert output_file.GetPath().c_str(), std::ios::out | std::ios::binary);
47*f6aab3d8Srobert if (!data.empty())
48*f6aab3d8Srobert out_fs.write(reinterpret_cast<const char *>(&data[0]), data.size());
49*f6aab3d8Srobert
50*f6aab3d8Srobert out_fs.close();
51*f6aab3d8Srobert if (!out_fs)
52*f6aab3d8Srobert return createStringError(inconvertibleErrorCode(),
53*f6aab3d8Srobert formatv("couldn't write to the file {0}",
54*f6aab3d8Srobert output_file.GetPath().c_str()));
55*f6aab3d8Srobert return Error::success();
56*f6aab3d8Srobert }
57*f6aab3d8Srobert
58*f6aab3d8Srobert /// Save the trace bundle description JSON object inside the given directory
59*f6aab3d8Srobert /// as a file named \a trace.json.
60*f6aab3d8Srobert ///
61*f6aab3d8Srobert /// \param[in] trace_bundle_description
62*f6aab3d8Srobert /// The trace bundle description as JSON Object.
63*f6aab3d8Srobert ///
64*f6aab3d8Srobert /// \param[in] directory
65*f6aab3d8Srobert /// The directory where the JSON file will be saved.
66*f6aab3d8Srobert ///
67*f6aab3d8Srobert /// \return
68*f6aab3d8Srobert /// A \a FileSpec pointing to the bundle description file, or an \a
69*f6aab3d8Srobert /// llvm::Error otherwise.
70*f6aab3d8Srobert static Expected<FileSpec>
SaveTraceBundleDescription(const llvm::json::Value & trace_bundle_description,const FileSpec & directory)71*f6aab3d8Srobert SaveTraceBundleDescription(const llvm::json::Value &trace_bundle_description,
72*f6aab3d8Srobert const FileSpec &directory) {
73*f6aab3d8Srobert FileSpec trace_path = directory;
74*f6aab3d8Srobert trace_path.AppendPathComponent("trace.json");
75*f6aab3d8Srobert std::ofstream os(trace_path.GetPath());
76*f6aab3d8Srobert os << formatv("{0:2}", trace_bundle_description).str();
77*f6aab3d8Srobert os.close();
78*f6aab3d8Srobert if (!os)
79*f6aab3d8Srobert return createStringError(inconvertibleErrorCode(),
80*f6aab3d8Srobert formatv("couldn't write to the file {0}",
81*f6aab3d8Srobert trace_path.GetPath().c_str()));
82*f6aab3d8Srobert return trace_path;
83*f6aab3d8Srobert }
84*f6aab3d8Srobert
85*f6aab3d8Srobert /// Build the threads sub-section of the trace bundle description file.
86*f6aab3d8Srobert /// Any associated binary files are created inside the given directory.
87*f6aab3d8Srobert ///
88*f6aab3d8Srobert /// \param[in] process
89*f6aab3d8Srobert /// The process being traced.
90*f6aab3d8Srobert ///
91*f6aab3d8Srobert /// \param[in] directory
92*f6aab3d8Srobert /// The directory where files will be saved when building the threads
93*f6aab3d8Srobert /// section.
94*f6aab3d8Srobert ///
95*f6aab3d8Srobert /// \return
96*f6aab3d8Srobert /// The threads section or \a llvm::Error in case of failures.
97*f6aab3d8Srobert static llvm::Expected<std::vector<JSONThread>>
BuildThreadsSection(Process & process,FileSpec directory)98*f6aab3d8Srobert BuildThreadsSection(Process &process, FileSpec directory) {
99*f6aab3d8Srobert std::vector<JSONThread> json_threads;
100*f6aab3d8Srobert TraceSP trace_sp = process.GetTarget().GetTrace();
101*f6aab3d8Srobert
102*f6aab3d8Srobert FileSpec threads_dir = directory;
103*f6aab3d8Srobert threads_dir.AppendPathComponent("threads");
104*f6aab3d8Srobert sys::fs::create_directories(threads_dir.GetPath().c_str());
105*f6aab3d8Srobert
106*f6aab3d8Srobert for (ThreadSP thread_sp : process.Threads()) {
107*f6aab3d8Srobert lldb::tid_t tid = thread_sp->GetID();
108*f6aab3d8Srobert if (!trace_sp->IsTraced(tid))
109*f6aab3d8Srobert continue;
110*f6aab3d8Srobert
111*f6aab3d8Srobert JSONThread json_thread;
112*f6aab3d8Srobert json_thread.tid = tid;
113*f6aab3d8Srobert
114*f6aab3d8Srobert if (trace_sp->GetTracedCpus().empty()) {
115*f6aab3d8Srobert FileSpec output_file = threads_dir;
116*f6aab3d8Srobert output_file.AppendPathComponent(std::to_string(tid) + ".intelpt_trace");
117*f6aab3d8Srobert json_thread.ipt_trace = GetRelativePath(directory, output_file);
118*f6aab3d8Srobert
119*f6aab3d8Srobert llvm::Error err = process.GetTarget().GetTrace()->OnThreadBinaryDataRead(
120*f6aab3d8Srobert tid, IntelPTDataKinds::kIptTrace,
121*f6aab3d8Srobert [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
122*f6aab3d8Srobert return WriteBytesToDisk(output_file, data);
123*f6aab3d8Srobert });
124*f6aab3d8Srobert if (err)
125*f6aab3d8Srobert return std::move(err);
126*f6aab3d8Srobert }
127*f6aab3d8Srobert
128*f6aab3d8Srobert json_threads.push_back(std::move(json_thread));
129*f6aab3d8Srobert }
130*f6aab3d8Srobert return json_threads;
131*f6aab3d8Srobert }
132*f6aab3d8Srobert
133*f6aab3d8Srobert /// \return
134*f6aab3d8Srobert /// an \a llvm::Error in case of failures, \a std::nullopt if the trace is not
135*f6aab3d8Srobert /// written to disk because the trace is empty and the \p compact flag is
136*f6aab3d8Srobert /// present, or the FileSpec of the trace file on disk.
137*f6aab3d8Srobert static Expected<std::optional<FileSpec>>
WriteContextSwitchTrace(TraceIntelPT & trace_ipt,lldb::cpu_id_t cpu_id,const FileSpec & cpus_dir,bool compact)138*f6aab3d8Srobert WriteContextSwitchTrace(TraceIntelPT &trace_ipt, lldb::cpu_id_t cpu_id,
139*f6aab3d8Srobert const FileSpec &cpus_dir, bool compact) {
140*f6aab3d8Srobert FileSpec output_context_switch_trace = cpus_dir;
141*f6aab3d8Srobert output_context_switch_trace.AppendPathComponent(std::to_string(cpu_id) +
142*f6aab3d8Srobert ".perf_context_switch_trace");
143*f6aab3d8Srobert
144*f6aab3d8Srobert bool should_skip = false;
145*f6aab3d8Srobert
146*f6aab3d8Srobert Error err = trace_ipt.OnCpuBinaryDataRead(
147*f6aab3d8Srobert cpu_id, IntelPTDataKinds::kPerfContextSwitchTrace,
148*f6aab3d8Srobert [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
149*f6aab3d8Srobert if (!compact)
150*f6aab3d8Srobert return WriteBytesToDisk(output_context_switch_trace, data);
151*f6aab3d8Srobert
152*f6aab3d8Srobert std::set<lldb::pid_t> pids;
153*f6aab3d8Srobert for (Process *process : trace_ipt.GetAllProcesses())
154*f6aab3d8Srobert pids.insert(process->GetID());
155*f6aab3d8Srobert
156*f6aab3d8Srobert Expected<std::vector<uint8_t>> compact_context_switch_trace =
157*f6aab3d8Srobert FilterProcessesFromContextSwitchTrace(data, pids);
158*f6aab3d8Srobert if (!compact_context_switch_trace)
159*f6aab3d8Srobert return compact_context_switch_trace.takeError();
160*f6aab3d8Srobert
161*f6aab3d8Srobert if (compact_context_switch_trace->empty()) {
162*f6aab3d8Srobert should_skip = true;
163*f6aab3d8Srobert return Error::success();
164*f6aab3d8Srobert }
165*f6aab3d8Srobert
166*f6aab3d8Srobert return WriteBytesToDisk(output_context_switch_trace,
167*f6aab3d8Srobert *compact_context_switch_trace);
168*f6aab3d8Srobert });
169*f6aab3d8Srobert if (err)
170*f6aab3d8Srobert return std::move(err);
171*f6aab3d8Srobert
172*f6aab3d8Srobert if (should_skip)
173*f6aab3d8Srobert return std::nullopt;
174*f6aab3d8Srobert return output_context_switch_trace;
175*f6aab3d8Srobert }
176*f6aab3d8Srobert
WriteIntelPTTrace(TraceIntelPT & trace_ipt,lldb::cpu_id_t cpu_id,const FileSpec & cpus_dir)177*f6aab3d8Srobert static Expected<FileSpec> WriteIntelPTTrace(TraceIntelPT &trace_ipt,
178*f6aab3d8Srobert lldb::cpu_id_t cpu_id,
179*f6aab3d8Srobert const FileSpec &cpus_dir) {
180*f6aab3d8Srobert FileSpec output_trace = cpus_dir;
181*f6aab3d8Srobert output_trace.AppendPathComponent(std::to_string(cpu_id) + ".intelpt_trace");
182*f6aab3d8Srobert
183*f6aab3d8Srobert Error err = trace_ipt.OnCpuBinaryDataRead(
184*f6aab3d8Srobert cpu_id, IntelPTDataKinds::kIptTrace,
185*f6aab3d8Srobert [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
186*f6aab3d8Srobert return WriteBytesToDisk(output_trace, data);
187*f6aab3d8Srobert });
188*f6aab3d8Srobert if (err)
189*f6aab3d8Srobert return std::move(err);
190*f6aab3d8Srobert return output_trace;
191*f6aab3d8Srobert }
192*f6aab3d8Srobert
193*f6aab3d8Srobert static llvm::Expected<std::optional<std::vector<JSONCpu>>>
BuildCpusSection(TraceIntelPT & trace_ipt,FileSpec directory,bool compact)194*f6aab3d8Srobert BuildCpusSection(TraceIntelPT &trace_ipt, FileSpec directory, bool compact) {
195*f6aab3d8Srobert if (trace_ipt.GetTracedCpus().empty())
196*f6aab3d8Srobert return std::nullopt;
197*f6aab3d8Srobert
198*f6aab3d8Srobert std::vector<JSONCpu> json_cpus;
199*f6aab3d8Srobert FileSpec cpus_dir = directory;
200*f6aab3d8Srobert cpus_dir.AppendPathComponent("cpus");
201*f6aab3d8Srobert sys::fs::create_directories(cpus_dir.GetPath().c_str());
202*f6aab3d8Srobert
203*f6aab3d8Srobert for (lldb::cpu_id_t cpu_id : trace_ipt.GetTracedCpus()) {
204*f6aab3d8Srobert JSONCpu json_cpu;
205*f6aab3d8Srobert json_cpu.id = cpu_id;
206*f6aab3d8Srobert Expected<std::optional<FileSpec>> context_switch_trace_path =
207*f6aab3d8Srobert WriteContextSwitchTrace(trace_ipt, cpu_id, cpus_dir, compact);
208*f6aab3d8Srobert if (!context_switch_trace_path)
209*f6aab3d8Srobert return context_switch_trace_path.takeError();
210*f6aab3d8Srobert if (!*context_switch_trace_path)
211*f6aab3d8Srobert continue;
212*f6aab3d8Srobert json_cpu.context_switch_trace =
213*f6aab3d8Srobert GetRelativePath(directory, **context_switch_trace_path);
214*f6aab3d8Srobert
215*f6aab3d8Srobert if (Expected<FileSpec> ipt_trace_path =
216*f6aab3d8Srobert WriteIntelPTTrace(trace_ipt, cpu_id, cpus_dir))
217*f6aab3d8Srobert json_cpu.ipt_trace = GetRelativePath(directory, *ipt_trace_path);
218*f6aab3d8Srobert else
219*f6aab3d8Srobert return ipt_trace_path.takeError();
220*f6aab3d8Srobert
221*f6aab3d8Srobert json_cpus.push_back(std::move(json_cpu));
222*f6aab3d8Srobert }
223*f6aab3d8Srobert return json_cpus;
224*f6aab3d8Srobert }
225*f6aab3d8Srobert
226*f6aab3d8Srobert /// Build modules sub-section of the trace bundle. The original modules
227*f6aab3d8Srobert /// will be copied over to the \a <directory/modules> folder. Invalid modules
228*f6aab3d8Srobert /// are skipped.
229*f6aab3d8Srobert /// Copying the modules has the benefit of making these
230*f6aab3d8Srobert /// directories self-contained, as the raw traces and modules are part of the
231*f6aab3d8Srobert /// output directory and can be sent to another machine, where lldb can load
232*f6aab3d8Srobert /// them and replicate exactly the same trace session.
233*f6aab3d8Srobert ///
234*f6aab3d8Srobert /// \param[in] process
235*f6aab3d8Srobert /// The process being traced.
236*f6aab3d8Srobert ///
237*f6aab3d8Srobert /// \param[in] directory
238*f6aab3d8Srobert /// The directory where the modules files will be saved when building
239*f6aab3d8Srobert /// the modules section.
240*f6aab3d8Srobert /// Example: If a module \a libbar.so exists in the path
241*f6aab3d8Srobert /// \a /usr/lib/foo/libbar.so, then it will be copied to
242*f6aab3d8Srobert /// \a <directory>/modules/usr/lib/foo/libbar.so.
243*f6aab3d8Srobert ///
244*f6aab3d8Srobert /// \return
245*f6aab3d8Srobert /// The modules section or \a llvm::Error in case of failures.
246*f6aab3d8Srobert static llvm::Expected<std::vector<JSONModule>>
BuildModulesSection(Process & process,FileSpec directory)247*f6aab3d8Srobert BuildModulesSection(Process &process, FileSpec directory) {
248*f6aab3d8Srobert std::vector<JSONModule> json_modules;
249*f6aab3d8Srobert ModuleList module_list = process.GetTarget().GetImages();
250*f6aab3d8Srobert for (size_t i = 0; i < module_list.GetSize(); ++i) {
251*f6aab3d8Srobert ModuleSP module_sp(module_list.GetModuleAtIndex(i));
252*f6aab3d8Srobert if (!module_sp)
253*f6aab3d8Srobert continue;
254*f6aab3d8Srobert std::string system_path = module_sp->GetPlatformFileSpec().GetPath();
255*f6aab3d8Srobert // TODO: support memory-only libraries like [vdso]
256*f6aab3d8Srobert if (!module_sp->GetFileSpec().IsAbsolute())
257*f6aab3d8Srobert continue;
258*f6aab3d8Srobert
259*f6aab3d8Srobert std::string file = module_sp->GetFileSpec().GetPath();
260*f6aab3d8Srobert ObjectFile *objfile = module_sp->GetObjectFile();
261*f6aab3d8Srobert if (objfile == nullptr)
262*f6aab3d8Srobert continue;
263*f6aab3d8Srobert
264*f6aab3d8Srobert lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
265*f6aab3d8Srobert Address base_addr(objfile->GetBaseAddress());
266*f6aab3d8Srobert if (base_addr.IsValid() &&
267*f6aab3d8Srobert !process.GetTarget().GetSectionLoadList().IsEmpty())
268*f6aab3d8Srobert load_addr = base_addr.GetLoadAddress(&process.GetTarget());
269*f6aab3d8Srobert
270*f6aab3d8Srobert if (load_addr == LLDB_INVALID_ADDRESS)
271*f6aab3d8Srobert continue;
272*f6aab3d8Srobert
273*f6aab3d8Srobert FileSpec path_to_copy_module = directory;
274*f6aab3d8Srobert path_to_copy_module.AppendPathComponent("modules");
275*f6aab3d8Srobert path_to_copy_module.AppendPathComponent(system_path);
276*f6aab3d8Srobert sys::fs::create_directories(path_to_copy_module.GetDirectory().AsCString());
277*f6aab3d8Srobert
278*f6aab3d8Srobert if (std::error_code ec =
279*f6aab3d8Srobert llvm::sys::fs::copy_file(file, path_to_copy_module.GetPath()))
280*f6aab3d8Srobert return createStringError(
281*f6aab3d8Srobert inconvertibleErrorCode(),
282*f6aab3d8Srobert formatv("couldn't write to the file. {0}", ec.message()));
283*f6aab3d8Srobert
284*f6aab3d8Srobert json_modules.push_back(
285*f6aab3d8Srobert JSONModule{system_path, GetRelativePath(directory, path_to_copy_module),
286*f6aab3d8Srobert JSONUINT64{load_addr}, module_sp->GetUUID().GetAsString()});
287*f6aab3d8Srobert }
288*f6aab3d8Srobert return json_modules;
289*f6aab3d8Srobert }
290*f6aab3d8Srobert
291*f6aab3d8Srobert /// Build the processes section of the trace bundle description object. Besides
292*f6aab3d8Srobert /// returning the processes information, this method saves to disk all modules
293*f6aab3d8Srobert /// and raw traces corresponding to the traced threads of the given process.
294*f6aab3d8Srobert ///
295*f6aab3d8Srobert /// \param[in] process
296*f6aab3d8Srobert /// The process being traced.
297*f6aab3d8Srobert ///
298*f6aab3d8Srobert /// \param[in] directory
299*f6aab3d8Srobert /// The directory where files will be saved when building the processes
300*f6aab3d8Srobert /// section.
301*f6aab3d8Srobert ///
302*f6aab3d8Srobert /// \return
303*f6aab3d8Srobert /// The processes section or \a llvm::Error in case of failures.
304*f6aab3d8Srobert static llvm::Expected<JSONProcess>
BuildProcessSection(Process & process,const FileSpec & directory)305*f6aab3d8Srobert BuildProcessSection(Process &process, const FileSpec &directory) {
306*f6aab3d8Srobert Expected<std::vector<JSONThread>> json_threads =
307*f6aab3d8Srobert BuildThreadsSection(process, directory);
308*f6aab3d8Srobert if (!json_threads)
309*f6aab3d8Srobert return json_threads.takeError();
310*f6aab3d8Srobert
311*f6aab3d8Srobert Expected<std::vector<JSONModule>> json_modules =
312*f6aab3d8Srobert BuildModulesSection(process, directory);
313*f6aab3d8Srobert if (!json_modules)
314*f6aab3d8Srobert return json_modules.takeError();
315*f6aab3d8Srobert
316*f6aab3d8Srobert return JSONProcess{
317*f6aab3d8Srobert process.GetID(),
318*f6aab3d8Srobert process.GetTarget().GetArchitecture().GetTriple().getTriple(),
319*f6aab3d8Srobert json_threads.get(), json_modules.get()};
320*f6aab3d8Srobert }
321*f6aab3d8Srobert
322*f6aab3d8Srobert /// See BuildProcessSection()
323*f6aab3d8Srobert static llvm::Expected<std::vector<JSONProcess>>
BuildProcessesSection(TraceIntelPT & trace_ipt,const FileSpec & directory)324*f6aab3d8Srobert BuildProcessesSection(TraceIntelPT &trace_ipt, const FileSpec &directory) {
325*f6aab3d8Srobert std::vector<JSONProcess> processes;
326*f6aab3d8Srobert for (Process *process : trace_ipt.GetAllProcesses()) {
327*f6aab3d8Srobert if (llvm::Expected<JSONProcess> json_process =
328*f6aab3d8Srobert BuildProcessSection(*process, directory))
329*f6aab3d8Srobert processes.push_back(std::move(*json_process));
330*f6aab3d8Srobert else
331*f6aab3d8Srobert return json_process.takeError();
332*f6aab3d8Srobert }
333*f6aab3d8Srobert return processes;
334*f6aab3d8Srobert }
335*f6aab3d8Srobert
336*f6aab3d8Srobert static llvm::Expected<JSONKernel>
BuildKernelSection(TraceIntelPT & trace_ipt,const FileSpec & directory)337*f6aab3d8Srobert BuildKernelSection(TraceIntelPT &trace_ipt, const FileSpec &directory) {
338*f6aab3d8Srobert JSONKernel json_kernel;
339*f6aab3d8Srobert std::vector<Process *> processes = trace_ipt.GetAllProcesses();
340*f6aab3d8Srobert Process *kernel_process = processes[0];
341*f6aab3d8Srobert
342*f6aab3d8Srobert assert(processes.size() == 1 && "User processeses exist in kernel mode");
343*f6aab3d8Srobert assert(kernel_process->GetID() == kDefaultKernelProcessID &&
344*f6aab3d8Srobert "Kernel process not exist");
345*f6aab3d8Srobert
346*f6aab3d8Srobert Expected<std::vector<JSONModule>> json_modules =
347*f6aab3d8Srobert BuildModulesSection(*kernel_process, directory);
348*f6aab3d8Srobert if (!json_modules)
349*f6aab3d8Srobert return json_modules.takeError();
350*f6aab3d8Srobert
351*f6aab3d8Srobert JSONModule kernel_image = json_modules.get()[0];
352*f6aab3d8Srobert return JSONKernel{kernel_image.load_address, kernel_image.system_path};
353*f6aab3d8Srobert }
354*f6aab3d8Srobert
SaveToDisk(TraceIntelPT & trace_ipt,FileSpec directory,bool compact)355*f6aab3d8Srobert Expected<FileSpec> TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt,
356*f6aab3d8Srobert FileSpec directory,
357*f6aab3d8Srobert bool compact) {
358*f6aab3d8Srobert if (std::error_code ec =
359*f6aab3d8Srobert sys::fs::create_directories(directory.GetPath().c_str()))
360*f6aab3d8Srobert return llvm::errorCodeToError(ec);
361*f6aab3d8Srobert
362*f6aab3d8Srobert Expected<pt_cpu> cpu_info = trace_ipt.GetCPUInfo();
363*f6aab3d8Srobert if (!cpu_info)
364*f6aab3d8Srobert return cpu_info.takeError();
365*f6aab3d8Srobert
366*f6aab3d8Srobert FileSystem::Instance().Resolve(directory);
367*f6aab3d8Srobert
368*f6aab3d8Srobert Expected<std::optional<std::vector<JSONCpu>>> json_cpus =
369*f6aab3d8Srobert BuildCpusSection(trace_ipt, directory, compact);
370*f6aab3d8Srobert if (!json_cpus)
371*f6aab3d8Srobert return json_cpus.takeError();
372*f6aab3d8Srobert
373*f6aab3d8Srobert std::optional<std::vector<JSONProcess>> json_processes;
374*f6aab3d8Srobert std::optional<JSONKernel> json_kernel;
375*f6aab3d8Srobert
376*f6aab3d8Srobert if (trace_ipt.GetTraceMode() == TraceIntelPT::TraceMode::KernelMode) {
377*f6aab3d8Srobert Expected<std::optional<JSONKernel>> exp_json_kernel =
378*f6aab3d8Srobert BuildKernelSection(trace_ipt, directory);
379*f6aab3d8Srobert if (!exp_json_kernel)
380*f6aab3d8Srobert return exp_json_kernel.takeError();
381*f6aab3d8Srobert else
382*f6aab3d8Srobert json_kernel = *exp_json_kernel;
383*f6aab3d8Srobert } else {
384*f6aab3d8Srobert Expected<std::optional<std::vector<JSONProcess>>> exp_json_processes =
385*f6aab3d8Srobert BuildProcessesSection(trace_ipt, directory);
386*f6aab3d8Srobert if (!exp_json_processes)
387*f6aab3d8Srobert return exp_json_processes.takeError();
388*f6aab3d8Srobert else
389*f6aab3d8Srobert json_processes = *exp_json_processes;
390*f6aab3d8Srobert }
391*f6aab3d8Srobert
392*f6aab3d8Srobert JSONTraceBundleDescription json_intel_pt_bundle_desc{
393*f6aab3d8Srobert "intel-pt",
394*f6aab3d8Srobert *cpu_info,
395*f6aab3d8Srobert json_processes,
396*f6aab3d8Srobert *json_cpus,
397*f6aab3d8Srobert trace_ipt.GetPerfZeroTscConversion(),
398*f6aab3d8Srobert json_kernel};
399*f6aab3d8Srobert
400*f6aab3d8Srobert return SaveTraceBundleDescription(toJSON(json_intel_pt_bundle_desc),
401*f6aab3d8Srobert directory);
402*f6aab3d8Srobert }
403