[Lldb-commits] [lldb] 6fb744b - [trace][intel pt] Support a new kernel section in LLDB’s trace bundle schema

Walter Erquinigo via lldb-commits lldb-commits at lists.llvm.org
Thu Aug 4 17:15:14 PDT 2022


Author: Walter Erquinigo
Date: 2022-08-04T17:15:08-07:00
New Revision: 6fb744be76704f075a4edc753916e0e475d10f20

URL: https://github.com/llvm/llvm-project/commit/6fb744be76704f075a4edc753916e0e475d10f20
DIFF: https://github.com/llvm/llvm-project/commit/6fb744be76704f075a4edc753916e0e475d10f20.diff

LOG: [trace][intel pt] Support a new kernel section in LLDB’s trace bundle schema

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.

Reviewed By: wallace

Differential Revision: https://reviews.llvm.org/D130805

Added: 
    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
    lldb/test/API/commands/trace/intelpt-kernel-trace/trace_with_loadAddress.json

Modified: 
    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/TestTraceSave.py

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
index 029f1b3c70ebf..a3c9064505af2 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -79,12 +79,14 @@ TraceIntelPTSP TraceIntelPT::GetSharedPtr() {
   return std::static_pointer_cast<TraceIntelPT>(shared_from_this());
 }
 
+TraceIntelPT::TraceMode TraceIntelPT::GetTraceMode() { return trace_mode; }
+
 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));
+      new TraceIntelPT(bundle_description, traced_processes, trace_mode));
   trace_sp->m_storage.tsc_conversion =
       bundle_description.tsc_perf_zero_conversion;
 
@@ -101,11 +103,6 @@ TraceIntelPTSP TraceIntelPT::CreateInstanceForPostmortemTrace(
       cpus.push_back(cpu.id);
     }
 
