| # 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 | |
| ``` |