diff --git a/lldb/include/lldb/Target/ThreadTrace.h b/lldb/include/lldb/Target/ThreadTrace.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Target/ThreadTrace.h @@ -0,0 +1,88 @@ +//===-- ThreadTrace.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_THREAD_TRACE_H +#define LLDB_TARGET_THREAD_TRACE_H + +#include "llvm/Support/Error.h" + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +/// Helper enumeration for traversing the instructions of a processor trace. +enum class TraceDirection { + Forwards = 0, + Backwards, +}; + +/// Class that represents a trace for a specific thread. +/// +/// This class attempts to be a generic interface for accessing the instructions +/// of the trace so that each Trace plug-in can reconstruct, represent and store +/// the instruction data in the best possible way for the given technology. +/// +/// Consumers of this class should make no assumptions on whether the trace is +/// fully loaded on memory or not, as the plug-in might load or reconstruct +/// instruction lazily. +class ThreadTrace { +public: + virtual ~ThreadTrace() = default; + + /// Run the provided callback on the instructions of the trace. + /// + /// The instructions will be traversed starting at the given \a start_position + /// sequentially until the callback returns \b false, in which case no more + /// instructions are inspected. + /// + /// \param[in] start_position + /// The instruction position to start iterating on, where 0 corresponds to + /// the oldest instruction chronologically. + /// + /// \param[in] direction + /// If \b TraceDirection::Forwards, then then instructions will be + /// traversed forwards chronologically, i.e. with incrementing indices. If + /// \b TraceDirection::Backwards, the traversal is done backwards + /// chronologically, i.e. with decrementing indices. + /// + /// \param[in] callback + /// The callback to execute on each instruction. If it returns \b false, + /// the iteration stops. + /// + /// If the \b load_address input of this callback is an \a llvm::Error, + /// then that means that the instruction failed to be reconstructed. + /// Otherwise, the load address of that instruction is returned. + virtual void TraverseInstructions( + size_t position, TraceDirection direction, + std::function load_address)> + callback) = 0; + + /// \return + /// The number of available instructions in the trace. + virtual size_t GetInstructionCount() = 0; + + /// Return an architecture-agnostic category for the given instruction. + /// + /// \param[in] index + /// The index of the instruction in question. It must be valid. + /// + /// \return + /// The instruction type. + virtual lldb::TraceInstructionType GetInstructionType(size_t index) = 0; + + /// Return the size in bytes of a given instruction. + /// + /// \param[in] index + /// The index of the instruction in question. It must be valid. + virtual size_t GetInstructionSize(size_t index) = 0; +}; + +} // namespace lldb_private + +#endif // LLDB_TARGET_THREAD_TRACE_H diff --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h --- a/lldb/include/lldb/Target/Trace.h +++ b/lldb/include/lldb/Target/Trace.h @@ -15,6 +15,7 @@ #include "lldb/Core/PluginInterface.h" #include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadTrace.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/TraceGDBRemotePackets.h" #include "lldb/Utility/UnimplementedError.h" @@ -44,11 +45,6 @@ class Trace : public PluginInterface, public std::enable_shared_from_this { public: - enum class TraceDirection { - Forwards = 0, - Backwards, - }; - /// Dump the trace data that this plug-in has access to. /// /// This function will dump all of the trace data for all threads in a user @@ -136,18 +132,6 @@ /// The JSON schema of this Trace plug-in. virtual llvm::StringRef GetSchema() = 0; - /// Each decoded thread contains a cursor to the current position the user is - /// stopped at. When reverse debugging, each operation like reverse-next or - /// reverse-continue will move this cursor, which is then picked by any - /// subsequent dump or reverse operation. - /// - /// The initial position for this cursor is the last element of the thread, - /// which is the most recent chronologically. - /// - /// \return - /// The current position of the thread's trace or \b 0 if empty. - virtual size_t GetCursorPosition(Thread &thread) = 0; - /// Dump \a count instructions of the given thread's trace ending at the /// given \a end_position position. /// @@ -173,55 +157,15 @@ void DumpTraceInstructions(Thread &thread, Stream &s, size_t count, size_t end_position, bool raw); - /// Run the provided callback on the instructions of the trace of the given - /// thread. - /// - /// The instructions will be traversed starting at the given \a position - /// sequentially until the callback returns \b false, in which case no more - /// instructions are inspected. - /// - /// The purpose of this method is to allow inspecting traced instructions - /// without exposing the internal representation of how they are stored on - /// memory. - /// - /// \param[in] thread - /// The thread whose trace will be traversed. - /// - /// \param[in] position - /// The instruction position to start iterating on. - /// - /// \param[in] direction - /// If \b TraceDirection::Forwards, then then instructions will be - /// traversed forwards chronologically, i.e. with incrementing indices. If - /// \b TraceDirection::Backwards, the traversal is done backwards - /// chronologically, i.e. with decrementing indices. - /// - /// \param[in] callback - /// The callback to execute on each instruction. If it returns \b false, - /// the iteration stops. - virtual void TraverseInstructions( - Thread &thread, size_t position, TraceDirection direction, - std::function load_addr)> - callback) = 0; - - /// Get the number of available instructions in the trace of the given thread. - /// - /// \param[in] thread - /// The thread whose trace will be inspected. - /// - /// \return - /// The total number of instructions in the trace, or \a llvm::None if the - /// thread is not being traced. - virtual llvm::Optional GetInstructionCount(Thread &thread) = 0; - - /// Check if a thread is currently traced by this object. + /// Get the trace corresponding to a specific thread. /// /// \param[in] thread - /// The thread in question. /// /// \return - /// \b true if the thread is traced by this instance, \b false otherwise. - virtual bool IsTraced(const Thread &thread) = 0; + /// The requested trace or an \a llvm::Error if the thread is not being + /// traced or if there was an error loading or fetching the trace. + virtual llvm::Expected + GetThreadTrace(Thread &thread) = 0; /// \return /// A description of the parameters to use for the \a Trace::Start method. @@ -342,13 +286,13 @@ /// /// \param[in] state /// The jLLDBTraceGetState response. - virtual void - DoRefreshLiveProcessState(llvm::Expected state) = 0; + virtual llvm::Error + DoRefreshLiveProcessState(const TraceGetStateResponse &state) = 0; /// Method to be invoked by the plug-in to refresh the live process state. /// /// The result is cached through the same process stop. - void RefreshLiveProcessState(); + llvm::Error RefreshLiveProcessState(); /// Process traced by this object if doing live tracing. Otherwise it's null. int64_t m_stop_id = -1; @@ -358,6 +302,8 @@ m_live_thread_data; /// data kind -> size std::unordered_map m_live_process_data; + /// Cached error if \a Trace::RefreshLiveProcessState fails. + llvm::Optional m_live_process_refresh_error; }; } // namespace lldb_private diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -9,6 +9,7 @@ #ifndef LLDB_LLDB_ENUMERATIONS_H #define LLDB_LLDB_ENUMERATIONS_H +#include #include #ifndef SWIG @@ -773,12 +774,23 @@ eBasicTypeOther }; -/// Deprecated -enum TraceType { - eTraceTypeNone = 0, - - /// Intel Processor Trace - eTraceTypeProcessorTrace +/// Architecture-agnostic categorization of instructions. Useful for doing +/// analysis on traces. +enum TraceInstructionType : uint8_t { + /// The instruction is not recognized by LLDB + eTraceInstructionUnknown = 0, + /// The instruction is something not listed below + eTraceInstructionOther, + /// The instruction is a (function) call + eTraceInstructionCall, + /// The instruction is a (function) return + eTraceInstructionReturn, + /// The instruction is a unconditional jump + eTraceInstructionJump, + /// The instruction is a conditional jump + eTraceInstructionCondJump, + /// The instruction writes custom data to the trace, e.g. Intel's PTWRITE. + eTraceInstructionTraceWrite, }; enum StructuredDataType { 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 @@ -226,8 +226,9 @@ class ThreadPlanStepRange; class ThreadPlanStepThrough; class ThreadPlanTracer; -class ThreadSpec; class ThreadPostMortemTrace; +class ThreadSpec; +class ThreadTrace; class Trace; class TraceSessionFileParser; class Type; @@ -440,6 +441,7 @@ ThreadPostMortemTraceSP; typedef std::weak_ptr ThreadPlanWP; typedef std::shared_ptr ThreadPlanTracerSP; +typedef std::shared_ptr ThreadTraceSP; typedef std::shared_ptr TraceSP; typedef std::shared_ptr TypeSP; typedef std::weak_ptr TypeWP; diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -2140,16 +2140,25 @@ const TraceSP &trace_sp = m_exe_ctx.GetTargetSP()->GetTrace(); ThreadSP thread_sp = m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid); + if (llvm::Expected thread_trace_sp = + trace_sp->GetThreadTrace(*thread_sp)) { + size_t count = m_options.m_count; + ssize_t position = + m_options.m_position.getValueOr( + (ssize_t)thread_trace_sp.get()->GetInstructionCount() - 1) - + m_consecutive_repetitions * count; + if (position < 0) + result.SetError("error: no more data"); + else + trace_sp->DumpTraceInstructions(*thread_sp, result.GetOutputStream(), + count, position, m_options.m_raw); + } else { + result.GetOutputStream().Printf( + "thread #%u: tid = %" PRIu64 ", %s", thread_sp->GetIndexID(), + thread_sp->GetID(), + llvm::toString(thread_trace_sp.takeError()).c_str()); + } - size_t count = m_options.m_count; - ssize_t position = m_options.m_position.getValueOr( - trace_sp->GetCursorPosition(*thread_sp)) - - m_consecutive_repetitions * count; - if (position < 0) - result.SetError("error: no more data"); - else - trace_sp->DumpTraceInstructions(*thread_sp, result.GetOutputStream(), - count, position, m_options.m_raw); return true; } 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 @@ -15,7 +15,7 @@ add_lldb_library(lldbPluginTraceIntelPT PLUGIN CommandObjectTraceStartIntelPT.cpp - DecodedThread.cpp + ThreadTraceIntelPT.cpp IntelPTDecoder.cpp TraceIntelPT.cpp TraceIntelPTSessionFileParser.cpp diff --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp deleted file mode 100644 --- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp +++ /dev/null @@ -1,69 +0,0 @@ -//===-- DecodedThread.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 "DecodedThread.h" - -#include "lldb/Utility/StreamString.h" - -using namespace lldb_private; -using namespace lldb_private::trace_intel_pt; -using namespace llvm; - -char IntelPTError::ID; - -IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address) - : m_libipt_error_code(libipt_error_code), m_address(address) { - assert(libipt_error_code < 0); -} - -void IntelPTError::log(llvm::raw_ostream &OS) const { - const char *libipt_error_message = pt_errstr(pt_errcode(m_libipt_error_code)); - if (m_address != LLDB_INVALID_ADDRESS && m_address > 0) { - write_hex(OS, m_address, HexPrintStyle::PrefixLower, 18); - OS << " "; - } - OS << "error: " << libipt_error_message; -} - -bool IntelPTInstruction::IsError() const { return (bool)m_error; } - -Expected IntelPTInstruction::GetLoadAddress() const { - if (IsError()) - return ToError(); - return m_pt_insn.ip; -} - -Error IntelPTInstruction::ToError() const { - if (!IsError()) - return Error::success(); - - if (m_error->isA()) - return make_error(static_cast(*m_error)); - return make_error(m_error->message(), - m_error->convertToErrorCode()); -} - -size_t DecodedThread::GetLastPosition() const { - return m_instructions.empty() ? 0 : m_instructions.size() - 1; -} - -ArrayRef DecodedThread::GetInstructions() const { - return makeArrayRef(m_instructions); -} - -size_t DecodedThread::GetCursorPosition() const { return m_position; } - -size_t DecodedThread::SetCursorPosition(size_t new_position) { - m_position = std::min(new_position, GetLastPosition()); - return m_position; -} - -DecodedThread::DecodedThread(Error error) { - m_instructions.emplace_back(std::move(error)); - m_position = GetLastPosition(); -} diff --git a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h --- a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h +++ b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h @@ -11,7 +11,7 @@ #include "intel-pt.h" -#include "DecodedThread.h" +#include "ThreadTraceIntelPT.h" #include "forward-declarations.h" #include "lldb/Target/Process.h" #include "lldb/Utility/FileSpec.h" @@ -31,7 +31,7 @@ /// /// \return /// A \a DecodedThread instance. - const DecodedThread &Decode(); + llvm::Expected Decode(); ThreadDecoder(const ThreadDecoder &other) = delete; ThreadDecoder &operator=(const ThreadDecoder &other) = delete; @@ -41,9 +41,9 @@ /// /// \return /// A \a DecodedThread instance. - virtual DecodedThread DoDecode() = 0; + virtual llvm::Expected DoDecode() = 0; - llvm::Optional m_decoded_thread; + llvm::Optional m_decoded_thread; }; /// Decoder implementation for \a lldb_private::ThreadPostMortemTrace, which are @@ -59,7 +59,7 @@ TraceIntelPT &trace); private: - DecodedThread DoDecode() override; + llvm::Expected DoDecode() override; lldb::ThreadPostMortemTraceSP m_trace_thread; TraceIntelPT &m_trace; @@ -75,7 +75,7 @@ LiveThreadDecoder(Thread &thread, TraceIntelPT &trace); private: - DecodedThread DoDecode() override; + llvm::Expected DoDecode() override; lldb::ThreadSP m_thread_sp; TraceIntelPT &m_trace; diff --git a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp --- a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp @@ -8,13 +8,13 @@ #include "IntelPTDecoder.h" #include "llvm/Support/MemoryBuffer.h" +#include #include "TraceIntelPT.h" #include "lldb/Core/Module.h" #include "lldb/Core/Section.h" #include "lldb/Target/Target.h" #include "lldb/Target/ThreadPostMortemTrace.h" -#include "lldb/Utility/StringExtractor.h" using namespace lldb; using namespace lldb_private; @@ -222,9 +222,14 @@ return cpu_info.takeError(); } -const DecodedThread &ThreadDecoder::Decode() { - if (!m_decoded_thread.hasValue()) - m_decoded_thread = DoDecode(); +Expected ThreadDecoder::Decode() { + if (!m_decoded_thread.hasValue()) { + if (Expected thread_trace_sp = DoDecode()) { + m_decoded_thread = *thread_trace_sp; + } else { + return thread_trace_sp.takeError(); + } + } return *m_decoded_thread; } @@ -232,22 +237,22 @@ const lldb::ThreadPostMortemTraceSP &trace_thread, TraceIntelPT &trace) : m_trace_thread(trace_thread), m_trace(trace) {} -DecodedThread PostMortemThreadDecoder::DoDecode() { +Expected PostMortemThreadDecoder::DoDecode() { if (Expected> instructions = DecodeTraceFile(*m_trace_thread->GetProcess(), m_trace, m_trace_thread->GetTraceFile())) - return DecodedThread(std::move(*instructions)); + return std::make_shared(std::move(*instructions)); else - return DecodedThread(instructions.takeError()); + return instructions.takeError(); } LiveThreadDecoder::LiveThreadDecoder(Thread &thread, TraceIntelPT &trace) : m_thread_sp(thread.shared_from_this()), m_trace(trace) {} -DecodedThread LiveThreadDecoder::DoDecode() { +Expected LiveThreadDecoder::DoDecode() { if (Expected> instructions = DecodeLiveThread(*m_thread_sp, m_trace)) - return DecodedThread(std::move(*instructions)); + return std::make_shared(std::move(*instructions)); else - return DecodedThread(instructions.takeError()); + return instructions.takeError(); } diff --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h b/lldb/source/Plugins/Trace/intel-pt/ThreadTraceIntelPT.h rename from lldb/source/Plugins/Trace/intel-pt/DecodedThread.h rename to lldb/source/Plugins/Trace/intel-pt/ThreadTraceIntelPT.h --- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h +++ b/lldb/source/Plugins/Trace/intel-pt/ThreadTraceIntelPT.h @@ -1,4 +1,4 @@ -//===-- DecodedThread.h -----------------------------------------*- C++ -*-===// +//===-- ThreadTraceIntelPT.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. @@ -6,12 +6,11 @@ // //===----------------------------------------------------------------------===// -#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H -#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H +#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_THREAD_TRACE_H +#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_THREAD_TRACE_H #include -#include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "lldb/Target/Trace.h" @@ -84,6 +83,10 @@ /// error. llvm::Expected GetLoadAddress() const; + lldb::TraceInstructionType GetInstructionType() const; + + size_t GetInstructionSize() const; + /// \return /// An \a llvm::Error object if this class corresponds to an Error, or an /// \a llvm::Error::success otherwise. @@ -99,53 +102,30 @@ std::unique_ptr m_error; }; -/// \class DecodedThread -/// Class holding the instructions and function call hierarchy obtained from -/// decoding a trace, as well as a position cursor used when reverse debugging -/// the trace. -/// -/// Each decoded thread contains a cursor to the current position the user is -/// stopped at. See \a Trace::GetCursorPosition for more information. -class DecodedThread { +class ThreadTraceIntelPT : public ThreadTrace { public: - DecodedThread(std::vector &&instructions) - : m_instructions(std::move(instructions)), m_position(GetLastPosition()) { - } + ThreadTraceIntelPT(std::vector &&instructions) + : m_instructions(std::move(instructions)) {} - /// Constructor with a single error signaling a complete failure of the - /// decoding process. - DecodedThread(llvm::Error error); + void TraverseInstructions( + size_t start_position, TraceDirection direction, + std::function load_address)> + callback) override; - /// Get the instructions from the decoded trace. Some of them might indicate - /// errors (i.e. gaps) in the trace. - /// - /// \return - /// The instructions of the trace. - llvm::ArrayRef GetInstructions() const; + size_t GetInstructionCount() override; - /// \return - /// The current position of the cursor of this trace, or 0 if there are no - /// instructions. - size_t GetCursorPosition() const; + lldb::TraceInstructionType GetInstructionType(size_t index) override; - /// Change the position of the cursor of this trace. If this value is to high, - /// the new position will be set as the last instruction of the trace. - /// - /// \return - /// The effective new position. - size_t SetCursorPosition(size_t new_position); - /// \} + size_t GetInstructionSize(size_t index) override; private: - /// \return - /// The index of the last element of the trace, or 0 if empty. - size_t GetLastPosition() const; - std::vector m_instructions; - size_t m_position; }; +using ThreadTraceIntelPTSP = std::shared_ptr; + } // namespace trace_intel_pt } // namespace lldb_private -#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H +#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_THREAD_TRACE_H diff --git a/lldb/source/Plugins/Trace/intel-pt/ThreadTraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/ThreadTraceIntelPT.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Trace/intel-pt/ThreadTraceIntelPT.cpp @@ -0,0 +1,98 @@ +//===-- ThreadTraceIntelPT.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 "ThreadTraceIntelPT.h" + +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::trace_intel_pt; +using namespace llvm; + +char IntelPTError::ID; + +IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address) + : m_libipt_error_code(libipt_error_code), m_address(address) { + assert(libipt_error_code < 0); +} + +void IntelPTError::log(llvm::raw_ostream &OS) const { + const char *libipt_error_message = pt_errstr(pt_errcode(m_libipt_error_code)); + if (m_address != LLDB_INVALID_ADDRESS && m_address > 0) { + write_hex(OS, m_address, HexPrintStyle::PrefixLower, 18); + OS << " "; + } + OS << "error: " << libipt_error_message; +} + +bool IntelPTInstruction::IsError() const { return (bool)m_error; } + +Expected IntelPTInstruction::GetLoadAddress() const { + if (IsError()) + return ToError(); + return m_pt_insn.ip; +} + +Error IntelPTInstruction::ToError() const { + if (!IsError()) + return Error::success(); + + if (m_error->isA()) + return make_error(static_cast(*m_error)); + return make_error(m_error->message(), + m_error->convertToErrorCode()); +} + +TraceInstructionType IntelPTInstruction::GetInstructionType() const { + switch (m_pt_insn.iclass) { + case ptic_other: + return lldb::eTraceInstructionOther; + case ptic_call: + case ptic_far_call: + return lldb::eTraceInstructionCall; + case ptic_return: + case ptic_far_return: + return lldb::eTraceInstructionReturn; + case ptic_jump: + case ptic_far_jump: + return lldb::eTraceInstructionJump; + case ptic_cond_jump: + return lldb::eTraceInstructionCondJump; + case ptic_ptwrite: + return lldb::eTraceInstructionTraceWrite; + default: + return lldb::eTraceInstructionUnknown; + } +} + +size_t IntelPTInstruction::GetInstructionSize() const { return m_pt_insn.size; } + +void ThreadTraceIntelPT::TraverseInstructions( + size_t start_position, TraceDirection direction, + std::function load_address)> + callback) { + ssize_t delta = direction == TraceDirection::Forwards ? 1 : -1; + for (ssize_t i = start_position; i < (ssize_t)m_instructions.size() && i >= 0; + i += delta) + if (!callback(i, m_instructions[i].GetLoadAddress())) + break; +} + +size_t ThreadTraceIntelPT::GetInstructionCount() { + return m_instructions.size(); +} + +lldb::TraceInstructionType +ThreadTraceIntelPT::GetInstructionType(size_t index) { + return m_instructions[index].GetInstructionType(); +} + +size_t ThreadTraceIntelPT::GetInstructionSize(size_t index) { + return m_instructions[index].GetInstructionSize(); +} 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 @@ -65,19 +65,10 @@ llvm::StringRef GetSchema() override; - void TraverseInstructions( - Thread &thread, size_t position, TraceDirection direction, - std::function load_addr)> - callback) override; + llvm::Error + DoRefreshLiveProcessState(const TraceGetStateResponse &state) override; - llvm::Optional GetInstructionCount(Thread &thread) override; - - size_t GetCursorPosition(Thread &thread) override; - - void DoRefreshLiveProcessState( - llvm::Expected state) override; - - bool IsTraced(const Thread &thread) override; + llvm::Expected GetThreadTrace(Thread &thread) override; const char *GetStartConfigurationHelp() override; @@ -144,25 +135,19 @@ TraceIntelPT(Process &live_process) : Trace(live_process), m_thread_decoders(){}; - /// Decode the trace of the given thread that, i.e. recontruct the traced - /// instructions. That trace must be managed by this class. - /// - /// \param[in] thread - /// If \a thread is a \a ThreadTrace, then its internal trace file will be - /// decoded. Live threads are not currently supported. + /// Decode the trace of the given thread, i.e. recontruct the traced + /// instructions. /// /// \return - /// A \a DecodedThread instance if decoding was successful, or a \b - /// nullptr if the thread's trace is not managed by this class. - const DecodedThread *Decode(Thread &thread); + /// A \a ThreadTraceIntelPT instance if decoding was successful, or an \a + /// llvm::Error if the thread's trace is not managed by this class or if + /// there was an error setting up the the decoder or the raw trace data. + llvm::Expected Decode(Thread &thread); /// It is provided by either a session file or a live process' "cpuInfo" /// binary data. llvm::Optional m_cpu_info; std::map> m_thread_decoders; - /// Dummy DecodedThread used when decoding threads after there were errors - /// when refreshing the live process state. - llvm::Optional m_failed_live_threads_decoder; }; } // 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 @@ -88,46 +88,18 @@ thread.get(), std::make_unique(thread, *this)); } -const DecodedThread *TraceIntelPT::Decode(Thread &thread) { - RefreshLiveProcessState(); - if (m_failed_live_threads_decoder.hasValue()) - return &*m_failed_live_threads_decoder; - - auto it = m_thread_decoders.find(&thread); - if (it == m_thread_decoders.end()) - return nullptr; - return &it->second->Decode(); +llvm::Expected TraceIntelPT::GetThreadTrace(Thread &thread) { + return Decode(thread); } -size_t TraceIntelPT::GetCursorPosition(Thread &thread) { - const DecodedThread *decoded_thread = Decode(thread); - if (!decoded_thread) - return 0; - return decoded_thread->GetCursorPosition(); -} +llvm::Expected TraceIntelPT::Decode(Thread &thread) { + if (llvm::Error err = RefreshLiveProcessState()) + return std::move(err); -void TraceIntelPT::TraverseInstructions( - Thread &thread, size_t position, TraceDirection direction, - std::function load_addr)> - callback) { - const DecodedThread *decoded_thread = Decode(thread); - if (!decoded_thread) - return; - - ArrayRef instructions = decoded_thread->GetInstructions(); - - ssize_t delta = direction == TraceDirection::Forwards ? 1 : -1; - for (ssize_t i = position; i < (ssize_t)instructions.size() && i >= 0; - i += delta) - if (!callback(i, instructions[i].GetLoadAddress())) - break; -} - -Optional TraceIntelPT::GetInstructionCount(Thread &thread) { - if (const DecodedThread *decoded_thread = Decode(thread)) - return decoded_thread->GetInstructions().size(); - else - return None; + auto it = m_thread_decoders.find(&thread); + if (it == m_thread_decoders.end()) + return createStringError(inconvertibleErrorCode(), "thread not traced"); + return it->second->Decode(); } Expected TraceIntelPT::GetCPUInfoForLiveProcess() { @@ -192,26 +164,16 @@ return *m_cpu_info; } -void TraceIntelPT::DoRefreshLiveProcessState( - Expected state) { +Error TraceIntelPT::DoRefreshLiveProcessState( + const TraceGetStateResponse &state) { m_thread_decoders.clear(); - - if (!state) { - m_failed_live_threads_decoder = DecodedThread(state.takeError()); - return; - } - - for (const TraceThreadState &thread_state : state->tracedThreads) { + for (const TraceThreadState &thread_state : state.tracedThreads) { Thread &thread = *m_live_process->GetThreadList().FindThreadByID(thread_state.tid); m_thread_decoders.emplace( &thread, std::make_unique(thread, *this)); } -} - -bool TraceIntelPT::IsTraced(const Thread &thread) { - RefreshLiveProcessState(); - return m_thread_decoders.count(&thread); + return Error::success(); } const char *TraceIntelPT::GetStartConfigurationHelp() { diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp --- a/lldb/source/Target/Trace.cpp +++ b/lldb/source/Target/Trace.cpp @@ -141,8 +141,8 @@ /// If \b true, then the \a InstructionSymbolInfo will have the /// \a disassembler and \a instruction objects calculated. static void TraverseInstructionsWithSymbolInfo( - Trace &trace, Thread &thread, size_t position, - Trace::TraceDirection direction, SymbolContextItem symbol_scope, + ThreadTraceSP thread_trace_sp, Thread &thread, size_t position, + TraceDirection direction, SymbolContextItem symbol_scope, bool include_disassembler, std::function insn)> callback) { @@ -197,8 +197,8 @@ : InstructionSP()); }; - trace.TraverseInstructions( - thread, position, direction, + thread_trace_sp->TraverseInstructions( + position, direction, [&](size_t index, Expected load_address) -> bool { if (!load_address) return callback(index, load_address.takeError()); @@ -301,59 +301,59 @@ void Trace::DumpTraceInstructions(Thread &thread, Stream &s, size_t count, size_t end_position, bool raw) { - Optional instructions_count = GetInstructionCount(thread); - if (!instructions_count) { - s.Printf("thread #%u: tid = %" PRIu64 ", not traced\n", thread.GetIndexID(), - thread.GetID()); - return; + s.Printf("thread #%u: tid = %" PRIu64, thread.GetIndexID(), thread.GetID()); + + if (Expected thread_trace_sp = GetThreadTrace(thread)) { + size_t instructions_count = thread_trace_sp.get()->GetInstructionCount(); + s.Printf(", total instructions = %zu\n", instructions_count); + + if (count == 0 || end_position >= instructions_count) + return; + + int digits_count = GetNumberOfDigits(end_position); + size_t start_position = + end_position + 1 < count ? 0 : end_position + 1 - count; + auto printInstructionIndex = [&](size_t index) { + s.Printf(" [%*zu] ", digits_count, index); + }; + + bool was_prev_instruction_an_error = false; + Optional prev_insn; + + TraverseInstructionsWithSymbolInfo( + *thread_trace_sp, thread, start_position, TraceDirection::Forwards, + eSymbolContextEverything, /*disassembler*/ true, + [&](size_t index, Expected insn) -> bool { + if (!insn) { + printInstructionIndex(index); + s << toString(insn.takeError()); + + prev_insn = None; + was_prev_instruction_an_error = true; + } else { + if (was_prev_instruction_an_error) + s.Printf(" ...missing instructions\n"); + + if (!raw) + DumpInstructionSymbolContext(s, prev_insn, *insn); + + printInstructionIndex(index); + s.Printf("0x%016" PRIx64, insn->load_address); + + if (!raw) + DumpInstructionDisassembly(s, *insn); + + prev_insn = *insn; + was_prev_instruction_an_error = false; + } + + s.Printf("\n"); + return index < end_position; + }); + + } else { + s.Printf(", %s\n", llvm::toString(thread_trace_sp.takeError()).c_str()); } - - s.Printf("thread #%u: tid = %" PRIu64 ", total instructions = %zu\n", - thread.GetIndexID(), thread.GetID(), *instructions_count); - - if (count == 0 || end_position >= *instructions_count) - return; - - int digits_count = GetNumberOfDigits(end_position); - size_t start_position = - end_position + 1 < count ? 0 : end_position + 1 - count; - auto printInstructionIndex = [&](size_t index) { - s.Printf(" [%*zu] ", digits_count, index); - }; - - bool was_prev_instruction_an_error = false; - Optional prev_insn; - - TraverseInstructionsWithSymbolInfo( - *this, thread, start_position, TraceDirection::Forwards, - eSymbolContextEverything, /*disassembler*/ true, - [&](size_t index, Expected insn) -> bool { - if (!insn) { - printInstructionIndex(index); - s << toString(insn.takeError()); - - prev_insn = None; - was_prev_instruction_an_error = true; - } else { - if (was_prev_instruction_an_error) - s.Printf(" ...missing instructions\n"); - - if (!raw) - DumpInstructionSymbolContext(s, prev_insn, *insn); - - printInstructionIndex(index); - s.Printf("0x%016" PRIx64, insn->load_address); - - if (!raw) - DumpInstructionDisassembly(s, *insn); - - prev_insn = *insn; - was_prev_instruction_an_error = false; - } - - s.Printf("\n"); - return index < end_position; - }); } Error Trace::Start(const llvm::json::Value &request) { @@ -439,27 +439,35 @@ return m_live_process->TraceGetBinaryData(request); } -void Trace::RefreshLiveProcessState() { +Error Trace::RefreshLiveProcessState() { if (!m_live_process) - return; + return Error::success(); uint32_t new_stop_id = m_live_process->GetStopID(); - if (new_stop_id == m_stop_id) - return; + if (new_stop_id == m_stop_id) { + if (m_live_process_refresh_error) + return createStringError(inconvertibleErrorCode(), + *m_live_process_refresh_error); + else + return Error::success(); + } m_stop_id = new_stop_id; m_live_thread_data.clear(); + m_live_process_refresh_error = None; Expected json_string = GetLiveProcessState(); if (!json_string) { - DoRefreshLiveProcessState(json_string.takeError()); - return; + m_live_process_refresh_error = toString(json_string.takeError()); + return createStringError(inconvertibleErrorCode(), + *m_live_process_refresh_error); } Expected live_process_state = json::parse(*json_string, "TraceGetStateResponse"); if (!live_process_state) { - DoRefreshLiveProcessState(live_process_state.takeError()); - return; + m_live_process_refresh_error = toString(live_process_state.takeError()); + return createStringError(inconvertibleErrorCode(), + *m_live_process_refresh_error); } for (const TraceThreadState &thread_state : @@ -471,5 +479,5 @@ for (const TraceBinaryData &item : live_process_state->processBinaryData) m_live_process_data[item.kind] = item.size; - DoRefreshLiveProcessState(std::move(live_process_state)); + return DoRefreshLiveProcessState(*live_process_state); } diff --git a/lldb/test/API/commands/trace/TestTraceDumpInstructions.py b/lldb/test/API/commands/trace/TestTraceDumpInstructions.py --- a/lldb/test/API/commands/trace/TestTraceDumpInstructions.py +++ b/lldb/test/API/commands/trace/TestTraceDumpInstructions.py @@ -160,8 +160,7 @@ self.expect("trace load " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace_wrong_cpu.json")) self.expect("thread trace dump instructions", - substrs=['''thread #1: tid = 3842849, total instructions = 1 - [0] error: unknown cpu''']) + substrs=['''thread #1: tid = 3842849, error: unknown cpu''']) def testMultiFileTraceWithMissingModule(self): self.expect("trace load " +