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/TraceCursor.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/TraceGDBRemotePackets.h" #include "lldb/Utility/UnimplementedError.h" @@ -204,6 +205,14 @@ std::function load_addr)> callback) = 0; + /// Get a \a TraceCursor for the given thread's trace. + /// + /// \return + /// A \a TraceCursorUP. If the thread is not traced or its trace + /// information failed to load, the corresponding error is embedded in the + /// trace. + virtual lldb::TraceCursorUP GetCursor(Thread &thread) = 0; + /// Get the number of available instructions in the trace of the given thread. /// /// \param[in] thread @@ -279,6 +288,11 @@ /// Get the trace file of the given post mortem thread. llvm::Expected GetPostMortemTraceFile(lldb::tid_t tid); + /// \return + /// The stop ID of the live process being traced, or \b 0 if this is a + /// post-mortem trace session. + uint32_t GetStopID(); + protected: /// Get binary data of a live thread given a data identifier. /// @@ -350,8 +364,8 @@ /// The result is cached through the same process stop. void RefreshLiveProcessState(); + uint32_t m_stop_id = 0; /// Process traced by this object if doing live tracing. Otherwise it's null. - int64_t m_stop_id = -1; Process *m_live_process = nullptr; /// tid -> data kind -> size std::map> diff --git a/lldb/include/lldb/Target/TraceCursor.h b/lldb/include/lldb/Target/TraceCursor.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Target/TraceCursor.h @@ -0,0 +1,136 @@ +//===-- TraceCursor.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_TRACE_CURSOR_H +#define LLDB_TARGET_TRACE_CURSOR_H + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +/// Class used for iterating over the instructions of a thread's trace. +/// +/// 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 an flexible way that is efficient for the given +/// technology. +/// +/// Live processes: +/// In the case of a live process trace, an instance of a \a TraceCursor should +/// point to the trace at the moment it was collected. If the process is later +/// resumed and new trace data is collected, that should leave that old cursor +/// unaffected. +/// +/// Errors in the trace: +/// As there could be errors when reconstructing the instructions of a trace, +/// these errors are represented as failed instructions, and the cursor can +/// point at them. The consumer should invoke \a TraceCursor::GetError() to +/// check if the cursor is pointing to either a valid instruction or an error. +/// +/// Instructions: +/// A \a TraceCursor always points to a specific instruction or error in the +/// trace. +/// +/// The Trace initially points to the last item in the trace. +/// +/// Sample usage: +/// +/// TraceCursorUP cursor = trace.GetTrace(thread); +/// +/// auto granularity = eTraceInstructionControlFlowTypeCall | +/// eTraceInstructionControlFlowTypeReturn; +/// +/// do { +/// if (llvm::Error error = cursor->GetError()) +/// cout << "error found at: " << llvm::toString(error) << endl; +/// else if (cursor->GetInstructionControlFlowType() & +/// eTraceInstructionControlFlowTypeCall) +/// std::cout << "call found at " << cursor->GetLoadAddress() << +/// std::endl; +/// else if (cursor->GetInstructionControlFlowType() & +/// eTraceInstructionControlFlowTypeReturn) +/// std::cout << "return found at " << cursor->GetLoadAddress() << +/// std::endl; +/// } while(cursor->Prev(granularity)); +class TraceCursor { +public: + virtual ~TraceCursor() = default; + + /// Move the cursor to the next instruction more recent chronologically in the + /// trace given the provided granularity. If such instruction is not found, + /// the cursor doesn't move. + /// + /// \param[in] granularity + /// Bitmask granularity filter. The cursor stops at the next + /// instruction that matches the specified granularity. + /// + /// \param[in] ignore_errors + /// If \b false, the cursor stops as soon as it finds a failure in the + /// trace and points at it. + /// + /// \return + /// \b true if the cursor moved, \b false otherwise. + virtual bool Next(lldb::TraceInstructionControlFlowType granularity = + lldb::eTraceInstructionControlFlowTypeInstruction, + bool ignore_errors = false) = 0; + + /// Similar to \a TraceCursor::Next(), but moves backwards chronologically. + virtual bool Prev(lldb::TraceInstructionControlFlowType granularity = + lldb::eTraceInstructionControlFlowTypeInstruction, + bool ignore_errors = false) = 0; + + /// Force the cursor to point to the end of the trace, i.e. the most recent + /// item. + virtual void SeekToEnd() = 0; + + /// Force the cursor to point to the beginning of the trace, i.e. the oldest + /// item. + virtual void SeekToBegin() = 0; + + /// \return + /// \b true if the trace corresponds to a live process who has resumed after + /// the trace cursor was created. Otherwise, including the case in which the + /// process is a post-mortem one, return \b false. + bool IsStale(); + + /// Instruction or error information + /// \{ + + /// Get the corresponding error message if the cursor points to an error in + /// the trace. + /// + /// \return + /// \b llvm::Error::success if the cursor is not pointing to an error in + /// the trace. Otherwise return an \a llvm::Error describing the issue. + virtual llvm::Error GetError() = 0; + + /// \return + /// The load address of the instruction the cursor is pointing at. If the + /// cursor points to an error in the trace, return \b + /// LLDB_INVALID_ADDRESS. + virtual lldb::addr_t GetLoadAddress() = 0; + + /// \return + /// The \a lldb::TraceInstructionControlFlowType categories the + /// instruction the cursor is pointing at falls into. If the cursor points + /// to an error in the trace, return \b 0. + virtual lldb::TraceInstructionControlFlowType + GetInstructionControlFlowType() = 0; + + /// \} + +private: + /// The stop ID when the cursor was created. + uint32_t m_stop_id = 0; + /// The trace that owns this cursor. + lldb::TraceSP m_trace_sp; +}; + +} // namespace lldb_private + +#endif // LLDB_TARGET_TRACE_CURSOR_H 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 @@ -958,6 +958,25 @@ eExpressionEvaluationComplete }; +/// Architecture-agnostic categorization of instructions for traversing the +/// control flow of a trace. +/// +/// A single instruction can match one or more of these categories. +FLAGS_ENUM(TraceInstructionControlFlowType){ + /// Any instruction. + eTraceInstructionControlFlowTypeInstruction = (1u << 1), + /// A conditional or unconditional branch/jump. + eTraceInstructionControlFlowTypeBranch = (1u << 2), + /// A conditional or unconditional branch/jump that changed + /// the control flow of the program. + eTraceInstructionControlFlowTypeTakenBranch = (1u << 3), + /// A call to a function. + eTraceInstructionControlFlowTypeCall = (1u << 4), + /// A return from a function. + eTraceInstructionControlFlowTypeReturn = (1u << 5)}; + +LLDB_MARK_AS_BITMASK_ENUM(TraceInstructionControlFlowType) + /// Watchpoint Kind. /// /// Indicates what types of events cause the watchpoint to fire. Used by Native 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 @@ -229,6 +229,7 @@ class ThreadSpec; class ThreadPostMortemTrace; class Trace; +class TraceCursor; class TraceSessionFileParser; class Type; class TypeAndOrName; @@ -441,6 +442,7 @@ typedef std::weak_ptr ThreadPlanWP; typedef std::shared_ptr ThreadPlanTracerSP; typedef std::shared_ptr TraceSP; +typedef std::unique_ptr TraceCursorUP; typedef std::shared_ptr TypeSP; typedef std::weak_ptr TypeWP; typedef std::shared_ptr TypeCategoryImplSP; 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 @@ -68,6 +68,7 @@ ThreadSpec.cpp ThreadPostMortemTrace.cpp Trace.cpp + TraceCursor.cpp TraceSessionFileParser.cpp UnixSignals.cpp UnwindAssembly.cpp 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 @@ -473,3 +473,8 @@ DoRefreshLiveProcessState(std::move(live_process_state)); } + +uint32_t Trace::GetStopID() { + RefreshLiveProcessState(); + return m_stop_id; +} diff --git a/lldb/source/Target/TraceCursor.cpp b/lldb/source/Target/TraceCursor.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Target/TraceCursor.cpp @@ -0,0 +1,15 @@ +//===-- TraceCursor.cpp -----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/TraceCursor.h" + +#include "lldb/Target/Trace.h" + +using namespace lldb_private; + +bool TraceCursor::IsStale() { return m_stop_id != m_trace_sp->GetStopID(); }