aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWalter Erquinigo <wallace@fb.com>2022-08-04 16:24:37 -0700
committerWalter Erquinigo <wallace@fb.com>2022-08-04 17:15:08 -0700
commit6fb744be76704f075a4edc753916e0e475d10f20 (patch)
treef1179ac51a4e01e6b2c789a0df3c03a0c819de9b
parent6f4c3c0f6463880b685bfbca1932c06fd0c1f015 (diff)
[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
-rw-r--r--lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp16
-rw-r--r--lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h23
-rw-r--r--lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.cpp149
-rw-r--r--lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h8
-rw-r--r--lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleSaver.cpp53
-rw-r--r--lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h7
-rw-r--r--lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp39
-rw-r--r--lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h13
-rw-r--r--lldb/test/API/commands/trace/TestTraceLoad.py32
-rw-r--r--lldb/test/API/commands/trace/TestTraceSave.py19
-rw-r--r--lldb/test/API/commands/trace/intelpt-kernel-trace/trace.json29
-rw-r--r--lldb/test/API/commands/trace/intelpt-kernel-trace/trace_kernel_with_process.json53
-rw-r--r--lldb/test/API/commands/trace/intelpt-kernel-trace/trace_kernel_wo_cpus.json20
-rw-r--r--lldb/test/API/commands/trace/intelpt-kernel-trace/trace_with_loadAddress.json30
14 files changed, 438 insertions, 53 deletions
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
index 029f1b3c70eb..a3c9064505af 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 7e8f030958f1..82034b0bfbc6 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -172,6 +172,10 @@ public:
TraceIntelPTSP GetSharedPtr();
+ enum class TraceMode { UserMode, KernelMode };
+
+ TraceMode GetTraceMode();
+
private:
friend class TraceIntelPTBundleLoader;
@@ -183,28 +187,34 @@ private:
/// 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 @@ private:
/// 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 d1d762bcc507..dae17ec52249 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 7fb4a6b3c9b7..691a3f4fa08c 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTBundleLoader.h
@@ -67,6 +67,10 @@ private:
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 @@ private:
/// 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 f3d1c674eb80..2e92ea1129a2 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 43c86fca3425..c2b73b5cb092 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 e1ed248282c3..cbde0432793e 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 d86c6ee65615..f39b33fc7ff3 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 9ce95171256d..cd2f80bfe32a 100644
--- a/lldb/test/API/commands/trace/TestTraceLoad.py
+++ b/lldb/test/API/commands/trace/TestTraceLoad.py
@@ -367,11 +367,37 @@ Context:
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 8c4a0c30f2b9..db8131cb501f 100644
--- a/lldb/test/API/commands/trace/TestTraceSave.py
+++ b/lldb/test/API/commands/trace/TestTraceSave.py
@@ -169,3 +169,22 @@ class TestTraceSave(TraceIntelPTTestCaseBase):
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 000000000000..9689e18332b4
--- /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 000000000000..34d8cde35a98
--- /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 000000000000..57e341a05c9d
--- /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 000000000000..b393dae816c9
--- /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"
+}