jj10306 updated this revision to Diff 449289.
jj10306 marked 4 inline comments as done.
jj10306 added a comment.

1. address comments
2. rebase
3. minor changes to `DecodedThread::GetInstructionLoadAddress` and 
`DecodedThread::GetErrorByIndex`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D130930/new/

https://reviews.llvm.org/D130930

Files:
  lldb/bindings/interface/SBTrace.i
  lldb/bindings/interface/SBTraceCursor.i
  lldb/bindings/interfaces.swig
  lldb/include/lldb/API/SBDefines.h
  lldb/include/lldb/API/SBTrace.h
  lldb/include/lldb/API/SBTraceCursor.h
  lldb/include/lldb/Target/TraceCursor.h
  lldb/include/lldb/lldb-defines.h
  lldb/include/lldb/lldb-enumerations.h
  lldb/source/API/CMakeLists.txt
  lldb/source/API/SBTrace.cpp
  lldb/source/API/SBTraceCursor.cpp
  lldb/source/Commands/CommandObjectThread.cpp
  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/TraceDumper.cpp
  lldb/test/API/commands/trace/TestTraceLoad.py

Index: lldb/test/API/commands/trace/TestTraceLoad.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceLoad.py
+++ lldb/test/API/commands/trace/TestTraceLoad.py
@@ -264,3 +264,115 @@
         expected_substrs = ['error: missing value at traceBundle.processes[1].pid']
         self.traceLoad(traceDescriptionFilePath=trace_description_file_path, error=True, substrs=expected_substrs)
         self.assertEqual(self.dbg.GetNumTargets(), 0)
