@@ -187,6 +187,9 @@ def help(self, *args):
187187 def construct_locals (self ) -> List [Tuple [List [str ], Any ]]:
188188 """Returns a listing of the functions to be added to the environment."""
189189 return [
190+ (["bc" , "breakpoint_clear" ], self .breakpoint_clear ),
191+ (["bl" , "breakpoint_list" ], self .breakpoint_list ),
192+ (["bp" , "breakpoint" ], self .breakpoint ),
190193 (["dt" , "display_type" ], self .display_type ),
191194 (["db" , "display_bytes" ], self .display_bytes ),
192195 (["dw" , "display_words" ], self .display_words ),
@@ -797,6 +800,89 @@ def create_configurable(
797800
798801 return constructed
799802
803+ def breakpoint (
804+ self , address : int , layer_name : Optional [str ] = None , lowest : bool = False
805+ ) -> None :
806+ """Sets a breakpoint on a particular address (within a specific layer)"""
807+ if layer_name is None :
808+ if self .current_layer is None :
809+ raise ValueError ("Current layer must be set" )
810+ layer_name = self .current_layer
811+
812+ layer : interfaces .layers .DataLayerInterface = self .context .layers [layer_name ]
813+
814+ if lowest :
815+ while isinstance (layer , interfaces .layers .TranslationLayerInterface ):
816+ mapping = layer .mapping (address , 1 )
817+ if not mapping :
818+ raise ValueError (
819+ "Offset cannot be mapped lower, cannot break at lowest layer"
820+ )
821+ _ , _ , mapped_offset , _ , mapped_layer_name = next (mapping )
822+ layer = self .context .layers [mapped_layer_name ]
823+ address = mapped_offset
824+
825+ # Check if the read value is already overloaded
826+ if not hasattr (layer .read , "breakpoints" ):
827+ # Layer read is not yet wrapped
828+ def wrapped_read (offset : int , length : int , pad : bool = False ) -> bytes :
829+ original_read = getattr (wrapped_read , "original_read" )
830+ for breakpoint in getattr (wrapped_read , "breakpoints" ):
831+ if (offset <= breakpoint ) and (breakpoint < offset + length ):
832+ print (
833+ "Hit breakpoint, entering python debugger. To continue running without the debugger use the command continue"
834+ )
835+ import pdb
836+
837+ pdb .set_trace ()
838+ _ = "First statement after the breakpoint, use u(p), d(own) and list to navigate through the execution frames"
839+ return original_read (offset , length , pad )
840+
841+ setattr (wrapped_read , "breakpoints" , set ())
842+ setattr (wrapped_read , "original_read" , layer .read )
843+ setattr (layer , "read" , wrapped_read )
844+
845+ # Add the new breakpoint
846+ print (f"Setting breakpoint { address :#x} on { layer .name } " )
847+ breakpoints = getattr (layer .read , "breakpoints" )
848+ breakpoints .add (address )
849+ setattr (layer .read , "breakpoints" , breakpoints )
850+
851+ def breakpoint_list (self , layer_names : Optional [List [str ]] = None ):
852+ """List available breakpoints for a set of layers"""
853+ if not layer_names :
854+ layer_names = [layer_name for layer_name in self .context .layers ]
855+
856+ print ("Listing breakpoints:" )
857+ for layer_name in layer_names :
858+ print (f" { layer_name } " )
859+ layer = self .context .layers .get (layer_name , None )
860+ if layer and hasattr (layer .read , "breakpoints" ):
861+ for breakpoint in layer .read .breakpoints :
862+ print (f" { breakpoint :#x} " )
863+
864+ def breakpoint_clear (
865+ self , offset : Optional [int ] = None , layer_name : Optional [str ] = None
866+ ):
867+ """Clears a offset breakpoint on a layer (or all breakpoints if offset or layer not specified)
868+
869+ Args:
870+ offset: Address of the breakpoint to clear (or all if None)
871+ layer_name: Layer to clear breakpoints from (or all if None)
872+ """
873+ print ("Clearing breakpoints:" )
874+ for candidate_layer_name in self .context .layers :
875+ candidate_layer = self .context .layers [candidate_layer_name ]
876+ if layer_name is None or layer_name == candidate_layer_name :
877+ print (f" { candidate_layer_name } " )
878+ if hasattr (candidate_layer .read , "breakpoints" ):
879+ breakpoints_to_remove = set ()
880+ for breakpoint in candidate_layer .read .breakpoints :
881+ if offset is None or offset == breakpoint :
882+ print (f" clearing { breakpoint :#x} " )
883+ breakpoints_to_remove .add (breakpoint )
884+ candidate_layer .read .breakpoints -= breakpoints_to_remove
885+
800886
801887class NullFileHandler (io .BytesIO , interfaces .plugins .FileHandlerInterface ):
802888 """Null FileHandler that swallows files whole without consuming memory"""
0 commit comments