[intel pt] Refactor parsing
With the feedback I was getting in different diffs, I realized that splitting the parsing logic into two classes was not easy to deal with. I do see value in doing that, but I'd rather leave that as a refactor after most of the intel-pt logic is in place. Thus, I'm merging the common parser into the intel pt one, having thus only one that is fully aware of Intel PT during parsing and object creation.
Besides, based on the feedback in https://reviews.llvm.org/D88769, I'm creating a ThreadIntelPT class that will be able to orchestrate decoding of its own trace and can handle the stop events correctly.
This leaves the TraceIntelPT class as an initialization class that glues together different components. Right now it can initialize a trace session from a json file, and in the future will be able to initialize a trace session from a live process.
Besides, I'm renaming SettingsParser to SessionParser, which I think is a better name, as the json object represents a trace session of possibly many processes.
With the current set of targets, we have the following
- Trace: main interface for dealing with trace sessions
- TraceIntelPT: plugin Trace for dealing with intel pt sessions
- TraceIntelPTSessionParser: a parser of a json trace session file that can create a corresponding TraceIntelPT instance along with Targets, ProcessTraces (to be created in https://reviews.llvm.org/D88769), and ThreadIntelPT threads.
- ProcessTrace: (to be created in https://reviews.llvm.org/D88769) can handle the correct state of the traces as the user traverses the trace. I don't think there'll be a need an intel-pt specific implementation of this class.
- ThreadIntelPT: a thread implementation that can handle the decoding of its own trace file, along with keeping track of the current position the user is looking at when doing reverse debugging.
Differential Revision: https://reviews.llvm.org/D88841
2020-10-04 03:23:12 +08:00
|
|
|
//===-- TraceSessionFileParser.cpp ---------------------------------------===//
|
|
|
|
//
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===/
|
|
|
|
|
|
|
|
#include "lldb/Target/TraceSessionFileParser.h"
|
|
|
|
|
|
|
|
#include <sstream>
|
|
|
|
|
2020-10-15 01:25:39 +08:00
|
|
|
#include "lldb/Core/Debugger.h"
|
[intel pt] Refactor parsing
With the feedback I was getting in different diffs, I realized that splitting the parsing logic into two classes was not easy to deal with. I do see value in doing that, but I'd rather leave that as a refactor after most of the intel-pt logic is in place. Thus, I'm merging the common parser into the intel pt one, having thus only one that is fully aware of Intel PT during parsing and object creation.
Besides, based on the feedback in https://reviews.llvm.org/D88769, I'm creating a ThreadIntelPT class that will be able to orchestrate decoding of its own trace and can handle the stop events correctly.
This leaves the TraceIntelPT class as an initialization class that glues together different components. Right now it can initialize a trace session from a json file, and in the future will be able to initialize a trace session from a live process.
Besides, I'm renaming SettingsParser to SessionParser, which I think is a better name, as the json object represents a trace session of possibly many processes.
With the current set of targets, we have the following
- Trace: main interface for dealing with trace sessions
- TraceIntelPT: plugin Trace for dealing with intel pt sessions
- TraceIntelPTSessionParser: a parser of a json trace session file that can create a corresponding TraceIntelPT instance along with Targets, ProcessTraces (to be created in https://reviews.llvm.org/D88769), and ThreadIntelPT threads.
- ProcessTrace: (to be created in https://reviews.llvm.org/D88769) can handle the correct state of the traces as the user traverses the trace. I don't think there'll be a need an intel-pt specific implementation of this class.
- ThreadIntelPT: a thread implementation that can handle the decoding of its own trace file, along with keeping track of the current position the user is looking at when doing reverse debugging.
Differential Revision: https://reviews.llvm.org/D88841
2020-10-04 03:23:12 +08:00
|
|
|
#include "lldb/Core/Module.h"
|
2020-10-15 01:25:39 +08:00
|
|
|
#include "lldb/Target/Process.h"
|
[intel pt] Refactor parsing
With the feedback I was getting in different diffs, I realized that splitting the parsing logic into two classes was not easy to deal with. I do see value in doing that, but I'd rather leave that as a refactor after most of the intel-pt logic is in place. Thus, I'm merging the common parser into the intel pt one, having thus only one that is fully aware of Intel PT during parsing and object creation.
Besides, based on the feedback in https://reviews.llvm.org/D88769, I'm creating a ThreadIntelPT class that will be able to orchestrate decoding of its own trace and can handle the stop events correctly.
This leaves the TraceIntelPT class as an initialization class that glues together different components. Right now it can initialize a trace session from a json file, and in the future will be able to initialize a trace session from a live process.
Besides, I'm renaming SettingsParser to SessionParser, which I think is a better name, as the json object represents a trace session of possibly many processes.
With the current set of targets, we have the following
- Trace: main interface for dealing with trace sessions
- TraceIntelPT: plugin Trace for dealing with intel pt sessions
- TraceIntelPTSessionParser: a parser of a json trace session file that can create a corresponding TraceIntelPT instance along with Targets, ProcessTraces (to be created in https://reviews.llvm.org/D88769), and ThreadIntelPT threads.
- ProcessTrace: (to be created in https://reviews.llvm.org/D88769) can handle the correct state of the traces as the user traverses the trace. I don't think there'll be a need an intel-pt specific implementation of this class.
- ThreadIntelPT: a thread implementation that can handle the decoding of its own trace file, along with keeping track of the current position the user is looking at when doing reverse debugging.
Differential Revision: https://reviews.llvm.org/D88841
2020-10-04 03:23:12 +08:00
|
|
|
#include "lldb/Target/Target.h"
|
2020-11-10 05:36:26 +08:00
|
|
|
#include "lldb/Target/ThreadPostMortemTrace.h"
|
[intel pt] Refactor parsing
With the feedback I was getting in different diffs, I realized that splitting the parsing logic into two classes was not easy to deal with. I do see value in doing that, but I'd rather leave that as a refactor after most of the intel-pt logic is in place. Thus, I'm merging the common parser into the intel pt one, having thus only one that is fully aware of Intel PT during parsing and object creation.
Besides, based on the feedback in https://reviews.llvm.org/D88769, I'm creating a ThreadIntelPT class that will be able to orchestrate decoding of its own trace and can handle the stop events correctly.
This leaves the TraceIntelPT class as an initialization class that glues together different components. Right now it can initialize a trace session from a json file, and in the future will be able to initialize a trace session from a live process.
Besides, I'm renaming SettingsParser to SessionParser, which I think is a better name, as the json object represents a trace session of possibly many processes.
With the current set of targets, we have the following
- Trace: main interface for dealing with trace sessions
- TraceIntelPT: plugin Trace for dealing with intel pt sessions
- TraceIntelPTSessionParser: a parser of a json trace session file that can create a corresponding TraceIntelPT instance along with Targets, ProcessTraces (to be created in https://reviews.llvm.org/D88769), and ThreadIntelPT threads.
- ProcessTrace: (to be created in https://reviews.llvm.org/D88769) can handle the correct state of the traces as the user traverses the trace. I don't think there'll be a need an intel-pt specific implementation of this class.
- ThreadIntelPT: a thread implementation that can handle the decoding of its own trace file, along with keeping track of the current position the user is looking at when doing reverse debugging.
Differential Revision: https://reviews.llvm.org/D88841
2020-10-04 03:23:12 +08:00
|
|
|
|
|
|
|
using namespace lldb;
|
|
|
|
using namespace lldb_private;
|
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
void TraceSessionFileParser::NormalizePath(lldb_private::FileSpec &file_spec) {
|
|
|
|
if (file_spec.IsRelative())
|
|
|
|
file_spec.PrependPathComponent(m_session_file_dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
Error TraceSessionFileParser::ParseModule(lldb::TargetSP &target_sp,
|
|
|
|
const JSONModule &module) {
|
|
|
|
FileSpec system_file_spec(module.system_path);
|
|
|
|
NormalizePath(system_file_spec);
|
|
|
|
|
|
|
|
FileSpec local_file_spec(module.file.hasValue() ? *module.file
|
|
|
|
: module.system_path);
|
|
|
|
NormalizePath(local_file_spec);
|
|
|
|
|
|
|
|
ModuleSpec module_spec;
|
|
|
|
module_spec.GetFileSpec() = local_file_spec;
|
|
|
|
module_spec.GetPlatformFileSpec() = system_file_spec;
|
|
|
|
|
|
|
|
if (module.uuid.hasValue())
|
|
|
|
module_spec.GetUUID().SetFromStringRef(*module.uuid);
|
|
|
|
|
|
|
|
Status error;
|
|
|
|
ModuleSP module_sp =
|
|
|
|
target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error);
|
[trace][intel-pt] Implement the basic decoding functionality
Depends on D89408.
This diff finally implements trace decoding!
The current interface is
$ trace load /path/to/trace/session/file.json
$ thread trace dump instructions
thread #1: tid = 3842849, total instructions = 22
[ 0] 0x40052d
[ 1] 0x40052d
...
[19] 0x400521
$ # simply enter, which is a repeat command
[20] 0x40052d
[21] 0x400529
...
This doesn't do any disassembly, which will be done in the next diff.
Changes:
- Added an IntelPTDecoder class, that is a wrapper for libipt, which is the actual library that performs the decoding.
- Added TraceThreadDecoder class that decodes traces and memoizes the result to avoid repeating the decoding step.
- Added a DecodedThread class, which represents the output from decoding and that for the time being only stores the list of reconstructed instructions. Later it'll contain the function call hierarchy, which will enable reconstructing backtraces.
- Added basic APIs for accessing the trace in Trace.h:
- GetInstructionCount, which counts the number of instructions traced for a given thread
- IsTraceFailed, which returns an Error if decoding a thread failed
- ForEachInstruction, which iterates on the instructions traced for a given thread, concealing the internal storage of threads, as plug-ins can decide to generate the instructions on the fly or to store them all in a vector, like I do.
- DumpTraceInstructions was updated to print the instructions or show an error message if decoding was impossible.
- Tests included
Differential Revision: https://reviews.llvm.org/D89283
2020-10-15 01:26:10 +08:00
|
|
|
|
|
|
|
if (error.Fail())
|
|
|
|
return error.ToError();
|
|
|
|
|
|
|
|
bool load_addr_changed = false;
|
|
|
|
module_sp->SetLoadAddress(*target_sp, module.load_address.value, false,
|
|
|
|
load_addr_changed);
|
|
|
|
return llvm::Error::success();
|
[intel pt] Refactor parsing
With the feedback I was getting in different diffs, I realized that splitting the parsing logic into two classes was not easy to deal with. I do see value in doing that, but I'd rather leave that as a refactor after most of the intel-pt logic is in place. Thus, I'm merging the common parser into the intel pt one, having thus only one that is fully aware of Intel PT during parsing and object creation.
Besides, based on the feedback in https://reviews.llvm.org/D88769, I'm creating a ThreadIntelPT class that will be able to orchestrate decoding of its own trace and can handle the stop events correctly.
This leaves the TraceIntelPT class as an initialization class that glues together different components. Right now it can initialize a trace session from a json file, and in the future will be able to initialize a trace session from a live process.
Besides, I'm renaming SettingsParser to SessionParser, which I think is a better name, as the json object represents a trace session of possibly many processes.
With the current set of targets, we have the following
- Trace: main interface for dealing with trace sessions
- TraceIntelPT: plugin Trace for dealing with intel pt sessions
- TraceIntelPTSessionParser: a parser of a json trace session file that can create a corresponding TraceIntelPT instance along with Targets, ProcessTraces (to be created in https://reviews.llvm.org/D88769), and ThreadIntelPT threads.
- ProcessTrace: (to be created in https://reviews.llvm.org/D88769) can handle the correct state of the traces as the user traverses the trace. I don't think there'll be a need an intel-pt specific implementation of this class.
- ThreadIntelPT: a thread implementation that can handle the decoding of its own trace file, along with keeping track of the current position the user is looking at when doing reverse debugging.
Differential Revision: https://reviews.llvm.org/D88841
2020-10-04 03:23:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Error TraceSessionFileParser::CreateJSONError(json::Path::Root &root,
|
|
|
|
const json::Value &value) {
|
|
|
|
std::string err;
|
|
|
|
raw_string_ostream os(err);
|
|
|
|
root.printErrorContext(value, os);
|
|
|
|
return createStringError(
|
|
|
|
std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s",
|
|
|
|
toString(root.getError()).c_str(), os.str().c_str(), m_schema.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string TraceSessionFileParser::BuildSchema(StringRef plugin_schema) {
|
|
|
|
std::ostringstream schema_builder;
|
|
|
|
schema_builder << "{\n \"trace\": ";
|
|
|
|
schema_builder << plugin_schema.data() << ",";
|
|
|
|
schema_builder << R"(
|
|
|
|
"processes": [
|
|
|
|
{
|
|
|
|
"pid": integer,
|
|
|
|
"triple": string, // llvm-triple
|
|
|
|
"threads": [
|
|
|
|
{
|
|
|
|
"tid": integer,
|
|
|
|
"traceFile": string
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"modules": [
|
|
|
|
{
|
|
|
|
"systemPath": string, // original path of the module at runtime
|
|
|
|
"file"?: string, // copy of the file if not available at "systemPath"
|
|
|
|
"loadAddress": string, // string address in hex or decimal form
|
|
|
|
"uuid"?: string,
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
// Notes:
|
|
|
|
// All paths are either absolute or relative to the session file.
|
|
|
|
}
|
|
|
|
)";
|
|
|
|
return schema_builder.str();
|
|
|
|
}
|
|
|
|
|
2020-11-10 05:36:26 +08:00
|
|
|
ThreadPostMortemTraceSP
|
|
|
|
TraceSessionFileParser::ParseThread(ProcessSP &process_sp,
|
|
|
|
const JSONThread &thread) {
|
2020-10-15 01:25:39 +08:00
|
|
|
lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
|
|
|
|
|
|
|
|
FileSpec trace_file(thread.trace_file);
|
|
|
|
NormalizePath(trace_file);
|
|
|
|
|
2020-11-10 05:36:26 +08:00
|
|
|
ThreadPostMortemTraceSP thread_sp =
|
|
|
|
std::make_shared<ThreadPostMortemTrace>(*process_sp, tid, trace_file);
|
2020-10-15 01:25:39 +08:00
|
|
|
process_sp->GetThreadList().AddThread(thread_sp);
|
|
|
|
return thread_sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
Expected<TraceSessionFileParser::ParsedProcess>
|
|
|
|
TraceSessionFileParser::ParseProcess(const JSONProcess &process) {
|
|
|
|
TargetSP target_sp;
|
|
|
|
Status error = m_debugger.GetTargetList().CreateTarget(
|
|
|
|
m_debugger, /*user_exe_path*/ StringRef(), process.triple,
|
|
|
|
eLoadDependentsNo,
|
|
|
|
/*platform_options*/ nullptr, target_sp);
|
|
|
|
|
|
|
|
if (!target_sp)
|
|
|
|
return error.ToError();
|
|
|
|
|
|
|
|
ParsedProcess parsed_process;
|
|
|
|
parsed_process.target_sp = target_sp;
|
|
|
|
|
|
|
|
ProcessSP process_sp = target_sp->CreateProcess(
|
|
|
|
/*listener*/ nullptr, "trace",
|
2020-11-21 00:12:22 +08:00
|
|
|
/*crash_file*/ nullptr,
|
|
|
|
/*can_connect*/ false);
|
2020-10-15 01:25:39 +08:00
|
|
|
|
|
|
|
process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
|
|
|
|
|
|
|
|
for (const JSONThread &thread : process.threads)
|
|
|
|
parsed_process.threads.push_back(ParseThread(process_sp, thread));
|
|
|
|
|
|
|
|
for (const JSONModule &module : process.modules)
|
|
|
|
if (Error err = ParseModule(target_sp, module))
|
|
|
|
return std::move(err);
|
|
|
|
|
|
|
|
if (!process.threads.empty())
|
|
|
|
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<TraceSessionFileParser::ParsedProcess>>
|
|
|
|
TraceSessionFileParser::ParseCommonSessionFile(
|
|
|
|
const JSONTraceSessionBase &session) {
|
|
|
|
std::vector<ParsedProcess> parsed_processes;
|
|
|
|
|
|
|
|
auto onError = [&]() {
|
|
|
|
// Delete all targets that were created so far in case of failures
|
|
|
|
for (ParsedProcess &parsed_process : parsed_processes)
|
|
|
|
m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const JSONProcess &process : session.processes) {
|
|
|
|
if (Expected<ParsedProcess> parsed_process = ParseProcess(process))
|
|
|
|
parsed_processes.push_back(std::move(*parsed_process));
|
|
|
|
else {
|
|
|
|
onError();
|
|
|
|
return parsed_process.takeError();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return parsed_processes;
|
|
|
|
}
|
|
|
|
|
[intel pt] Refactor parsing
With the feedback I was getting in different diffs, I realized that splitting the parsing logic into two classes was not easy to deal with. I do see value in doing that, but I'd rather leave that as a refactor after most of the intel-pt logic is in place. Thus, I'm merging the common parser into the intel pt one, having thus only one that is fully aware of Intel PT during parsing and object creation.
Besides, based on the feedback in https://reviews.llvm.org/D88769, I'm creating a ThreadIntelPT class that will be able to orchestrate decoding of its own trace and can handle the stop events correctly.
This leaves the TraceIntelPT class as an initialization class that glues together different components. Right now it can initialize a trace session from a json file, and in the future will be able to initialize a trace session from a live process.
Besides, I'm renaming SettingsParser to SessionParser, which I think is a better name, as the json object represents a trace session of possibly many processes.
With the current set of targets, we have the following
- Trace: main interface for dealing with trace sessions
- TraceIntelPT: plugin Trace for dealing with intel pt sessions
- TraceIntelPTSessionParser: a parser of a json trace session file that can create a corresponding TraceIntelPT instance along with Targets, ProcessTraces (to be created in https://reviews.llvm.org/D88769), and ThreadIntelPT threads.
- ProcessTrace: (to be created in https://reviews.llvm.org/D88769) can handle the correct state of the traces as the user traverses the trace. I don't think there'll be a need an intel-pt specific implementation of this class.
- ThreadIntelPT: a thread implementation that can handle the decoding of its own trace file, along with keeping track of the current position the user is looking at when doing reverse debugging.
Differential Revision: https://reviews.llvm.org/D88841
2020-10-04 03:23:12 +08:00
|
|
|
namespace llvm {
|
|
|
|
namespace json {
|
|
|
|
|
|
|
|
bool fromJSON(const Value &value, TraceSessionFileParser::JSONAddress &address,
|
|
|
|
Path path) {
|
|
|
|
Optional<StringRef> s = value.getAsString();
|
|
|
|
if (s.hasValue() && !s->getAsInteger(0, address.value))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
path.report("expected numeric string");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool fromJSON(const Value &value, TraceSessionFileParser::JSONModule &module,
|
|
|
|
Path path) {
|
|
|
|
ObjectMapper o(value, path);
|
|
|
|
return o && o.map("systemPath", module.system_path) &&
|
|
|
|
o.map("file", module.file) &&
|
|
|
|
o.map("loadAddress", module.load_address) &&
|
|
|
|
o.map("uuid", module.uuid);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool fromJSON(const Value &value, TraceSessionFileParser::JSONThread &thread,
|
|
|
|
Path path) {
|
|
|
|
ObjectMapper o(value, path);
|
|
|
|
return o && o.map("tid", thread.tid) && o.map("traceFile", thread.trace_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool fromJSON(const Value &value, TraceSessionFileParser::JSONProcess &process,
|
|
|
|
Path path) {
|
|
|
|
ObjectMapper o(value, path);
|
|
|
|
return o && o.map("pid", process.pid) && o.map("triple", process.triple) &&
|
|
|
|
o.map("threads", process.threads) && o.map("modules", process.modules);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool fromJSON(const Value &value,
|
|
|
|
TraceSessionFileParser::JSONTracePluginSettings &plugin_settings,
|
|
|
|
Path path) {
|
|
|
|
ObjectMapper o(value, path);
|
|
|
|
return o && o.map("type", plugin_settings.type);
|
|
|
|
}
|
|
|
|
|
2020-10-15 01:25:39 +08:00
|
|
|
bool fromJSON(const Value &value,
|
|
|
|
TraceSessionFileParser::JSONTraceSessionBase &session,
|
|
|
|
Path path) {
|
|
|
|
ObjectMapper o(value, path);
|
|
|
|
return o && o.map("processes", session.processes);
|
|
|
|
}
|
|
|
|
|
[intel pt] Refactor parsing
With the feedback I was getting in different diffs, I realized that splitting the parsing logic into two classes was not easy to deal with. I do see value in doing that, but I'd rather leave that as a refactor after most of the intel-pt logic is in place. Thus, I'm merging the common parser into the intel pt one, having thus only one that is fully aware of Intel PT during parsing and object creation.
Besides, based on the feedback in https://reviews.llvm.org/D88769, I'm creating a ThreadIntelPT class that will be able to orchestrate decoding of its own trace and can handle the stop events correctly.
This leaves the TraceIntelPT class as an initialization class that glues together different components. Right now it can initialize a trace session from a json file, and in the future will be able to initialize a trace session from a live process.
Besides, I'm renaming SettingsParser to SessionParser, which I think is a better name, as the json object represents a trace session of possibly many processes.
With the current set of targets, we have the following
- Trace: main interface for dealing with trace sessions
- TraceIntelPT: plugin Trace for dealing with intel pt sessions
- TraceIntelPTSessionParser: a parser of a json trace session file that can create a corresponding TraceIntelPT instance along with Targets, ProcessTraces (to be created in https://reviews.llvm.org/D88769), and ThreadIntelPT threads.
- ProcessTrace: (to be created in https://reviews.llvm.org/D88769) can handle the correct state of the traces as the user traverses the trace. I don't think there'll be a need an intel-pt specific implementation of this class.
- ThreadIntelPT: a thread implementation that can handle the decoding of its own trace file, along with keeping track of the current position the user is looking at when doing reverse debugging.
Differential Revision: https://reviews.llvm.org/D88841
2020-10-04 03:23:12 +08:00
|
|
|
} // namespace json
|
|
|
|
} // namespace llvm
|