diff --git a/lldb/include/lldb/Target/TraceSessionFileParser.h b/lldb/include/lldb/Target/TraceSessionFileParser.h --- a/lldb/include/lldb/Target/TraceSessionFileParser.h +++ b/lldb/include/lldb/Target/TraceSessionFileParser.h @@ -15,6 +15,8 @@ namespace lldb_private { +typedef std::shared_ptr TraceThreadSP; + /// \class TraceSessionFileParser TraceSessionFileParser.h /// /// Base class for parsing the common information of JSON trace session files. @@ -61,9 +63,16 @@ }; /// \} - TraceSessionFileParser(llvm::StringRef session_file_dir, + /// Helper struct holding the objects created when parsing a process + struct ParsedProcess { + lldb::TargetSP target_sp; + std::vector threads; + }; + + TraceSessionFileParser(Debugger &debugger, llvm::StringRef session_file_dir, llvm::StringRef schema) - : m_session_file_dir(session_file_dir), m_schema(schema) {} + : m_debugger(debugger), m_session_file_dir(session_file_dir), + m_schema(schema) {} /// Build the full schema for a Trace plug-in. /// @@ -75,11 +84,34 @@ /// specific attributes. static std::string BuildSchema(llvm::StringRef plugin_schema); + /// Parse the fields common to all trace session schemas. + /// + /// \param[in] session + /// The session json objects already deserialized. + /// + /// \return + //// A list of \a ParsedProcess containing all threads and targets created + ///during the parsing, or an error in case of failures. In case of errors, no + ///side effects are produced. + template + llvm::Expected> + ParseSessionFile(const JSONTraceSession &session) { + return ParseProcesses(session.processes); + } + protected: /// Resolve non-absolute paths relative to the session file folder. It /// modifies the given file_spec. void NormalizePath(lldb_private::FileSpec &file_spec); + TraceThreadSP ParseThread(lldb::ProcessSP &process_sp, + const JSONThread &thread); + + llvm::Expected ParseProcess(const JSONProcess &process); + + llvm::Expected> + ParseProcesses(const std::vector &processes); + llvm::Error ParseModule(lldb::TargetSP &target_sp, const JSONModule &module); /// Create a user-friendly error message upon a JSON-parsing failure using the @@ -96,6 +128,7 @@ llvm::Error CreateJSONError(llvm::json::Path::Root &root, const llvm::json::Value &value); + Debugger &m_debugger; std::string m_session_file_dir; llvm::StringRef m_schema; }; diff --git a/lldb/include/lldb/Target/TraceThread.h b/lldb/include/lldb/Target/TraceThread.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Target/TraceThread.h @@ -0,0 +1,59 @@ +//===-- TraceThread.h -------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_TARGET_TRACETHREAD_H +#define LLDB_TARGET_TRACETHREAD_H + +#include "lldb/Target/Thread.h" + +namespace lldb_private { + +/// \class TraceThread TraceThread.h +/// +/// Thread implementation used for representing threads gotten from trace +/// session files, which are similar to threads from core files. +/// +/// See \a TraceSessionFileParser for more information regarding trace session +/// files. +class TraceThread : public Thread { +public: + /// \param[in] process + /// The process who owns this thread. + /// + /// \param[in] tid + /// The tid of this thread. + /// + /// \param[in] trace_file. + /// The file that contains the list of instructions that were traced when + /// this thread was being executed. + TraceThread(Process &process, lldb::tid_t tid, const FileSpec trace_file) + : Thread(process, tid), m_trace_file(trace_file) {} + + void RefreshStateAfterStop() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame) override; + + /// \return + /// The trace file of this thread. + const FileSpec &GetTraceFile() const; + +protected: + bool CalculateStopInfo() override; + + lldb::RegisterContextSP m_thread_reg_ctx_sp; + +private: + FileSpec m_trace_file; +}; + +} // namespace lldb_private + +#endif // LLDB_TARGET_TRACETHREAD_H diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -228,6 +228,7 @@ class ThreadSpec; class Trace; class TraceSessionFileParser; +class TraceThread; class TraceOptions; class Type; class TypeAndOrName; diff --git a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt --- a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt +++ b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt @@ -12,7 +12,6 @@ add_lldb_library(lldbPluginTraceIntelPT PLUGIN TraceIntelPT.cpp TraceIntelPTSessionFileParser.cpp - ThreadIntelPT.cpp LINK_LIBS lldbCore diff --git a/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h deleted file mode 100644 --- a/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h +++ /dev/null @@ -1,54 +0,0 @@ -//===-- ThreadIntelPT.h -----------------------------------------*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_THREADINTELPT_H -#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_THREADINTELPT_H - -#include "lldb/Target/Thread.h" - -namespace lldb_private { -namespace trace_intel_pt { - -class ThreadIntelPT : public Thread { -public: - /// Create an Intel PT-traced thread. - /// - /// \param[in] process - /// The process that owns this thread. - /// - /// \param[in] tid - /// The thread id of this thread. - /// - /// \param[in] trace_file - /// The trace file for this thread. - /// - /// \param[in] pt_cpu - /// The Intel CPU information required to decode the \a trace_file. - ThreadIntelPT(Process &process, lldb::tid_t tid, const FileSpec &trace_file) - : Thread(process, tid), m_trace_file(trace_file) {} - - void RefreshStateAfterStop() override; - - lldb::RegisterContextSP GetRegisterContext() override; - - lldb::RegisterContextSP - CreateRegisterContextForFrame(StackFrame *frame) override; - -protected: - bool CalculateStopInfo() override; - - lldb::RegisterContextSP m_thread_reg_ctx_sp; - -private: - FileSpec m_trace_file; -}; - -} // namespace trace_intel_pt -} // namespace lldb_private - -#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_THREADINTELPT_H diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h @@ -52,21 +52,6 @@ CreateInstance(const llvm::json::Value &trace_session_file, llvm::StringRef session_file_dir, Debugger &debugger); - /// Create an instance of this class. - /// - /// \param[in] pt_cpu - /// The libipt.h cpu information needed for decoding correctling the - /// traces. - /// - /// \param[in] targets - /// The list of targets to associate with this trace instance - /// - /// \return - /// An intel-pt trace instance. - static lldb::TraceSP - CreateInstance(const pt_cpu &pt_cpu, - const std::vector &targets); - static ConstString GetPluginNameStatic(); uint32_t GetPluginVersion() override; @@ -75,14 +60,17 @@ llvm::StringRef GetSchema() override; private: - TraceIntelPT(const pt_cpu &pt_cpu, const std::vector &targets) - : m_pt_cpu(pt_cpu) { - for (const lldb::TargetSP &target_sp : targets) - m_targets.push_back(target_sp); - } + friend class TraceIntelPTSessionFileParser; + + /// \param[in] trace_threads + /// TraceThread instances, which are not live-processes and whose trace + /// files are fixed. + TraceIntelPT(const pt_cpu &pt_cpu, + const std::vector> &traced_threads); pt_cpu m_pt_cpu; - std::vector> m_targets; + std::map, std::shared_ptr> + m_trace_threads; }; } // namespace trace_intel_pt diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -10,7 +10,9 @@ #include "TraceIntelPTSessionFileParser.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Target/Process.h" #include "lldb/Target/Target.h" +#include "lldb/Target/TraceThread.h" using namespace lldb; using namespace lldb_private; @@ -56,11 +58,11 @@ .Parse(); } -TraceSP TraceIntelPT::CreateInstance(const pt_cpu &pt_cpu, - const std::vector &targets) { - TraceSP trace_instance(new TraceIntelPT(pt_cpu, targets)); - for (const TargetSP &target_sp : targets) - target_sp->SetTrace(trace_instance); - - return trace_instance; +TraceIntelPT::TraceIntelPT( + const pt_cpu &pt_cpu, + const std::vector> &traced_threads) + : m_pt_cpu(pt_cpu) { + for (const std::shared_ptr &thread : traced_threads) + m_trace_threads.emplace( + std::make_pair(thread->GetProcess()->GetID(), thread->GetID()), thread); } diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h @@ -9,8 +9,6 @@ #ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H #define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H -#include "intel-pt.h" - #include "TraceIntelPT.h" #include "lldb/Target/TraceSessionFileParser.h" @@ -38,8 +36,8 @@ TraceIntelPTSessionFileParser(Debugger &debugger, const llvm::json::Value &trace_session_file, llvm::StringRef session_file_dir) - : TraceSessionFileParser(session_file_dir, GetSchema()), - m_debugger(debugger), m_trace_session_file(trace_session_file) {} + : TraceSessionFileParser(debugger, session_file_dir, GetSchema()), + m_trace_session_file(trace_session_file) {} /// \return /// The JSON schema for the session data. @@ -53,24 +51,14 @@ /// errors, return a null pointer. llvm::Expected Parse(); -private: - llvm::Error ParseImpl(); - - llvm::Error ParseProcess(const TraceSessionFileParser::JSONProcess &process); + lldb::TraceSP + CreateTraceIntelPTInstance(const pt_cpu &pt_cpu, + std::vector &parsed_processes); - void ParseThread(lldb::ProcessSP &process_sp, - const TraceSessionFileParser::JSONThread &thread); - - void ParsePTCPU(const JSONPTCPU &pt_cpu); +private: + pt_cpu ParsePTCPU(const JSONPTCPU &pt_cpu); - Debugger &m_debugger; const llvm::json::Value &m_trace_session_file; - - /// Objects created as product of the parsing - /// \{ - pt_cpu m_pt_cpu; - std::vector m_targets; - /// \} }; } // namespace trace_intel_pt diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp @@ -8,10 +8,11 @@ #include "TraceIntelPTSessionFileParser.h" -#include "ThreadIntelPT.h" #include "lldb/Core/Debugger.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" +#include "lldb/Target/ThreadList.h" +#include "lldb/Target/TraceThread.h" using namespace lldb; using namespace lldb_private; @@ -34,88 +35,39 @@ return schema; } -void TraceIntelPTSessionFileParser::ParseThread( - ProcessSP &process_sp, const TraceSessionFileParser::JSONThread &thread) { - lldb::tid_t tid = static_cast(thread.tid); - - FileSpec trace_file(thread.trace_file); - NormalizePath(trace_file); - - ThreadSP thread_sp = - std::make_shared(*process_sp, tid, trace_file); - process_sp->GetThreadList().AddThread(thread_sp); +pt_cpu TraceIntelPTSessionFileParser::ParsePTCPU(const JSONPTCPU &pt_cpu) { + return {pt_cpu.vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown, + static_cast(pt_cpu.family), + static_cast(pt_cpu.model), + static_cast(pt_cpu.stepping)}; } -Error TraceIntelPTSessionFileParser::ParseProcess( - const TraceSessionFileParser::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(); - - m_targets.push_back(target_sp); - m_debugger.GetTargetList().SetSelectedTarget(target_sp.get()); - - ProcessSP process_sp(target_sp->CreateProcess( - /*listener*/ nullptr, "trace", - /*crash_file*/ nullptr)); - process_sp->SetID(static_cast(process.pid)); +TraceSP TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance( + const pt_cpu &pt_cpu, std::vector &parsed_processes) { + std::vector threads; + for (const ParsedProcess &parsed_process : parsed_processes) + threads.insert(threads.end(), parsed_process.threads.begin(), + parsed_process.threads.end()); - for (const TraceSessionFileParser::JSONThread &thread : process.threads) - ParseThread(process_sp, thread); + TraceSP trace_instance(new TraceIntelPT(pt_cpu, threads)); + for (const ParsedProcess &parsed_process : parsed_processes) + parsed_process.target_sp->SetTrace(trace_instance); - for (const TraceSessionFileParser::JSONModule &module : process.modules) { - if (Error err = ParseModule(target_sp, module)) - return 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 llvm::Error::success(); + return trace_instance; } -void TraceIntelPTSessionFileParser::ParsePTCPU(const JSONPTCPU &pt_cpu) { - m_pt_cpu = {pt_cpu.vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown, - static_cast(pt_cpu.family), - static_cast(pt_cpu.model), - static_cast(pt_cpu.stepping)}; -} - -Error TraceIntelPTSessionFileParser::ParseImpl() { +Expected TraceIntelPTSessionFileParser::Parse() { json::Path::Root root("traceSession"); TraceSessionFileParser::JSONTraceSession session; - if (!json::fromJSON(m_trace_session_file, session, root)) { + if (!json::fromJSON(m_trace_session_file, session, root)) return CreateJSONError(root, m_trace_session_file); - } - - ParsePTCPU(session.trace.pt_cpu); - for (const TraceSessionFileParser::JSONProcess &process : session.processes) { - if (Error err = ParseProcess(process)) - return err; - } - return Error::success(); -} - -Expected TraceIntelPTSessionFileParser::Parse() { - if (Error err = ParseImpl()) { - // Delete all targets that were created - for (auto target_sp : m_targets) - m_debugger.GetTargetList().DeleteTarget(target_sp); - m_targets.clear(); - return std::move(err); - } - return TraceIntelPT::CreateInstance(m_pt_cpu, m_targets); + if (Expected> parsed_processes = + ParseSessionFile(session)) + return CreateTraceIntelPTInstance(ParsePTCPU(session.trace.pt_cpu), + *parsed_processes); + else + return parsed_processes.takeError(); } namespace llvm { diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -67,6 +67,7 @@ ThreadSpec.cpp Trace.cpp TraceSessionFileParser.cpp + TraceThread.cpp UnixSignals.cpp UnwindAssembly.cpp UnwindLLDB.cpp diff --git a/lldb/source/Target/TraceSessionFileParser.cpp b/lldb/source/Target/TraceSessionFileParser.cpp --- a/lldb/source/Target/TraceSessionFileParser.cpp +++ b/lldb/source/Target/TraceSessionFileParser.cpp @@ -10,8 +10,11 @@ #include +#include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" +#include "lldb/Target/Process.h" #include "lldb/Target/Target.h" +#include "lldb/Target/TraceThread.h" using namespace lldb; using namespace lldb_private; @@ -34,7 +37,6 @@ 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); @@ -42,7 +44,14 @@ Status error; ModuleSP module_sp = target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error); - return error.ToError(); + + 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, @@ -87,6 +96,80 @@ return schema_builder.str(); } +TraceThreadSP 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); + + TraceThreadSP 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)); + 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::ParseProcesses( + const std::vector &processes) { + 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 : 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 { diff --git a/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.cpp b/lldb/source/Target/TraceThread.cpp rename from lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.cpp rename to lldb/source/Target/TraceThread.cpp --- a/lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.cpp +++ b/lldb/source/Target/TraceThread.cpp @@ -1,4 +1,4 @@ -//===-- ThreadIntelPT.cpp -------------------------------------------------===// +//===-- TraceThread.cpp ---------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "ThreadIntelPT.h" +#include "lldb/Target/TraceThread.h" #include @@ -16,11 +16,10 @@ using namespace lldb; using namespace lldb_private; -using namespace lldb_private::trace_intel_pt; -void ThreadIntelPT::RefreshStateAfterStop() {} +void TraceThread::RefreshStateAfterStop() {} -RegisterContextSP ThreadIntelPT::GetRegisterContext() { +RegisterContextSP TraceThread::GetRegisterContext() { if (!m_reg_context_sp) m_reg_context_sp = CreateRegisterContextForFrame(nullptr); @@ -28,11 +27,13 @@ } RegisterContextSP -ThreadIntelPT::CreateRegisterContextForFrame(StackFrame *frame) { +TraceThread::CreateRegisterContextForFrame(StackFrame *frame) { // Eventually this will calculate the register context based on the current // trace position. return std::make_shared( *this, 0, GetProcess()->GetAddressByteSize(), LLDB_INVALID_ADDRESS); } -bool ThreadIntelPT::CalculateStopInfo() { return false; } +bool TraceThread::CalculateStopInfo() { return false; } + +const FileSpec &TraceThread::GetTraceFile() const { return m_trace_file; }