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