//===-- 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 #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(thread.tid); FileSpec trace_file(thread.trace_file); NormalizePath(trace_file); ThreadTraceSP thread_sp = std::make_shared(*process_sp, tid, trace_file); process_sp->GetThreadList().AddThread(thread_sp); return thread_sp; } Expected 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(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> TraceSessionFileParser::ParseCommonSessionFile( const JSONTraceSessionBase &session) { std::vector 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 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 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