You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
7.0 KiB
226 lines
7.0 KiB
//===-- 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>
|
|
|
|
#include "lldb/Core/Debugger.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Target/ThreadTrace.h"
|
|
|
|
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);
|
|
|
|
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();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
ThreadTraceSP TraceSessionFileParser::ParseThread(ProcessSP &process_sp,
|
|
const JSONThread &thread) {
|
|
lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
|
|
|
|
FileSpec trace_file(thread.trace_file);
|
|
NormalizePath(trace_file);
|
|
|
|
ThreadTraceSP thread_sp =
|
|
std::make_shared<ThreadTrace>(*process_sp, tid, trace_file);
|
|
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;
|
|
|
|
m_debugger.GetTargetList().SetSelectedTarget(target_sp.get());
|
|
|
|
ProcessSP process_sp = target_sp->CreateProcess(
|
|
/*listener*/ nullptr, "trace",
|
|
/*crash_file*/ nullptr,
|
|
/*can_connect*/ false);
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
bool fromJSON(const Value &value,
|
|
TraceSessionFileParser::JSONTraceSessionBase &session,
|
|
Path path) {
|
|
ObjectMapper o(value, path);
|
|
return o && o.map("processes", session.processes);
|
|
}
|
|
|
|
} // namespace json
|
|
} // namespace llvm
|