+
+    def testLoadTraceCursor(self):
+        src_dir = self.getSourceDir()
+        trace_description_file_path = os.path.join(src_dir, "intelpt-multi-core-trace", "trace.json")
+        traceDescriptionFile = lldb.SBFileSpec(trace_description_file_path, True)
+
+        error = lldb.SBError()
+        trace = self.dbg.LoadTraceFromFile(error, traceDescriptionFile)
+        self.assertSBError(error)
+
+        target = self.dbg.GetSelectedTarget()
+        process = target.process
+
+
+        # 1. Test some expected items of thread 1's trace cursor.
+        thread1 = process.threads[1]
+        cursor = trace.CreateNewCursor(error, thread1) 
+        self.assertTrue(cursor)
+        self.assertTrue(cursor.HasValue())
+        cursor.Seek(0, lldb.eTraceCursorSeekTypeBeginning)
+        cursor.SetForwards(True)
+
+        self.assertTrue(cursor.IsEvent())
+        self.assertEqual(cursor.GetEventTypeAsString(), "HW clock tick")
+        self.assertEqual(cursor.GetCPU(), lldb.LLDB_INVALID_CPU_ID)
+        self.assertEqual(cursor.GetLoadAddress(), lldb.LLDB_INVALID_ADDRESS)
+
+        cursor.Next()
+
+        self.assertTrue(cursor.IsEvent())
+        self.assertEqual(cursor.GetEventTypeAsString(), "CPU core changed")
+        self.assertEqual(cursor.GetCPU(), 51)
+
+        cursor.GoToId(19531)
+
+        self.assertTrue(cursor.IsError())
+        self.assertEqual(cursor.GetError(), "expected tracing enabled event")
+
+        cursor.GoToId(19523)
+
+        self.assertTrue(cursor.IsInstruction())
+        self.assertEqual(cursor.GetLoadAddress(), 4197287)
+
+
+
+        # Helper function to check equality of the current item of two trace cursors.
+        def assertCurrentTraceCursorItemEqual(lhs, rhs):
+            self.assertTrue(lhs.HasValue() and rhs.HasValue())
+
+            self.assertEqual(lhs.GetId(), rhs.GetId())
+            self.assertEqual(lhs.GetItemKind(), rhs.GetItemKind())
+            if lhs.IsError():
+                self.assertEqual(lhs.GetError(), rhs.GetError())
+            elif lhs.IsEvent():
+                self.assertEqual(lhs.GetEventType(), rhs.GetEventType())
+                self.assertEqual(lhs.GetEventTypeAsString(), rhs.GetEventTypeAsString())
+            elif lhs.IsInstruction():
+                self.assertEqual(lhs.GetLoadAddress(), rhs.GetLoadAddress())
+            else:
+                self.fail("Unknown trace item kind")
+
+        for thread in process.threads:
+            sequentialTraversalCursor = trace.CreateNewCursor(error, thread) 
+            self.assertSBError(error)
+            # Skip threads with no trace items
+            if not sequentialTraversalCursor.HasValue():
+                continue
+
+            # 2. Test "End" boundary of the trace by advancing past the trace's last item. 
+            sequentialTraversalCursor.Seek(0, lldb.eTraceCursorSeekTypeEnd)
+            self.assertTrue(sequentialTraversalCursor.HasValue())
+            sequentialTraversalCursor.SetForwards(True)
+            sequentialTraversalCursor.Next()
+            self.assertFalse(sequentialTraversalCursor.HasValue())
+
+
+
+            # 3. Test sequential traversal using sequential access API (ie Next())
+            # and random access API (ie GoToId()) simultaneously.
+            randomAccessCursor = trace.CreateNewCursor(error, thread) 
+            self.assertSBError(error)
+            # Reset the sequential cursor 
+            sequentialTraversalCursor.Seek(0, lldb.eTraceCursorSeekTypeBeginning)
+            sequentialTraversalCursor.SetForwards(True)
+            self.assertTrue(sequentialTraversalCursor.IsForwards())
+
+            while sequentialTraversalCursor.HasValue():
+                itemId = sequentialTraversalCursor.GetId()
+                randomAccessCursor.GoToId(itemId)
+                assertCurrentTraceCursorItemEqual(sequentialTraversalCursor, randomAccessCursor)
+                sequentialTraversalCursor.Next()
+
+
+
+            # 4. Test a random access with random access API (ie Seek()) and
+            # sequential access API (ie consecutive calls to Next()).
+            TEST_SEEK_ID = 3
+            randomAccessCursor.GoToId(TEST_SEEK_ID )
+            # Reset the sequential cursor 
+            sequentialTraversalCursor.Seek(0, lldb.eTraceCursorSeekTypeBeginning)
+            sequentialTraversalCursor.SetForwards(True)
+            for _ in range(TEST_SEEK_ID): sequentialTraversalCursor.Next()
+            assertCurrentTraceCursorItemEqual(sequentialTraversalCursor, randomAccessCursor)
+
+
+            
+                
+
+
+
+
+        
Index: lldb/source/Target/TraceDumper.cpp
===================================================================
--- lldb/source/Target/TraceDumper.cpp
+++ lldb/source/Target/TraceDumper.cpp
@@ -304,14 +304,14 @@
   if (m_options.id)
     m_cursor_sp->GoToId(*m_options.id);
   else if (m_options.forwards)
-    m_cursor_sp->Seek(0, TraceCursor::SeekType::Beginning);
+    m_cursor_sp->Seek(0, lldb::eTraceCursorSeekTypeBeginning);
   else
-    m_cursor_sp->Seek(0, TraceCursor::SeekType::End);
+    m_cursor_sp->Seek(0, lldb::eTraceCursorSeekTypeEnd);
 
   m_cursor_sp->SetForwards(m_options.forwards);
   if (m_options.skip) {
     m_cursor_sp->Seek((m_options.forwards ? 1 : -1) * *m_options.skip,
-                      TraceCursor::SeekType::Current);
+                      lldb::eTraceCursorSeekTypeCurrent);
   }
 }
 
Index: lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp
===================================================================
--- lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp
+++ lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp
@@ -130,7 +130,7 @@
 
   // Move cursor to the first instruction in the trace
   cursor.SetForwards(true);
