181ad6265SDimitry Andric //===-- TraceIntelPTBundleSaver.cpp ---------------------------------------===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric
981ad6265SDimitry Andric #include "TraceIntelPTBundleSaver.h"
10753f127fSDimitry Andric #include "PerfContextSwitchDecoder.h"
1181ad6265SDimitry Andric #include "TraceIntelPT.h"
12*bdd1243dSDimitry Andric #include "TraceIntelPTConstants.h"
1381ad6265SDimitry Andric #include "TraceIntelPTJSONStructs.h"
1481ad6265SDimitry Andric #include "lldb/Core/Module.h"
1581ad6265SDimitry Andric #include "lldb/Core/ModuleList.h"
1681ad6265SDimitry Andric #include "lldb/Target/Process.h"
1781ad6265SDimitry Andric #include "lldb/Target/SectionLoadList.h"
1881ad6265SDimitry Andric #include "lldb/Target/Target.h"
1981ad6265SDimitry Andric #include "lldb/Target/ThreadList.h"
2081ad6265SDimitry Andric #include "lldb/lldb-types.h"
2181ad6265SDimitry Andric #include "llvm/Support/Error.h"
2281ad6265SDimitry Andric #include "llvm/Support/JSON.h"
2381ad6265SDimitry Andric #include <fstream>
2481ad6265SDimitry Andric #include <iostream>
25*bdd1243dSDimitry Andric #include <optional>
2681ad6265SDimitry Andric #include <sstream>
2781ad6265SDimitry Andric #include <string>
2881ad6265SDimitry Andric
2981ad6265SDimitry Andric using namespace lldb;
3081ad6265SDimitry Andric using namespace lldb_private;
3181ad6265SDimitry Andric using namespace lldb_private::trace_intel_pt;
3281ad6265SDimitry Andric using namespace llvm;
3381ad6265SDimitry Andric
34753f127fSDimitry Andric /// Strip the \p directory component from the given \p path. It assumes that \p
35753f127fSDimitry Andric /// directory is a prefix of \p path.
GetRelativePath(const FileSpec & directory,const FileSpec & path)36753f127fSDimitry Andric static std::string GetRelativePath(const FileSpec &directory,
37753f127fSDimitry Andric const FileSpec &path) {
38753f127fSDimitry Andric return path.GetPath().substr(directory.GetPath().size() + 1);
39753f127fSDimitry Andric }
40753f127fSDimitry Andric
4181ad6265SDimitry Andric /// Write a stream of bytes from \p data to the given output file.
4281ad6265SDimitry Andric /// It creates or overwrites the output file, but not append.
WriteBytesToDisk(FileSpec & output_file,ArrayRef<uint8_t> data)4381ad6265SDimitry Andric static llvm::Error WriteBytesToDisk(FileSpec &output_file,
4481ad6265SDimitry Andric ArrayRef<uint8_t> data) {
4581ad6265SDimitry Andric std::basic_fstream<char> out_fs = std::fstream(
4681ad6265SDimitry Andric output_file.GetPath().c_str(), std::ios::out | std::ios::binary);
4781ad6265SDimitry Andric if (!data.empty())
4881ad6265SDimitry Andric out_fs.write(reinterpret_cast<const char *>(&data[0]), data.size());
4981ad6265SDimitry Andric
5081ad6265SDimitry Andric out_fs.close();
5181ad6265SDimitry Andric if (!out_fs)
5281ad6265SDimitry Andric return createStringError(inconvertibleErrorCode(),
5381ad6265SDimitry Andric formatv("couldn't write to the file {0}",
5481ad6265SDimitry Andric output_file.GetPath().c_str()));
5581ad6265SDimitry Andric return Error::success();
5681ad6265SDimitry Andric }
5781ad6265SDimitry Andric
5881ad6265SDimitry Andric /// Save the trace bundle description JSON object inside the given directory
5981ad6265SDimitry Andric /// as a file named \a trace.json.
6081ad6265SDimitry Andric ///
6181ad6265SDimitry Andric /// \param[in] trace_bundle_description
6281ad6265SDimitry Andric /// The trace bundle description as JSON Object.
6381ad6265SDimitry Andric ///
6481ad6265SDimitry Andric /// \param[in] directory
6581ad6265SDimitry Andric /// The directory where the JSON file will be saved.
6681ad6265SDimitry Andric ///
6781ad6265SDimitry Andric /// \return
68753f127fSDimitry Andric /// A \a FileSpec pointing to the bundle description file, or an \a
69753f127fSDimitry Andric /// llvm::Error otherwise.
70753f127fSDimitry Andric static Expected<FileSpec>
SaveTraceBundleDescription(const llvm::json::Value & trace_bundle_description,const FileSpec & directory)7181ad6265SDimitry Andric SaveTraceBundleDescription(const llvm::json::Value &trace_bundle_description,
7281ad6265SDimitry Andric const FileSpec &directory) {
7381ad6265SDimitry Andric FileSpec trace_path = directory;
7481ad6265SDimitry Andric trace_path.AppendPathComponent("trace.json");
7581ad6265SDimitry Andric std::ofstream os(trace_path.GetPath());
7681ad6265SDimitry Andric os << formatv("{0:2}", trace_bundle_description).str();
7781ad6265SDimitry Andric os.close();
7881ad6265SDimitry Andric if (!os)
7981ad6265SDimitry Andric return createStringError(inconvertibleErrorCode(),
8081ad6265SDimitry Andric formatv("couldn't write to the file {0}",
8181ad6265SDimitry Andric trace_path.GetPath().c_str()));
82753f127fSDimitry Andric return trace_path;
8381ad6265SDimitry Andric }
8481ad6265SDimitry Andric
8581ad6265SDimitry Andric /// Build the threads sub-section of the trace bundle description file.
8681ad6265SDimitry Andric /// Any associated binary files are created inside the given directory.
8781ad6265SDimitry Andric ///
8881ad6265SDimitry Andric /// \param[in] process
8981ad6265SDimitry Andric /// The process being traced.
9081ad6265SDimitry Andric ///
9181ad6265SDimitry Andric /// \param[in] directory
9281ad6265SDimitry Andric /// The directory where files will be saved when building the threads
9381ad6265SDimitry Andric /// section.
9481ad6265SDimitry Andric ///
9581ad6265SDimitry Andric /// \return
9681ad6265SDimitry Andric /// The threads section or \a llvm::Error in case of failures.
9781ad6265SDimitry Andric static llvm::Expected<std::vector<JSONThread>>
BuildThreadsSection(Process & process,FileSpec directory)9881ad6265SDimitry Andric BuildThreadsSection(Process &process, FileSpec directory) {
9981ad6265SDimitry Andric std::vector<JSONThread> json_threads;
10081ad6265SDimitry Andric TraceSP trace_sp = process.GetTarget().GetTrace();
10181ad6265SDimitry Andric
10281ad6265SDimitry Andric FileSpec threads_dir = directory;
10381ad6265SDimitry Andric threads_dir.AppendPathComponent("threads");
104*bdd1243dSDimitry Andric sys::fs::create_directories(threads_dir.GetPath().c_str());
10581ad6265SDimitry Andric
10681ad6265SDimitry Andric for (ThreadSP thread_sp : process.Threads()) {
10781ad6265SDimitry Andric lldb::tid_t tid = thread_sp->GetID();
10881ad6265SDimitry Andric if (!trace_sp->IsTraced(tid))
10981ad6265SDimitry Andric continue;
11081ad6265SDimitry Andric
11181ad6265SDimitry Andric JSONThread json_thread;
11281ad6265SDimitry Andric json_thread.tid = tid;
11381ad6265SDimitry Andric
11481ad6265SDimitry Andric if (trace_sp->GetTracedCpus().empty()) {
11581ad6265SDimitry Andric FileSpec output_file = threads_dir;
11681ad6265SDimitry Andric output_file.AppendPathComponent(std::to_string(tid) + ".intelpt_trace");
117753f127fSDimitry Andric json_thread.ipt_trace = GetRelativePath(directory, output_file);
11881ad6265SDimitry Andric
11981ad6265SDimitry Andric llvm::Error err = process.GetTarget().GetTrace()->OnThreadBinaryDataRead(
12081ad6265SDimitry Andric tid, IntelPTDataKinds::kIptTrace,
12181ad6265SDimitry Andric [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
12281ad6265SDimitry Andric return WriteBytesToDisk(output_file, data);
12381ad6265SDimitry Andric });
12481ad6265SDimitry Andric if (err)
12581ad6265SDimitry Andric return std::move(err);
12681ad6265SDimitry Andric }
12781ad6265SDimitry Andric
12881ad6265SDimitry Andric json_threads.push_back(std::move(json_thread));
12981ad6265SDimitry Andric }
13081ad6265SDimitry Andric return json_threads;
13181ad6265SDimitry Andric }
13281ad6265SDimitry Andric
133753f127fSDimitry Andric /// \return
134*bdd1243dSDimitry Andric /// an \a llvm::Error in case of failures, \a std::nullopt if the trace is not
135*bdd1243dSDimitry Andric /// written to disk because the trace is empty and the \p compact flag is
136*bdd1243dSDimitry Andric /// present, or the FileSpec of the trace file on disk.
137*bdd1243dSDimitry Andric static Expected<std::optional<FileSpec>>
WriteContextSwitchTrace(TraceIntelPT & trace_ipt,lldb::cpu_id_t cpu_id,const FileSpec & cpus_dir,bool compact)138753f127fSDimitry Andric WriteContextSwitchTrace(TraceIntelPT &trace_ipt, lldb::cpu_id_t cpu_id,
139753f127fSDimitry Andric const FileSpec &cpus_dir, bool compact) {
140753f127fSDimitry Andric FileSpec output_context_switch_trace = cpus_dir;
141753f127fSDimitry Andric output_context_switch_trace.AppendPathComponent(std::to_string(cpu_id) +
142753f127fSDimitry Andric ".perf_context_switch_trace");
143753f127fSDimitry Andric
144753f127fSDimitry Andric bool should_skip = false;
145753f127fSDimitry Andric
146753f127fSDimitry Andric Error err = trace_ipt.OnCpuBinaryDataRead(
147753f127fSDimitry Andric cpu_id, IntelPTDataKinds::kPerfContextSwitchTrace,
148753f127fSDimitry Andric [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
149753f127fSDimitry Andric if (!compact)
150753f127fSDimitry Andric return WriteBytesToDisk(output_context_switch_trace, data);
151753f127fSDimitry Andric
152753f127fSDimitry Andric std::set<lldb::pid_t> pids;
153753f127fSDimitry Andric for (Process *process : trace_ipt.GetAllProcesses())
154753f127fSDimitry Andric pids.insert(process->GetID());
155753f127fSDimitry Andric
156753f127fSDimitry Andric Expected<std::vector<uint8_t>> compact_context_switch_trace =
157753f127fSDimitry Andric FilterProcessesFromContextSwitchTrace(data, pids);
158753f127fSDimitry Andric if (!compact_context_switch_trace)
159753f127fSDimitry Andric return compact_context_switch_trace.takeError();
160753f127fSDimitry Andric
161753f127fSDimitry Andric if (compact_context_switch_trace->empty()) {
162753f127fSDimitry Andric should_skip = true;
163753f127fSDimitry Andric return Error::success();
164753f127fSDimitry Andric }
165753f127fSDimitry Andric
166753f127fSDimitry Andric return WriteBytesToDisk(output_context_switch_trace,
167753f127fSDimitry Andric *compact_context_switch_trace);
168753f127fSDimitry Andric });
169753f127fSDimitry Andric if (err)
170753f127fSDimitry Andric return std::move(err);
171753f127fSDimitry Andric
172753f127fSDimitry Andric if (should_skip)
173*bdd1243dSDimitry Andric return std::nullopt;
174753f127fSDimitry Andric return output_context_switch_trace;
175753f127fSDimitry Andric }
176753f127fSDimitry Andric
WriteIntelPTTrace(TraceIntelPT & trace_ipt,lldb::cpu_id_t cpu_id,const FileSpec & cpus_dir)177753f127fSDimitry Andric static Expected<FileSpec> WriteIntelPTTrace(TraceIntelPT &trace_ipt,
178753f127fSDimitry Andric lldb::cpu_id_t cpu_id,
179753f127fSDimitry Andric const FileSpec &cpus_dir) {
180753f127fSDimitry Andric FileSpec output_trace = cpus_dir;
181753f127fSDimitry Andric output_trace.AppendPathComponent(std::to_string(cpu_id) + ".intelpt_trace");
182753f127fSDimitry Andric
183753f127fSDimitry Andric Error err = trace_ipt.OnCpuBinaryDataRead(
184753f127fSDimitry Andric cpu_id, IntelPTDataKinds::kIptTrace,
185753f127fSDimitry Andric [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
186753f127fSDimitry Andric return WriteBytesToDisk(output_trace, data);
187753f127fSDimitry Andric });
188753f127fSDimitry Andric if (err)
189753f127fSDimitry Andric return std::move(err);
190753f127fSDimitry Andric return output_trace;
191753f127fSDimitry Andric }
192753f127fSDimitry Andric
193*bdd1243dSDimitry Andric static llvm::Expected<std::optional<std::vector<JSONCpu>>>
BuildCpusSection(TraceIntelPT & trace_ipt,FileSpec directory,bool compact)194753f127fSDimitry Andric BuildCpusSection(TraceIntelPT &trace_ipt, FileSpec directory, bool compact) {
19581ad6265SDimitry Andric if (trace_ipt.GetTracedCpus().empty())
196*bdd1243dSDimitry Andric return std::nullopt;
19781ad6265SDimitry Andric
19881ad6265SDimitry Andric std::vector<JSONCpu> json_cpus;
19981ad6265SDimitry Andric FileSpec cpus_dir = directory;
20081ad6265SDimitry Andric cpus_dir.AppendPathComponent("cpus");
201*bdd1243dSDimitry Andric sys::fs::create_directories(cpus_dir.GetPath().c_str());
20281ad6265SDimitry Andric
20381ad6265SDimitry Andric for (lldb::cpu_id_t cpu_id : trace_ipt.GetTracedCpus()) {
20481ad6265SDimitry Andric JSONCpu json_cpu;
20581ad6265SDimitry Andric json_cpu.id = cpu_id;
206*bdd1243dSDimitry Andric Expected<std::optional<FileSpec>> context_switch_trace_path =
207753f127fSDimitry Andric WriteContextSwitchTrace(trace_ipt, cpu_id, cpus_dir, compact);
208753f127fSDimitry Andric if (!context_switch_trace_path)
209753f127fSDimitry Andric return context_switch_trace_path.takeError();
210753f127fSDimitry Andric if (!*context_switch_trace_path)
211753f127fSDimitry Andric continue;
212753f127fSDimitry Andric json_cpu.context_switch_trace =
213753f127fSDimitry Andric GetRelativePath(directory, **context_switch_trace_path);
21481ad6265SDimitry Andric
215753f127fSDimitry Andric if (Expected<FileSpec> ipt_trace_path =
216753f127fSDimitry Andric WriteIntelPTTrace(trace_ipt, cpu_id, cpus_dir))
217753f127fSDimitry Andric json_cpu.ipt_trace = GetRelativePath(directory, *ipt_trace_path);
218753f127fSDimitry Andric else
219753f127fSDimitry Andric return ipt_trace_path.takeError();
22081ad6265SDimitry Andric
22181ad6265SDimitry Andric json_cpus.push_back(std::move(json_cpu));
22281ad6265SDimitry Andric }
22381ad6265SDimitry Andric return json_cpus;
22481ad6265SDimitry Andric }
22581ad6265SDimitry Andric
22681ad6265SDimitry Andric /// Build modules sub-section of the trace bundle. The original modules
22781ad6265SDimitry Andric /// will be copied over to the \a <directory/modules> folder. Invalid modules
22881ad6265SDimitry Andric /// are skipped.
22981ad6265SDimitry Andric /// Copying the modules has the benefit of making these
23081ad6265SDimitry Andric /// directories self-contained, as the raw traces and modules are part of the
23181ad6265SDimitry Andric /// output directory and can be sent to another machine, where lldb can load
23281ad6265SDimitry Andric /// them and replicate exactly the same trace session.
23381ad6265SDimitry Andric ///
23481ad6265SDimitry Andric /// \param[in] process
23581ad6265SDimitry Andric /// The process being traced.
23681ad6265SDimitry Andric ///
23781ad6265SDimitry Andric /// \param[in] directory
23881ad6265SDimitry Andric /// The directory where the modules files will be saved when building
23981ad6265SDimitry Andric /// the modules section.
24081ad6265SDimitry Andric /// Example: If a module \a libbar.so exists in the path
24181ad6265SDimitry Andric /// \a /usr/lib/foo/libbar.so, then it will be copied to
24281ad6265SDimitry Andric /// \a <directory>/modules/usr/lib/foo/libbar.so.
24381ad6265SDimitry Andric ///
24481ad6265SDimitry Andric /// \return
24581ad6265SDimitry Andric /// The modules section or \a llvm::Error in case of failures.
24681ad6265SDimitry Andric static llvm::Expected<std::vector<JSONModule>>
BuildModulesSection(Process & process,FileSpec directory)24781ad6265SDimitry Andric BuildModulesSection(Process &process, FileSpec directory) {
24881ad6265SDimitry Andric std::vector<JSONModule> json_modules;
24981ad6265SDimitry Andric ModuleList module_list = process.GetTarget().GetImages();
25081ad6265SDimitry Andric for (size_t i = 0; i < module_list.GetSize(); ++i) {
25181ad6265SDimitry Andric ModuleSP module_sp(module_list.GetModuleAtIndex(i));
25281ad6265SDimitry Andric if (!module_sp)
25381ad6265SDimitry Andric continue;
25481ad6265SDimitry Andric std::string system_path = module_sp->GetPlatformFileSpec().GetPath();
25581ad6265SDimitry Andric // TODO: support memory-only libraries like [vdso]
25681ad6265SDimitry Andric if (!module_sp->GetFileSpec().IsAbsolute())
25781ad6265SDimitry Andric continue;
25881ad6265SDimitry Andric
25981ad6265SDimitry Andric std::string file = module_sp->GetFileSpec().GetPath();
26081ad6265SDimitry Andric ObjectFile *objfile = module_sp->GetObjectFile();
26181ad6265SDimitry Andric if (objfile == nullptr)
26281ad6265SDimitry Andric continue;
26381ad6265SDimitry Andric
26481ad6265SDimitry Andric lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
26581ad6265SDimitry Andric Address base_addr(objfile->GetBaseAddress());
26681ad6265SDimitry Andric if (base_addr.IsValid() &&
26781ad6265SDimitry Andric !process.GetTarget().GetSectionLoadList().IsEmpty())
26881ad6265SDimitry Andric load_addr = base_addr.GetLoadAddress(&process.GetTarget());
26981ad6265SDimitry Andric
27081ad6265SDimitry Andric if (load_addr == LLDB_INVALID_ADDRESS)
27181ad6265SDimitry Andric continue;
27281ad6265SDimitry Andric
27381ad6265SDimitry Andric FileSpec path_to_copy_module = directory;
27481ad6265SDimitry Andric path_to_copy_module.AppendPathComponent("modules");
27581ad6265SDimitry Andric path_to_copy_module.AppendPathComponent(system_path);
27681ad6265SDimitry Andric sys::fs::create_directories(path_to_copy_module.GetDirectory().AsCString());
27781ad6265SDimitry Andric
278753f127fSDimitry Andric if (std::error_code ec =
279753f127fSDimitry Andric llvm::sys::fs::copy_file(file, path_to_copy_module.GetPath()))
28081ad6265SDimitry Andric return createStringError(
28181ad6265SDimitry Andric inconvertibleErrorCode(),
28281ad6265SDimitry Andric formatv("couldn't write to the file. {0}", ec.message()));
28381ad6265SDimitry Andric
28481ad6265SDimitry Andric json_modules.push_back(
285753f127fSDimitry Andric JSONModule{system_path, GetRelativePath(directory, path_to_copy_module),
28681ad6265SDimitry Andric JSONUINT64{load_addr}, module_sp->GetUUID().GetAsString()});
28781ad6265SDimitry Andric }
28881ad6265SDimitry Andric return json_modules;
28981ad6265SDimitry Andric }
29081ad6265SDimitry Andric
29181ad6265SDimitry Andric /// Build the processes section of the trace bundle description object. Besides
29281ad6265SDimitry Andric /// returning the processes information, this method saves to disk all modules
29381ad6265SDimitry Andric /// and raw traces corresponding to the traced threads of the given process.
29481ad6265SDimitry Andric ///
29581ad6265SDimitry Andric /// \param[in] process
29681ad6265SDimitry Andric /// The process being traced.
29781ad6265SDimitry Andric ///
29881ad6265SDimitry Andric /// \param[in] directory
29981ad6265SDimitry Andric /// The directory where files will be saved when building the processes
30081ad6265SDimitry Andric /// section.
30181ad6265SDimitry Andric ///
30281ad6265SDimitry Andric /// \return
30381ad6265SDimitry Andric /// The processes section or \a llvm::Error in case of failures.
30481ad6265SDimitry Andric static llvm::Expected<JSONProcess>
BuildProcessSection(Process & process,const FileSpec & directory)30581ad6265SDimitry Andric BuildProcessSection(Process &process, const FileSpec &directory) {
30681ad6265SDimitry Andric Expected<std::vector<JSONThread>> json_threads =
30781ad6265SDimitry Andric BuildThreadsSection(process, directory);
30881ad6265SDimitry Andric if (!json_threads)
30981ad6265SDimitry Andric return json_threads.takeError();
31081ad6265SDimitry Andric
31181ad6265SDimitry Andric Expected<std::vector<JSONModule>> json_modules =
31281ad6265SDimitry Andric BuildModulesSection(process, directory);
31381ad6265SDimitry Andric if (!json_modules)
31481ad6265SDimitry Andric return json_modules.takeError();
31581ad6265SDimitry Andric
31681ad6265SDimitry Andric return JSONProcess{
31781ad6265SDimitry Andric process.GetID(),
31881ad6265SDimitry Andric process.GetTarget().GetArchitecture().GetTriple().getTriple(),
31981ad6265SDimitry Andric json_threads.get(), json_modules.get()};
32081ad6265SDimitry Andric }
32181ad6265SDimitry Andric
32281ad6265SDimitry Andric /// See BuildProcessSection()
32381ad6265SDimitry Andric static llvm::Expected<std::vector<JSONProcess>>
BuildProcessesSection(TraceIntelPT & trace_ipt,const FileSpec & directory)32481ad6265SDimitry Andric BuildProcessesSection(TraceIntelPT &trace_ipt, const FileSpec &directory) {
32581ad6265SDimitry Andric std::vector<JSONProcess> processes;
32681ad6265SDimitry Andric for (Process *process : trace_ipt.GetAllProcesses()) {
32781ad6265SDimitry Andric if (llvm::Expected<JSONProcess> json_process =
32881ad6265SDimitry Andric BuildProcessSection(*process, directory))
32981ad6265SDimitry Andric processes.push_back(std::move(*json_process));
33081ad6265SDimitry Andric else
33181ad6265SDimitry Andric return json_process.takeError();
33281ad6265SDimitry Andric }
33381ad6265SDimitry Andric return processes;
33481ad6265SDimitry Andric }
33581ad6265SDimitry Andric
336*bdd1243dSDimitry Andric static llvm::Expected<JSONKernel>
BuildKernelSection(TraceIntelPT & trace_ipt,const FileSpec & directory)337*bdd1243dSDimitry Andric BuildKernelSection(TraceIntelPT &trace_ipt, const FileSpec &directory) {
338*bdd1243dSDimitry Andric JSONKernel json_kernel;
339*bdd1243dSDimitry Andric std::vector<Process *> processes = trace_ipt.GetAllProcesses();
340*bdd1243dSDimitry Andric Process *kernel_process = processes[0];
341*bdd1243dSDimitry Andric
342*bdd1243dSDimitry Andric assert(processes.size() == 1 && "User processeses exist in kernel mode");
343*bdd1243dSDimitry Andric assert(kernel_process->GetID() == kDefaultKernelProcessID &&
344*bdd1243dSDimitry Andric "Kernel process not exist");
345*bdd1243dSDimitry Andric
346*bdd1243dSDimitry Andric Expected<std::vector<JSONModule>> json_modules =
347*bdd1243dSDimitry Andric BuildModulesSection(*kernel_process, directory);
348*bdd1243dSDimitry Andric if (!json_modules)
349*bdd1243dSDimitry Andric return json_modules.takeError();
350*bdd1243dSDimitry Andric
351*bdd1243dSDimitry Andric JSONModule kernel_image = json_modules.get()[0];
352*bdd1243dSDimitry Andric return JSONKernel{kernel_image.load_address, kernel_image.system_path};
353*bdd1243dSDimitry Andric }
354*bdd1243dSDimitry Andric
SaveToDisk(TraceIntelPT & trace_ipt,FileSpec directory,bool compact)355753f127fSDimitry Andric Expected<FileSpec> TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt,
356753f127fSDimitry Andric FileSpec directory,
357753f127fSDimitry Andric bool compact) {
35881ad6265SDimitry Andric if (std::error_code ec =
35981ad6265SDimitry Andric sys::fs::create_directories(directory.GetPath().c_str()))
36081ad6265SDimitry Andric return llvm::errorCodeToError(ec);
36181ad6265SDimitry Andric
36281ad6265SDimitry Andric Expected<pt_cpu> cpu_info = trace_ipt.GetCPUInfo();
36381ad6265SDimitry Andric if (!cpu_info)
36481ad6265SDimitry Andric return cpu_info.takeError();
36581ad6265SDimitry Andric
36681ad6265SDimitry Andric FileSystem::Instance().Resolve(directory);
36781ad6265SDimitry Andric
368*bdd1243dSDimitry Andric Expected<std::optional<std::vector<JSONCpu>>> json_cpus =
369753f127fSDimitry Andric BuildCpusSection(trace_ipt, directory, compact);
37081ad6265SDimitry Andric if (!json_cpus)
37181ad6265SDimitry Andric return json_cpus.takeError();
37281ad6265SDimitry Andric
373*bdd1243dSDimitry Andric std::optional<std::vector<JSONProcess>> json_processes;
374*bdd1243dSDimitry Andric std::optional<JSONKernel> json_kernel;
37581ad6265SDimitry Andric
376*bdd1243dSDimitry Andric if (trace_ipt.GetTraceMode() == TraceIntelPT::TraceMode::KernelMode) {
377*bdd1243dSDimitry Andric Expected<std::optional<JSONKernel>> exp_json_kernel =
378*bdd1243dSDimitry Andric BuildKernelSection(trace_ipt, directory);
379*bdd1243dSDimitry Andric if (!exp_json_kernel)
380*bdd1243dSDimitry Andric return exp_json_kernel.takeError();
381*bdd1243dSDimitry Andric else
382*bdd1243dSDimitry Andric json_kernel = *exp_json_kernel;
383*bdd1243dSDimitry Andric } else {
384*bdd1243dSDimitry Andric Expected<std::optional<std::vector<JSONProcess>>> exp_json_processes =
385*bdd1243dSDimitry Andric BuildProcessesSection(trace_ipt, directory);
386*bdd1243dSDimitry Andric if (!exp_json_processes)
387*bdd1243dSDimitry Andric return exp_json_processes.takeError();
388*bdd1243dSDimitry Andric else
389*bdd1243dSDimitry Andric json_processes = *exp_json_processes;
390*bdd1243dSDimitry Andric }
391*bdd1243dSDimitry Andric
392*bdd1243dSDimitry Andric JSONTraceBundleDescription json_intel_pt_bundle_desc{
393*bdd1243dSDimitry Andric "intel-pt",
394*bdd1243dSDimitry Andric *cpu_info,
395*bdd1243dSDimitry Andric json_processes,
396*bdd1243dSDimitry Andric *json_cpus,
397*bdd1243dSDimitry Andric trace_ipt.GetPerfZeroTscConversion(),
398*bdd1243dSDimitry Andric json_kernel};
399*bdd1243dSDimitry Andric
400*bdd1243dSDimitry Andric return SaveTraceBundleDescription(toJSON(json_intel_pt_bundle_desc),
401*bdd1243dSDimitry Andric directory);
40281ad6265SDimitry Andric }
403