persona0220 created this revision.
persona0220 added reviewers: wallace, jj10306.
Herald added a project: All.
persona0220 requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

Add a new "kernel" section with following schema.

  "kernel": {
    "loadAddress"?: decimal | hex string | string decimal 
    # This is optional. If it's not specified, use default address 
0xffffffff81000000.
    "file": string
    # path to the kernel image
  }

Here's more details of the diff:

- If "kernel" section exist, it means current tracing mode is //KernelMode//.
- If tracing mode is //KernelMode//, the "processes" section must be empty and 
the "kernel" and "cpus" section must be provided. This is tested with 
`TestTraceLoad`.
- "kernel" section is parsed and turned into a new process with a single module 
which is the kernel image. The kernel process has N fake threads, one for each 
cpu.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D130805

Files:
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
  lldb/test/API/commands/trace/TestTraceLoad.py
  lldb/test/API/commands/trace/intelpt-kernel-trace/cores/45.intelpt_trace
  
lldb/test/API/commands/trace/intelpt-kernel-trace/cores/45.perf_context_switch_trace
  lldb/test/API/commands/trace/intelpt-kernel-trace/cores/51.intelpt_trace
  
lldb/test/API/commands/trace/intelpt-kernel-trace/cores/51.perf_context_switch_trace
  lldb/test/API/commands/trace/intelpt-kernel-trace/modules/m.out
  lldb/test/API/commands/trace/intelpt-kernel-trace/trace.json
  
lldb/test/API/commands/trace/intelpt-kernel-trace/trace_kernel_with_process.json
  lldb/test/API/commands/trace/intelpt-kernel-trace/trace_kernel_wo_cpus.json

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"
+}
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.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/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/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<JSONUINT64> load_address;
+  std::string file;
+};
+
 struct JSONTraceBundleDescription {
   std::string type;
   pt_cpu cpu_info;
   std::vector<JSONProcess> processes;
   llvm::Optional<std::vector<JSONCpu>> cpus;
   llvm::Optional<LinuxPerfZeroTscConversion> tsc_perf_zero_conversion;
+  llvm::Optional<JSONKernel> kernel;
 
   llvm::Optional<std::vector<lldb::cpu_id_t>> 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/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<size_t> 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/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/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<ParsedProcess> 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::ParsedProcess>
+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<lldb::pid_t>(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<lldb::tid_t>(cpu.id);
+
+    Optional<FileSpec> trace_file;
+    trace_file = FileSpec(cpu.ipt_trace);
+
+    ThreadPostMortemTraceSP thread_sp = std::make_shared<ThreadPostMortemTrace>(*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<std::vector<TraceIntelPTBundleLoader::ParsedProcess>>
 TraceIntelPTBundleLoader::LoadBundle(
     const JSONTraceBundleDescription &bundle_description) {
@@ -146,6 +212,12 @@
       return HandleError(parsed_process.takeError());
   }
 
+  if (bundle_description.kernel) {
+    if (Expected<ParsedProcess> 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<TraceSP> TraceIntelPTBundleLoader::Load() {
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<lldb::ProcessSP> traced_processes,
-      llvm::ArrayRef<lldb::ThreadPostMortemTraceSP> traced_threads);
+      llvm::ArrayRef<lldb::ThreadPostMortemTraceSP> 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<ProcessSP> traced_processes,
-    ArrayRef<ThreadPostMortemTraceSP> traced_threads) {
+    ArrayRef<ThreadPostMortemTraceSP> 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;
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to