-  cursor.Seek(0, TraceCursor::SeekType::Beginning);
+  cursor.Seek(0, lldb::eTraceCursorSeekTypeBeginning);
 
   // TODO: fix after persona0220's patch on a new way to access instruction
   // kinds
Index: lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
@@ -21,7 +21,7 @@
       const llvm::Optional<LinuxPerfZeroTscConversion> &tsc_conversion,
       llvm::Optional<uint64_t> beginning_of_time_nanos);
 
-  bool Seek(int64_t offset, SeekType origin) override;
+  bool Seek(int64_t offset, lldb::TraceCursorSeekType origin) override;
 
   void Next() override;
 
@@ -33,7 +33,7 @@
 
   lldb::TraceEvent GetEventType() const override;
 
-  llvm::Optional<lldb::cpu_id_t> GetCPU() const override;
+  lldb::cpu_id_t GetCPU() const override;
 
   llvm::Optional<uint64_t> GetHWClock() const override;
 
Index: lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
@@ -24,7 +24,7 @@
     : TraceCursor(thread_sp), m_decoded_thread_sp(decoded_thread_sp),
       m_tsc_conversion(tsc_conversion),
       m_beginning_of_time_nanos(beginning_of_time_nanos) {
-  Seek(0, SeekType::End);
+  Seek(0, lldb::eTraceCursorSeekTypeEnd);
 }
 
 void TraceCursorIntelPT::Next() {
@@ -68,15 +68,16 @@
   return m_nanoseconds_range;
 }
 
-bool TraceCursorIntelPT::Seek(int64_t offset, SeekType origin) {
+bool TraceCursorIntelPT::Seek(int64_t offset,
+                              lldb::TraceCursorSeekType origin) {
   switch (origin) {
-  case TraceCursor::SeekType::Beginning:
+  case lldb::eTraceCursorSeekTypeBeginning:
     m_pos = offset;
     break;
-  case TraceCursor::SeekType::End:
+  case lldb::eTraceCursorSeekTypeEnd:
     m_pos = m_decoded_thread_sp->GetItemsCount() - 1 + offset;
     break;
-  case TraceCursor::SeekType::Current:
+  case lldb::eTraceCursorSeekTypeCurrent:
     m_pos += offset;
   }
 
@@ -116,7 +117,7 @@
   return None;
 }
 
-Optional<lldb::cpu_id_t> TraceCursorIntelPT::GetCPU() const {
+lldb::cpu_id_t TraceCursorIntelPT::GetCPU() const {
   return m_decoded_thread_sp->GetCPUByIndex(m_pos);
 }
 
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -173,8 +173,8 @@
   ///   The trace item index to compare with.
   ///
   /// \return
-  ///   The requested cpu id, or \a llvm::None if not available.
-  llvm::Optional<lldb::cpu_id_t> GetCPUByIndex(uint64_t item_index) const;
+  ///   The requested cpu id, or \a LLDB_INVALID_CPU_ID if not available.
+  lldb::cpu_id_t GetCPUByIndex(uint64_t item_index) const;
 
   /// Get a maximal range of trace items that include the given \p item_index
   /// that have the same TSC value.
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -103,7 +103,10 @@
 
 lldb::addr_t
 DecodedThread::GetInstructionLoadAddress(uint64_t item_index) const {
-  return m_item_data[item_index].load_address;
+
+  return GetItemKindByIndex(item_index) == lldb::eTraceItemKindInstruction
+             ? m_item_data[item_index].load_address
+             : LLDB_INVALID_ADDRESS;
 }
 
 ThreadSP DecodedThread::GetThread() { return m_thread_sp; }
@@ -151,12 +154,9 @@
   }
 }
 
