Files
clang-p2996/lldb/source/Target/TraceSessionFileParser.cpp
Tatyana Krasnukha 2634ec6ce9 [lldb] "target create" shouldn't save target if the command failed
TargetList::CreateTarget automatically adds created target to the list, however,
CommandObjectTargetCreate does some additional preparation after creating a target
and which can fail. The command should remove created target if it failed. Since
the function has many ways to return, scope guard does this work safely.

Changes to the TargetList make target adding and selection more transparent.

Other changes remove unnecessary SetSelectedTarget after CreateTarget.

Differential Revision: https://reviews.llvm.org/D93052
2020-12-12 16:40:58 +03:00

224 lines
7.0 KiB
C++

//===-- 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;
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