[Lldb-commits] [lldb] 05b4bf2 - [trace][intelpt] Introduce instruction Ids

Walter Erquinigo via lldb-commits lldb-commits at lists.llvm.org
Wed Apr 6 12:19:44 PDT 2022


Author: Walter Erquinigo
Date: 2022-04-06T12:19:36-07:00
New Revision: 05b4bf2571244da2cef438e907036b35a0e1f99a

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

LOG: [trace][intelpt] Introduce instruction Ids

In order to support quick arbitrary access to instructions in the trace, we need
each instruction to have an id. It could be an index or any other value that the
trace plugin defines.

This will be useful for reverse debugging or for creating callstacks, as each
frame will need an instruction id associated with them.

I've updated the `thread trace dump instructions` command accordingly. It now
prints the instruction id instead of relative offset. I've also added a new --id
argument that allows starting the dump from an arbitrary position.

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

Added: 
    lldb/test/API/commands/trace/TestTraceTSC.py

Modified: 
    lldb/include/lldb/Target/TraceCursor.h
    lldb/include/lldb/Target/TraceInstructionDumper.h
    lldb/source/Commands/CommandObjectThread.cpp
    lldb/source/Commands/Options.td
    lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
    lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
    lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
    lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp
    lldb/source/Target/TraceInstructionDumper.cpp
    lldb/test/API/commands/trace/TestTraceDumpInstructions.py
    lldb/test/API/commands/trace/TestTraceStartStop.py

Removed: 
    lldb/test/API/commands/trace/TestTraceTimestampCounters.py


