@@ -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+
738770class 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.
0 commit comments