-Optional<lldb::cpu_id_t>
-DecodedThread::GetCPUByIndex(uint64_t item_index) const {
+lldb::cpu_id_t DecodedThread::GetCPUByIndex(uint64_t item_index) const {
   auto it = m_cpus.upper_bound(item_index);
-  if (it == m_cpus.begin())
-    return None;
-  return prev(it)->second;
+  return it == m_cpus.begin() ? LLDB_INVALID_CPU_ID : prev(it)->second;
 }
 
 Optional<DecodedThread::TSCRange>
@@ -198,6 +198,7 @@
 }
 
 lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const {
+  // This currently returns an undefined value when the item isn't an event.
   return m_item_data[item_index].event;
 }
 
@@ -230,7 +231,9 @@
 }
 
 const char *DecodedThread::GetErrorByIndex(uint64_t item_index) const {
-  return m_item_data[item_index].error;
+  return GetItemKindByIndex(item_index) == lldb::eTraceItemKindError
+             ? m_item_data[item_index].error
+             : nullptr;
 }
 
 DecodedThread::DecodedThread(
Index: lldb/source/Commands/CommandObjectThread.cpp
===================================================================
--- lldb/source/Commands/CommandObjectThread.cpp
+++ lldb/source/Commands/CommandObjectThread.cpp
@@ -2295,7 +2295,7 @@
       // We need to stop processing data when we already ran out of instructions
       // in a previous command. We can fake this by setting the cursor past the
       // end of the trace.
-      cursor_sp->Seek(1, TraceCursor::SeekType::End);
+      cursor_sp->Seek(1, lldb::eTraceCursorSeekTypeEnd);
     }
 
     TraceDumper dumper(std::move(cursor_sp),
Index: lldb/source/API/SBTraceCursor.cpp
===================================================================
--- /dev/null
+++ lldb/source/API/SBTraceCursor.cpp
@@ -0,0 +1,120 @@
+//===-- SBTraceCursor.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/API/SBTraceCursor.h"
+#include "Utils.h"
+#include "lldb/Utility/Instrumentation.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBTraceCursor::SBTraceCursor() { LLDB_INSTRUMENT_VA(this); }
+
+SBTraceCursor::SBTraceCursor(TraceCursorSP trace_cursor_sp)
+    : m_opaque_sp{std::move(trace_cursor_sp)} {
+  LLDB_INSTRUMENT_VA(this, trace_cursor_sp);
+}
+
+void SBTraceCursor::SetForwards(bool forwards) {
+  LLDB_INSTRUMENT_VA(this, forwards);
+  m_opaque_sp->SetForwards(forwards);
+}
+
+bool SBTraceCursor::IsForwards() const {
+  LLDB_INSTRUMENT_VA(this);
+  return m_opaque_sp->IsForwards();
+}
+
+void SBTraceCursor::Next() {
+  LLDB_INSTRUMENT_VA(this);
+  return m_opaque_sp->Next();
+}
+
+bool SBTraceCursor::HasValue() const {
+  LLDB_INSTRUMENT_VA(this);
+  return m_opaque_sp->HasValue();
+}
+
+bool SBTraceCursor::GoToId(lldb::user_id_t id) {
+  LLDB_INSTRUMENT_VA(this, id);
+  return m_opaque_sp->GoToId(id);
+}
+
+bool SBTraceCursor::HasId(lldb::user_id_t id) const {
+  LLDB_INSTRUMENT_VA(this, id);
+  return m_opaque_sp->HasId(id);
+}
+
+lldb::user_id_t SBTraceCursor::GetId() const {
+  LLDB_INSTRUMENT_VA(this);
+  return m_opaque_sp->GetId();
+}
+
+bool SBTraceCursor::Seek(int64_t offset, lldb::TraceCursorSeekType origin) {
+  LLDB_INSTRUMENT_VA(this, offset);
+
+  return m_opaque_sp->Seek(offset, origin);
+}
+
+lldb::TraceItemKind SBTraceCursor::GetItemKind() const {
+  LLDB_INSTRUMENT_VA(this);
+  return m_opaque_sp->GetItemKind();
+}
+
+bool SBTraceCursor::IsError() const {
+  LLDB_INSTRUMENT_VA(this);
+  return m_opaque_sp->IsError();
+}
+
+const char *SBTraceCursor::GetError() const {
+  LLDB_INSTRUMENT_VA(this);
+  return m_opaque_sp->GetError();
+}
+
+bool SBTraceCursor::IsEvent() const {
+  LLDB_INSTRUMENT_VA(this);
+  return m_opaque_sp->IsEvent();
+}
+
+lldb::TraceEvent SBTraceCursor::GetEventType() const {
+  LLDB_INSTRUMENT_VA(this);
+  return m_opaque_sp->GetEventType();
+}
+
+const char *SBTraceCursor::GetEventTypeAsString() const {
+  LLDB_INSTRUMENT_VA(this);
+  return m_opaque_sp->GetEventTypeAsString();
+}
+
+bool SBTraceCursor::IsInstruction() const {
+  LLDB_INSTRUMENT_VA(this);
+  return m_opaque_sp->IsInstruction();
+}
+
+lldb::addr_t SBTraceCursor::GetLoadAddress() const {
+  LLDB_INSTRUMENT_VA(this);
+  return m_opaque_sp->GetLoadAddress();
+}
+
+lldb::cpu_id_t SBTraceCursor::GetCPU() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  return m_opaque_sp->GetCPU();
+}
+
+bool SBTraceCursor::IsValid() const {
+  LLDB_INSTRUMENT_VA(this);
+  return this->operator bool();
+}
+
+SBTraceCursor::operator bool() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  return m_opaque_sp.get() != nullptr;
+}
Index: lldb/source/API/SBTrace.cpp
===================================================================
--- lldb/source/API/SBTrace.cpp
+++ lldb/source/API/SBTrace.cpp
@@ -43,6 +43,27 @@
   return SBTrace(trace_or_err.get());
 }
 
