[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
This commit is contained in:
Walter Erquinigo 2022-08-04 16:24:37 -07:00
parent 6f4c3c0f64
commit 6fb744be76
14 changed files with 438 additions and 53 deletions

View File

@ -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())

View File

@ -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

View File

@ -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() {

View File

@ -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.
///

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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"]))

View File

@ -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"
}

View File

@ -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"
}
}

View File

@ -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"
}

View File

@ -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"
}