diff options
Diffstat (limited to 'lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp')
-rw-r--r-- | lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp b/lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp new file mode 100644 index 000000000000..c88ad9dc6a59 --- /dev/null +++ b/lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp @@ -0,0 +1,224 @@ +//===-- 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 "TraceSessionFileParser.h" +#include "ThreadPostMortemTrace.h" + +#include <sstream> + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.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(); +} + +ThreadPostMortemTraceSP +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); + + ThreadPostMortemTraceSP thread_sp = + std::make_shared<ThreadPostMortemTrace>(*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 |