+SBTraceCursor SBTrace::CreateNewCursor(SBError &error, SBThread &thread) {
+  LLDB_INSTRUMENT_VA(this, error, thread);
+
+  if (!m_opaque_sp) {
+    error.SetErrorString("error: invalid trace");
+    return SBTraceCursor();
+  }
+  if (!thread.get()) {
+    error.SetErrorString("error: invalid thread");
+    return SBTraceCursor();
+  }
+
+  if (llvm::Expected<lldb::TraceCursorSP> trace_cursor_sp =
+          m_opaque_sp->CreateNewCursor(*thread.get())) {
+    return SBTraceCursor(std::move(*trace_cursor_sp));
+  } else {
+    error.SetErrorString(llvm::toString(trace_cursor_sp.takeError()).c_str());
+    return SBTraceCursor();
+  }
+}
+
 SBFileSpec SBTrace::SaveToDisk(SBError &error, const SBFileSpec &bundle_dir,
                                bool compact) {
   LLDB_INSTRUMENT_VA(this, error, bundle_dir, compact);
Index: lldb/source/API/CMakeLists.txt
===================================================================
--- lldb/source/API/CMakeLists.txt
+++ lldb/source/API/CMakeLists.txt
@@ -72,6 +72,7 @@
   SBThreadCollection.cpp
   SBThreadPlan.cpp
   SBTrace.cpp
+  SBTraceCursor.cpp
   SBType.cpp
   SBTypeCategory.cpp
   SBTypeEnumMember.cpp
Index: lldb/include/lldb/lldb-enumerations.h
===================================================================
--- lldb/include/lldb/lldb-enumerations.h
+++ lldb/include/lldb/lldb-enumerations.h
@@ -1179,6 +1179,19 @@
   eTraceItemKindInstruction,
 };
 
+/// Enum to indicate the reference point when invoking
+/// \a TraceCursor::Seek().
+/// The following values are inspired by \a std::istream::seekg.
+enum TraceCursorSeekType {
+  /// The beginning of the trace, i.e the oldest item.
+  eTraceCursorSeekTypeBeginning = 0,
+  /// The current position in the trace.
+  eTraceCursorSeekTypeCurrent,
+  /// The end of the trace, i.e the most recent item.
+  eTraceCursorSeekTypeEnd
+};
+
+
 } // namespace lldb
 
 #endif // LLDB_LLDB_ENUMERATIONS_H
