[trace] Add SBTraceCursor bindings

Add bindings for the `TraceCursor` to allow for programatic traversal of
traces.
This diff adds bindings for all public `TraceCursor` methods except
`GetHwClock` and also adds `SBTrace::CreateNewCursor`. A new unittest
has been added to TestTraceLoad.py that uses the new `SBTraceCursor` API
to test that the sequential and random access APIs of the `TraceCursor`
are equivalent.

This diff depends on D130925.

Test Plan:
`ninja lldb-dotest && ./bin/lldb-dotest -p TestTraceLoad`

Differential Revision: https://reviews.llvm.org/D130930
This commit is contained in:
Jakob Johnson
2022-08-01 12:23:22 -07:00
parent 3dfa562643
commit f9b4ea0ce9
20 changed files with 548 additions and 37 deletions

View File

@@ -15,6 +15,8 @@ class LLDB_API SBTrace {
public:
SBTrace();
SBTraceCursor CreateNewCursor(SBError &error, SBThread &thread);
const char *GetStartConfigurationHelp();
SBFileSpec SaveToDisk(SBError &error, const SBFileSpec &bundle_dir, bool compact = false);

View File

@@ -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

View File

@@ -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"

View File

@@ -88,6 +88,7 @@ class LLDB_API SBThread;
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;

View File

@@ -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 @@ public:
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

View File

@@ -0,0 +1,182 @@
//===-- 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.
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

View File

@@ -92,18 +92,6 @@ namespace lldb_private {
/// 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 @@ public:
///
/// \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 @@ public:
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
@@ -261,9 +248,9 @@ public:
/// 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.

View File

@@ -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"

View File

@@ -1179,6 +1179,19 @@ enum TraceItemKind {
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

View File

@@ -72,6 +72,7 @@ add_lldb_library(liblldb SHARED ${option_framework}
SBThreadCollection.cpp
SBThreadPlan.cpp
SBTrace.cpp
SBTraceCursor.cpp
SBType.cpp
SBTypeCategory.cpp
SBTypeEnumMember.cpp

View File

@@ -43,6 +43,27 @@ SBTrace SBTrace::LoadTraceFromFile(SBError &error, SBDebugger &debugger,
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);

View File

@@ -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;
}

View File

@@ -2295,7 +2295,7 @@ protected:
// 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),

View File

@@ -151,12 +151,9 @@ void DecodedThread::NotifyCPU(lldb::cpu_id_t cpu_id) {
}
}
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>

View File

@@ -173,8 +173,8 @@ public:
/// 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.

View File

@@ -24,7 +24,7 @@ TraceCursorIntelPT::TraceCursorIntelPT(
: 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 @@ TraceCursorIntelPT::GetNanosecondsRange() const {
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 @@ Optional<double> TraceCursorIntelPT::GetWallClockTime() const {
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);
}

View File

@@ -21,7 +21,7 @@ public:
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 @@ public:
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;

View File

@@ -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::Beginning);
cursor.Seek(0, lldb::eTraceCursorSeekTypeBeginning);
// TODO: fix after persona0220's patch on a new way to access instruction
// kinds

View File

@@ -304,14 +304,14 @@ TraceDumper::TraceDumper(lldb::TraceCursorSP cursor_sp, Stream &s,
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);
}
}

View File

@@ -264,3 +264,114 @@ Context:
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)
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)