From 94994d372d014ce4c8758b9605d63fae651bd8aa Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Sat, 19 Jan 2019 10:06:29 +0000 Subject: Vendor import of lldb trunk r351319 (just before the release_80 branch point): https://llvm.org/svn/llvm-project/lldb/trunk@351319 --- www/python-reference.html | 259 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 251 insertions(+), 8 deletions(-) (limited to 'www/python-reference.html') diff --git a/www/python-reference.html b/www/python-reference.html index d6aa80f87a1b..bde728f9a4c9 100755 --- a/www/python-reference.html +++ b/www/python-reference.html @@ -316,6 +316,195 @@ Enter your Python command(s). Type 'DONE' to end. + +
+

Using the Python API's to create custom breakpoints

+
+ +

Another use of the Python API's in lldb is to create a custom breakpoint resolver. This facility + was added in r342259. +

+

+ It allows you to provide the algorithm which will be used in the breakpoint's + search of the space of the code in a given Target + to determine where to set the breakpoint locations - the actual places where the breakpoint will trigger. + To understand how this works you need to know a little about how lldb handles breakpoints. +

+

+ In lldb, a breakpoint is composed of three parts: the Searcher, the Resolver, and the Stop Options. The Searcher and + Resolver cooperate to determine how breakpoint locations are set and differ between each breakpoint type. + Stop options determine what happens when a location triggers and includes the commands, conditions, ignore counts, etc. + Stop options are common between all breakpoint types, so for our purposes only the Searcher and Resolver are relevant. +

+

+ The Searcher's job is to traverse in a structured way the code in the current target. It + proceeds from the Target, to search all the Modules in the Target, in each Module it can recurse + into the Compile Units in that module, and within each Compile Unit it can recurse over the Functions + it contains. +

+

+ The Searcher can be provided with a SearchFilter that it will use to restrict this search. For instance, if the + SearchFilter specifies a list of Modules, the Searcher will not recurse into Modules that aren't on the list. + When you pass the -s modulename flag to break set you are creating a Module-based search filter. + When you pass -f filename.c to break set -n you are creating a file based search filter. If neither + of these is specified, the breakpoint will have a no-op search filter, so all parts of the program are searched + and all locations accepted. +

+

+ The Resolver has two functions. The most important one is the callback it provides. This will get called at the appropriate time + in the course of the search. The callback is where the job of adding locations to the breakpoint gets done. +

+

+ The other function is specifying to the Searcher at what depth in the above described recursion it wants to be + called. Setting a search depth also provides a stop for the recursion. For instance, if you request a Module depth + search, then the callback will be called for each Module as it gets added to the Target, but the searcher will not recurse into the + Compile Units in the module. +

+

+ One other slight sublety is that the depth at which you get called back is not necessarily the depth at which the + the SearchFilter is specified. For instance, if you are doing symbol searches, it is convenient to use the Module + depth for the search, since symbols are stored in the module. + But the SearchFilter might specify some subset of CompileUnits, so not all the symbols you might find in each module + will pass the search. You don't need to + handle this situation yourself, since SBBreakpoint::AddLocation will only add locations that pass the Search Filter. + This API returns an SBError to inform you whether your location was added. +

+

+ When the breakpoint is originally created, its Searcher will process all the currently loaded modules. + The Searcher will also visit any new modules as they are added to the target. This happens, for instance, when + a new shared library gets added to the target in the course of running, or on rerunning if any of the currently + loaded modules have been changed. Note, in the latter case, all the locations set in the old module will get + deleted and you will be asked to recreate them in the new version of the module when your callback gets called + with that module. For this reason, you shouldn't + try to manage the locations you add to the breakpoint yourself. Note that the Breakpoint takes care of + deduplicating equal addresses in AddLocation, so you shouldn't need to worry about that anyway. +

+

+ At present, when adding a scripted Breakpoint type, you can only provide a custom Resolver, not a custom SearchFilter. +

+

+ The custom Resolver is provided as a Python class with the following methods: +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameArgumentsDescription
+ __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 +

+ __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. +

+ __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. +
+ 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. +
+ +

To define a new breakpoint command defined by this class from the lldb command line, use the command:

+ +
(lldb) breakpoint set -P MyModule.MyResolverClass
+
+

You can also populate the extra_args SBStructuredData with a dictionary of key/value pairs with:

