xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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