forked from OSchip/llvm-project
[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:
parent
6f4c3c0f64
commit
6fb744be76
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"]))
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
Loading…
Reference in New Issue