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:
def breakpoint_function_wrapper(frame, bp_loc, internal_dict):
# Your code goes here
or:
def breakpoint_function_wrapper(frame, bp_loc, extra_args, internal_dict):
# Your code goes here
Argument |
Type |
Description |
---|---|---|
|
|
The current stack frame where the breakpoint got hit. The object will always be valid. This |
|
|
The breakpoint location that just got hit. Breakpoints are represented by |
|
|
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 |
|
|
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:
(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:
(lldb) breakpoint command delete 1