wallace created this revision. wallace added reviewers: clayborg, vsk. wallace requested review of this revision. Herald added a project: LLDB. Herald added a subscriber: lldb-commits.
As a follow up of D103588 <https://reviews.llvm.org/D103588>, I'm reinitiating the discussion with a new proposal for traversing instructions in a trace which uses the feedback gotten in that diff. See the embedded documentation in TraceCursor for more information. The idea is to offer an OOP way to traverse instructions exposing a minimal interface that makes no assumptions on: - the number of instructions in the trace (i.e. having indices for instructions might be impractical for gigantic intel-pt traces, as it would require to decode the entire trace). This renders the use of indices to point to instructions impractical. Traces are big and expensive, and the consumer should try to do look linear lookups (forwards and/or backwards) and avoid random accesses (the API could be extended though, but for now I want to dicard that funcionality and leave the API extensible if needed). - the way the instructions are represented internally by each Trace plug-in. They could be mmap'ed from a file, exist in plain vector or generated on the fly as the user requests the data. - the actual data structure used internally for each plug-in. Ideas like having a struct TraceInstruction have been discarded because that would make the plug-in follow a certain data type, which might be costly. Instead, the user can ask the cursor for each independent property of the instruction it's pointing at. The way to get a cursor is to ask Trace.h for the end or being cursor or a thread's trace. There are some benefits of this approach: - there's little cost to create a cursor, and this allows for lazily decoding a trace as the user requests data. - each trace plug-in could decide how to cache the instructions it generates. For example, if a trace is small, it might decide to keep everything in memory, or if the trace is massive, it might decide to keep around the last thousands of instructions to speed up local searches. - a cursor can outlive a stop point, which makes trace comparison for live processes feasible. An application of this is to compare profiling data of two runs of the same function, which should be doable with intel pt. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D104422 Files: lldb/include/lldb/Target/Trace.h lldb/include/lldb/Target/TraceCursor.h lldb/include/lldb/lldb-enumerations.h lldb/include/lldb/lldb-forward.h
Index: lldb/include/lldb/lldb-forward.h =================================================================== --- lldb/include/lldb/lldb-forward.h +++ 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<lldb_private::ThreadPlan> ThreadPlanWP; typedef std::shared_ptr<lldb_private::ThreadPlanTracer> ThreadPlanTracerSP; typedef std::shared_ptr<lldb_private::Trace> TraceSP; +typedef std::shared_ptr<lldb_private::TraceCursor> TraceCursorSP; typedef std::shared_ptr<lldb_private::Type> TypeSP; typedef std::weak_ptr<lldb_private::Type> TypeWP; typedef std::shared_ptr<lldb_private::TypeCategoryImpl> TypeCategoryImplSP; Index: lldb/include/lldb/lldb-enumerations.h =================================================================== --- lldb/include/lldb/lldb-enumerations.h +++ lldb/include/lldb/lldb-enumerations.h @@ -958,6 +958,22 @@ eExpressionEvaluationComplete }; +/// Architecture-agnostic categorization of instructions. Useful for doing +/// analysis on traces. +FLAGS_ENUM(TraceInstructionType){ + /// Any instruction not listed below. + eTraceInstructionTypeNormal = (1u << 1), + /// This includes both conditional and unconditional jumps. + eTraceInstructionTypeJump = (1u << 2), + /// This represents a call to a function. + eTraceInstructionTypeCall = (1u << 3), + /// This represents a return from a function. + eTraceInstructionTypeReturn = (1u << 4), + /// Helper mask to include anything above. + eTraceInstructionTypeAny = (0xffffffffu)}; + +LLDB_MARK_AS_BITMASK_ENUM(TraceInstructionType) + /// Watchpoint Kind. /// /// Indicates what types of events cause the watchpoint to fire. Used by Native Index: lldb/include/lldb/Target/TraceCursor.h =================================================================== --- /dev/null +++ lldb/include/lldb/Target/TraceCursor.h @@ -0,0 +1,102 @@ +//===-- 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::IsError() 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. An empty trace can't have a cursor. +/// +class TraceCursor { +public: + /// Move the cursor to the next instruction in the trace given the provided + /// filter rules. 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] filter + /// Bitmask filter. The cursor stops at the next + /// instruction whose type is included in the filter. + /// + /// \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. + bool Next(lldb::TraceInstructionType filter = lldb::eTraceInstructionTypeAny, + bool ignore_errors = false); + + /// Similar to \a TraceCursor::Next(), but moves backwards chronologically. + bool Prev(); + + /// \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 + /// \{ + + /// \return + /// \b true if the cursor is pointing to an error in the trace. The actual + /// error message can be gotten by calling \a TraceCursor::GetError(). + bool IsError(); + + /// \return + /// \b nullptr if the cursor is not pointing to an error in the trace. + /// Otherwise return an error message describing the issue. + const char *GetError(); + + /// \return + /// The load address of the instruction. If the cursor points to an error + /// in the trace, return \b LLDB_INVALID_ADDRESS. + lldb::addr_t GetLoadAddress(); + + /// \return + /// The size in bytes of the instruction. If the cursor points to an error + /// in the trace, return \b 0. + size_t GetInstructionSize(); + + /// \return + /// The type of the instruction. If the cursor points to an error in the + /// trace, the return value is undefined. + lldb::TraceInstructionType GetInstructionType(); + + /// \} +}; + +} // namespace lldb_private + +#endif // LLDB_TARGET_TRACE_H Index: lldb/include/lldb/Target/Trace.h =================================================================== --- lldb/include/lldb/Target/Trace.h +++ 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,34 @@ std::function<bool(size_t index, llvm::Expected<lldb::addr_t> load_addr)> callback) = 0; + /// Get a cursor pointing to the most recent instruction of a given thread's + /// trace that passes the provided filter rules. + /// + /// \param[in] filter + /// Bitmask filter used to define which instruction type the cursor will + /// start pointing to. + /// + /// \param[in] ignore_errors + /// If \b false and there are errors in the trace more recent than the + /// requested instruction, the cursor will point at the most recent error + /// instead. + /// + /// \return + /// A \a TraceCursorSP if the cursor points to a valid instruction or an + /// error. Otherwise, if the thread is not traced or its trace information + /// failed to load, an \a llvm::Error is returned. + virtual llvm::Expected<lldb::TraceCursorSP> GetThreadEndCursor( + Thread &thread, + lldb::TraceInstructionType filter = lldb::eTraceInstructionTypeAny, + bool ignore_errors = false) = 0; + + /// Similar to \a Trace::GetThreadEndCursor() but it gets the oldest + /// instruction instead. + virtual llvm::Expected<lldb::TraceCursorSP> GetThreadBeginCursor( + Thread &thread, + lldb::TraceInstructionType filter = lldb::eTraceInstructionTypeAny, + bool ignore_errors = false) = 0; + /// Get the number of available instructions in the trace of the given thread. /// /// \param[in] thread
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits