Skip to content

Commit d55e7bf

Browse files
authored
Support bookmarking timestamps in TTD (#1012)
* Support bookmarking timestamps in TTD. Fix #704 * Add the "Add TTD Bookmark" action to the menu
1 parent 42d7550 commit d55e7bf

15 files changed

Lines changed: 1431 additions & 0 deletions

api/debuggerapi.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,18 @@ namespace BinaryNinjaDebuggerAPI {
627627
};
628628

629629

630+
struct TTDBookmark
631+
{
632+
TTDPosition position;
633+
uint64_t viewAddress;
634+
std::string note;
635+
636+
TTDBookmark() : viewAddress(0) {}
637+
TTDBookmark(const TTDPosition& pos, const std::string& n = "", uint64_t addr = 0)
638+
: position(pos), viewAddress(addr), note(n) {}
639+
};
640+
641+
630642
typedef BNDebugAdapterConnectionStatus DebugAdapterConnectionStatus;
631643
typedef BNDebugAdapterTargetStatus DebugAdapterTargetStatus;
632644

@@ -831,6 +843,13 @@ namespace BinaryNinjaDebuggerAPI {
831843
std::pair<bool, TTDMemoryEvent> GetTTDNextMemoryAccess(uint64_t address, uint64_t size, TTDMemoryAccessType accessType);
832844
std::pair<bool, TTDMemoryEvent> GetTTDPrevMemoryAccess(uint64_t address, uint64_t size, TTDMemoryAccessType accessType);
833845

846+
// TTD Bookmark Methods
847+
std::vector<TTDBookmark> GetTTDBookmarks();
848+
bool AddTTDBookmark(const TTDPosition& position, const std::string& note = "", uint64_t viewAddress = 0);
849+
bool RemoveTTDBookmark(const TTDPosition& position);
850+
bool UpdateTTDBookmark(const TTDPosition& position, const std::string& note, uint64_t viewAddress);
851+
void ClearTTDBookmarks();
852+
834853
// TTD Code Coverage Analysis Methods
835854
bool IsInstructionExecuted(uint64_t address);
836855
bool RunCodeCoverageAnalysis(uint64_t startAddress, uint64_t endAddress, TTDPosition startTime, TTDPosition endTime);

api/debuggercontroller.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,51 @@ std::vector<TTDEvent> DebuggerController::GetAllTTDEvents()
14301430
}
14311431

14321432

1433+
std::vector<TTDBookmark> DebuggerController::GetTTDBookmarks()
1434+
{
1435+
std::vector<TTDBookmark> result;
1436+
size_t count = 0;
1437+
BNDebuggerTTDBookmark* bookmarks = BNDebuggerGetTTDBookmarks(m_object, &count);
1438+
if (!bookmarks)
1439+
return result;
1440+
1441+
for (size_t i = 0; i < count; ++i)
1442+
{
1443+
TTDBookmark bm;
1444+
bm.position = TTDPosition(bookmarks[i].position.sequence, bookmarks[i].position.step);
1445+
bm.viewAddress = bookmarks[i].viewAddress;
1446+
bm.note = bookmarks[i].note ? std::string(bookmarks[i].note) : "";
1447+
result.push_back(bm);
1448+
}
1449+
1450+
BNDebuggerFreeTTDBookmarks(bookmarks, count);
1451+
return result;
1452+
}
1453+
1454+
bool DebuggerController::AddTTDBookmark(const TTDPosition& position, const std::string& note, uint64_t viewAddress)
1455+
{
1456+
BNDebuggerTTDPosition pos = {position.sequence, position.step};
1457+
return BNDebuggerAddTTDBookmark(m_object, pos, note.c_str(), viewAddress);
1458+
}
1459+
1460+
bool DebuggerController::RemoveTTDBookmark(const TTDPosition& position)
1461+
{
1462+
BNDebuggerTTDPosition pos = {position.sequence, position.step};
1463+
return BNDebuggerRemoveTTDBookmark(m_object, pos);
1464+
}
1465+
1466+
bool DebuggerController::UpdateTTDBookmark(const TTDPosition& position, const std::string& note, uint64_t viewAddress)
1467+
{
1468+
BNDebuggerTTDPosition pos = {position.sequence, position.step};
1469+
return BNDebuggerUpdateTTDBookmark(m_object, pos, note.c_str(), viewAddress);
1470+
}
1471+
1472+
void DebuggerController::ClearTTDBookmarks()
1473+
{
1474+
BNDebuggerClearTTDBookmarks(m_object);
1475+
}
1476+
1477+
14331478
bool DebuggerController::IsInstructionExecuted(uint64_t address)
14341479
{
14351480
return BNDebuggerIsInstructionExecuted(m_object, address);

api/ffi.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ extern "C"
274274
ThreadStateChangedEvent,
275275

276276
ForceMemoryCacheUpdateEvent,
277+
278+
TTDBookmarkChangedEvent,
277279
} BNDebuggerEventType;
278280

279281

@@ -701,6 +703,21 @@ extern "C"
701703
DEBUGGER_FFI_API void BNDebuggerFreeTTDCallEvents(BNDebuggerTTDCallEvent* events, size_t count);
702704
DEBUGGER_FFI_API void BNDebuggerFreeTTDEvents(BNDebuggerTTDEvent* events, size_t count);
703705

706+
// TTD Bookmark structures and functions
707+
typedef struct BNDebuggerTTDBookmark
708+
{
709+
BNDebuggerTTDPosition position;
710+
uint64_t viewAddress;
711+
char* note;
712+
} BNDebuggerTTDBookmark;
713+
714+
DEBUGGER_FFI_API BNDebuggerTTDBookmark* BNDebuggerGetTTDBookmarks(BNDebuggerController* controller, size_t* count);
715+
DEBUGGER_FFI_API bool BNDebuggerAddTTDBookmark(BNDebuggerController* controller, BNDebuggerTTDPosition position, const char* note, uint64_t viewAddress);
716+
DEBUGGER_FFI_API bool BNDebuggerRemoveTTDBookmark(BNDebuggerController* controller, BNDebuggerTTDPosition position);
717+
DEBUGGER_FFI_API bool BNDebuggerUpdateTTDBookmark(BNDebuggerController* controller, BNDebuggerTTDPosition position, const char* note, uint64_t viewAddress);
718+
DEBUGGER_FFI_API void BNDebuggerClearTTDBookmarks(BNDebuggerController* controller);
719+
DEBUGGER_FFI_API void BNDebuggerFreeTTDBookmarks(BNDebuggerTTDBookmark* bookmarks, size_t count);
720+
704721
// TTD Code Coverage Analysis Functions
705722
DEBUGGER_FFI_API bool BNDebuggerIsInstructionExecuted(BNDebuggerController* controller, uint64_t address);
706723
DEBUGGER_FFI_API bool BNDebuggerRunCodeCoverageAnalysisRange(BNDebuggerController* controller, uint64_t startAddress, uint64_t endAddress, BNDebuggerTTDPosition startTime, BNDebuggerTTDPosition endTime);

api/python/debuggercontroller.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,38 @@ def from_string(cls, timestamp_str):
735735
return cls(parts[0], parts[1])
736736

737737

738+
class TTDBookmark:
739+
"""
740+
TTDBookmark represents a saved position in a TTD trace with an optional note and view address.
741+
742+
* ``position``: the TTD position (TTDPosition object)
743+
* ``view_address``: the address the user was viewing when the bookmark was created
744+
* ``note``: an optional note describing the bookmark
745+
"""
746+
747+
def __init__(self, position: TTDPosition, note: str = "", view_address: int = 0):
748+
self.position = position
749+
self.note = note
750+
self.view_address = view_address
751+
752+
def __eq__(self, other):
753+
if not isinstance(other, self.__class__):
754+
return NotImplemented
755+
return self.position == other.position
756+
757+
def __ne__(self, other):
758+
if not isinstance(other, self.__class__):
759+
return NotImplemented
760+
return not (self == other)
761+
762+
def __hash__(self):
763+
return hash(self.position)
764+
765+
def __repr__(self):
766+
note_str = f" ({self.note})" if self.note else ""
767+
return f"<TTDBookmark: {self.position}{note_str}>"
768+
769+
738770
class TTDMemoryEvent:
739771
"""
740772
TTDMemoryEvent represents a memory access event in a TTD trace. It has the following fields:
@@ -2553,6 +2585,103 @@ def navigate_to_timestamp(self, timestamp_str):
25532585
binaryninja.log_error(f"Invalid timestamp format: {e}")
25542586
return False
25552587

2588+
@property
2589+
def ttd_bookmarks(self):
2590+
"""
2591+
Get all TTD bookmarks.
2592+
2593+
:return: list of TTDBookmark objects
2594+
:rtype: list[TTDBookmark]
2595+
"""
2596+
count = ctypes.c_size_t()
2597+
bookmarks = dbgcore.BNDebuggerGetTTDBookmarks(self.handle, ctypes.byref(count))
2598+
2599+
result = []
2600+
if not bookmarks or count.value == 0:
2601+
return result
2602+
2603+
for i in range(count.value):
2604+
bm = bookmarks[i]
2605+
position = TTDPosition(bm.position.sequence, bm.position.step)
2606+
note = bm.note.decode('utf-8') if bm.note else ""
2607+
result.append(TTDBookmark(position, note, bm.viewAddress))
2608+
2609+
dbgcore.BNDebuggerFreeTTDBookmarks(bookmarks, count.value)
2610+
return result
2611+
2612+
def add_ttd_bookmark(self, position, note="", view_address=0):
2613+
"""
2614+
Add a TTD bookmark. If a bookmark with the same position already exists, it is updated.
2615+
2616+
:param position: TTDPosition object or string in format "sequence:step"
2617+
:param note: optional note for the bookmark
2618+
:param view_address: optional view address to navigate to when the bookmark is activated
2619+
:return: True if the bookmark was added/updated successfully
2620+
:rtype: bool
2621+
"""
2622+
if isinstance(position, str):
2623+
position = TTDPosition.from_string(position)
2624+
elif not isinstance(position, TTDPosition):
2625+
raise TypeError("Position must be TTDPosition object or string")
2626+
2627+
pos = dbgcore.BNDebuggerTTDPosition()
2628+
pos.sequence = position.sequence
2629+
pos.step = position.step
2630+
2631+
if isinstance(note, str):
2632+
note = note.encode('utf-8')
2633+
2634+
return dbgcore.BNDebuggerAddTTDBookmark(self.handle, pos, note, view_address)
2635+
2636+
def remove_ttd_bookmark(self, position):
2637+
"""
2638+
Remove a TTD bookmark by position.
2639+
2640+
:param position: TTDPosition object or string in format "sequence:step"
2641+
:return: True if the bookmark was found and removed
2642+
:rtype: bool
2643+
"""
2644+
if isinstance(position, str):
2645+
position = TTDPosition.from_string(position)
2646+
elif not isinstance(position, TTDPosition):
2647+
raise TypeError("Position must be TTDPosition object or string")
2648+
2649+
pos = dbgcore.BNDebuggerTTDPosition()
2650+
pos.sequence = position.sequence
2651+
pos.step = position.step
2652+
2653+
return dbgcore.BNDebuggerRemoveTTDBookmark(self.handle, pos)
2654+
2655+
def update_ttd_bookmark(self, position, note="", view_address=0):
2656+
"""
2657+
Update an existing TTD bookmark's note and view address.
2658+
2659+
:param position: TTDPosition object or string in format "sequence:step"
2660+
:param note: new note for the bookmark
2661+
:param view_address: new view address for the bookmark
2662+
:return: True if the bookmark was found and updated
2663+
:rtype: bool
2664+
"""
2665+
if isinstance(position, str):
2666+
position = TTDPosition.from_string(position)
2667+
elif not isinstance(position, TTDPosition):
2668+
raise TypeError("Position must be TTDPosition object or string")
2669+
2670+
pos = dbgcore.BNDebuggerTTDPosition()
2671+
pos.sequence = position.sequence
2672+
pos.step = position.step
2673+
2674+
if isinstance(note, str):
2675+
note = note.encode('utf-8')
2676+
2677+
return dbgcore.BNDebuggerUpdateTTDBookmark(self.handle, pos, note, view_address)
2678+
2679+
def clear_ttd_bookmarks(self):
2680+
"""
2681+
Remove all TTD bookmarks.
2682+
"""
2683+
dbgcore.BNDebuggerClearTTDBookmarks(self.handle)
2684+
25562685
def get_ttd_next_memory_access(self, address: int, size: int, access_type = DebuggerTTDMemoryAccessType.DebuggerTTDMemoryRead):
25572686
"""
25582687
Get the next memory access to a specific address from the current TTD position.

core/debuggercommon.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,18 @@ namespace BinaryNinjaDebugger {
119119
}
120120
};
121121

122+
// TTD Bookmark - a saved position in the trace with optional metadata
123+
struct TTDBookmark
124+
{
125+
TTDPosition position;
126+
uint64_t viewAddress;
127+
std::string note;
128+
129+
TTDBookmark() : viewAddress(0) {}
130+
TTDBookmark(const TTDPosition& pos, const std::string& n = "", uint64_t addr = 0)
131+
: position(pos), viewAddress(addr), note(n) {}
132+
};
133+
122134
// TTD Memory Access Event - complete set of fields from Microsoft documentation
123135
struct TTDMemoryEvent
124136
{

0 commit comments

Comments
 (0)