Index: lldb/include/lldb/lldb-defines.h
===================================================================
--- lldb/include/lldb/lldb-defines.h
+++ lldb/include/lldb/lldb-defines.h
@@ -86,6 +86,7 @@
 #define LLDB_INVALID_LINE_NUMBER UINT32_MAX
 #define LLDB_INVALID_COLUMN_NUMBER 0
 #define LLDB_INVALID_QUEUE_ID 0
+#define LLDB_INVALID_CPU_ID UINT32_MAX
 
 /// CPU Type definitions
 #define LLDB_ARCH_DEFAULT "systemArch"
Index: lldb/include/lldb/Target/TraceCursor.h
===================================================================
--- lldb/include/lldb/Target/TraceCursor.h
+++ lldb/include/lldb/Target/TraceCursor.h
@@ -92,18 +92,6 @@
 ///   You can read more in the documentation of these methods.
 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.
-    Beginning = 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);
@@ -208,7 +196,7 @@
   ///
   /// \return
   ///     \b true if and only if the cursor ends up pointing to a valid item.
-  virtual bool Seek(int64_t offset, SeekType origin) = 0;
+  virtual bool Seek(int64_t offset, lldb::TraceCursorSeekType origin) = 0;
 
   /// \return
   ///   The \a ExecutionContextRef of the backing thread from the creation time
@@ -235,8 +223,7 @@
   bool IsEvent() const;
 
   /// \return
-  ///     The specific kind of event the cursor is pointing at, or \b
-  ///     TraceEvent::eTraceEventNone if the cursor not pointing to an event.
+  ///     The specific kind of event the cursor is pointing at.
   virtual lldb::TraceEvent GetEventType() const = 0;
 
   /// \return
@@ -252,7 +239,8 @@
   bool IsInstruction() const;
 
   /// \return
-  ///     The load address of the instruction the cursor is pointing at.
+  ///     The load address of the instruction the cursor is pointing at,
+  ///     or LLDB_INVALID_ADDRESS if the current item is not an instruction.
   virtual lldb::addr_t GetLoadAddress() const = 0;
 
   /// Get the CPU associated with the current trace item.
@@ -261,9 +249,9 @@
   /// whenever an eTraceEventCPUChanged event is fired.
   ///
   /// \return
-  ///    The requested CPU id, or \a llvm::None if this information is
+  ///    The requested CPU id, or LLDB_INVALID_CPU_ID if this information is
   ///    not available for the current item.
-  virtual llvm::Optional<lldb::cpu_id_t> GetCPU() const = 0;
+  virtual lldb::cpu_id_t GetCPU() const = 0;
 
   /// Get the last hardware clock value that was emitted before the current
   /// trace item.
