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); + /// Get the number of available instructions in the trace of the given thread. /// /// \param[in] thread 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,137 @@ +//===-- 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 a dummy invalid instruction error signaling +/// the end of a trace, similar to a C++ collections' end iterator. +/// +/// Sample usage: +/// +/// TraceCursorUP cursor = trace.GetTrace(thread); +/// +/// while(cursor->Prev(eTraceCursorInstructionTypeCall | +/// eTraceCursorInstructionTypeReturn)) { +/// if (llvm::Error error = cursor->GetError()) +/// cout << llvm::toString(error) << endl; +/// else if (cursor->GetInstructionType() & eTraceCursorInstructionTypeCall) +/// std::cout << "call found at " << cursor->GetLoadAddress() << +/// std::endl; +/// else +/// std::cout << "return found at " << cursor->GetLoadAddress() << +/// std::endl; +/// } +class TraceCursor { +public: + virtual ~TraceCursor() = default; + + /// Move the cursor to the next instruction in the trace given the provided + /// granularity. If such instruction is not found, the cursor doesn't move. + /// + /// The instructions are traversed chronologically, and a "next" instruction + /// is newer than the current one. + /// + /// \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::TraceCursorInstructionType granularity = + lldb::eTraceCursorInstructionTypeInstruction, + bool ignore_errors = false) = 0; + + /// Similar to \a TraceCursor::Next(), but moves backwards chronologically. + virtual bool Prev(lldb::TraceCursorInstructionType granularity = + lldb::eTraceCursorInstructionTypeInstruction, + bool ignore_errors = false) = 0; + + /// Force the cursor to point to the end of the trace. + virtual void ResetToEnd() = 0; + + /// Force the cursor to point to the first (i.e. oldest) item of the trace. + virtual void ResetToBegin() = 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() { return m_stop_id != m_trace_sp->GetStopID(); } + + /// 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 size in bytes of the instruction opcode the cursor is pointing at. + /// If the cursor points to an error in the trace, return \b 0. + virtual size_t GetInstructionSize() = 0; + + /// \return + /// The \a lldb::TraceCursorInstructionType categories the instruction the + /// cursor is pointing at falls into. If the cursor points to an error in + /// the trace, the return value is undefined. + virtual lldb::TraceCursorInstructionType GetInstructionType() = 0; + + /// \} + +private: + /// The stop ID when the cursor was created. + ssize_t m_stop_id = -1; + /// 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(TraceCursorInstructionType){ + /// Any instruction. + eTraceCursorInstructionTypeInstruction = (1u << 1), + /// A conditional or unconditional branch/jump. + eTraceCursorInstructionTypeBranch = (1u << 2), + /// A conditional or unconditional branch/jump that changed + /// the control flow of the program. + eTraceCursorInstructionTypeTakenBranch = (1u << 3), + /// A next call to a function. + eTraceCursorInstructionTypeCall = (1u << 4), + /// A return from a function. + eTraceCursorInstructionTypeReturn = (1u << 5)} + +LLDB_MARK_AS_BITMASK_ENUM(TraceCursorInstructionType) + /// 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;