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,11 @@ TraceIntelPTSP GetSharedPtr(); + enum TraceMode { + UserMode, + KernelMode + }; + private: friend class TraceIntelPTBundleLoader; @@ -190,13 +195,17 @@ /// The threads traced in the live session. They must belong to the /// processes mentioned above. /// + /// \param[in] trace_mode + /// The tracing mode in the live session. One of TraceMode enum value. + /// /// \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++. 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 @@ -80,7 +80,7 @@ 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)); trace_sp->m_storage.tsc_conversion = bundle_description.tsc_perf_zero_conversion; 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 @@ -76,6 +76,8 @@ /// Create a module associated with the given \p target using the definition from \p module. llvm::Error ParseModule(Target &target, const JSONModule &module); + 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 @@ -11,6 +11,7 @@ #include "../common/ThreadPostMortemTrace.h" #include "TraceIntelPT.h" #include "TraceIntelPTJSONStructs.h" +#include "TraceIntelPTConstants.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" @@ -127,6 +128,71 @@ return parsed_process; } +Expected +TraceIntelPTBundleLoader::ParseKernel(const JSONTraceBundleDescription &bundle_description) { + TargetSP target_sp; + Status error = m_debugger.GetTargetList().CreateTarget( + m_debugger, /*user_exe_path*/ StringRef(), "", + eLoadDependentsNo, + /*platform_options*/ nullptr, target_sp); + + if (!target_sp) + return error.ToError(); + + ParsedProcess kernel_process; + kernel_process.target_sp = target_sp; + + ProcessSP process_sp = target_sp->CreateProcess( + /*listener*/ nullptr, "trace", + /*crash_file*/ nullptr, + /*can_connect*/ false); + + // Let fake kernel process's pid to be 1. + process_sp->SetID(static_cast(1)); + + // Add cpus as fake threads + for (const JSONCpu &cpu : *bundle_description.cpus) { + char thread_name[20]; + sprintf(thread_name, "kernel_cpu_%d", cpu.id); + lldb::tid_t tid = static_cast(cpu.id); + + Optional trace_file; + trace_file = FileSpec(cpu.ipt_trace); + + ThreadPostMortemTraceSP thread_sp = std::make_shared(*process_sp, tid, trace_file); + thread_sp->SetName(thread_name); + process_sp->GetThreadList().AddThread(thread_sp); + kernel_process.threads.push_back(thread_sp); + } + + // Add kernel image + FileSpec system_file_spec(bundle_description.kernel->file); + ModuleSpec module_spec; + module_spec.GetFileSpec() = system_file_spec; + module_spec.GetPlatformFileSpec() = system_file_spec; + + ModuleSP module_sp = 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(*target_sp, bundle_description.kernel->load_address->value, false, load_addr_changed); + else + module_sp->SetLoadAddress(*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 kernel_process; +} + + Expected> TraceIntelPTBundleLoader::LoadBundle( const JSONTraceBundleDescription &bundle_description) { @@ -146,6 +212,12 @@ 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; } @@ -213,6 +285,12 @@ "timeMult": integer, "timeShift": integer, "timeZero": integer | string decimal | hex string, + }, + "kernel"?: { + "loadAddress"?: integer | string decimal | hex string, + // Kernel's start address. 0xffffffff81000000 on default. + "file": string, + // Path to the kernel image. } } @@ -221,6 +299,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 tracing mode is "kernel", then the "processes" section must be empty and the "kernel" and "cpus" section must be provided. })"; } return schema; @@ -284,8 +363,11 @@ 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 +394,8 @@ 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 @@ -359,6 +359,8 @@ if (!json_cpus) return json_cpus.takeError(); + //TODO: Add kernel section + JSONTraceBundleDescription json_intel_pt_bundle_desc{"intel-pt", *cpu_info, *json_processes, *json_cpus, trace_ipt.GetPerfZeroTscConversion()}; 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 @@ -22,6 +22,7 @@ const llvm::Optional kDefaultPsbPeriod = llvm::None; const bool kDefaultPerCpuTracing = false; const bool kDefaultDisableCgroupFiltering = false; +const uint64_t kDefaultKernelLoadAddress = 0xffffffff81000000; } // 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,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}, {"processes", bundle_description.processes}, @@ -123,14 +137,16 @@ // 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, 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))) + 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 +158,18 @@ if (!fromJSON(*value.getAsObject()->get("cpuInfo"), bundle_description.cpu_info, path.field("cpuInfo"))) return false; + // When kernel section is present, this is kernel mode tracing. Thus, throw an + // error if there is any user process or cpus section is not present. + if (bundle_description.kernel){ + if(!bundle_description.processes.empty()) { + path.report("\"processes\" must be empty when \"kernel\" is provided"); + return false; + } + else if(!bundle_description.cpus) { + path.report("\"cpus\" is required when \"kernel\" is provided"); + 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/intelpt-kernel-trace/trace.json =================================================================== --- /dev/null +++ lldb/test/API/commands/trace/intelpt-kernel-trace/trace.json @@ -0,0 +1,32 @@ +{ + "cpus": [ + { + "contextSwitchTrace": "cores/45.perf_context_switch_trace", + "id": 45, + "iptTrace": "cores/45.intelpt_trace" + }, + { + "contextSwitchTrace": "cores/51.perf_context_switch_trace", + "id": 51, + "iptTrace": "cores/51.intelpt_trace" + } + ], + "cpuInfo": { + "family": 6, + "model": 85, + "stepping": 4, + "vendor": "GenuineIntel" + }, + "processes": [ + ], + "tscPerfZeroConversion": { + "timeMult": 1076264588, + "timeShift": 31, + "timeZero": 18433473881008870804 + }, + "kernel": { + "file": "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": "cores/45.perf_context_switch_trace", + "id": 45, + "iptTrace": "cores/45.intelpt_trace" + }, + { + "contextSwitchTrace": "cores/51.perf_context_switch_trace", + "id": 51, + "iptTrace": "cores/51.intelpt_trace" + } + ], + "cpuInfo": { + "family": 6, + "model": 85, + "stepping": 4, + "vendor": "GenuineIntel" + }, + "processes": [ + { + "modules": [ + { + "file": "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": "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": "modules/m.out", + "loadAddress": 4194304 + }, + "type": "intel-pt" +}