# Breakpoint-Triggered Scripts One very powerful use of the lldb Python API is to have a python script run when a breakpoint gets hit. Adding python scripts to breakpoints provides a way to create complex breakpoint conditions and also allows for smart logging and data gathering. When your process hits a breakpoint to which you have attached some python code, the code is executed as the body of a function which takes three arguments: ```python3 def breakpoint_function_wrapper(frame, bp_loc, internal_dict): # Your code goes here ``` or: ```python3 def breakpoint_function_wrapper(frame, bp_loc, extra_args, internal_dict): # Your code goes here ``` | Argument | Type | Description | |----------|------|-------------| | `frame` | `lldb.SBFrame` | The current stack frame where the breakpoint got hit. The object will always be valid. This `frame` argument might *not* match the currently selected stack frame found in the `lldb` module global variable `lldb.frame`. | | `bp_loc` | `lldb.SBBreakpointLocation` | The breakpoint location that just got hit. Breakpoints are represented by `lldb.SBBreakpoint` objects. These breakpoint objects can have one or more locations. These locations are represented by `lldb.SBBreakpointLocation` objects. | | `extra_args` | `lldb.SBStructuredData` | **Optional** If your breakpoint callback function takes this extra parameter, then when the callback gets added to a breakpoint, its contents can parametrize this use of the callback. For instance, instead of writing a callback that stops when the caller is "Foo", you could take the function name from a field in the `extra_args`, making the callback more general. The `-k` and `-v` options to `breakpoint command add` will be passed as a Dictionary in the `extra_args` parameter, or you can provide it with the SB API's. | | `internal_dict` | `dict` | The python session dictionary as a standard python dictionary object. | Optionally, a Python breakpoint command can return a value. Returning `False` tells LLDB that you do not want to stop at the breakpoint. Any other return value (including None or leaving out the return statement altogether) is akin to telling LLDB to actually stop at the breakpoint. This can be useful in situations where a breakpoint only needs to stop the process when certain conditions are met, and you do not want to inspect the program state manually at every stop and then continue. An example will show how simple it is to write some python code and attach it to a breakpoint. The following example will allow you to track the order in which the functions in a given shared library are first executed during one run of your program. This is a simple method to gather an order file which can be used to optimize function placement within a binary for execution locality. We do this by setting a regular expression breakpoint that will match every function in the shared library. The regular expression '.' will match any string that has at least one character in it, so we will use that. This will result in one lldb.SBBreakpoint object that contains an lldb.SBBreakpointLocation object for each function. As the breakpoint gets hit, we use a counter to track the order in which the function at this particular breakpoint location got hit. Since our code is passed the location that was hit, we can get the name of the function from the location, disable the location so we won't count this function again; then log some info and continue the process. Note we also have to initialize our counter, which we do with the simple one-line version of the script command. Here is the code: ```python3 (lldb) breakpoint set --func-regex=. --shlib=libfoo.dylib Breakpoint created: 1: regex = '.', module = libfoo.dylib, locations = 223 (lldb) script counter = 0 (lldb) breakpoint command add --script-type python 1 Enter your Python command(s). Type 'DONE' to end. > # Increment our counter. Since we are in a function, this must be a global python variable > global counter > counter += 1 > # Get the name of the function > name = frame.GetFunctionName() > # Print the order and the function name > print('[%i] %s' % (counter, name)) > # Disable the current breakpoint location so it doesn't get hit again > bp_loc.SetEnabled(False) > # No need to stop here > return False > DONE ``` The breakpoint command add command above attaches a python script to breakpoint 1. To remove the breakpoint command: ```python3 (lldb) breakpoint command delete 1 ```