Index: lldb/include/lldb/API/SBTraceCursor.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/API/SBTraceCursor.h
@@ -0,0 +1,183 @@
+//===-- SBTraceCursor.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_API_SBTRACECURSOR_H
+#define LLDB_API_SBTRACECURSOR_H
+
+#include "lldb/API/SBDefines.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBExecutionContext.h"
+#include "lldb/Target/TraceCursor.h"
+
+namespace lldb {
+
+class LLDB_API SBTraceCursor {
+public:
+  /// Default constructor for an invalid \a SBTraceCursor object.
+  SBTraceCursor();
+
+  /// Create a cursor that initially points to the end of the trace, i.e. the
+  /// most recent item.
+  SBTraceCursor(lldb::TraceCursorSP trace_cursor_sp);
+
+  /// Set the direction to use in the \a SBTraceCursor::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 SBTraceCursor::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 item (instruction or error).
+  ///
+  /// 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 SBTraceCursor::SetForwards().
+  void Next();
+
+  /// \return
+  ///     \b true if the cursor is pointing to a valid item. \b false if the
+  ///     cursor has reached the end of the trace.
+  bool HasValue() const;
+
+  /// 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
+  ///   SBTraceCursor::Next() method and only use \a SBTraceCursor::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 to it. Otherwise, \b false is returned and the cursor now points
+  ///     to an invalid item, i.e. calling \a HasValue() will return \b false.
+  bool GoToId(lldb::user_id_t id);
+
+  /// \return
+  ///     \b true if and only if there's an instruction item with the given \p
+  ///     id.
+  bool HasId(lldb::user_id_t id) const;
+
+  /// \return
+  ///     A unique identifier for the instruction or error this cursor is
+  ///     pointing to.
+  lldb::user_id_t GetId() const;
+  /// \}
+
+  /// Make the cursor point to an item in the trace based on an origin point and
+  /// an offset.
+  ///
+  /// The resulting position of the trace is
+  ///     origin + offset
+  ///
+  /// If this resulting position would be out of bounds, the trace then points
+  /// to an invalid item, i.e. calling \a HasValue() returns \b false.
+  ///
+  /// \param[in] offset
+  ///     How many items to move forwards (if positive) or backwards (if
+  ///     negative) from the given origin point. For example, if origin is \b
+  ///     End, then a negative offset would move backward in the trace, but a
+  ///     positive offset would move past the trace to an invalid item.
+  ///
+  /// \param[in] origin
+  ///     The reference point to use when moving the cursor.
+  ///
+  /// \return
+  ///     \b true if and only if the cursor ends up pointing to a valid item.
+  bool Seek(int64_t offset, lldb::TraceCursorSeekType origin);
+
+  /// \return
+  ///   The \a ExecutionContextRef of the backing thread from the creation time
+  ///   of this cursor.
+  SBExecutionContext &GetExecutionContextRef();
+
+  /// Trace item information (instructions, errors and events)
+  /// \{
+
+  /// \return
+  ///     The kind of item the cursor is pointing at.
+  lldb::TraceItemKind GetItemKind() const;
+
+  /// \return
+  ///     Whether the cursor points to an error or not.
+  bool IsError() const;
+
+  /// \return
+  ///     The error message the cursor is pointing at.
+  const char *GetError() const;
+
+  /// \return
+  ///     Whether the cursor points to an event or not.
+  bool IsEvent() const;
+
+  /// \return
+  ///     The specific kind of event the cursor is pointing at.
+  lldb::TraceEvent GetEventType() const;
+
+  /// \return
+  ///     A human-readable description of the event this cursor is pointing at.
+  const char *GetEventTypeAsString() const;
+
+  /// \return
+  ///     Whether the cursor points to an instruction.
+  bool IsInstruction() const;
+
+  /// \return
+  ///     The load address of the instruction the cursor is pointing at,
+  ///     or LLDB_INVALID_ADDRESS if the current item is not an instruction.
+  lldb::addr_t GetLoadAddress() const;
+
+  /// \return
+  ///    The requested CPU id, or LLDB_INVALID_CPU_ID if this information is
+  ///    not available for the current item.
+  lldb::cpu_id_t GetCPU() const;
+
+  bool IsValid() const;
+
+  explicit operator bool() const;
+
+protected:
+  lldb::TraceCursorSP m_opaque_sp;
+};
+} // namespace lldb
+
+#endif // LLDB_API_SBTRACECURSOR_H
Index: lldb/include/lldb/API/SBTrace.h
===================================================================
--- lldb/include/lldb/API/SBTrace.h
+++ lldb/include/lldb/API/SBTrace.h
@@ -11,6 +11,7 @@
 
 #include "lldb/API/SBDefines.h"
 #include "lldb/API/SBError.h"
