|
| 1 | +# Custom Breakpoint Resolvers |
| 2 | + |
| 3 | +Another use of the Python API's in lldb is to create a custom breakpoint |
| 4 | +resolver. |
| 5 | + |
| 6 | +It allows you to provide the algorithm which will be used in the breakpoint's |
| 7 | +search of the space of the code in a given Target to determine where to set the |
| 8 | +breakpoint locations - the actual places where the breakpoint will trigger. To |
| 9 | +understand how this works you need to know a little about how lldb handles |
| 10 | +breakpoints. |
| 11 | + |
| 12 | +In lldb, a breakpoint is composed of three parts: |
| 13 | +1. the Searcher |
| 14 | +2. the Resolver, |
| 15 | +3. the Stop Options. |
| 16 | + |
| 17 | +The Searcher and Resolver cooperate to determine how breakpoint locations are |
| 18 | +set and differ between each breakpoint type. Stop options determine what |
| 19 | +happens when a location triggers and includes the commands, conditions, ignore |
| 20 | +counts, etc. Stop options are common between all breakpoint types, so for our |
| 21 | +purposes only the Searcher and Resolver are relevant. |
| 22 | + |
| 23 | +### Breakpoint Searcher |
| 24 | + |
| 25 | +The Searcher's job is to traverse in a structured way the code in the current |
| 26 | +target. It proceeds from the Target, to search all the Modules in the Target, |
| 27 | +in each Module it can recurse into the Compile Units in that module, and within |
| 28 | +each Compile Unit it can recurse over the Functions it contains. |
| 29 | + |
| 30 | +The Searcher can be provided with a SearchFilter that it will use to restrict |
| 31 | +this search. For instance, if the SearchFilter specifies a list of Modules, the |
| 32 | +Searcher will not recurse into Modules that aren't on the list. When you pass |
| 33 | +the -s modulename flag to break set you are creating a Module-based search |
| 34 | +filter. When you pass -f filename.c to break set -n you are creating a file |
| 35 | +based search filter. If neither of these is specified, the breakpoint will have |
| 36 | +a no-op search filter, so all parts of the program are searched and all |
| 37 | +locations accepted. |
| 38 | + |
| 39 | +### Breakpoint Resolver |
| 40 | + |
| 41 | +The Resolver has two functions: |
| 42 | + |
| 43 | +The most important one is the callback it provides. This will get called at the |
| 44 | +appropriate time in the course of the search. The callback is where the job of |
| 45 | +adding locations to the breakpoint gets done. |
| 46 | + |
| 47 | +The other function is specifying to the Searcher at what depth in the above |
| 48 | +described recursion it wants to be called. Setting a search depth also provides |
| 49 | +a stop for the recursion. For instance, if you request a Module depth search, |
| 50 | +then the callback will be called for each Module as it gets added to the |
| 51 | +Target, but the searcher will not recurse into the Compile Units in the module. |
| 52 | + |
| 53 | +One other slight subtlety is that the depth at which you get called back is not |
| 54 | +necessarily the depth at which the SearchFilter is specified. For instance, |
| 55 | +if you are doing symbol searches, it is convenient to use the Module depth for |
| 56 | +the search, since symbols are stored in the module. But the SearchFilter might |
| 57 | +specify some subset of CompileUnits, so not all the symbols you might find in |
| 58 | +each module will pass the search. You don't need to handle this situation |
| 59 | +yourself, since SBBreakpoint::AddLocation will only add locations that pass the |
| 60 | +Search Filter. This API returns an SBError to inform you whether your location |
| 61 | +was added. |
| 62 | + |
| 63 | +When the breakpoint is originally created, its Searcher will process all the |
| 64 | +currently loaded modules. The Searcher will also visit any new modules as they |
| 65 | +are added to the target. This happens, for instance, when a new shared library |
| 66 | +gets added to the target in the course of running, or on rerunning if any of |
| 67 | +the currently loaded modules have been changed. Note, in the latter case, all |
| 68 | +the locations set in the old module will get deleted and you will be asked to |
| 69 | +recreate them in the new version of the module when your callback gets called |
| 70 | +with that module. For this reason, you shouldn't try to manage the locations |
| 71 | +you add to the breakpoint yourself. Note that the Breakpoint takes care of |
| 72 | +deduplicating equal addresses in AddLocation, so you shouldn't need to worry |
| 73 | +about that anyway. |
| 74 | + |
| 75 | +### Scripted Breakpoint Resolver |
| 76 | + |
| 77 | +At present, when adding a ScriptedBreakpoint type, you can only provide a |
| 78 | +custom Resolver, not a custom SearchFilter. |
| 79 | + |
| 80 | +The custom Resolver is provided as a Python class with the following methods: |
| 81 | + |
| 82 | +| Name | Arguments | Description | |
| 83 | +|------|-----------|-------------| |
| 84 | +| `__init__` | `bkpt`: `lldb.SBBreakpoint` `extra_args`: `lldb.SBStructuredData` | This is the constructor for the new Resolver. `bkpt` is the breakpoint owning this Resolver. `extra_args` is an `SBStructuredData` object that the user can pass in when creating instances of this breakpoint. It is not required, but is quite handy. For instance if you were implementing a breakpoint on some symbol name, you could write a generic symbol name based Resolver, and then allow the user to pass in the particular symbol in the extra_args | |
| 85 | +| `__callback__` | `sym_ctx`: `lldb.SBSymbolContext` | This is the Resolver callback. The `sym_ctx` argument will be filled with the current stage of the search. For instance, if you asked for a search depth of lldb.eSearchDepthCompUnit, then the target, module and compile_unit fields of the sym_ctx will be filled. The callback should look just in the context passed in `sym_ctx` for new locations. If the callback finds an address of interest, it can add it to the breakpoint with the `SBBreakpoint.AddLocation` method, using the breakpoint passed in to the `__init__` method. | |
| 86 | +| `__get_depth__` | `None` | Specify the depth at which you wish your callback to get called. The currently supported options are: `lldb.eSearchDepthModule` `lldb.eSearchDepthCompUnit` `lldb.eSearchDepthFunction` For instance, if you are looking up symbols, which are stored at the Module level, you will want to get called back module by module. So you would want to return `lldb.eSearchDepthModule`. This method is optional. If not provided the search will be done at Module depth. | |
| 87 | +| `get_short_help` | `None` | This is an optional method. If provided, the returned string will be printed at the beginning of the description for this breakpoint. | |
| 88 | + |
| 89 | +To define a new breakpoint command defined by this class from the lldb command |
| 90 | +line, use the command: |
| 91 | + |
| 92 | +``` |
| 93 | +(lldb) breakpoint set -P MyModule.MyResolverClass |
| 94 | +``` |
| 95 | + |
| 96 | +You can also populate the extra_args SBStructuredData with a dictionary of |
| 97 | +key/value pairs with: |
| 98 | + |
| 99 | +``` |
| 100 | +(lldb) breakpoint set -P MyModule.MyResolverClass -k key_1 -v value_1 -k key_2 -v value_2 |
| 101 | +``` |
| 102 | + |
| 103 | +Although you can't write a scripted SearchFilter, both the command line and the |
| 104 | +SB API's for adding a scripted resolver allow you to specify a SearchFilter |
| 105 | +restricted to certain modules or certain compile units. When using the command |
| 106 | +line to create the resolver, you can specify a Module specific SearchFilter by |
| 107 | +passing the -s ModuleName option - which can be specified multiple times. You |
| 108 | +can also specify a SearchFilter restricted to certain compile units by passing |
| 109 | +in the -f CompUnitName option. This can also be specified more than once. And |
| 110 | +you can mix the two to specify "this comp unit in this module". So, for |
| 111 | +instance, |
| 112 | + |
| 113 | +``` |
| 114 | +(lldb) breakpoint set -P MyModule.MyResolverClass -s a.out |
| 115 | +``` |
| 116 | + |
| 117 | +will use your resolver, but will only recurse into or accept new locations in |
| 118 | +the module a.out. |
| 119 | + |
| 120 | +Another option for creating scripted breakpoints is to use the |
| 121 | +SBTarget.BreakpointCreateFromScript API. This one has the advantage that you |
| 122 | +can pass in an arbitrary SBStructuredData object, so you can create more |
| 123 | +complex parametrizations. SBStructuredData has a handy SetFromJSON method which |
| 124 | +you can use for this purpose. Your __init__ function gets passed this |
| 125 | +SBStructuredData object. This API also allows you to directly provide the list |
| 126 | +of Modules and the list of CompileUnits that will make up the SearchFilter. If |
| 127 | +you pass in empty lists, the breakpoint will use the default "search |
| 128 | +everywhere,accept everything" filter. |
0 commit comments