[Lldb-commits] [lldb] b0aa707 - [trace][intel pt] Implement the Intel PT cursor

Walter Erquinigo via lldb-commits lldb-commits at lists.llvm.org
Fri Jul 16 16:47:53 PDT 2021


Author: Walter Erquinigo
Date: 2021-07-16T16:47:43-07:00
New Revision: b0aa70761b8324d0c9ebc57da58a44c9e266ce0e

URL: https://github.com/llvm/llvm-project/commit/b0aa70761b8324d0c9ebc57da58a44c9e266ce0e
DIFF: https://github.com/llvm/llvm-project/commit/b0aa70761b8324d0c9ebc57da58a44c9e266ce0e.diff

LOG: [trace][intel pt] Implement the Intel PT cursor

D104422 added the interface for TraceCursor, which is the main way to traverse instructions in a trace. This diff implements the corresponding cursor class for Intel PT and deletes the now obsolete code.

Besides that, the logic for the "thread trace dump instructions" was adapted to use this cursor (pretty much I ended up moving code from Trace.cpp to TraceCursor.cpp). The command by default traverses the instructions backwards, and if the user passes --forwards, then it's not forwards. More information about that is in the Options.td file.

Regarding the Intel PT cursor. All Intel PT cursors for the same thread share the same DecodedThread instance. I'm not yet implementing lazy decoding because we don't need it. That'll be for later. For the time being, the entire thread trace is decoded when the first cursor for that thread is requested.

Differential Revision: https://reviews.llvm.org/D105531

Added: 
    lldb/include/lldb/Target/TraceInstructionDumper.h
    lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
    lldb/source/Target/TraceInstructionDumper.cpp

Modified: 
    lldb/include/lldb/Target/Trace.h
    lldb/include/lldb/Target/TraceCursor.h
    lldb/source/Commands/CommandObjectThread.cpp
    lldb/source/Commands/Options.td
    lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
    lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
    lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
    lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
    lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
    lldb/source/Target/CMakeLists.txt
    lldb/source/Target/Trace.cpp
    lldb/source/Target/TraceCursor.cpp
    lldb/test/API/commands/trace/TestTraceDumpInstructions.py
    lldb/test/API/commands/trace/TestTraceStartStop.py
    lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h
