[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:
@@ -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);
|
||||
|
||||
58
lldb/bindings/interface/SBTraceCursor.i
Normal file
58
lldb/bindings/interface/SBTraceCursor.i
Normal 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
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
182
lldb/include/lldb/API/SBTraceCursor.h
Normal file
182
lldb/include/lldb/API/SBTraceCursor.h
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
120
lldb/source/API/SBTraceCursor.cpp
Normal file
120
lldb/source/API/SBTraceCursor.cpp
Normal 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;
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user