+ +
(lldb) breakpoint set -P MyModule.MyResolverClass -k key_1 -v value_1 -k key_2 -v value_2
+
+

Although you can't write a scripted SearchFilter, both the command line and the SB API's for adding a + scripted resolver allow you to specify a SearchFilter restricted to certain modules or certain compile + units. When using the command line to create the resolver, you can specify a Module specific SearchFilter + by passing the -s ModuleName option - which can be specified multiple times. + You can also specify a SearchFilter restricted to certain + compile units by passing in the -f CompUnitName option. This can also be specified more than + once. And you can mix the two to specify "this comp unit in this module". So, for instance, +

+ +
(lldb) breakpoint set -P MyModule.MyResolverClass -s a.out
+
+

+ will use your resolver, but will only recurse into or accept new locations in the module a.out. +

+ +

Another option for creating scripted breakpoints is to use the SBTarget.CreateBreakpointFromScript API. + This one has the advantage that you can pass in an arbitrary SBStructuredData object, so you can + create more complex parametrizations. + SBStructuredData has a handy SetFromJSON method which you can use for this purpose. + Your __init__ function gets passed this SBStructuredData object. + This API also allows you to directly provide the list of Modules and the list of CompileUnits that will + make up the SearchFilter. If you pass in empty lists, the breakpoint will use the default "search everywhere,accept + everything" filter. +

+ +

Using the Python API's to create custom stepping logic

@@ -654,13 +843,14 @@ $2 = 0x000000010010aba0 Let's Be Friendsfoobar LLDB.framework to create your own stand-alone python programs, you will need to tell python where to look in order to find this module. This is done by setting the PYTHONPATH environment variable, adding - a path to the directory that contains the lldb.py python module. On - Mac OS X, this is contained inside the LLDB.framework, so you would do: + a path to the directory that contains the lldb.py python module. The + lldb driver program has an option to report the path to the lldb module. + You can use that to point to correct lldb.py:

For csh and tcsh:

-

% setenv PYTHONPATH /Developer/Library/PrivateFrameworks/LLDB.framework/Resources/Python

+

% setenv PYTHONPATH `lldb -P`

For sh and bash: -

% export PYTHONPATH=/Developer/Library/PrivateFrameworks/LLDB.framework/Resources/Python

+

% export PYTHONPATH=`lldb -P`

Alternately, you can append the LLDB Python directory to the sys.path list directly in your Python code before importing the lldb module.

@@ -738,11 +928,64 @@ if target: # We do have a symbol, print some info for the symbol print symbol
-
- +
+ +
+ +
+

Writing LLDB frame recognizers in Python

+
+ +

Frame recognizers allow for retrieving information about special frames based on + ABI, arguments or other special properties of that frame, even without source + code or debug info. Currently, one use case is to extract function arguments + that would otherwise be unaccesible, or augment existing arguments.

+ +

Adding a custom frame recognizer is done by implementing a Python class + and using the 'frame recognizer add' command. The Python class should have a + 'get_recognized_arguments' method and it will receive an argument of type + lldb.SBFrame representing the current frame that we are trying to recognize. + The method should return a (possibly empty) list of lldb.SBValue objects that + represent the recognized arguments.

+ +

An example of a recognizer that retrieves the file descriptor values from libc + functions 'read', 'write' and 'close' follows:

+ +
  class LibcFdRecognizer(object):
+    def get_recognized_arguments(self, frame):
+      if frame.name in ["read", "write", "close"]:
+        fd = frame.EvaluateExpression("$arg1").unsigned
+        value = lldb.target.CreateValueFromExpression("fd", "(int)%d" % fd)
+        return [value]
+      return []
+
+ +

The file containing this implementation can be imported via 'command script + import' and then we can register this recognizer with 'frame recognizer add'. + It's important to restrict the recognizer to the libc library (which is + libsystem_kernel.dylib on macOS) to avoid matching functions with the same name in other modules:

+ +
(lldb) command script import .../fd_recognizer.py
+(lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib
+
+ +

When the program is stopped at the beginning of the 'read' function in libc, we + can view the recognizer arguments in 'frame variable':

+ +
(lldb) b read
+(lldb) r
+Process 1234 stopped
+* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
+    frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read
+(lldb) frame variable
+(int) fd = 3
+
-
-
+ + + + + -- cgit v1.3