index cc84270b80081..c82d17b029557 100644
--- a/lldb/include/lldb/Target/Trace.h
+++ b/lldb/include/lldb/Target/Trace.h
@@ -45,11 +45,6 @@ namespace lldb_private {
 class Trace : public PluginInterface,
               public std::enable_shared_from_this<Trace> {
 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
@@ -137,62 +132,6 @@ class Trace : public PluginInterface,
   ///     The JSON schema of this Trace plug-in.
   virtual llvm::StringRef GetSchema() = 0;
 
-  /// Dump \a count instructions of the given thread's trace ending at the
-  /// given \a end_position position.
-  ///
-  /// The instructions are printed along with their indices or positions, which
-  /// are increasing chronologically. This means that the \a index 0 represents
-  /// the oldest instruction of the trace chronologically.
-  ///
-  /// \param[in] thread
-  ///     The thread whose trace will be dumped.
-  ///
-  /// \param[in] s
-  ///     The stream object where the instructions are printed.
-  ///
-  /// \param[in] count
-  ///     The number of instructions to print.
-  ///
-  /// \param[in] end_position
-  ///     The position of the last instruction to print.
-  ///
-  /// \param[in] raw
-  ///     Dump only instruction addresses without disassembly nor symbol
-  ///     information.
-  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<bool(size_t index, llvm::Expected<lldb::addr_t> load_addr)>
-          callback) = 0;
-
   /// Get a \a TraceCursor for the given thread's trace.
   ///
   /// \return
@@ -201,16 +140,6 @@ class Trace : public PluginInterface,
   ///     trace.
   virtual lldb::TraceCursorUP GetCursor(Thread &thread) = 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<size_t> GetInstructionCount(Thread &thread) = 0;
-
   /// Check if a thread is currently traced by this object.
   ///
   /// \param[in] thread

diff  --git a/lldb/include/lldb/Target/TraceCursor.h b/lldb/include/lldb/Target/TraceCursor.h
index c27bba3abf4cb..e15ced82a470f 100644
--- a/lldb/include/lldb/Target/TraceCursor.h
+++ b/lldb/include/lldb/Target/TraceCursor.h
@@ -11,6 +11,8 @@
 
 #include "lldb/lldb-private.h"
 
+#include "lldb/Target/ExecutionContext.h"
+
 namespace lldb_private {
 
 /// Class used for iterating over the instructions of a thread's trace.
@@ -36,74 +38,134 @@ namespace lldb_private {
 ///  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.
+/// Defaults:
+///   By default, the cursor points at the end item of the trace, moves
+///   backwards, has a move granularity of \a
+///   eTraceInstructionControlFlowTypeInstruction (i.e. visit every instruction)
+///   and stops at every error (the "ignore errors" flag is \b false). See the
+///   \a TraceCursor::Next() method for more documentation.
 ///
 /// Sample usage:
 ///
 ///  TraceCursorUP cursor = trace.GetTrace(thread);
 ///
-///  auto granularity = eTraceInstructionControlFlowTypeCall |
-///  eTraceInstructionControlFlowTypeReturn;
+///  cursor->SetGranularity(eTraceInstructionControlFlowTypeCall |
+///    eTraceInstructionControlFlowTypeReturn);
 ///
 ///  do {
 ///     if (llvm::Error error = cursor->GetError())
 ///       cout << "error found at: " << llvm::toString(error) << endl;
 ///     else if (cursor->GetInstructionControlFlowType() &
-///     eTraceInstructionControlFlowTypeCall)
+///         eTraceInstructionControlFlowTypeCall)
 ///       std::cout << "call found at " << cursor->GetLoadAddress() <<
 ///       std::endl;
 ///     else if (cursor->GetInstructionControlFlowType() &
-///     eTraceInstructionControlFlowTypeReturn)
+///         eTraceInstructionControlFlowTypeReturn)
 ///       std::cout << "return found at " << cursor->GetLoadAddress() <<
 ///       std::endl;
-///  } while(cursor->Prev(granularity));
+///  } while(cursor->Next());
+///
+/// Low level traversal:
+///   Unlike the \a TraceCursor::Next() API, which uses a given granularity and
+///   direction to advance the cursor, the \a TraceCursor::Seek() method can be
+///   used to reposition the cursor to an offset of the end, beginning, or
+///   current position of the trace.
 class TraceCursor {
 public:
+  /// Helper enum to indicate the reference point when invoking
+  /// \a TraceCursor::Seek().
+  enum class SeekType {
+    /// The beginning of the trace, i.e the oldest item.
+    Set = 0,
+    /// The current position in the trace.
+    Current,
+    /// The end of the trace, i.e the most recent item.
+    End
+  };
+
+  /// Create a cursor that initially points to the end of the trace, i.e. the
+  /// most recent item.
+  TraceCursor(lldb::ThreadSP thread_sp);
+
   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.
+  /// Set the granularity to use in the \a TraceCursor::Next() method.
+  void SetGranularity(lldb::TraceInstructionControlFlowType granularity);
+
+  /// Set the "ignore errors" flag to use in the \a TraceCursor::Next() method.
+  void SetIgnoreErrors(bool ignore_errors);
+
+  /// Set the direction to use in the \a TraceCursor::Next() method.
+  ///
+  /// \param[in] forwards
+  ///     If \b true, then the traversal will be forwards, otherwise backwards.
+  void SetForwards(bool forwards);
+
+  /// Check if the direction to use in the \a TraceCursor::Next() method is
+  /// forwards.
+  ///
+  /// \return
+  ///     \b true if the current direction is forwards, \b false if backwards.
+  bool IsForwards() const;
+
+  /// Move the cursor to the next instruction that matches the current
+  /// granularity.
+  ///
+  /// Direction:
+  ///     The traversal is done following the current direction of the trace. If
+  ///     it is forwards, the instructions are visited forwards
+  ///     chronologically. Otherwise, the traversal is done in
+  ///     the opposite direction. By default, a cursor moves backwards unless
+  ///     changed with \a TraceCursor::SetForwards().
+  ///
+  /// Granularity:
+  ///     The cursor will traverse the trace looking for the first instruction
+  ///     that matches the current granularity. If there aren't any matching
+  ///     instructions, the cursor won't move, to give the opportunity of
+  ///     changing granularities.
+  ///
+  /// Ignore errors:
+  ///     If the "ignore errors" flags is \b false, the traversal will stop as
+  ///     soon as it finds an error in the trace and the cursor will point at
+  ///     it.
+  ///
+  /// \return
+  ///     \b true if the cursor effectively moved, \b false otherwise.
+  virtual bool Next() = 0;
+
+  /// Make the cursor point to an item in the trace based on an origin point and
+  /// an offset. This API doesn't distinguishes instruction types nor errors in
+  /// the trace, unlike the \a TraceCursor::Next() method.
+  ///
+  /// The resulting position of the trace is
+  ///     origin + offset
   ///
-  /// \param[in] granularity
-  ///     Bitmask granularity filter. The cursor stops at the next
-  ///     instruction that matches the specified granularity.
+  /// If this resulting position would be out of bounds, it will be adjusted to
+  /// the last or first item in the trace correspondingly.
   ///
-  /// \param[in] ignore_errors
-  ///     If \b false, the cursor stops as soon as it finds a failure in the
-  ///     trace and points at it.
+  /// \param[in] offset
+  ///     How many items to move forwards (if positive) or backwards (if
+  ///     negative) from the given origin point.
+  ///
+  /// \param[in] origin
+  ///     The reference point to use when moving the cursor.
   ///
   /// \return
-  ///     \b true if the cursor effectively moved and now points to a 
diff erent
-  ///     item in the trace, including errors when \b ignore_errors is \b false.
-  ///     In other words, if \b false is returned, then the trace is pointing at
-  ///     the same item in the trace as before.
-  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;
+  ///     The number of trace items moved from the origin.
+  virtual size_t Seek(ssize_t offset, SeekType origin) = 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();
+  ///   The \a ExecutionContextRef of the backing thread from the creation time
+  ///   of this cursor.
+  ExecutionContextRef &GetExecutionContextRef();
 
   /// Instruction or error information
   /// \{
 
+  /// \return
+  ///     Whether the cursor points to an error or not.
+  virtual bool IsError() = 0;
+
   /// Get the corresponding error message if the cursor points to an error in
   /// the trace.
   ///
@@ -124,14 +186,15 @@ class TraceCursor {
   ///     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;
+protected:
+  ExecutionContextRef m_exe_ctx_ref;
+
+  lldb::TraceInstructionControlFlowType m_granularity =
+      lldb::eTraceInstructionControlFlowTypeInstruction;
+  bool m_ignore_errors = false;
+  bool m_forwards = false;
 };
 
 } // namespace lldb_private

diff  --git a/lldb/include/lldb/Target/TraceInstructionDumper.h b/lldb/include/lldb/Target/TraceInstructionDumper.h
new file mode 100644
index 0000000000000..388e5063d1725
--- /dev/null
+++ b/lldb/include/lldb/Target/TraceInstructionDumper.h
@@ -0,0 +1,72 @@
+//===-- TraceInstructionDumper.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/TraceCursor.h"
+
+#ifndef LLDB_TARGET_TRACE_INSTRUCTION_DUMPER_H
+#define LLDB_TARGET_TRACE_INSTRUCTION_DUMPER_H
+
+namespace lldb_private {
+
+/// Class used to dump the instructions of a \a TraceCursor using its current
+/// state and granularity.
+class TraceInstructionDumper {
+public:
+  /// Create a instruction dumper for the cursor.
+  ///
+  /// \param[in] cursor
+  ///     The cursor whose instructions will be dumped.
+  ///
+  /// \param[in] initial_index
+  ///     Presentation index to use for referring to the current instruction
+  ///     of the cursor. If the direction is forwards, the index will increase,
+  ///     and if the direction is backwards, the index will decrease.
+  ///
+  /// \param[in] raw
+  ///     Dump only instruction addresses without disassembly nor symbol
+  ///     information.
+  TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up, int initial_index = 0,
+                         bool raw = false);
+
+  /// Dump \a count instructions of the thread trace starting at the current
+  /// cursor position.
+  ///
+  /// This effectively moves the cursor to the next unvisited position, so that
+  /// a subsequent call to this method continues where it left off.
+  ///
+  /// \param[in] s
+  ///     The stream object where the instructions are printed.
+  ///
+  /// \param[in] count
+  ///     The number of instructions to print.
+  void DumpInstructions(Stream &s, size_t count);
+
+  /// Indicate the dumper that no more data is available in the trace.
+  void SetNoMoreData();
+
+  /// \return
+  ///     \b true if there's still more data to traverse in the trace.
+  bool HasMoreData();
+
+private:
+  /// Move the cursor one step.
+  ///
+  /// \return
+  ///     \b true if the cursor moved.
+  bool TryMoveOneStep();
+
+  lldb::TraceCursorUP m_cursor_up;
+  int m_index;
+  bool m_raw;
+  /// If \b true, all the instructions have been traversed.
+  bool m_no_more_data = false;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_TRACE_INSTRUCTION_DUMPER_H

diff  --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index 292f9c6a0a74e..e4cf8a411b22b 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -8,6 +8,7 @@
 
 #include "CommandObjectThread.h"
 
+#include <memory>
 #include <sstream>
 
 #include "CommandObjectThreadUtil.h"
@@ -32,6 +33,7 @@
 #include "lldb/Target/ThreadPlan.h"
 #include "lldb/Target/ThreadPlanStepInRange.h"
 #include "lldb/Target/Trace.h"
+#include "lldb/Target/TraceInstructionDumper.h"
 #include "lldb/Utility/State.h"
 
 using namespace lldb;
@@ -2004,21 +2006,24 @@ class CommandObjectTraceDumpInstructions
           m_count = count;
         break;
       }
-      case 'p': {
-        int32_t position;
-        if (option_arg.empty() || option_arg.getAsInteger(0, position) ||
-            position < 0)
+      case 's': {
+        int32_t skip;
+        if (option_arg.empty() || option_arg.getAsInteger(0, skip) || skip < 0)
           error.SetErrorStringWithFormat(
               "invalid integer value for option '%s'",
               option_arg.str().c_str());
         else
-          m_position = position;
+          m_skip = skip;
         break;
       }
       case 'r': {
         m_raw = true;
         break;
       }
+      case 'f': {
+        m_forwards = true;
+        break;
+      }
       default:
         llvm_unreachable("Unimplemented option");
       }
@@ -2027,8 +2032,9 @@ class CommandObjectTraceDumpInstructions
 
     void OptionParsingStarting(ExecutionContext *execution_context) override {
       m_count = kDefaultCount;
-      m_position = llvm::None;
+      m_skip = 0;
       m_raw = false;
+      m_forwards = false;
     }
 
     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -2039,14 +2045,15 @@ class CommandObjectTraceDumpInstructions
 
     // Instance variables to hold the values for command options.
     size_t m_count;
-    llvm::Optional<ssize_t> m_position;
+    size_t m_skip;
     bool m_raw;
+    bool m_forwards;
   };
 
   CommandObjectTraceDumpInstructions(CommandInterpreter &interpreter)
       : CommandObjectIterateOverThreads(
             interpreter, "thread trace dump instructions",
-            "Dump the traced instructions for one or more threads.  If no "
+            "Dump the traced instructions for one or more threads. If no "
             "threads are specified, show the current thread.  Use the "
             "thread-index \"all\" to see all threads.",
             nullptr,
@@ -2063,14 +2070,14 @@ class CommandObjectTraceDumpInstructions
                                uint32_t index) override {
     current_command_args.GetCommandString(m_repeat_command);
     m_create_repeat_command_just_invoked = true;
-    m_consecutive_repetitions = 0;
     return m_repeat_command.c_str();
   }
 
 protected:
   bool DoExecute(Args &args, CommandReturnObject &result) override {
-    if (IsRepeatCommand())
-      m_consecutive_repetitions++;
+    if (!IsRepeatCommand())
+      m_dumpers.clear();
+
     bool status = CommandObjectIterateOverThreads::DoExecute(args, result);
 
     m_create_repeat_command_just_invoked = false;
@@ -2082,24 +2089,37 @@ class CommandObjectTraceDumpInstructions
   }
 
   bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+    Stream &s = result.GetOutputStream();
+
     const TraceSP &trace_sp = m_exe_ctx.GetTargetSP()->GetTrace();
     ThreadSP thread_sp =
         m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
 
-    if (llvm::Optional<size_t> insn_count =
-            trace_sp->GetInstructionCount(*thread_sp)) {
-      size_t count = m_options.m_count;
-      ssize_t position =
-          m_options.m_position.getValueOr((ssize_t)*insn_count - 1) -
-          m_consecutive_repetitions * count;
-      if (position < 0)
-        result.AppendError("error: no more data");
-      else
-        trace_sp->DumpTraceInstructions(*thread_sp, result.GetOutputStream(),
-                                        count, position, m_options.m_raw);
-    } else {
-      result.AppendError("error: not traced");
+    if (!m_dumpers.count(thread_sp->GetID())) {
+      lldb::TraceCursorUP cursor_up = trace_sp->GetCursor(*thread_sp);
+      // Set up the cursor and return the presentation index of the first
+      // instruction to dump after skipping instructions.
+      auto setUpCursor = [&]() {
+        cursor_up->SetForwards(m_options.m_forwards);
+        if (m_options.m_forwards)
+          return cursor_up->Seek(m_options.m_skip, TraceCursor::SeekType::Set);
+        return -cursor_up->Seek(-m_options.m_skip, TraceCursor::SeekType::End);
+      };
+
+      int initial_index = setUpCursor();
+
+      auto dumper = std::make_unique<TraceInstructionDumper>(
+          std::move(cursor_up), initial_index, m_options.m_raw);
+
+      // This happens when the seek value was more than the number of available
+      // instructions.
+      if (std::abs(initial_index) < (int)m_options.m_skip)
+        dumper->SetNoMoreData();
+
+      m_dumpers[thread_sp->GetID()] = std::move(dumper);
     }
+
+    m_dumpers[thread_sp->GetID()]->DumpInstructions(s, m_options.m_count);
     return true;
   }
 
@@ -2108,7 +2128,7 @@ class CommandObjectTraceDumpInstructions
   // Repeat command helpers
   std::string m_repeat_command;
   bool m_create_repeat_command_just_invoked;
-  size_t m_consecutive_repetitions = 0;
+  std::map<lldb::tid_t, std::unique_ptr<TraceInstructionDumper>> m_dumpers;
 };
 
 // CommandObjectMultiwordTraceDump

diff  --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 57208c5b30450..36b5a82a8831b 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1049,13 +1049,19 @@ let Command = "thread plan list" in {
 }
 
 let Command = "thread trace dump instructions" in {
+  def thread_trace_dump_instructions_forwards: Option<"forwards", "f">, Group<1>,
+    Desc<"If specified, the trace is traversed forwards chronologically "
+    "starting at the oldest instruction. Otherwise, it starts at the most "
+    "recent one and the traversal is backwards.">;
   def thread_trace_dump_instructions_count : Option<"count", "c">, Group<1>,
     Arg<"Count">,
-    Desc<"The number of instructions to display ending at the current position.">;
-  def thread_trace_dump_instructions_position : Option<"position", "p">,
+    Desc<"The number of instructions to display starting at the most recent "
+    "instruction, or the oldest if --forwards is provided.">;
+  def thread_trace_dump_instructions_skip: Option<"skip", "s">,
     Group<1>,
     Arg<"Index">,
-    Desc<"The position to use instead of the current position of the trace.">;
+    Desc<"How many instruction to skip from the end of the trace to start "
+    "dumping instructions, or from the beginning if --forwards is provided">;
   def thread_trace_dump_instructions_raw : Option<"raw", "r">,
     Group<1>,
     Desc<"Dump only instruction address without disassembly nor symbol information.">;

diff  --git a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
index a75d9676a6f3a..5b58a5703c09b 100644
--- a/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
+++ b/lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
@@ -17,6 +17,7 @@ add_lldb_library(lldbPluginTraceIntelPT PLUGIN
   CommandObjectTraceStartIntelPT.cpp
   DecodedThread.cpp
   IntelPTDecoder.cpp
+  TraceCursorIntelPT.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
index c65fa692e7d67..5c15e663fd638 100644
--- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -8,8 +8,13 @@
 
 #include "DecodedThread.h"
 
+#include <intel-pt.h>
+#include <memory>
+
+#include "TraceCursorIntelPT.h"
 #include "lldb/Utility/StreamString.h"
 
+using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::trace_intel_pt;
 using namespace llvm;
@@ -30,13 +35,18 @@ void IntelPTError::log(llvm::raw_ostream &OS) const {
   OS << "error: " << libipt_error_message;
 }
 
+IntelPTInstruction::IntelPTInstruction(llvm::Error err) {
+  llvm::handleAllErrors(std::move(err),
+                        [&](std::unique_ptr<llvm::ErrorInfoBase> info) {
+                          m_error = std::move(info);
+                        });
+  m_pt_insn.ip = LLDB_INVALID_ADDRESS;
+  m_pt_insn.iclass = ptic_error;
+}
+
 bool IntelPTInstruction::IsError() const { return (bool)m_error; }
 
-Expected<lldb::addr_t> IntelPTInstruction::GetLoadAddress() const {
-  if (IsError())
-    return ToError();
-  return m_pt_insn.ip;
-}
+lldb::addr_t IntelPTInstruction::GetLoadAddress() const { return m_pt_insn.ip; }
 
 Error IntelPTInstruction::ToError() const {
   if (!IsError())
@@ -48,22 +58,54 @@ Error IntelPTInstruction::ToError() const {
                                  m_error->convertToErrorCode());
 }
 
-size_t DecodedThread::GetLastPosition() const {
-  return m_instructions.empty() ? 0 : m_instructions.size() - 1;
+TraceInstructionControlFlowType
+IntelPTInstruction::GetControlFlowType(lldb::addr_t next_load_address) const {
+  if (IsError())
+    return (TraceInstructionControlFlowType)0;
+
+  TraceInstructionControlFlowType mask =
+      eTraceInstructionControlFlowTypeInstruction;
+
+  switch (m_pt_insn.iclass) {
+  case ptic_cond_jump:
+  case ptic_jump:
+  case ptic_far_jump:
+    mask |= eTraceInstructionControlFlowTypeBranch;
+    if (m_pt_insn.ip + m_pt_insn.size != next_load_address)
+      mask |= eTraceInstructionControlFlowTypeTakenBranch;
+    break;
+  case ptic_return:
+  case ptic_far_return:
+    mask |= eTraceInstructionControlFlowTypeReturn;
+    break;
+  case ptic_call:
+  case ptic_far_call:
+    mask |= eTraceInstructionControlFlowTypeCall;
+    break;
+  default:
+    break;
+  }
+
+  return mask;
 }
 
 ArrayRef<IntelPTInstruction> DecodedThread::GetInstructions() const {
   return makeArrayRef(m_instructions);
 }
 
-size_t DecodedThread::GetCursorPosition() const { return m_position; }
+DecodedThread::DecodedThread(ThreadSP thread_sp, Error error)
+    : m_thread_sp(thread_sp) {
+  m_instructions.emplace_back(std::move(error));
+}
 
-size_t DecodedThread::SetCursorPosition(size_t new_position) {
-  m_position = std::min(new_position, GetLastPosition());
-  return m_position;
+DecodedThread::DecodedThread(ThreadSP thread_sp,
+                             std::vector<IntelPTInstruction> &&instructions)
+    : m_thread_sp(thread_sp), m_instructions(std::move(instructions)) {
+  if (m_instructions.empty())
+    m_instructions.emplace_back(
+        createStringError(inconvertibleErrorCode(), "empty trace"));
 }
 
-DecodedThread::DecodedThread(Error error) {
-  m_instructions.emplace_back(std::move(error));
-  m_position = GetLastPosition();
+lldb::TraceCursorUP DecodedThread::GetCursor() {
+  return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this());
 }

diff  --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
index aabd85bcfd314..d5983c363367b 100644
--- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
+++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -66,12 +66,7 @@ class IntelPTInstruction {
   /// Error constructor
   ///
   /// libipt errors should use the underlying \a IntelPTError class.
-  IntelPTInstruction(llvm::Error err) {
-    llvm::handleAllErrors(std::move(err),
-                          [&](std::unique_ptr<llvm::ErrorInfoBase> info) {
-                            m_error = std::move(info);
-                          });
-  }
+  IntelPTInstruction(llvm::Error err);
 
   /// Check if this object represents an error (i.e. a gap).
   ///
@@ -80,15 +75,27 @@ class IntelPTInstruction {
   bool IsError() const;
 
   /// \return
-  ///     The instruction pointer address, or an \a llvm::Error if it is an
-  ///     error.
-  llvm::Expected<lldb::addr_t> GetLoadAddress() const;
+  ///     The instruction pointer address, or \a LLDB_INVALID_ADDRESS if it is
+  ///     an error.
+  lldb::addr_t GetLoadAddress() const;
 
   /// \return
   ///     An \a llvm::Error object if this class corresponds to an Error, or an
   ///     \a llvm::Error::success otherwise.
   llvm::Error ToError() const;
 
+  /// Get the \a lldb::TraceInstructionControlFlowType categories of the
+  /// instruction.
+  ///
+  /// \param[in] next_load_address
+  ///     The address of the next instruction in the trace or \b
+  ///     LLDB_INVALID_ADDRESS if not available.
+  ///
+  /// \return
+  ///     The control flow categories, or \b 0 if the instruction is an error.
+  lldb::TraceInstructionControlFlowType
+  GetControlFlowType(lldb::addr_t next_load_address) const;
+
   IntelPTInstruction(IntelPTInstruction &&other) = default;
 
 private:
@@ -106,15 +113,14 @@ class IntelPTInstruction {
 ///
 /// 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 DecodedThread : public std::enable_shared_from_this<DecodedThread> {
 public:
-  DecodedThread(std::vector<IntelPTInstruction> &&instructions)
-      : m_instructions(std::move(instructions)), m_position(GetLastPosition()) {
-  }
+  DecodedThread(lldb::ThreadSP thread_sp,
+                std::vector<IntelPTInstruction> &&instructions);
 
   /// Constructor with a single error signaling a complete failure of the
   /// decoding process.
-  DecodedThread(llvm::Error error);
+  DecodedThread(lldb::ThreadSP thread_sp, llvm::Error error);
 
   /// Get the instructions from the decoded trace. Some of them might indicate
   /// errors (i.e. gaps) in the trace.
@@ -123,28 +129,16 @@ class DecodedThread {
   ///   The instructions of the trace.
   llvm::ArrayRef<IntelPTInstruction> GetInstructions() const;
 
-  /// \return
-  ///   The current position of the cursor of this trace, or 0 if there are no
-  ///   instructions.
-  size_t GetCursorPosition() const;
-
-  /// 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);
-  /// \}
+  /// Get a new cursor for the decoded thread.
+  lldb::TraceCursorUP GetCursor();
 
 private:
-  /// \return
-  ///     The index of the last element of the trace, or 0 if empty.
-  size_t GetLastPosition() const;
-
+  lldb::ThreadSP m_thread_sp;
   std::vector<IntelPTInstruction> m_instructions;
-  size_t m_position;
 };
 
+using DecodedThreadSP = std::shared_ptr<DecodedThread>;
+
 } // namespace trace_intel_pt
 } // namespace lldb_private
 

diff  --git a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
index 5d9aa0a1c743c..fa9e42131f0a0 100644
--- a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -222,7 +222,7 @@ DecodeLiveThread(Thread &thread, TraceIntelPT &trace) {
     return cpu_info.takeError();
 }
 
-const DecodedThread &ThreadDecoder::Decode() {
+DecodedThreadSP ThreadDecoder::Decode() {
   if (!m_decoded_thread.hasValue())
     m_decoded_thread = DoDecode();
   return *m_decoded_thread;
@@ -232,22 +232,26 @@ PostMortemThreadDecoder::PostMortemThreadDecoder(
     const lldb::ThreadPostMortemTraceSP &trace_thread, TraceIntelPT &trace)
     : m_trace_thread(trace_thread), m_trace(trace) {}
 
-DecodedThread PostMortemThreadDecoder::DoDecode() {
+DecodedThreadSP PostMortemThreadDecoder::DoDecode() {
   if (Expected<std::vector<IntelPTInstruction>> instructions =
           DecodeTraceFile(*m_trace_thread->GetProcess(), m_trace,
                           m_trace_thread->GetTraceFile()))
-    return DecodedThread(std::move(*instructions));
+    return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(),
+                                           std::move(*instructions));
   else
-    return DecodedThread(instructions.takeError());
+    return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(),
+                                           instructions.takeError());
 }
 
 LiveThreadDecoder::LiveThreadDecoder(Thread &thread, TraceIntelPT &trace)
     : m_thread_sp(thread.shared_from_this()), m_trace(trace) {}
 
-DecodedThread LiveThreadDecoder::DoDecode() {
+DecodedThreadSP LiveThreadDecoder::DoDecode() {
   if (Expected<std::vector<IntelPTInstruction>> instructions =
           DecodeLiveThread(*m_thread_sp, m_trace))
-    return DecodedThread(std::move(*instructions));
+    return std::make_shared<DecodedThread>(m_thread_sp,
+                                           std::move(*instructions));
   else
-    return DecodedThread(instructions.takeError());
+    return std::make_shared<DecodedThread>(m_thread_sp,
+                                           instructions.takeError());
 }

diff  --git a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
index 3768a8d80a2bf..e969db579e52a 100644
--- a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
+++ b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
@@ -31,7 +31,7 @@ class ThreadDecoder {
   ///
   /// \return
   ///     A \a DecodedThread instance.
-  const DecodedThread &Decode();
+  DecodedThreadSP Decode();
 
   ThreadDecoder(const ThreadDecoder &other) = delete;
   ThreadDecoder &operator=(const ThreadDecoder &other) = delete;
@@ -41,9 +41,9 @@ class ThreadDecoder {
   ///
   /// \return
   ///     A \a DecodedThread instance.
-  virtual DecodedThread DoDecode() = 0;
+  virtual DecodedThreadSP DoDecode() = 0;
 
-  llvm::Optional<DecodedThread> m_decoded_thread;
+  llvm::Optional<DecodedThreadSP> m_decoded_thread;
 };
 
 /// Decoder implementation for \a lldb_private::ThreadPostMortemTrace, which are
@@ -59,7 +59,7 @@ class PostMortemThreadDecoder : public ThreadDecoder {
                           TraceIntelPT &trace);
 
 private:
-  DecodedThread DoDecode() override;
+  DecodedThreadSP DoDecode() override;
 
   lldb::ThreadPostMortemTraceSP m_trace_thread;
   TraceIntelPT &m_trace;
@@ -75,7 +75,7 @@ class LiveThreadDecoder : public ThreadDecoder {
   LiveThreadDecoder(Thread &thread, TraceIntelPT &trace);
 
 private:
-  DecodedThread DoDecode() override;
+  DecodedThreadSP DoDecode() override;
 
   lldb::ThreadSP m_thread_sp;
   TraceIntelPT &m_trace;

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
new file mode 100644
index 0000000000000..25c2446162994
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
@@ -0,0 +1,96 @@
+//===-- TraceCursorIntelPT.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 "TraceCursorIntelPT.h"
+#include "DecodedThread.h"
+#include "TraceIntelPT.h"
+
+#include <cstdlib>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+TraceCursorIntelPT::TraceCursorIntelPT(ThreadSP thread_sp,
+                                       DecodedThreadSP decoded_thread_sp)
+    : TraceCursor(thread_sp), m_decoded_thread_sp(decoded_thread_sp) {
+  assert(!m_decoded_thread_sp->GetInstructions().empty() &&
+         "a trace should have at least one instruction or error");
+  m_pos = m_decoded_thread_sp->GetInstructions().size() - 1;
+}
+
+size_t TraceCursorIntelPT::GetInternalInstructionSize() {
+  return m_decoded_thread_sp->GetInstructions().size();
+}
+
+bool TraceCursorIntelPT::Next() {
+  auto canMoveOne = [&]() {
+    if (IsForwards())
+      return m_pos + 1 < GetInternalInstructionSize();
+    return m_pos > 0;
+  };
+
+  size_t initial_pos = m_pos;
+
+  while (canMoveOne()) {
+    m_pos += IsForwards() ? 1 : -1;
+    if (!m_ignore_errors && IsError())
+      return true;
+    if (GetInstructionControlFlowType() & m_granularity)
+      return true;
+  }
+
+  // Didn't find any matching instructions
+  m_pos = initial_pos;
+  return false;
+}
+
+size_t TraceCursorIntelPT::Seek(int64_t offset, SeekType origin) {
+  int64_t last_index = GetInternalInstructionSize() - 1;
+
+  auto fitPosToBounds = [&](int64_t raw_pos) -> int64_t {
+    return std::min(std::max((int64_t)0, raw_pos), last_index);
+  };
+
+  switch (origin) {
+  case TraceCursor::SeekType::Set:
+    m_pos = fitPosToBounds(offset);
+    return m_pos;
+  case TraceCursor::SeekType::End:
+    m_pos = fitPosToBounds(offset + last_index);
+    return last_index - m_pos;
+  case TraceCursor::SeekType::Current:
+    int64_t new_pos = fitPosToBounds(offset + m_pos);
+    int64_t dist = m_pos - new_pos;
+    m_pos = new_pos;
+    return std::abs(dist);
+  }
+}
+
+bool TraceCursorIntelPT::IsError() {
+  return m_decoded_thread_sp->GetInstructions()[m_pos].IsError();
+}
+
+Error TraceCursorIntelPT::GetError() {
+  return m_decoded_thread_sp->GetInstructions()[m_pos].ToError();
+}
+
+lldb::addr_t TraceCursorIntelPT::GetLoadAddress() {
+  return m_decoded_thread_sp->GetInstructions()[m_pos].GetLoadAddress();
+}
+
+TraceInstructionControlFlowType
+TraceCursorIntelPT::GetInstructionControlFlowType() {
+  lldb::addr_t next_load_address =
+      m_pos + 1 < GetInternalInstructionSize()
+          ? m_decoded_thread_sp->GetInstructions()[m_pos + 1].GetLoadAddress()
+          : LLDB_INVALID_ADDRESS;
+  return m_decoded_thread_sp->GetInstructions()[m_pos].GetControlFlowType(
+      next_load_address);
+}

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
new file mode 100644
index 0000000000000..4eb4e638905fa
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
@@ -0,0 +1,48 @@
+//===-- TraceCursorIntelPT.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_TRACECURSORINTELPT_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACECURSORINTELPT_H
+
+#include "IntelPTDecoder.h"
+#include "TraceIntelPTSessionFileParser.h"
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class TraceCursorIntelPT : public TraceCursor {
+public:
+  TraceCursorIntelPT(lldb::ThreadSP thread_sp,
+                     DecodedThreadSP decoded_thread_sp);
+
+  size_t Seek(int64_t offset, SeekType origin) override;
+
+  virtual bool Next() override;
+
+  llvm::Error GetError() override;
+
+  lldb::addr_t GetLoadAddress() override;
+
+  lldb::TraceInstructionControlFlowType
+  GetInstructionControlFlowType() override;
+
+  bool IsError() override;
+
+private:
+  size_t GetInternalInstructionSize();
+
+  /// Storage of the actual instructions
+  DecodedThreadSP m_decoded_thread_sp;
+  /// Internal instruction index currently pointing at.
+  size_t m_pos;
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACECURSORINTELPT_H

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
index df4e1e11b99a2..527e0d5e66287 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -9,6 +9,7 @@
 #include "TraceIntelPT.h"
 
 #include "CommandObjectTraceStartIntelPT.h"
+#include "DecodedThread.h"
 #include "TraceIntelPTConstants.h"
 #include "TraceIntelPTSessionFileParser.h"
 #include "lldb/Core/PluginManager.h"
@@ -88,44 +89,23 @@ TraceIntelPT::TraceIntelPT(
         thread.get(), std::make_unique<PostMortemThreadDecoder>(thread, *this));
 }
 
-const DecodedThread *TraceIntelPT::Decode(Thread &thread) {
+DecodedThreadSP TraceIntelPT::Decode(Thread &thread) {
   RefreshLiveProcessState();
-  if (m_failed_live_threads_decoder.hasValue())
-    return &*m_failed_live_threads_decoder;
+  if (m_live_refresh_error.hasValue())
+    return std::make_shared<DecodedThread>(
+        thread.shared_from_this(),
+        createStringError(inconvertibleErrorCode(), *m_live_refresh_error));
 
   auto it = m_thread_decoders.find(&thread);
   if (it == m_thread_decoders.end())
-    return nullptr;
-  return &it->second->Decode();
+    return std::make_shared<DecodedThread>(
+        thread.shared_from_this(),
+        createStringError(inconvertibleErrorCode(), "thread not traced"));
+  return it->second->Decode();
 }
 
 lldb::TraceCursorUP TraceIntelPT::GetCursor(Thread &thread) {
-  // TODO: to implement
-  return nullptr;
-}
-
-void TraceIntelPT::TraverseInstructions(
-    Thread &thread, size_t position, TraceDirection direction,
-    std::function<bool(size_t index, Expected<lldb::addr_t> load_addr)>
-        callback) {
-  const DecodedThread *decoded_thread = Decode(thread);
-  if (!decoded_thread)
-    return;
-
-  ArrayRef<IntelPTInstruction> 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<size_t> TraceIntelPT::GetInstructionCount(Thread &thread) {
-  if (const DecodedThread *decoded_thread = Decode(thread))
-    return decoded_thread->GetInstructions().size();
-  else
-    return None;
+  return Decode(thread)->GetCursor();
 }
 
 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() {
@@ -195,7 +175,7 @@ void TraceIntelPT::DoRefreshLiveProcessState(
   m_thread_decoders.clear();
 
   if (!state) {
-    m_failed_live_threads_decoder = DecodedThread(state.takeError());
+    m_live_refresh_error = toString(state.takeError());
     return;
   }
 

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
index 2e414abca4ea6..9433ab8601f13 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -65,13 +65,6 @@ class TraceIntelPT : public Trace {
 
   llvm::StringRef GetSchema() override;
 
-  void TraverseInstructions(
-      Thread &thread, size_t position, TraceDirection direction,
-      std::function<bool(size_t index, llvm::Expected<lldb::addr_t> load_addr)>
-          callback) override;
-
-  llvm::Optional<size_t> GetInstructionCount(Thread &thread) override;
-
   lldb::TraceCursorUP GetCursor(Thread &thread) override;
 
   void DoRefreshLiveProcessState(
@@ -145,24 +138,23 @@ class TraceIntelPT : public Trace {
       : 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.
+  /// instructions.
   ///
   /// \param[in] thread
   ///     If \a thread is a \a ThreadTrace, then its internal trace file will be
   ///     decoded. Live threads are not currently supported.
   ///
   /// \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 DecodedThread shared pointer with the decoded instructions. Any
+  ///     errors are embedded in the instruction list.
+  DecodedThreadSP Decode(Thread &thread);
 
   /// It is provided by either a session file or a live process' "cpuInfo"
   /// binary data.
   llvm::Optional<pt_cpu> m_cpu_info;
   std::map<const Thread *, std::unique_ptr<ThreadDecoder>> m_thread_decoders;
-  /// Dummy DecodedThread used when decoding threads after there were errors
-  /// when refreshing the live process state.
-  llvm::Optional<DecodedThread> m_failed_live_threads_decoder;
+  /// Error gotten after a failed live process update, if any.
+  llvm::Optional<std::string> m_live_refresh_error;
 };
 
 } // namespace trace_intel_pt

diff  --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt
index 13e9568d52f12..3875bc987dd02 100644
--- a/lldb/source/Target/CMakeLists.txt
+++ b/lldb/source/Target/CMakeLists.txt
@@ -69,6 +69,7 @@ add_lldb_library(lldbTarget
   ThreadPostMortemTrace.cpp
   Trace.cpp
   TraceCursor.cpp
+  TraceInstructionDumper.cpp
   TraceSessionFileParser.cpp
   UnixSignals.cpp
   UnwindAssembly.cpp

diff  --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp
index c703ba7b93633..f55346fbeff7b 100644
--- a/lldb/source/Target/Trace.cpp
+++ b/lldb/source/Target/Trace.cpp
@@ -98,264 +98,6 @@ Expected<StringRef> Trace::FindPluginSchema(StringRef name) {
   return createInvalidPlugInError(name);
 }
 
-static int GetNumberOfDigits(size_t num) {
-  return num == 0 ? 1 : static_cast<int>(log10(num)) + 1;
-}
-
-/// \return
-///     \b true if the provided line entries match line, column and source file.
-///     This function assumes that the line entries are valid.
-static bool FileLineAndColumnMatches(const LineEntry &a, const LineEntry &b) {
-  if (a.line != b.line)
-    return false;
-  if (a.column != b.column)
-    return false;
-  return a.file == b.file;
-}
-
-// This custom LineEntry validator is neded because some line_entries have
-// 0 as line, which is meaningless. Notice that LineEntry::IsValid only
-// checks that line is not LLDB_INVALID_LINE_NUMBER, i.e. UINT32_MAX.
-static bool IsLineEntryValid(const LineEntry &line_entry) {
-  return line_entry.IsValid() && line_entry.line > 0;
-}
-
-/// Helper structure for \a TraverseInstructionsWithSymbolInfo.
-struct InstructionSymbolInfo {
-  SymbolContext sc;
-  Address address;
-  lldb::addr_t load_address;
-  lldb::DisassemblerSP disassembler;
-  lldb::InstructionSP instruction;
-  lldb_private::ExecutionContext exe_ctx;
-};
-
-/// InstructionSymbolInfo object with symbol information for the given
-/// instruction, calculated efficiently.
-///
-/// \param[in] symbol_scope
-///     If not \b 0, then the \a InstructionSymbolInfo will have its
-///     SymbolContext calculated up to that level.
-///
-/// \param[in] include_disassembler
-///     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,
-    bool include_disassembler,
-    std::function<bool(size_t index, Expected<InstructionSymbolInfo> insn)>
-        callback) {
-  InstructionSymbolInfo prev_insn;
-
-  Target &target = thread.GetProcess()->GetTarget();
-  ExecutionContext exe_ctx;
-  target.CalculateExecutionContext(exe_ctx);
-  const ArchSpec &arch = target.GetArchitecture();
-
-  // Find the symbol context for the given address reusing the previous
-  // instruction's symbol context when possible.
-  auto calculate_symbol_context = [&](const Address &address) {
-    AddressRange range;
-    if (prev_insn.sc.GetAddressRange(symbol_scope, 0,
-                                     /*inline_block_range*/ false, range) &&
-        range.Contains(address))
-      return prev_insn.sc;
-
-    SymbolContext sc;
-    address.CalculateSymbolContext(&sc, symbol_scope);
-    return sc;
-  };
-
-  // Find the disassembler for the given address reusing the previous
-  // instruction's disassembler when possible.
-  auto calculate_disass = [&](const Address &address, const SymbolContext &sc) {
-    if (prev_insn.disassembler) {
-      if (InstructionSP instruction =
-              prev_insn.disassembler->GetInstructionList()
-                  .GetInstructionAtAddress(address))
-        return std::make_tuple(prev_insn.disassembler, instruction);
-    }
-
-    if (sc.function) {
-      if (DisassemblerSP disassembler =
-              sc.function->GetInstructions(exe_ctx, nullptr)) {
-        if (InstructionSP instruction =
-                disassembler->GetInstructionList().GetInstructionAtAddress(
-                    address))
-          return std::make_tuple(disassembler, instruction);
-      }
-    }
-    // We fallback to a single instruction disassembler
-    AddressRange range(address, arch.GetMaximumOpcodeByteSize());
-    DisassemblerSP disassembler =
-        Disassembler::DisassembleRange(arch, /*plugin_name*/ nullptr,
-                                       /*flavor*/ nullptr, target, range);
-    return std::make_tuple(disassembler,
-                           disassembler ? disassembler->GetInstructionList()
-                                              .GetInstructionAtAddress(address)
-                                        : InstructionSP());
-  };
-
-  trace.TraverseInstructions(
-      thread, position, direction,
-      [&](size_t index, Expected<lldb::addr_t> load_address) -> bool {
-        if (!load_address)
-          return callback(index, load_address.takeError());
-
-        InstructionSymbolInfo insn;
-        insn.load_address = *load_address;
-        insn.exe_ctx = exe_ctx;
-        insn.address.SetLoadAddress(*load_address, &target);
-        if (symbol_scope != 0)
-          insn.sc = calculate_symbol_context(insn.address);
-        if (include_disassembler)
-          std::tie(insn.disassembler, insn.instruction) =
-              calculate_disass(insn.address, insn.sc);
-        prev_insn = insn;
-        return callback(index, insn);
-      });
-}
-
-/// Compare the symbol contexts of the provided \a InstructionSymbolInfo
-/// objects.
-///
-/// \return
-///     \a true if both instructions belong to the same scope level analized
-///     in the following order:
-///       - module
-///       - symbol
-///       - function
-///       - line
-static bool
-IsSameInstructionSymbolContext(const InstructionSymbolInfo &prev_insn,
-                               const InstructionSymbolInfo &insn) {
-  // module checks
-  if (insn.sc.module_sp != prev_insn.sc.module_sp)
-    return false;
-
-  // symbol checks
-  if (insn.sc.symbol != prev_insn.sc.symbol)
-    return false;
-
-  // function checks
-  if (!insn.sc.function && !prev_insn.sc.function)
-    return true;
-  else if (insn.sc.function != prev_insn.sc.function)
-    return false;
-
-  // line entry checks
-  const bool curr_line_valid = IsLineEntryValid(insn.sc.line_entry);
-  const bool prev_line_valid = IsLineEntryValid(prev_insn.sc.line_entry);
-  if (curr_line_valid && prev_line_valid)
-    return FileLineAndColumnMatches(insn.sc.line_entry,
-                                    prev_insn.sc.line_entry);
-  return curr_line_valid == prev_line_valid;
-}
-
-/// Dump the symbol context of the given instruction address if it's 
diff erent
-/// from the symbol context of the previous instruction in the trace.
-///
-/// \param[in] prev_sc
-///     The symbol context of the previous instruction in the trace.
-///
-/// \param[in] address
-///     The address whose symbol information will be dumped.
-///
-/// \return
-///     The symbol context of the current address, which might 
diff er from the
-///     previous one.
-static void
-DumpInstructionSymbolContext(Stream &s,
-                             Optional<InstructionSymbolInfo> prev_insn,
-                             InstructionSymbolInfo &insn) {
-  if (prev_insn && IsSameInstructionSymbolContext(*prev_insn, insn))
-    return;
-
-  s.Printf("  ");
-
-  if (!insn.sc.module_sp)
-    s.Printf("(none)");
-  else if (!insn.sc.function && !insn.sc.symbol)
-    s.Printf("%s`(none)",
-             insn.sc.module_sp->GetFileSpec().GetFilename().AsCString());
-  else
-    insn.sc.DumpStopContext(&s, insn.exe_ctx.GetTargetPtr(), insn.address,
-                            /*show_fullpath*/ false,
-                            /*show_module*/ true, /*show_inlined_frames*/ false,
-                            /*show_function_arguments*/ true,
-                            /*show_function_name*/ true);
-  s.Printf("\n");
-}
-
-static void DumpInstructionDisassembly(Stream &s, InstructionSymbolInfo &insn) {
-  if (!insn.instruction)
-    return;
-  s.Printf("    ");
-  insn.instruction->Dump(&s, /*show_address*/ false, /*show_bytes*/ false,
-                         /*max_opcode_byte_size*/ 0, &insn.exe_ctx, &insn.sc,
-                         /*prev_sym_ctx*/ nullptr,
-                         /*disassembly_addr_format*/ nullptr,
-                         /*max_address_text_size*/ 0);
-}
-
-void Trace::DumpTraceInstructions(Thread &thread, Stream &s, size_t count,
-                                  size_t end_position, bool raw) {
-  Optional<size_t> 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 ", 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<InstructionSymbolInfo> prev_insn;
-
-  TraverseInstructionsWithSymbolInfo(
-      *this, thread, start_position, TraceDirection::Forwards,
-      eSymbolContextEverything, /*disassembler*/ true,
-      [&](size_t index, Expected<InstructionSymbolInfo> 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) {
   if (!m_live_process)
     return createStringError(inconvertibleErrorCode(),

diff  --git a/lldb/source/Target/TraceCursor.cpp b/lldb/source/Target/TraceCursor.cpp
index 80a7dadf3df5c..d0f69642cb90b 100644
--- a/lldb/source/Target/TraceCursor.cpp
+++ b/lldb/source/Target/TraceCursor.cpp
@@ -8,8 +8,28 @@
 
 #include "lldb/Target/TraceCursor.h"
 
-#include "lldb/Target/Trace.h"
+#include "lldb/Target/ExecutionContext.h"
 
+using namespace lldb;
 using namespace lldb_private;
+using namespace llvm;
 
-bool TraceCursor::IsStale() { return m_stop_id != m_trace_sp->GetStopID(); }
+TraceCursor::TraceCursor(lldb::ThreadSP thread_sp)
+    : m_exe_ctx_ref(ExecutionContext(thread_sp)) {}
+
+ExecutionContextRef &TraceCursor::GetExecutionContextRef() {
+  return m_exe_ctx_ref;
+}
+
+void TraceCursor::SetGranularity(
+    lldb::TraceInstructionControlFlowType granularity) {
+  m_granularity = granularity;
+}
+
+void TraceCursor::SetIgnoreErrors(bool ignore_errors) {
+  m_ignore_errors = ignore_errors;
+}
+
+void TraceCursor::SetForwards(bool forwards) { m_forwards = forwards; }
+
+bool TraceCursor::IsForwards() const { return m_forwards; }

diff  --git a/lldb/source/Target/TraceInstructionDumper.cpp b/lldb/source/Target/TraceInstructionDumper.cpp
new file mode 100644
index 0000000000000..a8525d6a65c45
--- /dev/null
+++ b/lldb/source/Target/TraceInstructionDumper.cpp
@@ -0,0 +1,279 @@
+//===-- TraceInstructionDumper.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 "lldb/Target/TraceInstructionDumper.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+TraceInstructionDumper::TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up,
+                                               int initial_index, bool raw)
+    : m_cursor_up(std::move(cursor_up)), m_index(initial_index), m_raw(raw) {}
+
+/// \return
+///     Return \b true if the cursor could move one step.
+bool TraceInstructionDumper::TryMoveOneStep() {
+  if (!m_cursor_up->Next()) {
+    SetNoMoreData();
+    return false;
+  }
+  m_index += m_cursor_up->IsForwards() ? 1 : -1;
+  return true;
+}
+
+/// \return
+///     The number of characters that would be needed to print the given
+///     integer.
+static int GetNumberOfChars(int num) {
+  if (num == 0)
+    return 1;
+  return (num < 0 ? 1 : 0) + static_cast<int>(log10(abs(num))) + 1;
+}
+
+/// Helper struct that holds symbol, disassembly and address information of an
+/// instruction.
+struct InstructionSymbolInfo {
+  SymbolContext sc;
+  Address address;
+  lldb::addr_t load_address;
+  lldb::DisassemblerSP disassembler;
+  lldb::InstructionSP instruction;
+  lldb_private::ExecutionContext exe_ctx;
+};
+
+// This custom LineEntry validator is neded because some line_entries have
+// 0 as line, which is meaningless. Notice that LineEntry::IsValid only
+// checks that line is not LLDB_INVALID_LINE_NUMBER, i.e. UINT32_MAX.
+static bool IsLineEntryValid(const LineEntry &line_entry) {
+  return line_entry.IsValid() && line_entry.line > 0;
+}
+
+/// \return
+///     \b true if the provided line entries match line, column and source file.
+///     This function assumes that the line entries are valid.
+static bool FileLineAndColumnMatches(const LineEntry &a, const LineEntry &b) {
+  if (a.line != b.line)
+    return false;
+  if (a.column != b.column)
+    return false;
+  return a.file == b.file;
+}
+
+/// Compare the symbol contexts of the provided \a InstructionSymbolInfo
+/// objects.
+///
+/// \return
+///     \a true if both instructions belong to the same scope level analized
+///     in the following order:
+///       - module
+///       - symbol
+///       - function
+///       - line
+static bool
+IsSameInstructionSymbolContext(const InstructionSymbolInfo &prev_insn,
+                               const InstructionSymbolInfo &insn) {
+  // module checks
+  if (insn.sc.module_sp != prev_insn.sc.module_sp)
+    return false;
+
+  // symbol checks
+  if (insn.sc.symbol != prev_insn.sc.symbol)
+    return false;
+
+  // function checks
+  if (!insn.sc.function && !prev_insn.sc.function)
+    return true;
+  else if (insn.sc.function != prev_insn.sc.function)
+    return false;
+
+  // line entry checks
+  const bool curr_line_valid = IsLineEntryValid(insn.sc.line_entry);
+  const bool prev_line_valid = IsLineEntryValid(prev_insn.sc.line_entry);
+  if (curr_line_valid && prev_line_valid)
+    return FileLineAndColumnMatches(insn.sc.line_entry,
+                                    prev_insn.sc.line_entry);
+  return curr_line_valid == prev_line_valid;
+}
+
+/// Dump the symbol context of the given instruction address if it's 
diff erent
+/// from the symbol context of the previous instruction in the trace.
+///
+/// \param[in] prev_sc
+///     The symbol context of the previous instruction in the trace.
+///
+/// \param[in] address
+///     The address whose symbol information will be dumped.
+///
+/// \return
+///     The symbol context of the current address, which might 
diff er from the
+///     previous one.
+static void
+DumpInstructionSymbolContext(Stream &s,
+                             Optional<InstructionSymbolInfo> prev_insn,
+                             InstructionSymbolInfo &insn) {
+  if (prev_insn && IsSameInstructionSymbolContext(*prev_insn, insn))
+    return;
+
+  s.Printf("  ");
+
+  if (!insn.sc.module_sp)
+    s.Printf("(none)");
+  else if (!insn.sc.function && !insn.sc.symbol)
+    s.Printf("%s`(none)",
+             insn.sc.module_sp->GetFileSpec().GetFilename().AsCString());
+  else
+    insn.sc.DumpStopContext(&s, insn.exe_ctx.GetTargetPtr(), insn.address,
+                            /*show_fullpath=*/false,
+                            /*show_module=*/true, /*show_inlined_frames=*/false,
+                            /*show_function_arguments=*/true,
+                            /*show_function_name=*/true);
+  s.Printf("\n");
+}
+
+static void DumpInstructionDisassembly(Stream &s, InstructionSymbolInfo &insn) {
+  if (!insn.instruction)
+    return;
+  s.Printf("    ");
+  insn.instruction->Dump(&s, /*show_address=*/false, /*show_bytes=*/false,
+                         /*max_opcode_byte_size=*/0, &insn.exe_ctx, &insn.sc,
+                         /*prev_sym_ctx=*/nullptr,
+                         /*disassembly_addr_format=*/nullptr,
+                         /*max_address_text_size=*/0);
+}
+
+void TraceInstructionDumper::SetNoMoreData() { m_no_more_data = true; }
+
+bool TraceInstructionDumper::HasMoreData() { return !m_no_more_data; }
+
+void TraceInstructionDumper::DumpInstructions(Stream &s, size_t count) {
+  ThreadSP thread_sp = m_cursor_up->GetExecutionContextRef().GetThreadSP();
+  if (!thread_sp) {
+    s.Printf("invalid thread");
+    return;
+  }
+
+  s.Printf("thread #%u: tid = %" PRIu64 "\n", thread_sp->GetIndexID(),
+           thread_sp->GetID());
+
+  int digits_count = GetNumberOfChars(
+      m_cursor_up->IsForwards() ? m_index + count - 1 : m_index - count + 1);
+  bool was_prev_instruction_an_error = false;
+
+  auto printMissingInstructionsMessage = [&]() {
+    s.Printf("    ...missing instructions\n");
+  };
+
+  auto printInstructionIndex = [&]() {
+    s.Printf("    [%*d] ", digits_count, m_index);
+  };
+
+  InstructionSymbolInfo prev_insn_info;
+
+  Target &target = thread_sp->GetProcess()->GetTarget();
+  ExecutionContext exe_ctx;
+  target.CalculateExecutionContext(exe_ctx);
+  const ArchSpec &arch = target.GetArchitecture();
+
+  // Find the symbol context for the given address reusing the previous
+  // instruction's symbol context when possible.
+  auto calculateSymbolContext = [&](const Address &address) {
+    AddressRange range;
+    if (prev_insn_info.sc.GetAddressRange(eSymbolContextEverything, 0,
+                                          /*inline_block_range*/ false,
+                                          range) &&
+        range.Contains(address))
+      return prev_insn_info.sc;
+
+    SymbolContext sc;
+    address.CalculateSymbolContext(&sc, eSymbolContextEverything);
+    return sc;
+  };
+
+  // Find the disassembler for the given address reusing the previous
+  // instruction's disassembler when possible.
+  auto calculateDisass = [&](const Address &address, const SymbolContext &sc) {
+    if (prev_insn_info.disassembler) {
+      if (InstructionSP instruction =
+              prev_insn_info.disassembler->GetInstructionList()
+                  .GetInstructionAtAddress(address))
+        return std::make_tuple(prev_insn_info.disassembler, instruction);
+    }
+
+    if (sc.function) {
+      if (DisassemblerSP disassembler =
+              sc.function->GetInstructions(exe_ctx, nullptr)) {
+        if (InstructionSP instruction =
+                disassembler->GetInstructionList().GetInstructionAtAddress(
+                    address))
+          return std::make_tuple(disassembler, instruction);
+      }
+    }
+    // We fallback to a single instruction disassembler
+    AddressRange range(address, arch.GetMaximumOpcodeByteSize());
+    DisassemblerSP disassembler =
+        Disassembler::DisassembleRange(arch, /*plugin_name*/ nullptr,
+                                       /*flavor*/ nullptr, target, range);
+    return std::make_tuple(disassembler,
+                           disassembler ? disassembler->GetInstructionList()
+                                              .GetInstructionAtAddress(address)
+                                        : InstructionSP());
+  };
+
+  for (size_t i = 0; i < count; i++) {
+    if (!HasMoreData()) {
+      s.Printf("    no more data\n");
+      break;
+    }
+
+    if (Error err = m_cursor_up->GetError()) {
+      if (!m_cursor_up->IsForwards() && !was_prev_instruction_an_error)
+        printMissingInstructionsMessage();
+
+      was_prev_instruction_an_error = true;
+
+      printInstructionIndex();
+      s << toString(std::move(err));
+    } else {
+      if (m_cursor_up->IsForwards() && was_prev_instruction_an_error)
+        printMissingInstructionsMessage();
+
+      was_prev_instruction_an_error = false;
+
+      InstructionSymbolInfo insn_info;
+
+      if (!m_raw) {
+        insn_info.load_address = m_cursor_up->GetLoadAddress();
+        insn_info.exe_ctx = exe_ctx;
+        insn_info.address.SetLoadAddress(insn_info.load_address, &target);
+        insn_info.sc = calculateSymbolContext(insn_info.address);
+        std::tie(insn_info.disassembler, insn_info.instruction) =
+            calculateDisass(insn_info.address, insn_info.sc);
+
+        DumpInstructionSymbolContext(s, prev_insn_info, insn_info);
+      }
+
+      printInstructionIndex();
+      s.Printf("0x%016" PRIx64, m_cursor_up->GetLoadAddress());
+
+      if (!m_raw)
+        DumpInstructionDisassembly(s, insn_info);
+
+      prev_insn_info = insn_info;
+    }
+
+    s.Printf("\n");
+    TryMoveOneStep();
+  }
+}

diff  --git a/lldb/test/API/commands/trace/TestTraceDumpInstructions.py b/lldb/test/API/commands/trace/TestTraceDumpInstructions.py
index c1cb9b611d193..745a4c61a5d56 100644
--- a/lldb/test/API/commands/trace/TestTraceDumpInstructions.py
+++ b/lldb/test/API/commands/trace/TestTraceDumpInstructions.py
@@ -35,8 +35,9 @@ def testRawDumpInstructions(self):
             os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"),
             substrs=["intel-pt"])
 
-        self.expect("thread trace dump instructions --raw",
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
+        self.expect("thread trace dump instructions --raw --count 21 --forwards",
+            substrs=['''thread #1: tid = 3842849
+    [ 0] 0x0000000000400511
     [ 1] 0x0000000000400518
     [ 2] 0x000000000040051f
     [ 3] 0x0000000000400529
@@ -58,19 +59,27 @@ def testRawDumpInstructions(self):
     [19] 0x0000000000400529
     [20] 0x000000000040052d'''])
 
-        # We check if we can pass count and position
-        self.expect("thread trace dump instructions --count 5 --position 10 --raw",
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
+        # We check if we can pass count and skip
+        self.expect("thread trace dump instructions --count 5 --skip 6 --raw --forwards",
+            substrs=['''thread #1: tid = 3842849
     [ 6] 0x0000000000400525
     [ 7] 0x0000000000400529
     [ 8] 0x000000000040052d
     [ 9] 0x0000000000400521
     [10] 0x0000000000400525'''])
 
+        self.expect("thread trace dump instructions --count 5 --skip 6 --raw",
+            substrs=['''thread #1: tid = 3842849
+    [ -6] 0x0000000000400525
+    [ -7] 0x0000000000400521
+    [ -8] 0x000000000040052d
+    [ -9] 0x0000000000400529
+    [-10] 0x0000000000400525'''])
+
         # We check if we can access the thread by index id
         self.expect("thread trace dump instructions 1 --raw",
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
-    [ 1] 0x0000000000400518'''])
+            substrs=['''thread #1: tid = 3842849
+    [  0] 0x000000000040052d'''])
 
         # We check that we get an error when using an invalid thread index id
         self.expect("thread trace dump instructions 10", error=True,
@@ -83,85 +92,87 @@ def testDumpFullInstructionsWithMultipleThreads(self):
 
         # We print the instructions of two threads simultaneously
         self.expect("thread trace dump instructions 1 2 --count 2",
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
-  a.out`main + 28 at main.cpp:4
-    [19] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
-    [20] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
-thread #2: tid = 3842850, total instructions = 21
-  a.out`main + 28 at main.cpp:4
-    [19] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
-    [20] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5'''])
-
-        # We use custom --count and --position, saving the command to history for later
-        self.expect("thread trace dump instructions 1 2 --count 2 --position 20", inHistory=True,
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
-  a.out`main + 28 at main.cpp:4
-    [19] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
-    [20] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
-thread #2: tid = 3842850, total instructions = 21
-  a.out`main + 28 at main.cpp:4
-    [19] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
-    [20] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5'''])
+            substrs=['''thread #1: tid = 3842849
+  a.out`main + 32 at main.cpp:4
+    [ 0] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
+    [-1] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
+thread #2: tid = 3842850
+  a.out`main + 32 at main.cpp:4
+    [ 0] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
+    [-1] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)'''])
+
+        # We use custom --count and --skip, saving the command to history for later
+        self.expect("thread trace dump instructions 1 2 --count 2 --skip 2", inHistory=True,
+            substrs=['''thread #1: tid = 3842849
+  a.out`main + 24 at main.cpp:4
+    [-2] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
+  a.out`main + 20 at main.cpp:5
+    [-3] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)
+thread #2: tid = 3842850
+  a.out`main + 24 at main.cpp:4
+    [-2] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
+  a.out`main + 20 at main.cpp:5
+    [-3] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)'''])
 
         # We use a repeat command twice and ensure the previous count is used and the
         # start position moves with each command.
         self.expect("", inHistory=True,
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
-  a.out`main + 20 at main.cpp:5
-    [17] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)
+            substrs=['''thread #1: tid = 3842849
+  a.out`main + 32 at main.cpp:4
+    [-4] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
+    [-5] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
+thread #2: tid = 3842850
+  a.out`main + 32 at main.cpp:4
+    [-4] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
+    [-5] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)'''])
+
+        self.expect("", inHistory=True,
+            substrs=['''thread #1: tid = 3842849
   a.out`main + 24 at main.cpp:4
-    [18] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
-thread #2: tid = 3842850, total instructions = 21
+    [-6] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
   a.out`main + 20 at main.cpp:5
-    [17] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)
+    [-7] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)
+thread #2: tid = 3842850
   a.out`main + 24 at main.cpp:4
-    [18] 0x0000000000400525    addl   $0x1, -0x8(%rbp)'''])
-
-        self.expect("", inHistory=True,
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
-  a.out`main + 28 at main.cpp:4
-    [15] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
-    [16] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
-thread #2: tid = 3842850, total instructions = 21
-  a.out`main + 28 at main.cpp:4
-    [15] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
-    [16] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5'''])
+    [-6] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
+  a.out`main + 20 at main.cpp:5
+    [-7] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)'''])
 
     def testInvalidBounds(self):
         self.expect("trace load -v " +
             os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"))
 
         # The output should be work when too many instructions are asked
-        self.expect("thread trace dump instructions --count 20 --position 2",
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
+        self.expect("thread trace dump instructions --count 20 --forwards",
+            substrs=['''thread #1: tid = 3842849
   a.out`main + 4 at main.cpp:2
-    [0] 0x0000000000400511    movl   $0x0, -0x4(%rbp)
+    [ 0] 0x0000000000400511    movl   $0x0, -0x4(%rbp)
   a.out`main + 11 at main.cpp:4
-    [1] 0x0000000000400518    movl   $0x0, -0x8(%rbp)
-    [2] 0x000000000040051f    jmp    0x400529                  ; <+28> at main.cpp:4'''])
+    [ 1] 0x0000000000400518    movl   $0x0, -0x8(%rbp)
+    [ 2] 0x000000000040051f    jmp    0x400529                  ; <+28> at main.cpp:4'''])
 
         # Should print no instructions if the position is out of bounds
-        self.expect("thread trace dump instructions --position 23",
-            endstr='thread #1: tid = 3842849, total instructions = 21\n')
+        self.expect("thread trace dump instructions --skip 23",
+            endstr='no more data\n')
 
         # Should fail with negative bounds
-        self.expect("thread trace dump instructions --position -1", error=True)
+        self.expect("thread trace dump instructions --skip -1", error=True)
         self.expect("thread trace dump instructions --count -1", error=True)
 
     def testWrongImage(self):
         self.expect("trace load " +
             os.path.join(self.getSourceDir(), "intelpt-trace", "trace_bad_image.json"))
-        self.expect("thread trace dump instructions",
-            substrs=['''thread #1: tid = 3842849, total instructions = 2
-    [0] 0x0000000000400511    error: no memory mapped at this address
-    [1] 0x0000000000400518    error: no memory mapped at this address'''])
+        self.expect("thread trace dump instructions --forwards",
+            substrs=['''thread #1: tid = 3842849
+    [ 0] 0x0000000000400511    error: no memory mapped at this address
+    [ 1] 0x0000000000400518    error: no memory mapped at this address'''])
 
     def testWrongCPU(self):
         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'''])
+        self.expect("thread trace dump instructions --forwards",
+            substrs=['''thread #1: tid = 3842849
+    [ 0] error: unknown cpu'''])
 
     def testMultiFileTraceWithMissingModule(self):
         self.expect("trace load " +
@@ -181,8 +192,8 @@ def testMultiFileTraceWithMissingModule(self):
         # line is printed showing the symbol context change.
         #
         # Finally, the instruction disassembly is included in the dump.
-        self.expect("thread trace dump instructions --count 50",
-            substrs=['''thread #1: tid = 815455, total instructions = 46
+        self.expect("thread trace dump instructions --count 50 --forwards",
+            substrs=['''thread #1: tid = 815455
   a.out`main + 15 at main.cpp:10
     [ 0] 0x000000000040066f    callq  0x400540                  ; symbol stub for: foo()
   a.out`symbol stub for: foo()
@@ -252,3 +263,83 @@ def testMultiFileTraceWithMissingModule(self):
     [43] 0x00000000004006a4    movl   -0xc(%rbp), %ecx
     [44] 0x00000000004006a7    addl   %eax, %ecx
     [45] 0x00000000004006a9    movl   %ecx, -0xc(%rbp)'''])
+
+
+        self.expect("thread trace dump instructions --count 50",
+            substrs=['''thread #1: tid = 815455
+  a.out`main + 73 at main.cpp:16
+    [  0] 0x00000000004006a9    movl   %ecx, -0xc(%rbp)
+    [ -1] 0x00000000004006a7    addl   %eax, %ecx
+    [ -2] 0x00000000004006a4    movl   -0xc(%rbp), %ecx
+  libfoo.so`foo() + 35 at foo.cpp:6
+    [ -3] 0x00007ffff7bd9703    retq''',
+    '''[ -4] 0x00007ffff7bd9702    popq   %rbp
+    [ -5] 0x00007ffff7bd96fe    addq   $0x10, %rsp
+    [ -6] 0x00007ffff7bd96fb    movl   -0x4(%rbp), %eax
+  libfoo.so`foo() + 24 at foo.cpp:5
+    [ -7] 0x00007ffff7bd96f8    movl   %eax, -0x4(%rbp)
+    [ -8] 0x00007ffff7bd96f3    addl   $0x1, %eax
+    [ -9] 0x00007ffff7bd96f0    movl   -0x4(%rbp), %eax
+  libfoo.so`foo() + 13 at foo.cpp:4
+    [-10] 0x00007ffff7bd96ed    movl   %eax, -0x4(%rbp)
+  libbar.so`bar() + 26 at bar.cpp:4
+    [-11] 0x00007ffff79d76aa    retq''',
+    '''[-12] 0x00007ffff79d76a9    popq   %rbp
+    [-13] 0x00007ffff79d76a6    movl   -0x4(%rbp), %eax
+  libbar.so`bar() + 19 at bar.cpp:3
+    [-14] 0x00007ffff79d76a3    movl   %eax, -0x4(%rbp)
+    [-15] 0x00007ffff79d769e    addl   $0x1, %eax
+    [-16] 0x00007ffff79d769b    movl   -0x4(%rbp), %eax
+  libbar.so`bar() + 4 at bar.cpp:2
+    [-17] 0x00007ffff79d7694    movl   $0x1, -0x4(%rbp)
+  libbar.so`bar() + 1 at bar.cpp:1
+    [-18] 0x00007ffff79d7691    movq   %rsp, %rbp
+    [-19] 0x00007ffff79d7690    pushq  %rbp
+  libfoo.so`symbol stub for: bar()
+    [-20] 0x00007ffff7bd95d0    jmpq   *0x200a4a(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 32
+  libfoo.so`foo() + 8 at foo.cpp:4
+    [-21] 0x00007ffff7bd96e8    callq  0x7ffff7bd95d0            ; symbol stub for: bar()
+    [-22] 0x00007ffff7bd96e4    subq   $0x10, %rsp
+  libfoo.so`foo() + 1 at foo.cpp:3
+    [-23] 0x00007ffff7bd96e1    movq   %rsp, %rbp
+    [-24] 0x00007ffff7bd96e0    pushq  %rbp
+  a.out`symbol stub for: foo()
+    [-25] 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
+  a.out`main + 63 at main.cpp:16
+    [-26] 0x000000000040069f    callq  0x400540                  ; symbol stub for: foo()
+  a.out`main + 60 at main.cpp:14
+    [-27] 0x000000000040069c    movl   %ecx, -0xc(%rbp)
+    [-28] 0x000000000040069a    addl   %eax, %ecx
+    [-29] 0x0000000000400697    movl   -0xc(%rbp), %ecx
+  a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6
+    [-30] 0x0000000000400694    movl   -0x4(%rbp), %eax
+  a.out`main + 49 [inlined] inline_function() + 15 at main.cpp:5
+    [-31] 0x0000000000400691    movl   %eax, -0x4(%rbp)
+    [-32] 0x000000000040068c    addl   $0x1, %eax
+    [-33] 0x0000000000400689    movl   -0x4(%rbp), %eax
+  a.out`main + 34 [inlined] inline_function() at main.cpp:4
+    [-34] 0x0000000000400682    movl   $0x0, -0x4(%rbp)
+  a.out`main + 31 at main.cpp:12
+    [-35] 0x000000000040067f    movl   %eax, -0xc(%rbp)
+    [-36] 0x000000000040067a    addl   $0x1, %eax
+    [-37] 0x0000000000400677    movl   -0xc(%rbp), %eax
+  a.out`main + 20 at main.cpp:10
+    [-38] 0x0000000000400674    movl   %eax, -0xc(%rbp)
+    ...missing instructions
+    [-39] 0x00007ffff7df1950    error: no memory mapped at this address
+  a.out`(none)
+    [-40] 0x0000000000400516    jmpq   *0x200af4(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 16
+    [-41] 0x0000000000400510    pushq  0x200af2(%rip)            ; _GLOBAL_OFFSET_TABLE_ + 8
+  a.out`symbol stub for: foo() + 11
+    [-42] 0x000000000040054b    jmp    0x400510
+    [-43] 0x0000000000400546    pushq  $0x2
+    [-44] 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
+  a.out`main + 15 at main.cpp:10
+    [-45] 0x000000000040066f    callq  0x400540                  ; symbol stub for: foo()'''])
+
+        self.expect("thread trace dump instructions --skip 100 --forwards", inHistory=True,
+            substrs=['''thread #1: tid = 815455
+    no more data'''])
+
+        self.expect("", substrs=['''thread #1: tid = 815455
+    no more data'''])

diff  --git a/lldb/test/API/commands/trace/TestTraceStartStop.py b/lldb/test/API/commands/trace/TestTraceStartStop.py
index 91cf5afa26002..841ca437b29f6 100644
--- a/lldb/test/API/commands/trace/TestTraceStartStop.py
+++ b/lldb/test/API/commands/trace/TestTraceStartStop.py
@@ -65,11 +65,12 @@ def testStoppingAThread(self):
         self.expect("r")
         self.expect("thread trace start")
         self.expect("n")
-        self.expect("thread trace dump instructions", substrs=["total instructions"])
+        self.expect("thread trace dump instructions", substrs=["""0x0000000000400511    movl   $0x0, -0x4(%rbp)
+    no more data"""])
         # process stopping should stop the thread
         self.expect("process trace stop")
         self.expect("n")
-        self.expect("thread trace dump instructions", error=True, substrs=["not traced"])
+        self.expect("thread trace dump instructions", substrs=["not traced"])
 
 
     @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
@@ -110,22 +111,32 @@ def testStartStopLiveThreads(self):
 
         # We can reconstruct the single instruction executed in the first line
         self.expect("n")
-        self.expect("thread trace dump instructions",
-            patterns=[f'''thread #1: tid = .*, total instructions = 1
+        self.expect("thread trace dump instructions -f",
+            patterns=[f'''thread #1: tid = .*
   a.out`main \+ 4 at main.cpp:2
-    \[0\] {ADDRESS_REGEX}    movl'''])
+    \[ 0\] {ADDRESS_REGEX}    movl'''])
 
         # We can reconstruct the instructions up to the second line
         self.expect("n")
-        self.expect("thread trace dump instructions",
-            patterns=[f'''thread #1: tid = .*, total instructions = 5
+        self.expect("thread trace dump instructions -f",
+            patterns=[f'''thread #1: tid = .*
   a.out`main \+ 4 at main.cpp:2
-    \[0\] {ADDRESS_REGEX}    movl .*
+    \[ 0\] {ADDRESS_REGEX}    movl .*
   a.out`main \+ 11 at main.cpp:4
-    \[1\] {ADDRESS_REGEX}    movl .*
-    \[2\] {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
-    \[3\] {ADDRESS_REGEX}    cmpl .*
-    \[4\] {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5'''])
+    \[ 1\] {ADDRESS_REGEX}    movl .*
+    \[ 2\] {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
+    \[ 3\] {ADDRESS_REGEX}    cmpl .*
+    \[ 4\] {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5'''])
+
+        self.expect("thread trace dump instructions",
+            patterns=[f'''thread #1: tid = .*
+  a.out`main \+ 32 at main.cpp:4
+    \[  0\] {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5
+    \[ -1\] {ADDRESS_REGEX}    cmpl .*
+    \[ -2\] {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
+    \[ -3\] {ADDRESS_REGEX}    movl .*
+  a.out`main \+ 4 at main.cpp:2
+    \[ -4\] {ADDRESS_REGEX}    movl .* '''])
 
         # We stop tracing
         self.expect("thread trace stop")
@@ -138,10 +149,15 @@ def testStartStopLiveThreads(self):
         # thread
         self.expect("thread trace start")
         self.expect("n")
+        self.expect("thread trace dump instructions -f",
+            patterns=[f'''thread #1: tid = .*
+  a.out`main \+ 20 at main.cpp:5
+    \[ 0\] {ADDRESS_REGEX}    xorl'''])
+
         self.expect("thread trace dump instructions",
-            patterns=[f'''thread #1: tid = .*, total instructions = 1
+            patterns=[f'''thread #1: tid = .*
   a.out`main \+ 20 at main.cpp:5
-    \[0\] {ADDRESS_REGEX}    xorl'''])
+    \[  0\] {ADDRESS_REGEX}    xorl'''])
 
         self.expect("c")
         # Now the process has finished, so the commands should fail

diff  --git a/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py b/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
index e58234ff18b88..4b7fbc9f388de 100644
--- a/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
+++ b/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
@@ -97,8 +97,8 @@ def testStartMultipleLiveThreadsWithStops(self):
         self.expect("continue")
         self.expect("thread trace dump instructions", substrs=['main.cpp:4'])
         self.expect("thread trace dump instructions 3", substrs=['main.cpp:4'])
-        self.expect("thread trace dump instructions 1", error=True, substrs=['not traced'])
-        self.expect("thread trace dump instructions 2", error=True, substrs=['not traced'])
+        self.expect("thread trace dump instructions 1", substrs=['not traced'])
+        self.expect("thread trace dump instructions 2", substrs=['not traced'])
 
     @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
     def testStartMultipleLiveThreadsWithThreadStartAll(self):
@@ -128,9 +128,9 @@ def testStartMultipleLiveThreadsWithThreadStartAll(self):
 
         # We'll stop at the next breakpoint in thread 3, and nothing should be traced
         self.expect("continue")
-        self.expect("thread trace dump instructions 3", error=True, substrs=['not traced'])
-        self.expect("thread trace dump instructions 1", error=True, substrs=['not traced'])
-        self.expect("thread trace dump instructions 2", error=True, substrs=['not traced'])
+        self.expect("thread trace dump instructions 3", substrs=['not traced'])
+        self.expect("thread trace dump instructions 1", substrs=['not traced'])
+        self.expect("thread trace dump instructions 2", substrs=['not traced'])
 
     @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
     @testSBAPIAndCommands


        


More information about the lldb-commits mailing list