+#include "lldb/API/SBTraceCursor.h"
 
 namespace lldb {
 
@@ -25,6 +26,20 @@
   static SBTrace LoadTraceFromFile(SBError &error, SBDebugger &debugger,
                                    const SBFileSpec &trace_description_file);
 
+  /// Get a \a TraceCursor for the given thread's trace.
+  ///
+  /// \param[out] error
+  ///   This will be set with an error in case of failures.
+  //
+  /// \param[in] thread
+  ///   The thread to get a \a TraceCursor for.
+  //
+  /// \return
+  ///     A \a SBTraceCursor. If the thread is not traced or its trace
+  ///     information failed to load, an invalid \a SBTraceCursor is returned
+  ///     and the \p error parameter is set.
+  SBTraceCursor CreateNewCursor(SBError &error, SBThread &thread);
+
   /// Save the trace to the specified directory, which will be created if
   /// needed. This will also create a a file \a <directory>/trace.json with the
   /// main properties of the trace session, along with others files which
Index: lldb/include/lldb/API/SBDefines.h
===================================================================
--- lldb/include/lldb/API/SBDefines.h
+++ lldb/include/lldb/API/SBDefines.h
@@ -88,6 +88,7 @@
 class LLDB_API SBThreadCollection;
 class LLDB_API SBThreadPlan;
 class LLDB_API SBTrace;
+class LLDB_API SBTraceCursor;
 class LLDB_API SBType;
 class LLDB_API SBTypeCategory;
 class LLDB_API SBTypeEnumMember;
Index: lldb/bindings/interfaces.swig
===================================================================
--- lldb/bindings/interfaces.swig
+++ lldb/bindings/interfaces.swig
@@ -69,6 +69,7 @@
 %include "./interface/SBThreadCollection.i"
 %include "./interface/SBThreadPlan.i"
 %include "./interface/SBTrace.i"
+%include "./interface/SBTraceCursor.i"
 %include "./interface/SBType.i"
 %include "./interface/SBTypeCategory.i"
 %include "./interface/SBTypeEnumMember.i"
Index: lldb/bindings/interface/SBTraceCursor.i
===================================================================
--- /dev/null
+++ lldb/bindings/interface/SBTraceCursor.i
@@ -0,0 +1,58 @@
+//===-- SWIG Interface for SBTraceCursor.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
+//
+//===----------------------------------------------------------------------===//
+
+namespace lldb {
+
+%feature("docstring",
+"Represents a trace cursor."
+) SBTrace;
+class LLDB_API SBTraceCursor {
+public:
+  SBTraceCursor();
+
+  SBTraceCursor(lldb::TraceCursorSP trace_cursor_sp);
+
+  void SetForwards(bool forwards);
+
+  bool IsForwards() const;
+
+  void Next();
+
+  bool HasValue();
+
+  bool GoToId(lldb::user_id_t id);
+
+  bool HasId(lldb::user_id_t id) const;
+
+  lldb::user_id_t GetId() const;
+
+  bool Seek(int64_t offset, lldb::TraceCursorSeekType origin);
+
+  lldb::TraceItemKind GetItemKind() const;
+
+  bool IsError() const;
+
+  const char *GetError() const;
+
+  bool IsEvent() const;
+
+  lldb::TraceEvent GetEventType() const;
+
+  const char *GetEventTypeAsString() const;
+
+  bool IsInstruction() const;
+
+  lldb::addr_t GetLoadAddress() const;
+
+  lldb::cpu_id_t GetCPU() const;
+
+  bool IsValid() const;
+
+  explicit operator bool() const;
+};
+} // namespace lldb
Index: lldb/bindings/interface/SBTrace.i
===================================================================
--- lldb/bindings/interface/SBTrace.i
+++ lldb/bindings/interface/SBTrace.i
@@ -15,6 +15,8 @@
 public:
   SBTrace();
 
+  SBTraceCursor CreateNewCursor(SBError &error, SBThread &thread);
+
   const char *GetStartConfigurationHelp();
 
   SBFileSpec SaveToDisk(SBError &error, const SBFileSpec &bundle_dir, bool compact = false);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to