llvm-project/lldb/source/Target/TraceSettingsParser.cpp
Walter Erquinigo bddebca61e [intel-pt] Refactor the JSON parsing
Recently https://reviews.llvm.org/D88103 introduced a nice API for
converting a JSON object into C++ types, which include nice error
messaging.

I'm using that new functioniality to perform the parsing in a much more
elegant way. As a result, the code looks simpler and more maintainable,
as we aren't parsing anymore individual fields manually.

I updated the test cases accordingly.

Differential Revision: https://reviews.llvm.org/D88264
2020-09-24 16:35:34 -07:00

176 lines
5.7 KiB
C++

//===-- TraceSettingParser.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/TraceSettingsParser.h"
#include <sstream>
#include "Plugins/Process/Utility/HistoryThread.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Target/Process.h"
using namespace lldb;
using namespace lldb_private;
using namespace llvm;
StringRef TraceSettingsParser::GetSchema() {
static std::string schema;
if (schema.empty()) {
std::ostringstream schema_builder;
schema_builder << "{\n \"trace\": ";
schema_builder << GetPluginSchema().str() << ",\n";
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 settings file.)";
schema = schema_builder.str();
}
return schema;
}
void TraceSettingsParser::NormalizePath(FileSpec &file_spec) {
if (file_spec.IsRelative())
file_spec.PrependPathComponent(m_settings_dir);
}
void TraceSettingsParser::ParseThread(ProcessSP &process_sp,
const JSONThread &thread) {
lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
FileSpec spec(thread.trace_file);
NormalizePath(spec);
m_thread_to_trace_file_map[process_sp->GetID()][tid] = spec;
ThreadSP thread_sp(new HistoryThread(*process_sp, tid, /*callstack*/ {}));
process_sp->GetThreadList().AddThread(thread_sp);
}
llvm::Error TraceSettingsParser::ParseModule(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;
module_spec.SetObjectOffset(module.load_address.value);
if (module.uuid.hasValue())
module_spec.GetUUID().SetFromStringRef(*module.uuid);
Status error;
ModuleSP module_sp =
target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error);
return error.ToError();
}
llvm::Error TraceSettingsParser::ParseProcess(Debugger &debugger,
const JSONProcess &process) {
TargetSP target_sp;
Status error = debugger.GetTargetList().CreateTarget(
debugger, /*user_exe_path*/ llvm::StringRef(), process.triple,
eLoadDependentsNo,
/*platform_options*/ nullptr, target_sp);
if (!target_sp)
return error.ToError();
m_targets.push_back(target_sp);
debugger.GetTargetList().SetSelectedTarget(target_sp.get());
ProcessSP process_sp(target_sp->CreateProcess(
/*listener*/ nullptr, /*plugin_name*/ llvm::StringRef(),
/*crash_file*/ nullptr));
process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
for (const JSONThread &thread : process.threads)
ParseThread(process_sp, thread);
for (const JSONModule &module : process.modules) {
if (llvm::Error err = ParseModule(target_sp, module))
return err;
}
return llvm::Error::success();
}
llvm::Error
TraceSettingsParser::CreateJSONError(json::Path::Root &root,
const llvm::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",
llvm::toString(root.getError()).c_str(),
os.str().c_str(), GetSchema().data());
}
llvm::Error
TraceSettingsParser::ParseSettingsImpl(Debugger &debugger,
const llvm::json::Value &raw_settings) {
json::Path::Root root("settings");
JSONTraceSettings settings;
if (!json::fromJSON(raw_settings, settings, root))
return CreateJSONError(root, raw_settings);
for (const JSONProcess &process : settings.processes) {
if (llvm::Error err = ParseProcess(debugger, process))
return err;
}
json::Object plugin_obj = *raw_settings.getAsObject()->getObject("trace");
json::Value plugin_settings(std::move(plugin_obj));
return ParsePluginSettings(plugin_settings);
}
llvm::Error
TraceSettingsParser::ParseSettings(Debugger &debugger,
const llvm::json::Value &raw_settings,
llvm::StringRef settings_dir) {
m_settings_dir = settings_dir.str();
if (llvm::Error err = ParseSettingsImpl(debugger, raw_settings)) {
// We clean all the targets that were created internally, which should leave
// the debugger unchanged
for (auto target_sp : m_targets)
debugger.GetTargetList().DeleteTarget(target_sp);
return err;
}
m_trace.m_settings = *raw_settings.getAsObject();
m_trace.m_settings_dir = m_settings_dir;
m_trace.m_thread_to_trace_file_map = m_thread_to_trace_file_map;
m_trace.m_targets = m_targets;
return llvm::Error::success();
}