################################################################################
diff  --git a/lldb/include/lldb/Target/TraceCursor.h b/lldb/include/lldb/Target/TraceCursor.h
index 2e3c4eb22b04e..710bb963a4aa9 100644
--- a/lldb/include/lldb/Target/TraceCursor.h
+++ b/lldb/include/lldb/Target/TraceCursor.h
@@ -74,9 +74,10 @@ class TraceCursor {
 public:
   /// Helper enum to indicate the reference point when invoking
   /// \a TraceCursor::Seek().
+  /// The following values are inspired by \a std::istream::seekg.
   enum class SeekType {
     /// The beginning of the trace, i.e the oldest item.
-    Set = 0,
+    Beginning = 0,
     /// The current position in the trace.
     Current,
     /// The end of the trace, i.e the most recent item.
@@ -133,6 +134,51 @@ class TraceCursor {
   ///     \b true if the cursor effectively moved, \b false otherwise.
   virtual bool Next() = 0;
 
+  /// Instruction identifiers:
+  ///
+  /// When building complex higher level tools, fast random accesses in the
+  /// trace might be needed, for which each instruction requires a unique
+  /// identifier within its thread trace. For example, a tool might want to
+  /// repeatedly inspect random consecutive portions of a trace. This means that
+  /// it will need to first move quickly to the beginning of each section and
+  /// then start its iteration. Given that the number of instructions can be in
+  /// the order of hundreds of millions, fast random access is necessary.
+  ///
+  /// An example of such a tool could be an inspector of the call graph of a
+  /// trace, where each call is represented with its start and end instructions.
+  /// Inspecting all the instructions of a call requires moving to its first
+  /// instruction and then iterating until the last instruction, which following
+  /// the pattern explained above.
+  ///
+  /// Instead of using 0-based indices as identifiers, each Trace plug-in can
+  /// decide the nature of these identifiers and thus no assumptions can be made
+  /// regarding their ordering and sequentiality. The reason is that an
+  /// instruction might be encoded by the plug-in in a way that hides its actual
+  /// 0-based index in the trace, but it's still possible to efficiently find
+  /// it.
+  ///
+  /// Requirements:
+  /// - For a given thread, no two instructions have the same id.
+  /// - In terms of efficiency, moving the cursor to a given id should be as
+  ///   fast as possible, but not necessarily O(1). That's why the recommended
+  ///   way to traverse sequential instructions is to use the \a
+  ///   TraceCursor::Next() method and only use \a TraceCursor::GoToId(id)
+  ///   sparingly.
+
+  /// Make the cursor point to the item whose identifier is \p id.
+  ///
+  /// \return
+  ///     \b true if the given identifier exists and the cursor effectively
+  ///     moved. Otherwise, \b false is returned and the cursor doesn't change
+  ///     its position.
+  virtual bool GoToId(lldb::user_id_t id) = 0;
+
+  /// \return
+  ///     A unique identifier for the instruction or error this cursor is
+  ///     pointing to.
+  virtual lldb::user_id_t GetId() const = 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.
@@ -152,7 +198,7 @@ class TraceCursor {
   ///
   /// \return
   ///     The number of trace items moved from the origin.
-  virtual size_t Seek(ssize_t offset, SeekType origin) = 0;
+  virtual uint64_t Seek(int64_t offset, SeekType origin) = 0;
 
   /// \return
   ///   The \a ExecutionContextRef of the backing thread from the creation time

diff  --git a/lldb/include/lldb/Target/TraceInstructionDumper.h b/lldb/include/lldb/Target/TraceInstructionDumper.h
index c4878bfd3fd59..f66509e462dd4 100644
--- a/lldb/include/lldb/Target/TraceInstructionDumper.h
+++ b/lldb/include/lldb/Target/TraceInstructionDumper.h
@@ -13,6 +13,26 @@
 
 namespace lldb_private {
 
+/// Class that holds the configuration used by \a TraceInstructionDumper for
+/// traversing and dumping instructions.
+struct TraceInstructionDumperOptions {
+  /// If \b true, the cursor will be iterated forwards starting from the
+  /// oldest instruction. Otherwise, the iteration starts from the most
+  /// recent instruction.
+  bool forwards = false;
+  /// Dump only instruction addresses without disassembly nor symbol
+  /// information.
+  bool raw = false;
+  /// For each instruction, print the corresponding timestamp counter if
+  /// available.
+  bool show_tsc = false;
+  /// Optional custom id to start traversing from.
+  llvm::Optional<uint64_t> id = llvm::None;
+  /// Optional number of instructions to skip from the starting position
+  /// of the cursor.
+  llvm::Optional<size_t> skip = llvm::None;
+};
+
 /// Class used to dump the instructions of a \a TraceCursor using its current
 /// state and granularity.
 class TraceInstructionDumper {
@@ -22,20 +42,13 @@ class TraceInstructionDumper {
   /// \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.
+  /// \param[in] s
+  ///     The stream where to dump the instructions to.
   ///
-  /// \param[in] show_tsc
-  ///     For each instruction, print the corresponding timestamp counter if
-  ///     available.
-  TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up, int initial_index = 0,
-                         bool raw = false, bool show_tsc = false);
+  /// \param[in] options
+  ///     Additional options for configuring the dumping.
+  TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up, Stream &s,
+                         const TraceInstructionDumperOptions &options);
 
   /// Dump \a count instructions of the thread trace starting at the current
   /// cursor position.
@@ -43,21 +56,23 @@ class TraceInstructionDumper {
   /// 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
+  ///     The instruction id of the last traversed instruction, or \b llvm::None
+  ///     if no instructions were visited.
+  llvm::Optional<lldb::user_id_t> DumpInstructions(size_t count);
 
   /// \return
   ///     \b true if there's still more data to traverse in the trace.
   bool HasMoreData();
 
 private:
+  /// Indicate to the dumper that no more data is available in the trace.
+  /// This will prevent further iterations.
+  void SetNoMoreData();
+
   /// Move the cursor one step.
   ///
   /// \return
@@ -65,9 +80,8 @@ class TraceInstructionDumper {
   bool TryMoveOneStep();
 
   lldb::TraceCursorUP m_cursor_up;
-  int m_index;
-  bool m_raw;
-  bool m_show_tsc;
+  TraceInstructionDumperOptions m_options;
+  Stream &m_s;
   /// If \b true, all the instructions have been traversed.
   bool m_no_more_data = false;
 };

diff  --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index a87addc4d3040..78ec5409eb7a5 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -2099,8 +2099,7 @@ class CommandObjectTraceStop : public CommandObjectMultipleThreads {
 #define LLDB_OPTIONS_thread_trace_dump_instructions
 #include "CommandOptions.inc"
 
-class CommandObjectTraceDumpInstructions
-    : public CommandObjectIterateOverThreads {
+class CommandObjectTraceDumpInstructions : public CommandObjectParsed {
 public:
   class CommandOptions : public Options {
   public:
@@ -2132,19 +2131,29 @@ class CommandObjectTraceDumpInstructions
               "invalid integer value for option '%s'",
               option_arg.str().c_str());
         else
-          m_skip = skip;
+          m_dumper_options.skip = skip;
+        break;
+      }
+      case 'i': {
+        uint64_t id;
+        if (option_arg.empty() || option_arg.getAsInteger(0, id))
+          error.SetErrorStringWithFormat(
+              "invalid integer value for option '%s'",
+              option_arg.str().c_str());
+        else
+          m_dumper_options.id = id;
         break;
       }
       case 'r': {
-        m_raw = true;
+        m_dumper_options.raw = true;
         break;
       }
       case 'f': {
-        m_forwards = true;
+        m_dumper_options.forwards = true;
         break;
       }
       case 't': {
-        m_show_tsc = true;
+        m_dumper_options.show_tsc = true;
         break;
       }
       case 'C': {
@@ -2159,11 +2168,8 @@ class CommandObjectTraceDumpInstructions
 
     void OptionParsingStarting(ExecutionContext *execution_context) override {
       m_count = kDefaultCount;
-      m_skip = 0;
-      m_raw = false;
-      m_forwards = false;
-      m_show_tsc = false;
       m_continue = false;
+      m_dumper_options = {};
     }
 
     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -2174,23 +2180,19 @@ class CommandObjectTraceDumpInstructions
 
     // Instance variables to hold the values for command options.
     size_t m_count;
-    size_t m_skip;
-    bool m_raw;
-    bool m_forwards;
-    bool m_show_tsc;
-    bool m_continue;
+    size_t m_continue;
+    TraceInstructionDumperOptions m_dumper_options;
   };
 
   CommandObjectTraceDumpInstructions(CommandInterpreter &interpreter)
-      : CommandObjectIterateOverThreads(
+      : CommandObjectParsed(
             interpreter, "thread trace dump instructions",
-            "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.",
+            "Dump the traced instructions for one thread. If no "
+            "thread is specified, show the current thread.",
             nullptr,
-            eCommandRequiresProcess | eCommandTryTargetAPILock |
-                eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
-                eCommandProcessMustBeTraced) {}
+            eCommandRequiresProcess | eCommandRequiresThread |
+                eCommandTryTargetAPILock | eCommandProcessMustBeLaunched |
+                eCommandProcessMustBePaused | eCommandProcessMustBeTraced) {}
 
   ~CommandObjectTraceDumpInstructions() override = default;
 
@@ -2200,57 +2202,63 @@ class CommandObjectTraceDumpInstructions
                                                uint32_t index) override {
     std::string cmd;
     current_command_args.GetCommandString(cmd);
-    if (cmd.find("--continue") == std::string::npos)
+    if (cmd.find(" --continue") == std::string::npos)
       cmd += " --continue";
     return cmd;
   }
 
 protected:
-  bool DoExecute(Args &args, CommandReturnObject &result) override {
-    if (!m_options.m_continue)
-      m_dumpers.clear();
+  ThreadSP GetThread(Args &args, CommandReturnObject &result) {
+    if (args.GetArgumentCount() == 0)
+      return m_exe_ctx.GetThreadSP();
 
-    return CommandObjectIterateOverThreads::DoExecute(args, result);
-  }
-
-  bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
-    Stream &s = result.GetOutputStream();
+    const char *arg = args.GetArgumentAtIndex(0);
+    uint32_t thread_idx;
 
-    const TraceSP &trace_sp = m_exe_ctx.GetTargetSP()->GetTrace();
+    if (!llvm::to_integer(arg, thread_idx)) {
+      result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
+                                   arg);
+      return nullptr;
+    }
     ThreadSP thread_sp =
-        m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
-
-    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();
+        m_exe_ctx.GetProcessRef().GetThreadList().FindThreadByIndexID(
+            thread_idx);
+    if (!thread_sp)
+      result.AppendErrorWithFormat("no thread with index: \"%s\"\n", arg);
+    return thread_sp;
+  }
 
-      auto dumper = std::make_unique<TraceInstructionDumper>(
-          std::move(cursor_up), initial_index, m_options.m_raw,
-          m_options.m_show_tsc);
+  bool DoExecute(Args &args, CommandReturnObject &result) override {
+    ThreadSP thread_sp = GetThread(args, result);
+    if (!thread_sp)
+      return false;
 
-      // 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();
+    Stream &s = result.GetOutputStream();
+    s.Printf("thread #%u: tid = %" PRIu64 "\n", thread_sp->GetIndexID(),
+             thread_sp->GetID());
 
-      m_dumpers[thread_sp->GetID()] = std::move(dumper);
+    if (m_options.m_continue) {
+      if (!m_last_id) {
+        result.AppendMessage("    no more data\n");
+        return true;
+      }
+      // We set up the options to continue one instruction past where
+      // the previous iteration stopped.
+      m_options.m_dumper_options.skip = 1;
+      m_options.m_dumper_options.id = m_last_id;
     }
 
-    m_dumpers[thread_sp->GetID()]->DumpInstructions(s, m_options.m_count);
+    const TraceSP &trace_sp = m_exe_ctx.GetTargetSP()->GetTrace();
+    TraceInstructionDumper dumper(trace_sp->GetCursor(*thread_sp), s,
+                                  m_options.m_dumper_options);
+    m_last_id = dumper.DumpInstructions(m_options.m_count);
     return true;
   }
 
   CommandOptions m_options;
-  std::map<lldb::tid_t, std::unique_ptr<TraceInstructionDumper>> m_dumpers;
+  // Last traversed id used to continue a repeat command. None means
+  // that all the trace has been consumed.
+  llvm::Optional<lldb::user_id_t> m_last_id;
 };
 
 // CommandObjectTraceDumpInfo

diff  --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 2720666cc5f0d..35924ad6f558d 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1097,7 +1097,8 @@ let Command = "thread plan list" in {
 }
 
 let Command = "thread trace dump instructions" in {
-  def thread_trace_dump_instructions_forwards: Option<"forwards", "f">, Group<1>,
+  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.">;
@@ -1105,17 +1106,21 @@ let Command = "thread trace dump instructions" in {
     Arg<"Count">,
     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>,
+  def thread_trace_dump_instructions_id: Option<"id", "i">, Group<1>,
     Arg<"Index">,
-    Desc<"How many instruction to skip from the end of the trace to start "
-    "dumping instructions, or from the beginning if --forwards is provided">;
+    Desc<"Custom starting instruction id from where to start traversing. This "
+    "id can be provided in decimal or hexadecimal representation.">;
+  def thread_trace_dump_instructions_skip: Option<"skip", "s">, Group<1>,
+    Arg<"Index">,
+    Desc<"How many instruction to skip from the starting position of the trace "
+    "before starting the traversal.">;
   def thread_trace_dump_instructions_raw : Option<"raw", "r">,
     Group<1>,
-    Desc<"Dump only instruction address without disassembly nor symbol information.">;
-  def thread_trace_dump_instructions_show_tsc : Option<"tsc", "t">,
-    Group<1>,
-    Desc<"For each instruction, print the corresponding timestamp counter if available.">;
+    Desc<"Dump only instruction address without disassembly nor symbol "
+    "information.">;
+  def thread_trace_dump_instructions_show_tsc : Option<"tsc", "t">, Group<1>,
+    Desc<"For each instruction, print the corresponding timestamp counter if "
+    "available.">;
   def thread_trace_dump_instructions_continue: Option<"continue", "C">,
     Group<1>,
     Desc<"Continue dumping instructions right where the previous invocation of this "

diff  --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
index a4bf0c725f7e6..f1a5a78ef0d11 100644
--- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -112,7 +112,7 @@ void DecodedThread::AppendError(llvm::Error &&error) {
   m_errors.try_emplace(m_instruction_ips.size(), toString(std::move(error)));
   m_instruction_ips.emplace_back(LLDB_INVALID_ADDRESS);
   m_instruction_sizes.emplace_back(0);
-  m_instruction_classes.emplace_back(pt_insn_class::ptic_unknown);
+  m_instruction_classes.emplace_back(pt_insn_class::ptic_error);
 }
 
 void DecodedThread::AppendError(llvm::Error &&error, uint64_t tsc) {
@@ -133,8 +133,25 @@ const DecodedThread::LibiptErrors &DecodedThread::GetTscErrors() const {
   return m_tsc_errors;
 }
 
-Optional<DecodedThread::TscRange>
-DecodedThread::CalculateTscRange(size_t insn_index) const {
+Optional<DecodedThread::TscRange> DecodedThread::CalculateTscRange(
+    size_t insn_index,
+    const Optional<DecodedThread::TscRange> &hint_range) const {
+  // We first try to check the given hint range in case we are traversing the
+  // trace in short jumps. If that fails, then we do the more expensive
+  // arbitrary lookup.
+  if (hint_range) {
+    Optional<TscRange> candidate_range;
+    if (insn_index < hint_range->GetStartInstructionIndex())
+      candidate_range = hint_range->Prev();
+    else if (insn_index > hint_range->GetEndInstructionIndex())
+      candidate_range = hint_range->Next();
+    else
+      candidate_range = hint_range;
+
+    if (candidate_range && candidate_range->InRange(insn_index))
+      return candidate_range;
+  }
+  // Now we do a more expensive lookup
   auto it = m_instruction_timestamps.upper_bound(insn_index);
   if (it == m_instruction_timestamps.begin())
     return None;
@@ -199,12 +216,12 @@ size_t DecodedThread::TscRange::GetEndInstructionIndex() const {
   return m_end_index;
 }
 
-bool DecodedThread::TscRange::InRange(size_t insn_index) {
+bool DecodedThread::TscRange::InRange(size_t insn_index) const {
   return GetStartInstructionIndex() <= insn_index &&
          insn_index <= GetEndInstructionIndex();
 }
 
-Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() {
+Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() const {
   auto next_it = m_it;
   ++next_it;
   if (next_it == m_decoded_thread->m_instruction_timestamps.end())
@@ -212,7 +229,7 @@ Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() {
   return TscRange(next_it, *m_decoded_thread);
 }
 
-Optional<DecodedThread::TscRange> DecodedThread::TscRange::Prev() {
+Optional<DecodedThread::TscRange> DecodedThread::TscRange::Prev() const {
   if (m_it == m_decoded_thread->m_instruction_timestamps.begin())
     return None;
   auto prev_it = m_it;

diff  --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
index f558fd4a8def6..dae69c90816d8 100644
--- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
+++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -71,13 +71,13 @@ class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
   class TscRange {
   public:
     /// Check if this TSC range includes the given instruction index.
-    bool InRange(size_t insn_index);
+    bool InRange(size_t insn_index) const;
 
     /// Get the next range chronologically.
-    llvm::Optional<TscRange> Next();
+    llvm::Optional<TscRange> Next() const;
 
     /// Get the previous range chronologically.
-    llvm::Optional<TscRange> Prev();
+    llvm::Optional<TscRange> Prev() const;
 
     /// Get the TSC value.
     size_t GetTsc() const;
@@ -150,7 +150,17 @@ class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
   /// If the trace was collected with TSC support, all the instructions of
   /// the trace will have associated TSCs. This means that this method will
   /// only return \b llvm::None if there are no TSCs whatsoever in the trace.
-  llvm::Optional<TscRange> CalculateTscRange(size_t insn_index) const;
+  ///
+  /// \param[in] insn_index
+  ///   The instruction index in question.
+  ///
+  /// \param[in] hint_range
+  ///   An optional range that might include the given index or might be a
+  ///   neighbor of it. It might help speed it traversals of the trace with
+  ///   short jumps.
+  llvm::Optional<TscRange> CalculateTscRange(
+      size_t insn_index,
+      const llvm::Optional<DecodedThread::TscRange> &hint_range) const;
 
   /// Check if an instruction given by its index is an error.
   bool IsInstructionAnError(size_t insn_idx) const;

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
index d9a52219d0d6f..1adb4b6b578e6 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
@@ -23,7 +23,8 @@ TraceCursorIntelPT::TraceCursorIntelPT(ThreadSP thread_sp,
   assert(m_decoded_thread_sp->GetInstructionsCount() > 0 &&
          "a trace should have at least one instruction or error");
   m_pos = m_decoded_thread_sp->GetInstructionsCount() - 1;
-  m_tsc_range = m_decoded_thread_sp->CalculateTscRange(m_pos);
+  m_tsc_range =
+      m_decoded_thread_sp->CalculateTscRange(m_pos, /*hint_range*/ None);
 }
 
 size_t TraceCursorIntelPT::GetInternalInstructionSize() {
@@ -56,7 +57,7 @@ bool TraceCursorIntelPT::Next() {
   return false;
 }
 
-size_t TraceCursorIntelPT::Seek(int64_t offset, SeekType origin) {
+uint64_t TraceCursorIntelPT::Seek(int64_t offset, SeekType origin) {
   int64_t last_index = GetInternalInstructionSize() - 1;
 
   auto fitPosToBounds = [&](int64_t raw_pos) -> int64_t {
@@ -65,7 +66,7 @@ size_t TraceCursorIntelPT::Seek(int64_t offset, SeekType origin) {
 
   auto FindDistanceAndSetPos = [&]() -> int64_t {
     switch (origin) {
-    case TraceCursor::SeekType::Set:
+    case TraceCursor::SeekType::Beginning:
       m_pos = fitPosToBounds(offset);
       return m_pos;
     case TraceCursor::SeekType::End:
@@ -80,7 +81,7 @@ size_t TraceCursorIntelPT::Seek(int64_t offset, SeekType origin) {
   };
 
   int64_t dist = FindDistanceAndSetPos();
-  m_tsc_range = m_decoded_thread_sp->CalculateTscRange(m_pos);
+  m_tsc_range = m_decoded_thread_sp->CalculateTscRange(m_pos, m_tsc_range);
   return dist;
 }
 
@@ -111,3 +112,14 @@ TraceInstructionControlFlowType
 TraceCursorIntelPT::GetInstructionControlFlowType() {
   return m_decoded_thread_sp->GetInstructionControlFlowType(m_pos);
 }
+
+bool TraceCursorIntelPT::GoToId(user_id_t id) {
+  if (m_decoded_thread_sp->GetInstructionsCount() <= id)
+    return false;
+  m_pos = id;
+  m_tsc_range = m_decoded_thread_sp->CalculateTscRange(m_pos, m_tsc_range);
+
+  return true;
+}
+
+user_id_t TraceCursorIntelPT::GetId() const { return m_pos; }

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
index ef40089f172fa..7cec257793fb2 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
@@ -20,7 +20,7 @@ class TraceCursorIntelPT : public TraceCursor {
   TraceCursorIntelPT(lldb::ThreadSP thread_sp,
                      DecodedThreadSP decoded_thread_sp);
 
-  size_t Seek(int64_t offset, SeekType origin) override;
+  uint64_t Seek(int64_t offset, SeekType origin) override;
 
   virtual bool Next() override;
 
@@ -35,6 +35,10 @@ class TraceCursorIntelPT : public TraceCursor {
 
   bool IsError() override;
 
+  bool GoToId(lldb::user_id_t id) override;
+
+  lldb::user_id_t GetId() const override;
+
 private:
   size_t GetInternalInstructionSize();
 

diff  --git a/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp b/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp
index d29445cc004f4..a927905464314 100644
--- a/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp
+++ b/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp
@@ -130,7 +130,7 @@ TraceHTR::TraceHTR(Thread &thread, TraceCursor &cursor)
 
   // Move cursor to the first instruction in the trace
   cursor.SetForwards(true);
-  cursor.Seek(0, TraceCursor::SeekType::Set);
+  cursor.Seek(0, TraceCursor::SeekType::Beginning);
 
   Target &target = thread.GetProcess()->GetTarget();
   auto function_name_from_load_address =

diff  --git a/lldb/source/Target/TraceInstructionDumper.cpp b/lldb/source/Target/TraceInstructionDumper.cpp
index 52a97babd0a26..09ff72d0fa1d3 100644
--- a/lldb/source/Target/TraceInstructionDumper.cpp
+++ b/lldb/source/Target/TraceInstructionDumper.cpp
@@ -18,11 +18,34 @@ using namespace lldb;
 using namespace lldb_private;
 using namespace llvm;
 
-TraceInstructionDumper::TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up,
-                                               int initial_index, bool raw,
-                                               bool show_tsc)
-    : m_cursor_up(std::move(cursor_up)), m_index(initial_index), m_raw(raw),
-      m_show_tsc(show_tsc) {}
+TraceInstructionDumper::TraceInstructionDumper(
+    lldb::TraceCursorUP &&cursor_up, Stream &s,
+    const TraceInstructionDumperOptions &options)
+    : m_cursor_up(std::move(cursor_up)), m_options(options), m_s(s) {
+  // We first set the cursor in its initial position
+  if (m_options.id) {
+    if (!m_cursor_up->GoToId(*m_options.id)) {
+      s.PutCString("    invalid instruction id\n");
+      SetNoMoreData();
+      return;
+    }
+  } else if (m_options.forwards) {
+    m_cursor_up->Seek(0, TraceCursor::SeekType::Beginning);
+  } else {
+    m_cursor_up->Seek(0, TraceCursor::SeekType::End);
+  }
+
+  m_cursor_up->SetForwards(m_options.forwards);
+  if (m_options.skip) {
+    uint64_t to_skip = m_options.skip.getValue();
+    if (m_cursor_up->Seek((m_options.forwards ? 1 : -1) * to_skip,
+                          TraceCursor::SeekType::Current) < to_skip) {
+      // This happens when the skip value was more than the number of
+      // available instructions.
+      SetNoMoreData();
+    }
+  }
+}
 
 /// \return
 ///     Return \b true if the cursor could move one step.
@@ -31,19 +54,9 @@ bool TraceInstructionDumper::TryMoveOneStep() {
     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 {
@@ -159,36 +172,31 @@ void TraceInstructionDumper::SetNoMoreData() { m_no_more_data = true; }
 
 bool TraceInstructionDumper::HasMoreData() { return !m_no_more_data; }
 
-void TraceInstructionDumper::DumpInstructions(Stream &s, size_t count) {
+Optional<lldb::tid_t> TraceInstructionDumper::DumpInstructions(size_t count) {
   ThreadSP thread_sp = m_cursor_up->GetExecutionContextRef().GetThreadSP();
   if (!thread_sp) {
-    s.Printf("invalid thread");
-    return;
+    m_s.Printf("invalid thread");
+    return None;
   }
 
-  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");
+    m_s.Printf("    ...missing instructions\n");
   };
 
-  auto printInstructionIndex = [&]() {
-    s.Printf("    [%*d] ", digits_count, m_index);
+  auto printInstructionHeader = [&](uint64_t id) {
+    m_s.Printf("    %" PRIu64 ": ", id);
 
-    if (m_show_tsc) {
-      s.Printf("[tsc=");
+    if (m_options.show_tsc) {
+      m_s.Printf("[tsc=");
 
       if (Optional<uint64_t> timestamp = m_cursor_up->GetCounter(lldb::eTraceCounterTSC))
-        s.Printf("0x%016" PRIx64, *timestamp);
+        m_s.Printf("0x%016" PRIx64, *timestamp);
       else
-        s.Printf("unavailable");
+        m_s.Printf("unavailable");
 
-      s.Printf("] ");
+      m_s.Printf("] ");
     }
   };
 
@@ -244,11 +252,13 @@ void TraceInstructionDumper::DumpInstructions(Stream &s, size_t count) {
                                         : InstructionSP());
   };
 
+  Optional<lldb::user_id_t> last_id;
   for (size_t i = 0; i < count; i++) {
     if (!HasMoreData()) {
-      s.Printf("    no more data\n");
+      m_s.Printf("    no more data\n");
       break;
     }
+    last_id = m_cursor_up->GetId();
 
     if (const char *err = m_cursor_up->GetError()) {
       if (!m_cursor_up->IsForwards() && !was_prev_instruction_an_error)
@@ -256,8 +266,8 @@ void TraceInstructionDumper::DumpInstructions(Stream &s, size_t count) {
 
       was_prev_instruction_an_error = true;
 
-      printInstructionIndex();
-      s << err;
+      printInstructionHeader(m_cursor_up->GetId());
+      m_s << err;
     } else {
       if (m_cursor_up->IsForwards() && was_prev_instruction_an_error)
         printMissingInstructionsMessage();
@@ -266,7 +276,7 @@ void TraceInstructionDumper::DumpInstructions(Stream &s, size_t count) {
 
       InstructionSymbolInfo insn_info;
 
-      if (!m_raw) {
+      if (!m_options.raw) {
         insn_info.load_address = m_cursor_up->GetLoadAddress();
         insn_info.exe_ctx = exe_ctx;
         insn_info.address.SetLoadAddress(insn_info.load_address, &target);
@@ -274,19 +284,20 @@ void TraceInstructionDumper::DumpInstructions(Stream &s, size_t count) {
         std::tie(insn_info.disassembler, insn_info.instruction) =
             calculateDisass(insn_info.address, insn_info.sc);
 
-        DumpInstructionSymbolContext(s, prev_insn_info, insn_info);
+        DumpInstructionSymbolContext(m_s, prev_insn_info, insn_info);
       }
 
-      printInstructionIndex();
-      s.Printf("0x%016" PRIx64, m_cursor_up->GetLoadAddress());
+      printInstructionHeader(m_cursor_up->GetId());
+      m_s.Printf("0x%016" PRIx64, m_cursor_up->GetLoadAddress());
 
-      if (!m_raw)
-        DumpInstructionDisassembly(s, insn_info);
+      if (!m_options.raw)
+        DumpInstructionDisassembly(m_s, insn_info);
 
       prev_insn_info = insn_info;
     }
 
-    s.Printf("\n");
+    m_s.Printf("\n");
     TryMoveOneStep();
   }
+  return last_id;
 }

diff  --git a/lldb/test/API/commands/trace/TestTraceDumpInstructions.py b/lldb/test/API/commands/trace/TestTraceDumpInstructions.py
index 7b2e629c6e18f..bbcee695a619c 100644
--- a/lldb/test/API/commands/trace/TestTraceDumpInstructions.py
+++ b/lldb/test/API/commands/trace/TestTraceDumpInstructions.py
@@ -37,49 +37,67 @@ def testRawDumpInstructions(self):
 
         self.expect("thread trace dump instructions --raw --count 21 --forwards",
             substrs=['''thread #1: tid = 3842849
-    [ 0] 0x0000000000400511
-    [ 1] 0x0000000000400518
-    [ 2] 0x000000000040051f
-    [ 3] 0x0000000000400529
-    [ 4] 0x000000000040052d
-    [ 5] 0x0000000000400521
-    [ 6] 0x0000000000400525
-    [ 7] 0x0000000000400529
-    [ 8] 0x000000000040052d
-    [ 9] 0x0000000000400521
-    [10] 0x0000000000400525
-    [11] 0x0000000000400529
-    [12] 0x000000000040052d
-    [13] 0x0000000000400521
-    [14] 0x0000000000400525
-    [15] 0x0000000000400529
-    [16] 0x000000000040052d
-    [17] 0x0000000000400521
-    [18] 0x0000000000400525
-    [19] 0x0000000000400529
-    [20] 0x000000000040052d'''])
+    0: 0x0000000000400511
+    1: 0x0000000000400518
+    2: 0x000000000040051f
+    3: 0x0000000000400529
+    4: 0x000000000040052d
+    5: 0x0000000000400521
+    6: 0x0000000000400525
+    7: 0x0000000000400529
+    8: 0x000000000040052d
+    9: 0x0000000000400521
+    10: 0x0000000000400525
+    11: 0x0000000000400529
+    12: 0x000000000040052d
+    13: 0x0000000000400521
+    14: 0x0000000000400525
+    15: 0x0000000000400529
+    16: 0x000000000040052d
+    17: 0x0000000000400521
+    18: 0x0000000000400525
+    19: 0x0000000000400529
+    20: 0x000000000040052d'''])
 
         # 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'''])
+    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'''])
+    14: 0x0000000000400525
+    13: 0x0000000000400521
+    12: 0x000000000040052d
+    11: 0x0000000000400529
+    10: 0x0000000000400525'''])
+
+        # We check if we can pass count and skip and instruction id in hex
+        self.expect("thread trace dump instructions --count 5 --skip 6 --raw --id 0xA",
+            substrs=['''thread #1: tid = 3842849
+    4: 0x000000000040052d
+    3: 0x0000000000400529
+    2: 0x000000000040051f
+    1: 0x0000000000400518
+    0: 0x0000000000400511'''])
+
+        # We check if we can pass count and skip and instruction id in decimal
+        self.expect("thread trace dump instructions --count 5 --skip 6 --raw --id 10",
+            substrs=['''thread #1: tid = 3842849
+    4: 0x000000000040052d
+    3: 0x0000000000400529
+    2: 0x000000000040051f
+    1: 0x0000000000400518
+    0: 0x0000000000400511'''])
 
         # We check if we can access the thread by index id
         self.expect("thread trace dump instructions 1 --raw",
             substrs=['''thread #1: tid = 3842849
-    [  0] 0x000000000040052d'''])
+    20: 0x000000000040052d'''])
 
         # We check that we get an error when using an invalid thread index id
         self.expect("thread trace dump instructions 10", error=True,
@@ -90,53 +108,35 @@ def testDumpFullInstructionsWithMultipleThreads(self):
         self.expect("trace load -v " +
             os.path.join(self.getSourceDir(), "intelpt-trace", "trace_2threads.json"))
 
-        # We print the instructions of two threads simultaneously
-        self.expect("thread trace dump instructions 1 2 --count 2",
-            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
+        # We print the instructions of a specific thread
+        self.expect("thread trace dump instructions 2 --count 2",
+            substrs=['''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)'''])
+    20: 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
+    19: 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
+        self.expect("thread trace dump instructions 2 --count 2 --skip 2", inHistory=True,
+            substrs=['''thread #2: tid = 3842850
   a.out`main + 24 at main.cpp:4
-    [-2] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
+    18: 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)'''])
+    17: 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
-  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
+            substrs=['''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)'''])
+    16: 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
+    15: 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)'''])
 
         self.expect("", inHistory=True,
-            substrs=['''thread #1: tid = 3842849
-  a.out`main + 24 at main.cpp:4
-    [-6] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
-  a.out`main + 20 at main.cpp:5
-    [-7] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)
-thread #2: tid = 3842850
+            substrs=['''thread #2: tid = 3842850
   a.out`main + 24 at main.cpp:4
-    [-6] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
+    14: 0x0000000000400525    addl   $0x1, -0x8(%rbp)
   a.out`main + 20 at main.cpp:5
-    [-7] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)'''])
+    13: 0x0000000000400521    xorl   $0x1, -0x4(%rbp)'''])
 
     def testInvalidBounds(self):
         self.expect("trace load -v " +
@@ -146,10 +146,10 @@ def testInvalidBounds(self):
         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 --skip 23",
@@ -164,15 +164,15 @@ def testWrongImage(self):
             os.path.join(self.getSourceDir(), "intelpt-trace", "trace_bad_image.json"))
         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'''])
+    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 --forwards",
             substrs=['''thread #1: tid = 3842849
-    [ 0] error: unknown cpu'''])
+    0: error: unknown cpu'''])
 
     def testMultiFileTraceWithMissingModule(self):
         self.expect("trace load " +
@@ -195,147 +195,147 @@ def testMultiFileTraceWithMissingModule(self):
         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()
+    0: 0x000000000040066f    callq  0x400540                  ; symbol stub for: foo()
   a.out`symbol stub for: foo()
-    [ 1] 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
-    [ 2] 0x0000000000400546    pushq  $0x2
-    [ 3] 0x000000000040054b    jmp    0x400510
+    1: 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
+    2: 0x0000000000400546    pushq  $0x2
+    3: 0x000000000040054b    jmp    0x400510
   a.out`(none)
-    [ 4] 0x0000000000400510    pushq  0x200af2(%rip)            ; _GLOBAL_OFFSET_TABLE_ + 8
-    [ 5] 0x0000000000400516    jmpq   *0x200af4(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 16
-    [ 6] 0x00007ffff7df1950    error: no memory mapped at this address
+    4: 0x0000000000400510    pushq  0x200af2(%rip)            ; _GLOBAL_OFFSET_TABLE_ + 8
+    5: 0x0000000000400516    jmpq   *0x200af4(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 16
+    6: 0x00007ffff7df1950    error: no memory mapped at this address
     ...missing instructions
   a.out`main + 20 at main.cpp:10
-    [ 7] 0x0000000000400674    movl   %eax, -0xc(%rbp)
+    7: 0x0000000000400674    movl   %eax, -0xc(%rbp)
   a.out`main + 23 at main.cpp:12
-    [ 8] 0x0000000000400677    movl   -0xc(%rbp), %eax
-    [ 9] 0x000000000040067a    addl   $0x1, %eax
-    [10] 0x000000000040067f    movl   %eax, -0xc(%rbp)
+    8: 0x0000000000400677    movl   -0xc(%rbp), %eax
+    9: 0x000000000040067a    addl   $0x1, %eax
+    10: 0x000000000040067f    movl   %eax, -0xc(%rbp)
   a.out`main + 34 [inlined] inline_function() at main.cpp:4
-    [11] 0x0000000000400682    movl   $0x0, -0x4(%rbp)
+    11: 0x0000000000400682    movl   $0x0, -0x4(%rbp)
   a.out`main + 41 [inlined] inline_function() + 7 at main.cpp:5
-    [12] 0x0000000000400689    movl   -0x4(%rbp), %eax
-    [13] 0x000000000040068c    addl   $0x1, %eax
-    [14] 0x0000000000400691    movl   %eax, -0x4(%rbp)
+    12: 0x0000000000400689    movl   -0x4(%rbp), %eax
+    13: 0x000000000040068c    addl   $0x1, %eax
+    14: 0x0000000000400691    movl   %eax, -0x4(%rbp)
   a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6
-    [15] 0x0000000000400694    movl   -0x4(%rbp), %eax
+    15: 0x0000000000400694    movl   -0x4(%rbp), %eax
   a.out`main + 55 at main.cpp:14
-    [16] 0x0000000000400697    movl   -0xc(%rbp), %ecx
-    [17] 0x000000000040069a    addl   %eax, %ecx
-    [18] 0x000000000040069c    movl   %ecx, -0xc(%rbp)
+    16: 0x0000000000400697    movl   -0xc(%rbp), %ecx
+    17: 0x000000000040069a    addl   %eax, %ecx
+    18: 0x000000000040069c    movl   %ecx, -0xc(%rbp)
   a.out`main + 63 at main.cpp:16
-    [19] 0x000000000040069f    callq  0x400540                  ; symbol stub for: foo()
+    19: 0x000000000040069f    callq  0x400540                  ; symbol stub for: foo()
   a.out`symbol stub for: foo()
-    [20] 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
+    20: 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
   libfoo.so`foo() at foo.cpp:3
-    [21] 0x00007ffff7bd96e0    pushq  %rbp
-    [22] 0x00007ffff7bd96e1    movq   %rsp, %rbp
+    21: 0x00007ffff7bd96e0    pushq  %rbp
+    22: 0x00007ffff7bd96e1    movq   %rsp, %rbp
   libfoo.so`foo() + 4 at foo.cpp:4
-    [23] 0x00007ffff7bd96e4    subq   $0x10, %rsp
-    [24] 0x00007ffff7bd96e8    callq  0x7ffff7bd95d0            ; symbol stub for: bar()
+    23: 0x00007ffff7bd96e4    subq   $0x10, %rsp
+    24: 0x00007ffff7bd96e8    callq  0x7ffff7bd95d0            ; symbol stub for: bar()
   libfoo.so`symbol stub for: bar()
-    [25] 0x00007ffff7bd95d0    jmpq   *0x200a4a(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 32
+    25: 0x00007ffff7bd95d0    jmpq   *0x200a4a(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 32
   libbar.so`bar() at bar.cpp:1
-    [26] 0x00007ffff79d7690    pushq  %rbp
-    [27] 0x00007ffff79d7691    movq   %rsp, %rbp
+    26: 0x00007ffff79d7690    pushq  %rbp
+    27: 0x00007ffff79d7691    movq   %rsp, %rbp
   libbar.so`bar() + 4 at bar.cpp:2
-    [28] 0x00007ffff79d7694    movl   $0x1, -0x4(%rbp)
+    28: 0x00007ffff79d7694    movl   $0x1, -0x4(%rbp)
   libbar.so`bar() + 11 at bar.cpp:3
-    [29] 0x00007ffff79d769b    movl   -0x4(%rbp), %eax
-    [30] 0x00007ffff79d769e    addl   $0x1, %eax
-    [31] 0x00007ffff79d76a3    movl   %eax, -0x4(%rbp)
+    29: 0x00007ffff79d769b    movl   -0x4(%rbp), %eax
+    30: 0x00007ffff79d769e    addl   $0x1, %eax
+    31: 0x00007ffff79d76a3    movl   %eax, -0x4(%rbp)
   libbar.so`bar() + 22 at bar.cpp:4
-    [32] 0x00007ffff79d76a6    movl   -0x4(%rbp), %eax
-    [33] 0x00007ffff79d76a9    popq   %rbp
-    [34] 0x00007ffff79d76aa    retq''',
+    32: 0x00007ffff79d76a6    movl   -0x4(%rbp), %eax
+    33: 0x00007ffff79d76a9    popq   %rbp
+    34: 0x00007ffff79d76aa    retq''',
   '''libfoo.so`foo() + 13 at foo.cpp:4
-    [35] 0x00007ffff7bd96ed    movl   %eax, -0x4(%rbp)
+    35: 0x00007ffff7bd96ed    movl   %eax, -0x4(%rbp)
   libfoo.so`foo() + 16 at foo.cpp:5
-    [36] 0x00007ffff7bd96f0    movl   -0x4(%rbp), %eax
-    [37] 0x00007ffff7bd96f3    addl   $0x1, %eax
-    [38] 0x00007ffff7bd96f8    movl   %eax, -0x4(%rbp)
+    36: 0x00007ffff7bd96f0    movl   -0x4(%rbp), %eax
+    37: 0x00007ffff7bd96f3    addl   $0x1, %eax
+    38: 0x00007ffff7bd96f8    movl   %eax, -0x4(%rbp)
   libfoo.so`foo() + 27 at foo.cpp:6
-    [39] 0x00007ffff7bd96fb    movl   -0x4(%rbp), %eax
-    [40] 0x00007ffff7bd96fe    addq   $0x10, %rsp
-    [41] 0x00007ffff7bd9702    popq   %rbp
-    [42] 0x00007ffff7bd9703    retq''',
+    39: 0x00007ffff7bd96fb    movl   -0x4(%rbp), %eax
+    40: 0x00007ffff7bd96fe    addq   $0x10, %rsp
+    41: 0x00007ffff7bd9702    popq   %rbp
+    42: 0x00007ffff7bd9703    retq''',
   '''a.out`main + 68 at main.cpp:16
-    [43] 0x00000000004006a4    movl   -0xc(%rbp), %ecx
-    [44] 0x00000000004006a7    addl   %eax, %ecx
-    [45] 0x00000000004006a9    movl   %ecx, -0xc(%rbp)'''])
+    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
+    45: 0x00000000004006a9    movl   %ecx, -0xc(%rbp)
+    44: 0x00000000004006a7    addl   %eax, %ecx
+    43: 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
+    42: 0x00007ffff7bd9703    retq''',
+    '''41: 0x00007ffff7bd9702    popq   %rbp
+    40: 0x00007ffff7bd96fe    addq   $0x10, %rsp
+    39: 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
+    38: 0x00007ffff7bd96f8    movl   %eax, -0x4(%rbp)
+    37: 0x00007ffff7bd96f3    addl   $0x1, %eax
+    36: 0x00007ffff7bd96f0    movl   -0x4(%rbp), %eax
   libfoo.so`foo() + 13 at foo.cpp:4
-    [-10] 0x00007ffff7bd96ed    movl   %eax, -0x4(%rbp)
+    35: 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
+    34: 0x00007ffff79d76aa    retq''',
+    '''33: 0x00007ffff79d76a9    popq   %rbp
+    32: 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
+    31: 0x00007ffff79d76a3    movl   %eax, -0x4(%rbp)
+    30: 0x00007ffff79d769e    addl   $0x1, %eax
+    29: 0x00007ffff79d769b    movl   -0x4(%rbp), %eax
   libbar.so`bar() + 4 at bar.cpp:2
-    [-17] 0x00007ffff79d7694    movl   $0x1, -0x4(%rbp)
+    28: 0x00007ffff79d7694    movl   $0x1, -0x4(%rbp)
   libbar.so`bar() + 1 at bar.cpp:1
-    [-18] 0x00007ffff79d7691    movq   %rsp, %rbp
-    [-19] 0x00007ffff79d7690    pushq  %rbp
+    27: 0x00007ffff79d7691    movq   %rsp, %rbp
+    26: 0x00007ffff79d7690    pushq  %rbp
   libfoo.so`symbol stub for: bar()
-    [-20] 0x00007ffff7bd95d0    jmpq   *0x200a4a(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 32
+    25: 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
+    24: 0x00007ffff7bd96e8    callq  0x7ffff7bd95d0            ; symbol stub for: bar()
+    23: 0x00007ffff7bd96e4    subq   $0x10, %rsp
   libfoo.so`foo() + 1 at foo.cpp:3
-    [-23] 0x00007ffff7bd96e1    movq   %rsp, %rbp
-    [-24] 0x00007ffff7bd96e0    pushq  %rbp
+    22: 0x00007ffff7bd96e1    movq   %rsp, %rbp
+    21: 0x00007ffff7bd96e0    pushq  %rbp
   a.out`symbol stub for: foo()
-    [-25] 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
+    20: 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
   a.out`main + 63 at main.cpp:16
-    [-26] 0x000000000040069f    callq  0x400540                  ; symbol stub for: foo()
+    19: 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
+    18: 0x000000000040069c    movl   %ecx, -0xc(%rbp)
+    17: 0x000000000040069a    addl   %eax, %ecx
+    16: 0x0000000000400697    movl   -0xc(%rbp), %ecx
   a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6
-    [-30] 0x0000000000400694    movl   -0x4(%rbp), %eax
+    15: 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
+    14: 0x0000000000400691    movl   %eax, -0x4(%rbp)
+    13: 0x000000000040068c    addl   $0x1, %eax
+    12: 0x0000000000400689    movl   -0x4(%rbp), %eax
   a.out`main + 34 [inlined] inline_function() at main.cpp:4
-    [-34] 0x0000000000400682    movl   $0x0, -0x4(%rbp)
+    11: 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
+    10: 0x000000000040067f    movl   %eax, -0xc(%rbp)
+    9: 0x000000000040067a    addl   $0x1, %eax
+    8: 0x0000000000400677    movl   -0xc(%rbp), %eax
   a.out`main + 20 at main.cpp:10
-    [-38] 0x0000000000400674    movl   %eax, -0xc(%rbp)
+    7: 0x0000000000400674    movl   %eax, -0xc(%rbp)
     ...missing instructions
-    [-39] 0x00007ffff7df1950    error: no memory mapped at this address
+    6: 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
+    5: 0x0000000000400516    jmpq   *0x200af4(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 16
+    4: 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
+    3: 0x000000000040054b    jmp    0x400510
+    2: 0x0000000000400546    pushq  $0x2
+    1: 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
   a.out`main + 15 at main.cpp:10
-    [-45] 0x000000000040066f    callq  0x400540                  ; symbol stub for: foo()'''])
+    0: 0x000000000040066f    callq  0x400540                  ; symbol stub for: foo()'''])
 
         self.expect("thread trace dump instructions --skip 100 --forwards", inHistory=True,
             substrs=['''thread #1: tid = 815455

diff  --git a/lldb/test/API/commands/trace/TestTraceStartStop.py b/lldb/test/API/commands/trace/TestTraceStartStop.py
index d0d65fdd9cf9c..765e3e829f1c5 100644
--- a/lldb/test/API/commands/trace/TestTraceStartStop.py
+++ b/lldb/test/API/commands/trace/TestTraceStartStop.py
@@ -114,29 +114,29 @@ def testStartStopLiveThreads(self):
         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 -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 .*
+    4: {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5
+    3: {ADDRESS_REGEX}    cmpl .*
+    2: {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
+    1: {ADDRESS_REGEX}    movl .*
   a.out`main \+ 4 at main.cpp:2
-    \[ -4\] {ADDRESS_REGEX}    movl .* '''])
+    0: {ADDRESS_REGEX}    movl .* '''])
 
         # We stop tracing
         self.expect("thread trace stop")
@@ -152,12 +152,12 @@ def testStartStopLiveThreads(self):
         self.expect("thread trace dump instructions -f",
             patterns=[f'''thread #1: tid = .*
   a.out`main \+ 20 at main.cpp:5
-    \[ 0\] {ADDRESS_REGEX}    xorl'''])
+    0: {ADDRESS_REGEX}    xorl'''])
 
         self.expect("thread trace dump instructions",
             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/TestTraceTimestampCounters.py b/lldb/test/API/commands/trace/TestTraceTSC.py
similarity index 72%
rename from lldb/test/API/commands/trace/TestTraceTimestampCounters.py
rename to lldb/test/API/commands/trace/TestTraceTSC.py
index 8126e36990133..4aefe0feacbe0 100644
--- a/lldb/test/API/commands/trace/TestTraceTimestampCounters.py
+++ b/lldb/test/API/commands/trace/TestTraceTSC.py
@@ -19,7 +19,35 @@ def testTscPerThread(self):
 
         self.expect("n")
         self.expect("thread trace dump instructions --tsc -c 1",
-            patterns=["\[0\] \[tsc=0x[0-9a-fA-F]+\] 0x0000000000400511    movl"])
+            patterns=["0: \[tsc=0x[0-9a-fA-F]+\] 0x0000000000400511    movl"])
+
+    @testSBAPIAndCommands
+    @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
+    def testMultipleTscsPerThread(self):
+        self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
+        self.expect("b main")
+        self.expect("r")
+
+        self.traceStartThread(enableTsc=True)
+
+        # After each stop there'll be a new TSC
+        self.expect("n")
+        self.expect("n")
+        self.expect("n")
+
+        # We'll get the most recent instructions, with at least 3 
diff erent TSCs
+        self.runCmd("thread trace dump instructions --tsc --raw")
+        id_to_tsc = {}
+        for line in self.res.GetOutput().splitlines():
+            m = re.search("    (.+): \[tsc=(.+)\].*", line)
+            if m:
+                id_to_tsc[int(m.group(1))] = m.group(2)
+        self.assertEqual(len(id_to_tsc), 6)
+
+        # We check that the values are right when dumping a specific id
+        for id in range(0, 6):
+            self.expect(f"thread trace dump instructions --tsc --id {id} -c 1",
+                substrs=[f"{id}: [tsc={id_to_tsc[id]}]"])
 
     @testSBAPIAndCommands
     @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
@@ -32,7 +60,7 @@ def testTscPerProcess(self):
 
         self.expect("n")
         self.expect("thread trace dump instructions --tsc -c 1",
-            patterns=["\[0\] \[tsc=0x[0-9a-fA-F]+\] 0x0000000000400511    movl"])
+            patterns=["0: \[tsc=0x[0-9a-fA-F]+\] 0x0000000000400511    movl"])
 
     @testSBAPIAndCommands
     @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
@@ -45,7 +73,7 @@ def testDumpingAfterTracingWithoutTsc(self):
 
         self.expect("n")
         self.expect("thread trace dump instructions --tsc -c 1",
-            patterns=["\[0\] \[tsc=unavailable\] 0x0000000000400511    movl"])
+            patterns=["0: \[tsc=unavailable\] 0x0000000000400511    movl"])
 
     @testSBAPIAndCommands
     @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))


        


More information about the lldb-commits mailing list