diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h @@ -172,6 +172,10 @@ TraceIntelPTSP GetSharedPtr(); + enum class TraceMode { UserMode, KernelMode }; + + TraceMode GetTraceMode(); + private: friend class TraceIntelPTBundleLoader; @@ -183,28 +187,34 @@ /// The definition file for the postmortem bundle. /// /// \param[in] traced_processes - /// The processes traced in the live session. + /// The processes traced in the postmortem session. /// /// \param[in] trace_threads - /// The threads traced in the live session. They must belong to the + /// The threads traced in the postmortem session. They must belong to the /// processes mentioned above. /// + /// \param[in] trace_mode + /// The tracing mode of the postmortem session. + /// /// \return /// A TraceIntelPT shared pointer instance. /// \{ static TraceIntelPTSP CreateInstanceForPostmortemTrace( JSONTraceBundleDescription &bundle_description, llvm::ArrayRef traced_processes, - llvm::ArrayRef traced_threads); + llvm::ArrayRef traced_threads, + TraceMode trace_mode); /// This constructor is used by CreateInstanceForPostmortemTrace to get the /// instance ready before using shared pointers, which is a limitation of C++. TraceIntelPT(JSONTraceBundleDescription &bundle_description, - llvm::ArrayRef traced_processes); + llvm::ArrayRef traced_processes, + TraceMode trace_mode); /// \} /// Constructor for live processes - TraceIntelPT(Process &live_process) : Trace(live_process){}; + TraceIntelPT(Process &live_process) + : Trace(live_process), trace_mode(TraceMode::UserMode){}; /// Decode the trace of the given thread that, i.e. recontruct the traced /// instructions. @@ -253,6 +263,9 @@ /// Get the storage after refreshing the data in the case of a live process. Storage &GetUpdatedStorage(); + + /// The tracing mode of post mortem trace. + TraceMode trace_mode; }; } // namespace trace_intel_pt diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -79,12 +79,14 @@ return std::static_pointer_cast(shared_from_this()); } +TraceIntelPT::TraceMode TraceIntelPT::GetTraceMode() { return trace_mode; } + TraceIntelPTSP TraceIntelPT::CreateInstanceForPostmortemTrace( JSONTraceBundleDescription &bundle_description, ArrayRef traced_processes, - ArrayRef traced_threads) { + ArrayRef traced_threads, TraceMode trace_mode) { TraceIntelPTSP trace_sp( - new TraceIntelPT(bundle_description, traced_processes)); + new TraceIntelPT(bundle_description, traced_processes, trace_mode)); trace_sp->m_storage.tsc_conversion = bundle_description.tsc_perf_zero_conversion; @@ -101,11 +103,6 @@ cpus.push_back(cpu.id); } - std::vector tids; - for (const JSONProcess &process : bundle_description.processes) - for (const JSONThread &thread : process.threads) - tids.push_back(thread.tid); - trace_sp->m_storage.multicpu_decoder.emplace(trace_sp); } else { for (const ThreadPostMortemTraceSP &thread : traced_threads) { @@ -124,9 +121,10 @@ } TraceIntelPT::TraceIntelPT(JSONTraceBundleDescription &bundle_description, - ArrayRef traced_processes) + ArrayRef traced_processes, + TraceMode trace_mode) : Trace(traced_processes, bundle_description.GetCpuIds()), - m_cpu_info(bundle_description.cpu_info) {} + m_cpu_info(bundle_description.cpu_info), trace_mode(trace_mode) {} Expected TraceIntelPT::Decode(Thread &thread) { if (const char *error = RefreshLiveProcessState()) diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h @@ -67,6 +67,10 @@ CreateTraceIntelPTInstance(JSONTraceBundleDescription &bundle_description, std::vector &parsed_processes); + /// Create an empty Process object with given pid and target. + llvm::Expected CreateEmptyProcess(lldb::pid_t pid, + llvm::StringRef triple); + /// Create the corresponding Threads and Process objects given the JSON /// process definition. /// @@ -78,6 +82,10 @@ /// from \p module. llvm::Error ParseModule(Target &target, const JSONModule &module); + /// Create a kernel process and cpu threads given the JSON kernel definition. + llvm::Expected + ParseKernel(const JSONTraceBundleDescription &bundle_description); + /// Create a user-friendly error message upon a JSON-parsing failure using the /// \a json::ObjectMapper functionality. /// diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp @@ -10,6 +10,7 @@ #include "../common/ThreadPostMortemTrace.h" #include "TraceIntelPT.h" +#include "TraceIntelPTConstants.h" #include "TraceIntelPTJSONStructs.h" #include "lldb/Core/Debugger.h" @@ -89,11 +90,11 @@ } Expected -TraceIntelPTBundleLoader::ParseProcess(const JSONProcess &process) { +TraceIntelPTBundleLoader::CreateEmptyProcess(lldb::pid_t pid, + llvm::StringRef triple) { TargetSP target_sp; Status error = m_debugger.GetTargetList().CreateTarget( - m_debugger, /*user_exe_path*/ StringRef(), process.triple.value_or(""), - eLoadDependentsNo, + m_debugger, /*user_exe_path*/ StringRef(), triple, eLoadDependentsNo, /*platform_options*/ nullptr, target_sp); if (!target_sp) @@ -107,13 +108,26 @@ /*crash_file*/ nullptr, /*can_connect*/ false); - process_sp->SetID(static_cast(process.pid)); + process_sp->SetID(static_cast(pid)); + + return parsed_process; +} + +Expected +TraceIntelPTBundleLoader::ParseProcess(const JSONProcess &process) { + Expected parsed_process = + CreateEmptyProcess(process.pid, process.triple.value_or("")); + + if (!parsed_process) + return parsed_process.takeError(); + + ProcessSP process_sp = parsed_process->target_sp->GetProcessSP(); for (const JSONThread &thread : process.threads) - parsed_process.threads.push_back(ParseThread(*process_sp, thread)); + parsed_process->threads.push_back(ParseThread(*process_sp, thread)); for (const JSONModule &module : process.modules) - if (Error err = ParseModule(*target_sp, module)) + if (Error err = ParseModule(*parsed_process->target_sp, module)) return std::move(err); if (!process.threads.empty()) @@ -127,6 +141,57 @@ return parsed_process; } +Expected +TraceIntelPTBundleLoader::ParseKernel( + const JSONTraceBundleDescription &bundle_description) { + Expected parsed_process = + CreateEmptyProcess(kDefaultKernelProcessID, ""); + + if (!parsed_process) + return parsed_process.takeError(); + + ProcessSP process_sp = parsed_process->target_sp->GetProcessSP(); + + // Add cpus as fake threads + for (const JSONCpu &cpu : *bundle_description.cpus) { + ThreadPostMortemTraceSP thread_sp = std::make_shared( + *process_sp, static_cast(cpu.id), FileSpec(cpu.ipt_trace)); + thread_sp->SetName(formatv("kernel_cpu_{0}", cpu.id).str().c_str()); + process_sp->GetThreadList().AddThread(thread_sp); + parsed_process->threads.push_back(thread_sp); + } + + // Add kernel image + FileSpec file_spec(bundle_description.kernel->file); + ModuleSpec module_spec; + module_spec.GetFileSpec() = file_spec; + + Status error; + ModuleSP module_sp = + parsed_process->target_sp->GetOrCreateModule(module_spec, false, &error); + + if (error.Fail()) + return error.ToError(); + + lldb::addr_t load_address = + bundle_description.kernel->load_address + ? bundle_description.kernel->load_address->value + : kDefaultKernelLoadAddress; + + bool load_addr_changed = false; + module_sp->SetLoadAddress(*parsed_process->target_sp, load_address, false, + load_addr_changed); + + process_sp->GetThreadList().SetSelectedThreadByIndexID(0); + + // We invoke DidAttach to create a correct stopped state for the process and + // its threads. + ArchSpec process_arch; + process_sp->DidAttach(process_arch); + + return parsed_process; +} + Expected> TraceIntelPTBundleLoader::LoadBundle( const JSONTraceBundleDescription &bundle_description) { @@ -139,11 +204,21 @@ return std::move(err); }; - for (const JSONProcess &process : bundle_description.processes) { - if (Expected parsed_process = ParseProcess(process)) - parsed_processes.push_back(std::move(*parsed_process)); + if (bundle_description.processes) { + for (const JSONProcess &process : *bundle_description.processes) { + if (Expected parsed_process = ParseProcess(process)) + parsed_processes.push_back(std::move(*parsed_process)); + else + return HandleError(parsed_process.takeError()); + } + } + + if (bundle_description.kernel) { + if (Expected kernel_process = + ParseKernel(bundle_description)) + parsed_processes.push_back(std::move(*kernel_process)); else - return HandleError(parsed_process.takeError()); + return HandleError(kernel_process.takeError()); } return parsed_processes; @@ -162,7 +237,7 @@ "model": integer, "stepping": integer }, - "processes": [ + "processes?": [ { "pid": integer, "triple"?: string, @@ -213,22 +288,36 @@ "timeMult": integer, "timeShift": integer, "timeZero": integer | string decimal | hex string, + }, + "kernel"?: { + "loadAddress"?: integer | string decimal | hex string, + // Kernel's image load address. Defaults to 0xffffffff81000000, which + // is a load address of x86 architecture if KASLR is not enabled. + "file": string, + // Path to the kernel image. } } Notes: -- All paths are either absolute or relative to folder containing the bundle description file. +- All paths are either absolute or relative to folder containing the bundle + description file. - "cpus" is provided if and only if processes[].threads[].iptTrace is not provided. - "tscPerfZeroConversion" must be provided if "cpus" is provided. - })"; +- If "kernel" is provided, then the "processes" section must be empty or not + passed at all, and the "cpus" section must be provided. This configuration + indicates that the kernel was traced and user processes weren't. Besides + that, the kernel is treated as a single process with one thread per CPU + core. This doesn't handle actual kernel threads, but instead treats + all the instructions executed by the kernel on each core as an + individual thread.})"; } return schema; } Error TraceIntelPTBundleLoader::AugmentThreadsFromContextSwitches( JSONTraceBundleDescription &bundle_description) { - if (!bundle_description.cpus) + if (!bundle_description.cpus || !bundle_description.processes) return Error::success(); if (!bundle_description.tsc_perf_zero_conversion) @@ -239,7 +328,7 @@ DenseMap indexed_processes; DenseMap> indexed_threads; - for (JSONProcess &process : bundle_description.processes) { + for (JSONProcess &process : *bundle_description.processes) { indexed_processes[process.pid] = &process; for (JSONThread &thread : process.threads) indexed_threads[&process].insert(thread.tid); @@ -285,8 +374,12 @@ parsed_process.threads.end()); } + TraceIntelPT::TraceMode trace_mode = bundle_description.kernel + ? TraceIntelPT::TraceMode::KernelMode + : TraceIntelPT::TraceMode::UserMode; + TraceSP trace_instance = TraceIntelPT::CreateInstanceForPostmortemTrace( - bundle_description, processes, threads); + bundle_description, processes, threads, trace_mode); for (const ParsedProcess &parsed_process : parsed_processes) parsed_process.target_sp->SetTrace(trace_instance); @@ -295,15 +388,17 @@ void TraceIntelPTBundleLoader::NormalizeAllPaths( JSONTraceBundleDescription &bundle_description) { - for (JSONProcess &process : bundle_description.processes) { - for (JSONModule &module : process.modules) { - module.system_path = NormalizePath(module.system_path).GetPath(); - if (module.file) - module.file = NormalizePath(*module.file).GetPath(); - } - for (JSONThread &thread : process.threads) { - if (thread.ipt_trace) - thread.ipt_trace = NormalizePath(*thread.ipt_trace).GetPath(); + if (bundle_description.processes) { + for (JSONProcess &process : *bundle_description.processes) { + for (JSONModule &module : process.modules) { + module.system_path = NormalizePath(module.system_path).GetPath(); + if (module.file) + module.file = NormalizePath(*module.file).GetPath(); + } + for (JSONThread &thread : process.threads) { + if (thread.ipt_trace) + thread.ipt_trace = NormalizePath(*thread.ipt_trace).GetPath(); + } } } if (bundle_description.cpus) { @@ -313,6 +408,10 @@ cpu.ipt_trace = NormalizePath(cpu.ipt_trace).GetPath(); } } + if (bundle_description.kernel) { + bundle_description.kernel->file = + NormalizePath(bundle_description.kernel->file).GetPath(); + } } Expected TraceIntelPTBundleLoader::Load() { diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp @@ -10,6 +10,7 @@ #include "PerfContextSwitchDecoder.h" #include "TraceIntelPT.h" +#include "TraceIntelPTConstants.h" #include "TraceIntelPTJSONStructs.h" #include "lldb/Core/Module.h" @@ -335,6 +336,25 @@ return processes; } +static llvm::Expected +BuildKernelSection(TraceIntelPT &trace_ipt, const FileSpec &directory) { + JSONKernel json_kernel; + std::vector processes = trace_ipt.GetAllProcesses(); + Process *kernel_process = processes[0]; + + assert(processes.size() == 1 && "User processeses exist in kernel mode"); + assert(kernel_process->GetID() == kDefaultKernelProcessID && + "Kernel process not exist"); + + Expected> json_modules = + BuildModulesSection(*kernel_process, directory); + if (!json_modules) + return json_modules.takeError(); + + JSONModule kernel_image = json_modules.get()[0]; + return JSONKernel{kernel_image.load_address, kernel_image.system_path}; +} + Expected TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt, FileSpec directory, bool compact) { @@ -348,20 +368,37 @@ FileSystem::Instance().Resolve(directory); - Expected> json_processes = - BuildProcessesSection(trace_ipt, directory); - - if (!json_processes) - return json_processes.takeError(); - Expected>> json_cpus = BuildCpusSection(trace_ipt, directory, compact); if (!json_cpus) return json_cpus.takeError(); + Optional> json_processes; + Optional json_kernel; + + if (trace_ipt.GetTraceMode() == TraceIntelPT::TraceMode::KernelMode) { + Expected> exp_json_kernel = + BuildKernelSection(trace_ipt, directory); + if (!exp_json_kernel) + return exp_json_kernel.takeError(); + else + json_kernel = *exp_json_kernel; + } else { + Expected>> exp_json_processes = + BuildProcessesSection(trace_ipt, directory); + if (!exp_json_processes) + return exp_json_processes.takeError(); + else + json_processes = *exp_json_processes; + } + JSONTraceBundleDescription json_intel_pt_bundle_desc{ - "intel-pt", *cpu_info, *json_processes, *json_cpus, - trace_ipt.GetPerfZeroTscConversion()}; + "intel-pt", + *cpu_info, + json_processes, + *json_cpus, + trace_ipt.GetPerfZeroTscConversion(), + json_kernel}; return SaveTraceBundleDescription(toJSON(json_intel_pt_bundle_desc), directory); diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h @@ -23,6 +23,13 @@ const bool kDefaultPerCpuTracing = false; const bool kDefaultDisableCgroupFiltering = false; +// Physical address where the kernel is loaded in x86 architecture. Refer to +// https://github.com/torvalds/linux/blob/master/Documentation/x86/x86_64/mm.rst +// for the start address of kernel text section. +// The kernel entry point is 0x1000000 by default when KASLR is disabled. +const lldb::addr_t kDefaultKernelLoadAddress = 0xffffffff81000000; +const lldb::pid_t kDefaultKernelProcessID = 1; + } // namespace trace_intel_pt } // namespace lldb_private diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h @@ -47,12 +47,18 @@ std::string context_switch_trace; }; +struct JSONKernel { + llvm::Optional load_address; + std::string file; +}; + struct JSONTraceBundleDescription { std::string type; pt_cpu cpu_info; - std::vector processes; + llvm::Optional> processes; llvm::Optional> cpus; llvm::Optional tsc_perf_zero_conversion; + llvm::Optional kernel; llvm::Optional> GetCpuIds(); }; @@ -67,6 +73,8 @@ llvm::json::Value toJSON(const pt_cpu &cpu_info); +llvm::json::Value toJSON(const JSONKernel &kernel); + llvm::json::Value toJSON(const JSONTraceBundleDescription &bundle_description); bool fromJSON(const llvm::json::Value &value, JSONModule &module, @@ -84,6 +92,9 @@ bool fromJSON(const llvm::json::Value &value, pt_cpu &cpu_info, llvm::json::Path path); +bool fromJSON(const llvm::json::Value &value, JSONModule &kernel, + llvm::json::Path path); + bool fromJSON(const llvm::json::Value &value, JSONTraceBundleDescription &bundle_description, llvm::json::Path path); diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp @@ -116,6 +116,20 @@ return true; } +json::Value toJSON(const JSONKernel &kernel) { + json::Object json_module; + if (kernel.load_address) + json_module["loadAddress"] = toJSON(*kernel.load_address, true); + json_module["file"] = kernel.file; + return std::move(json_module); +} + +bool fromJSON(const json::Value &value, JSONKernel &kernel, Path path) { + ObjectMapper o(value, path); + return o && o.map("loadAddress", kernel.load_address) && + o.map("file", kernel.file); +} + json::Value toJSON(const JSONTraceBundleDescription &bundle_description) { return Object{ {"type", bundle_description.type}, @@ -124,7 +138,8 @@ // automatically because pt_cpu is not in a namespace {"cpuInfo", toJSON(bundle_description.cpu_info)}, {"cpus", bundle_description.cpus}, - {"tscPerfZeroConversion", bundle_description.tsc_perf_zero_conversion}}; + {"tscPerfZeroConversion", bundle_description.tsc_perf_zero_conversion}, + {"kernel", bundle_description.kernel}}; } bool fromJSON(const json::Value &value, @@ -134,7 +149,8 @@ o.map("type", bundle_description.type) && o.map("cpus", bundle_description.cpus) && o.map("tscPerfZeroConversion", - bundle_description.tsc_perf_zero_conversion))) + bundle_description.tsc_perf_zero_conversion) && + o.map("kernel", bundle_description.kernel))) return false; if (bundle_description.cpus && !bundle_description.tsc_perf_zero_conversion) { path.report( @@ -146,6 +162,25 @@ if (!fromJSON(*value.getAsObject()->get("cpuInfo"), bundle_description.cpu_info, path.field("cpuInfo"))) return false; + + // When kernel section is present, this is kernel-only tracing. Thus, throw an + // error if the "processes" section is non-empty or the "cpus" section is not + // present. + if (bundle_description.kernel) { + if (bundle_description.processes && + !bundle_description.processes->empty()) { + path.report("\"processes\" must be empty when \"kernel\" is provided"); + return false; + } + if (!bundle_description.cpus) { + path.report("\"cpus\" is required when \"kernel\" is provided"); + return false; + } + } else if (!bundle_description.processes) { + // Usermode tracing requires processes section. + path.report("\"processes\" is required when \"kernel\" is not provided"); + return false; + } return true; } diff --git a/lldb/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py --- a/lldb/test/API/commands/trace/TestTraceLoad.py +++ b/lldb/test/API/commands/trace/TestTraceLoad.py @@ -367,11 +367,37 @@ for _ in range(TEST_SEEK_ID): sequentialTraversalCursor.Next() assertCurrentTraceCursorItemEqual(sequentialTraversalCursor, randomAccessCursor) + @testSBAPIAndCommands + def testLoadKernelTrace(self): + # kernel section without loadAddress (using default loadAddress). + src_dir = self.getSourceDir() + trace_description_file_path = os.path.join(src_dir, "intelpt-kernel-trace", "trace.json") + self.traceLoad(traceDescriptionFilePath=trace_description_file_path, substrs=["intel-pt"]) + + self.expect("image list", substrs=["0xffffffff81000000", "modules/m.out"]) + + self.expect("thread list", substrs=[ + "Process 1 stopped", + "* thread #1: tid = 0x002d", + " thread #2: tid = 0x0033"]) - - + # kernel section with custom loadAddress. + trace_description_file_path = os.path.join(src_dir, "intelpt-kernel-trace", + "trace_with_loadAddress.json") + self.traceLoad(traceDescriptionFilePath=trace_description_file_path, substrs=["intel-pt"]) + self.expect("image list", substrs=["0x400000", "modules/m.out"]) + @testSBAPIAndCommands + def testLoadInvalidKernelTrace(self): + src_dir = self.getSourceDir() + # Test kernel section with non-empty processeses section. + trace_description_file_path = os.path.join(src_dir, "intelpt-kernel-trace", "trace_kernel_with_process.json") + expected_substrs = ['error: "processes" must be empty when "kernel" is provided when parsing traceBundle'] + self.traceLoad(traceDescriptionFilePath=trace_description_file_path, error=True, substrs=expected_substrs) - + # Test kernel section without cpus section. + trace_description_file_path = os.path.join(src_dir, "intelpt-kernel-trace", "trace_kernel_wo_cpus.json") + expected_substrs = ['error: "cpus" is required when "kernel" is provided when parsing traceBundle'] + self.traceLoad(traceDescriptionFilePath=trace_description_file_path, error=True, substrs=expected_substrs) diff --git a/lldb/test/API/commands/trace/TestTraceSave.py b/lldb/test/API/commands/trace/TestTraceSave.py --- a/lldb/test/API/commands/trace/TestTraceSave.py +++ b/lldb/test/API/commands/trace/TestTraceSave.py @@ -169,3 +169,22 @@ ci.HandleCommand("thread trace dump instructions -c 10", res) self.assertEqual(res.Succeeded(), True) self.assertEqual(res.GetOutput(), last_ten_instructions) + + def testSaveKernelTrace(self): + original_trace_file = os.path.join(self.getSourceDir(), "intelpt-kernel-trace", + "trace.json") + copied_trace_dir = os.path.join(self.getBuildDir(), "intelpt-kernel-trace") + copied_trace_file = os.path.join(copied_trace_dir, "trace.json") + + self.expect("trace load -v " + original_trace_file, substrs=["intel-pt"]) + self.expect("trace save " + copied_trace_dir) + + # We finally check that the new json has the same information as the original one + with open(original_trace_file) as original_file: + original_file = json.load(original_file) + with open(copied_trace_file) as copy_file: + copy_file = json.load(copy_file) + self.assertTrue("kernel" in copy_file) + + self.assertEqual(os.path.basename(original_file["kernel"]["file"]), + os.path.basename(copy_file["kernel"]["file"])) diff --git a/lldb/test/API/commands/trace/intelpt-kernel-trace/trace.json b/lldb/test/API/commands/trace/intelpt-kernel-trace/trace.json new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/trace/intelpt-kernel-trace/trace.json @@ -0,0 +1,29 @@ +{ + "cpus": [ + { + "contextSwitchTrace": "../intelpt-multi-core-trace/cores/45.perf_context_switch_trace", + "id": 45, + "iptTrace": "../intelpt-multi-core-trace/cores/45.intelpt_trace" + }, + { + "contextSwitchTrace": "../intelpt-multi-core-trace/cores/51.perf_context_switch_trace", + "id": 51, + "iptTrace": "../intelpt-multi-core-trace/cores/51.intelpt_trace" + } + ], + "cpuInfo": { + "family": 6, + "model": 85, + "stepping": 4, + "vendor": "GenuineIntel" + }, + "tscPerfZeroConversion": { + "timeMult": 1076264588, + "timeShift": 31, + "timeZero": 18433473881008870804 + }, + "kernel": { + "file": "../intelpt-multi-core-trace/modules/m.out" + }, + "type": "intel-pt" +} diff --git a/lldb/test/API/commands/trace/intelpt-kernel-trace/trace_kernel_with_process.json b/lldb/test/API/commands/trace/intelpt-kernel-trace/trace_kernel_with_process.json new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/trace/intelpt-kernel-trace/trace_kernel_with_process.json @@ -0,0 +1,53 @@ +{ + "cpus": [ + { + "contextSwitchTrace": "../intelpt-multi-core-trace/cores/45.perf_context_switch_trace", + "id": 45, + "iptTrace": "../intelpt-multi-core-trace/cores/45.intelpt_trace" + }, + { + "contextSwitchTrace": "../intelpt-multi-core-trace/cores/51.perf_context_switch_trace", + "id": 51, + "iptTrace": "../intelpt-multi-core-trace/cores/51.intelpt_trace" + } + ], + "cpuInfo": { + "family": 6, + "model": 85, + "stepping": 4, + "vendor": "GenuineIntel" + }, + "processes": [ + { + "modules": [ + { + "file": "../intelpt-multi-core-trace/modules/m.out", + "systemPath": "/tmp/m.out", + "loadAddress": 4194304, + "uuid": "AEFB0D59-233F-80FF-6D3C-4DED498534CF-11017B3B" + } + ], + "pid": 3497234, + "threads": [ + { + "tid": 3497234 + }, + { + "tid": 3497496 + }, + { + "tid": 3497497 + } + ] + } + ], + "tscPerfZeroConversion": { + "timeMult": 1076264588, + "timeShift": 31, + "timeZero": 18433473881008870804 + }, + "type": "intel-pt", + "kernel": { + "file": "../intelpt-multi-core-trace/modules/m.out" + } +} diff --git a/lldb/test/API/commands/trace/intelpt-kernel-trace/trace_kernel_wo_cpus.json b/lldb/test/API/commands/trace/intelpt-kernel-trace/trace_kernel_wo_cpus.json new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/trace/intelpt-kernel-trace/trace_kernel_wo_cpus.json @@ -0,0 +1,20 @@ +{ + "cpuInfo": { + "family": 6, + "model": 85, + "stepping": 4, + "vendor": "GenuineIntel" + }, + "processes": [ + ], + "tscPerfZeroConversion": { + "timeMult": 1076264588, + "timeShift": 31, + "timeZero": 18433473881008870804 + }, + "kernel": { + "file": "../intelpt-multi-core-trace/modules/m.out", + "loadAddress": 4194304 + }, + "type": "intel-pt" +} diff --git a/lldb/test/API/commands/trace/intelpt-kernel-trace/trace_with_loadAddress.json b/lldb/test/API/commands/trace/intelpt-kernel-trace/trace_with_loadAddress.json new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/trace/intelpt-kernel-trace/trace_with_loadAddress.json @@ -0,0 +1,30 @@ +{ + "cpus": [ + { + "contextSwitchTrace": "../intelpt-multi-core-trace/cores/45.perf_context_switch_trace", + "id": 45, + "iptTrace": "../intelpt-multi-core-trace/cores/45.intelpt_trace" + }, + { + "contextSwitchTrace": "../intelpt-multi-core-trace/cores/51.perf_context_switch_trace", + "id": 51, + "iptTrace": "../intelpt-multi-core-trace/cores/51.intelpt_trace" + } + ], + "cpuInfo": { + "family": 6, + "model": 85, + "stepping": 4, + "vendor": "GenuineIntel" + }, + "tscPerfZeroConversion": { + "timeMult": 1076264588, + "timeShift": 31, + "timeZero": 18433473881008870804 + }, + "kernel": { + "file": "../intelpt-multi-core-trace/modules/m.out", + "loadAddress": "0x400000" + }, + "type": "intel-pt" +}