-    std::vector<tid_t> 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 @@ TraceIntelPTSP TraceIntelPT::CreateInstanceForPostmortemTrace(
 }
 
 TraceIntelPT::TraceIntelPT(JSONTraceBundleDescription &bundle_description,
-                           ArrayRef<ProcessSP> traced_processes)
+                           ArrayRef<ProcessSP> 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<DecodedThreadSP> TraceIntelPT::Decode(Thread &thread) {
   if (const char *error = RefreshLiveProcessState())

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
index 7e8f030958f11..82034b0bfbc63 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -172,6 +172,10 @@ class TraceIntelPT : public Trace {
 
   TraceIntelPTSP GetSharedPtr();
 
+  enum class TraceMode { UserMode, KernelMode };
+
+  TraceMode GetTraceMode();
+
 private:
   friend class TraceIntelPTBundleLoader;
 
@@ -183,28 +187,34 @@ class TraceIntelPT : public Trace {
   ///     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<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++.
   TraceIntelPT(JSONTraceBundleDescription &bundle_description,
-               llvm::ArrayRef<lldb::ProcessSP> traced_processes);
+               llvm::ArrayRef<lldb::ProcessSP> 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 @@ class TraceIntelPT : public Trace {
 
   /// 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/TraceIntelPTBundleLoader.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp
index d1d762bcc5070..dae17ec522491 100644
--- 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 @@ TraceIntelPTBundleLoader::ParseThread(Process &process,
 }
 
 Expected<TraceIntelPTBundleLoader::ParsedProcess>
-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 @@ TraceIntelPTBundleLoader::ParseProcess(const JSONProcess &process) {
       /*crash_file*/ nullptr,
       /*can_connect*/ false);
 
-  process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
+  process_sp->SetID(static_cast<lldb::pid_t>(pid));
+
+  return parsed_process;
+}
+
+Expected<TraceIntelPTBundleLoader::ParsedProcess>
+TraceIntelPTBundleLoader::ParseProcess(const JSONProcess &process) {
+  Expected<ParsedProcess> 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 @@ TraceIntelPTBundleLoader::ParseProcess(const JSONProcess &process) {
   return parsed_process;
 }
 
+Expected<TraceIntelPTBundleLoader::ParsedProcess>
+TraceIntelPTBundleLoader::ParseKernel(
+    const JSONTraceBundleDescription &bundle_description) {
+  Expected<ParsedProcess> 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<ThreadPostMortemTrace>(
+        *process_sp, static_cast<lldb::tid_t>(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<std::vector<TraceIntelPTBundleLoader::ParsedProcess>>
 TraceIntelPTBundleLoader::LoadBundle(
     const JSONTraceBundleDescription &bundle_description) {
@@ -139,11 +204,21 @@ TraceIntelPTBundleLoader::LoadBundle(
     return std::move(err);
   };
 
-  for (const JSONProcess &process : bundle_description.processes) {
-    if (Expected<ParsedProcess> 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<ParsedProcess> parsed_process = ParseProcess(process))
+        parsed_processes.push_back(std::move(*parsed_process));
+      else
+        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(parsed_process.takeError());
+      return HandleError(kernel_process.takeError());
   }
 
   return parsed_processes;
@@ -162,7 +237,7 @@ StringRef TraceIntelPTBundleLoader::GetSchema() {
     "model": integer,
     "stepping": integer
   },
-  "processes": [
+  "processes?": [
     {
       "pid": integer,
       "triple"?: string,
@@ -213,22 +288,36 @@ StringRef TraceIntelPTBundleLoader::GetSchema() {
     "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 @@ Error TraceIntelPTBundleLoader::AugmentThreadsFromContextSwitches(
   DenseMap<lldb::pid_t, JSONProcess *> indexed_processes;
   DenseMap<JSONProcess *, DenseSet<tid_t>> 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 @@ Expected<TraceSP> TraceIntelPTBundleLoader::CreateTraceIntelPTInstance(
                    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 @@ Expected<TraceSP> TraceIntelPTBundleLoader::CreateTraceIntelPTInstance(
 
 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 @@ void TraceIntelPTBundleLoader::NormalizeAllPaths(
       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() {

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h
index 7fb4a6b3c9b7d..691a3f4fa08c4 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h
@@ -67,6 +67,10 @@ class TraceIntelPTBundleLoader {
   CreateTraceIntelPTInstance(JSONTraceBundleDescription &bundle_description,
                              std::vector<ParsedProcess> &parsed_processes);
 
+  /// Create an empty Process object with given pid and target.
+  llvm::Expected<ParsedProcess> CreateEmptyProcess(lldb::pid_t pid,
+                                                   llvm::StringRef triple);
+
   /// Create the corresponding Threads and Process objects given the JSON
   /// process definition.
   ///
@@ -78,6 +82,10 @@ class TraceIntelPTBundleLoader {
   /// 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<ParsedProcess>
+  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/TraceIntelPTBundleSaver.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp
index f3d1c674eb80b..2e92ea1129a21 100644
--- 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 @@ BuildProcessesSection(TraceIntelPT &trace_ipt, const FileSpec &directory) {
   return processes;
 }
 
+static llvm::Expected<JSONKernel>
+BuildKernelSection(TraceIntelPT &trace_ipt, const FileSpec &directory) {
+  JSONKernel json_kernel;
+  std::vector<Process *> 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<std::vector<JSONModule>> 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<FileSpec> TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt,
                                                        FileSpec directory,
                                                        bool compact) {
@@ -348,20 +368,37 @@ Expected<FileSpec> TraceIntelPTBundleSaver::SaveToDisk(TraceIntelPT &trace_ipt,
 
   FileSystem::Instance().Resolve(directory);
 
-  Expected<std::vector<JSONProcess>> json_processes =
-      BuildProcessesSection(trace_ipt, directory);
-
-  if (!json_processes)
-    return json_processes.takeError();
-
   Expected<Optional<std::vector<JSONCpu>>> json_cpus =
       BuildCpusSection(trace_ipt, directory, compact);
   if (!json_cpus)
     return json_cpus.takeError();
 
+  Optional<std::vector<JSONProcess>> json_processes;
+  Optional<JSONKernel> json_kernel;
+
+  if (trace_ipt.GetTraceMode() == TraceIntelPT::TraceMode::KernelMode) {
+    Expected<Optional<JSONKernel>> exp_json_kernel =
+        BuildKernelSection(trace_ipt, directory);
+    if (!exp_json_kernel)
+      return exp_json_kernel.takeError();
+    else
+      json_kernel = *exp_json_kernel;
+  } else {
+    Expected<Optional<std::vector<JSONProcess>>> 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
index 43c86fca3425f..c2b73b5cb092f 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h
@@ -23,6 +23,13 @@ const llvm::Optional<size_t> kDefaultPsbPeriod = llvm::None;
 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.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
index e1ed248282c38..cbde0432793eb 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
@@ -116,6 +116,20 @@ bool fromJSON(const json::Value &value, pt_cpu &cpu_info, Path path) {
   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 @@ json::Value toJSON(const JSONTraceBundleDescription &bundle_description) {
       // 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 @@ bool fromJSON(const json::Value &value,
         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 @@ bool fromJSON(const json::Value &value,
   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/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
index d86c6ee65615e..f39b33fc7ff34 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
@@ -47,12 +47,18 @@ struct JSONCpu {
   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<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 JSONCpu &cpu);
 
 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, JSONCpu &cpu,
 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/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py
index 9ce95171256df..cd2f80bfe32a2 100644
--- a/lldb/test/API/commands/trace/TestTraceLoad.py
+++ b/lldb/test/API/commands/trace/TestTraceLoad.py
@@ -367,11 +367,37 @@ def assertCurrentTraceCursorItemEqual(lhs, rhs):
             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
index 8c4a0c30f2b91..db8131cb501f4 100644
--- a/lldb/test/API/commands/trace/TestTraceSave.py
+++ b/lldb/test/API/commands/trace/TestTraceSave.py
@@ -169,3 +169,22 @@ def testSaveTrace(self):
         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
index 0000000000000..9689e18332b42
--- /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
index 0000000000000..34d8cde35a98c
--- /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
index 0000000000000..57e341a05c9d2
--- /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
index 0000000000000..b393dae816c9f
--- /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"
+}


        


More information about the lldb-commits mailing list