@@ -721,6 +721,93 @@ def __repr__(self):
721721 return f"<TTDCallEvent: { self .function } @ { self .function_address :#x} , thread { self .thread_id } >"
722722
723723
724+ class TTDHeapEvent :
725+ """
726+ TTDHeapEvent represents a heap operation event in a TTD trace. It has the following fields:
727+
728+ * ``event_type``: type of the event (always "Heap" for TTD.Heap objects)
729+ * ``action``: heap action that occurred (Alloc, ReAlloc, Free, Create, Protect, Lock, Unlock, Destroy)
730+ * ``thread_id``: OS thread ID that performed the heap operation
731+ * ``unique_thread_id``: unique thread ID across the trace
732+ * ``heap``: handle for the Win32 heap
733+ * ``address``: address of the allocated object (if applicable)
734+ * ``previous_address``: address before reallocation (for ReAlloc operations)
735+ * ``size``: size of allocated object (if applicable)
736+ * ``base_address``: base address of allocated object (if applicable)
737+ * ``flags``: heap API flags (meaning depends on the specific API)
738+ * ``result``: result of heap API call (non-zero means success)
739+ * ``reserve_size``: amount of memory to reserve (for Create operations)
740+ * ``commit_size``: initial committed size (for Create operations)
741+ * ``make_read_only``: non-zero indicates request to make heap read-only
742+ * ``parameters``: list of raw parameters from the heap call
743+ * ``time_start``: TTD position when heap operation started
744+ * ``time_end``: TTD position when heap operation ended
745+ """
746+
747+ def __init__ (self , event_type : str , action : str , thread_id : int , unique_thread_id : int ,
748+ heap : int , address : int , previous_address : int , size : int , base_address : int ,
749+ flags : int , result : int , reserve_size : int , commit_size : int , make_read_only : int ,
750+ parameters : List [str ], time_start : TTDPosition , time_end : TTDPosition ):
751+ self .event_type = event_type
752+ self .action = action
753+ self .thread_id = thread_id
754+ self .unique_thread_id = unique_thread_id
755+ self .heap = heap
756+ self .address = address
757+ self .previous_address = previous_address
758+ self .size = size
759+ self .base_address = base_address
760+ self .flags = flags
761+ self .result = result
762+ self .reserve_size = reserve_size
763+ self .commit_size = commit_size
764+ self .make_read_only = make_read_only
765+ self .parameters = parameters
766+ self .time_start = time_start
767+ self .time_end = time_end
768+
769+ def __eq__ (self , other ):
770+ if not isinstance (other , self .__class__ ):
771+ return NotImplemented
772+ return (self .event_type == other .event_type and
773+ self .action == other .action and
774+ self .thread_id == other .thread_id and
775+ self .unique_thread_id == other .unique_thread_id and
776+ self .heap == other .heap and
777+ self .address == other .address and
778+ self .previous_address == other .previous_address and
779+ self .size == other .size and
780+ self .base_address == other .base_address and
781+ self .flags == other .flags and
782+ self .result == other .result and
783+ self .reserve_size == other .reserve_size and
784+ self .commit_size == other .commit_size and
785+ self .make_read_only == other .make_read_only and
786+ self .parameters == other .parameters and
787+ self .time_start == other .time_start and
788+ self .time_end == other .time_end )
789+
790+ def __ne__ (self , other ):
791+ if not isinstance (other , self .__class__ ):
792+ return NotImplemented
793+ return not (self == other )
794+
795+ def __hash__ (self ):
796+ return hash ((self .event_type , self .action , self .thread_id , self .unique_thread_id ,
797+ self .heap , self .address , self .previous_address , self .size , self .base_address ,
798+ self .flags , self .result , self .reserve_size , self .commit_size , self .make_read_only ,
799+ tuple (self .parameters ), self .time_start , self .time_end ))
800+
801+ def __setattr__ (self , name , value ):
802+ try :
803+ object .__setattr__ (self , name , value )
804+ except AttributeError :
805+ raise AttributeError (f"attribute '{ name } ' is read only" )
806+
807+ def __repr__ (self ):
808+ return f"<TTDHeapEvent: { self .action } @ heap { self .heap :#x} , thread { self .thread_id } >"
809+
810+
724811class DebuggerController :
725812 """
726813 The ``DebuggerController`` object is the core of the debugger. Most debugger operations can be performed on it.
@@ -2042,6 +2129,58 @@ def get_ttd_calls_for_symbols(self, symbols: str, start_return_address: int = 0,
20422129 dbgcore .BNDebuggerFreeTTDCallEvents (events , count .value )
20432130 return result
20442131
2132+ def get_ttd_heap_objects (self ) -> List [TTDHeapEvent ]:
2133+ """
2134+ Get TTD heap operation events.
2135+
2136+ This method is only available when debugging with TTD (Time Travel Debugging).
2137+ Use the is_ttd property to check if TTD is available before calling this method.
2138+
2139+ :return: list of TTDHeapEvent objects representing heap operations
2140+ :raises: May raise an exception if TTD is not available
2141+ """
2142+ count = ctypes .c_ulonglong ()
2143+ events = dbgcore .BNDebuggerGetTTDHeapObjects (self .handle , count )
2144+
2145+ if not events :
2146+ return []
2147+
2148+ result = []
2149+ for i in range (count .value ):
2150+ event = events [i ]
2151+ time_start = TTDPosition (event .timeStart .sequence , event .timeStart .step )
2152+ time_end = TTDPosition (event .timeEnd .sequence , event .timeEnd .step )
2153+
2154+ # Convert parameters array to Python list
2155+ parameters = []
2156+ if event .parameters and event .parameterCount > 0 :
2157+ for j in range (event .parameterCount ):
2158+ parameters .append (event .parameters [j ])
2159+
2160+ heap_event = TTDHeapEvent (
2161+ event_type = event .eventType if event .eventType else "" ,
2162+ action = event .action if event .action else "" ,
2163+ thread_id = event .threadId ,
2164+ unique_thread_id = event .uniqueThreadId ,
2165+ heap = event .heap ,
2166+ address = event .address ,
2167+ previous_address = event .previousAddress ,
2168+ size = event .size ,
2169+ base_address = event .baseAddress ,
2170+ flags = event .flags ,
2171+ result = event .result ,
2172+ reserve_size = event .reserveSize ,
2173+ commit_size = event .commitSize ,
2174+ make_read_only = event .makeReadOnly ,
2175+ parameters = parameters ,
2176+ time_start = time_start ,
2177+ time_end = time_end
2178+ )
2179+ result .append (heap_event )
2180+
2181+ dbgcore .BNDebuggerFreeTTDHeapEvents (events , count .value )
2182+ return result
2183+
20452184 def __del__ (self ):
20462185 if dbgcore is not None :
20472186 dbgcore .BNDebuggerFreeController (self .handle )
0 commit comments