Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h @@ -173,6 +173,10 @@ TraceIntelPTSP GetSharedPtr(); + enum class TraceMode { UserMode, KernelMode }; + + TraceMode GetTraceMode(); + private: friend class TraceIntelPTBundleLoader; @@ -184,28 +188,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. @@ -245,6 +255,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 Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -78,10 +78,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) { - TraceIntelPTSP trace_sp(new TraceIntelPT(bundle_description, traced_processes)); + JSONTraceBundleDescription &bundle_description, + ArrayRef traced_processes, + ArrayRef traced_threads, TraceMode trace_mode) { + TraceIntelPTSP trace_sp( + new TraceIntelPT(bundle_description, traced_processes, trace_mode)); trace_sp->m_storage.tsc_conversion = bundle_description.tsc_perf_zero_conversion; if (bundle_description.cpus) { @@ -120,9 +124,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()) Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h @@ -66,6 +66,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. /// @@ -76,6 +80,10 @@ /// Create a module associated with the given \p target using the definition 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. /// Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp +++ 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,59 @@ return parsed_process; } +Expected +TraceIntelPTBundleLoader::ParseKernel( + const JSONTraceBundleDescription &bundle_description) { + // Let fake kernel process's pid to be 1. + 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(); + + bool load_addr_changed = false; + if (bundle_description.kernel->load_address) + module_sp->SetLoadAddress(*parsed_process->target_sp, + bundle_description.kernel->load_address->value, + false, load_addr_changed); + else + module_sp->SetLoadAddress(*parsed_process->target_sp, + kDefaultKernelLoadAddress, 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) { @@ -146,6 +213,13 @@ 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(kernel_process.takeError()); + } return parsed_processes; } @@ -162,7 +236,7 @@ "model": integer, "stepping": integer }, - "processes": [ + "processes"?: [ { "pid": integer, "triple"?: string, @@ -213,6 +287,13 @@ "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 common load address if KASLR is not enabled. + "file": string, + // Path to the kernel image. } } @@ -221,6 +302,7 @@ - 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 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; @@ -284,8 +366,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); @@ -312,6 +398,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() { Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp +++ 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,28 @@ 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]; + + if (processes.size() != 1) + return createStringError(inconvertibleErrorCode(), + "User processes exist in kernel mode"); + if (kernel_process->GetID() != kDefaultKernelProcessID) + return createStringError(inconvertibleErrorCode(), + "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 +371,42 @@ 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(); - JSONTraceBundleDescription json_intel_pt_bundle_desc{"intel-pt", *cpu_info, *json_processes, - *json_cpus, - trace_ipt.GetPerfZeroTscConversion()}; - - return SaveTraceBundleDescription(toJSON(json_intel_pt_bundle_desc), directory); + if (trace_ipt.GetTraceMode() == TraceIntelPT::TraceMode::KernelMode) { + std::vector json_processes; + Expected json_kernel = BuildKernelSection(trace_ipt, directory); + + if (!json_kernel) + return json_kernel.takeError(); + + JSONTraceBundleDescription json_intel_pt_bundle_desc{ + "intel-pt", + *cpu_info, + json_processes, + *json_cpus, + trace_ipt.GetPerfZeroTscConversion(), + *json_kernel}; + return SaveTraceBundleDescription(toJSON(json_intel_pt_bundle_desc), + directory); + } else { + Expected> json_processes = + BuildProcessesSection(trace_ipt, directory); + + if (!json_processes) + return json_processes.takeError(); + + JSONTraceBundleDescription json_intel_pt_bundle_desc{ + "intel-pt", + *cpu_info, + *json_processes, + *json_cpus, + trace_ipt.GetPerfZeroTscConversion(), + llvm::None}; + return SaveTraceBundleDescription(toJSON(json_intel_pt_bundle_desc), + directory); + } } Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h @@ -23,6 +23,9 @@ const bool kDefaultPerCpuTracing = false; const bool kDefaultDisableCgroupFiltering = false; +const lldb::addr_t kDefaultKernelLoadAddress = 0xffffffff81000000; +const lldb::pid_t kDefaultKernelProcessID = 1; + } // namespace trace_intel_pt } // namespace lldb_private Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h +++ 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> 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); } // namespace trace_intel_pt Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp =================================================================== --- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp +++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp @@ -116,21 +116,39 @@ 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}, - {"processes", bundle_description.processes}, - // We have to do this because the compiler fails at doing it - // 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}}; + return Object{ + {"type", bundle_description.type}, + {"processes", bundle_description.processes}, + // We have to do this because the compiler fails at doing it + // 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}, + {"kernel", bundle_description.kernel}}; } bool fromJSON(const json::Value &value, JSONTraceBundleDescription &bundle_description, Path path) { ObjectMapper o(value, path); - if (!(o && o.map("processes", bundle_description.processes) && - o.map("type", bundle_description.type) && o.map("cpus", bundle_description.cpus) && - o.map("tscPerfZeroConversion", bundle_description.tsc_perf_zero_conversion))) + if (!(o && o.map("type", bundle_description.type) && + o.map("cpus", bundle_description.cpus) && + o.map("tscPerfZeroConversion", + 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( @@ -142,6 +160,24 @@ 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 (o.map("processes", 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 (!o.map("processes", bundle_description.processes)) { + // Usermode tracing requires processes section. + return false; + } return true; } Index: lldb/test/API/commands/trace/TestTraceLoad.py =================================================================== --- lldb/test/API/commands/trace/TestTraceLoad.py +++ lldb/test/API/commands/trace/TestTraceLoad.py @@ -263,3 +263,30 @@ expected_substrs = ['error: missing value at traceBundle.processes[1].pid'] self.traceLoad(traceDescriptionFilePath=trace_description_file_path, error=True, substrs=expected_substrs) self.assertEqual(self.dbg.GetNumTargets(), 0) + + @testSBAPIAndCommands + def testLoadKernelTrace(self): + 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=["modules/m.out"]) + + self.expect("thread list", substrs=[ + "Process 1 stopped", + "* thread #1: tid = 0x002d", + " thread #2: tid = 0x0033"]) + + @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) Index: lldb/test/API/commands/trace/TestTraceSave.py =================================================================== --- lldb/test/API/commands/trace/TestTraceSave.py +++ 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"])) Index: lldb/test/API/commands/trace/intelpt-kernel-trace/trace.json =================================================================== --- /dev/null +++ lldb/test/API/commands/trace/intelpt-kernel-trace/trace.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": 4194304 + }, + "type": "intel-pt" +} Index: lldb/test/API/commands/trace/intelpt-kernel-trace/trace_kernel_with_process.json =================================================================== --- /dev/null +++ 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" + } +} Index: lldb/test/API/commands/trace/intelpt-kernel-trace/trace_kernel_wo_cpus.json =================================================================== --- /dev/null +++ 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" +}