| # Implementing Standalone Scripts | |
| ## Configuring `PYTHONPATH` | |
| LLDB has all of its core code built into a shared library which gets used by | |
| the `lldb` command line application. | |
| - On macOS this shared library is a framework: `LLDB.framework`. | |
| - On other unix variants the program is a shared library: lldb.so. | |
| LLDB also provides an `lldb.py` module that contains the bindings from LLDB | |
| into Python. To use the `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. 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: | |
| ```csh | |
| % setenv PYTHONPATH `lldb -P` | |
| ``` | |
| For sh and bash: | |
| ```bash | |
| $ export PYTHONPATH=`lldb -P` | |
| ``` | |
| Alternatively, you can append the LLDB Python directory to the sys.path list | |
| directly in your Python code before importing the lldb module. | |
| ## Initialization | |
| The standard test for `__main__`, like many python modules do, is useful for | |
| creating scripts that can be run from the command line. However, for command | |
| line scripts, the debugger instance must be created manually. Sample code would | |
| look like: | |
| ```python3 | |
| if __name__ == '__main__': | |
| # Initialize the debugger before making any API calls. | |
| lldb.SBDebugger.Initialize() | |
| # Create a new debugger instance in your module if your module | |
| # can be run from the command line. When we run a script from | |
| # the command line, we won't have any debugger object in | |
| # lldb.debugger, so we can just create it if it will be needed | |
| debugger = lldb.SBDebugger.Create() | |
| # Next, do whatever work this module should do when run as a command. | |
| # ... | |
| # Finally, dispose of the debugger you just made. | |
| lldb.SBDebugger.Destroy(debugger) | |
| # Terminate the debug session | |
| lldb.SBDebugger.Terminate() | |
| ``` | |
| ## Example | |
| Now your python scripts are ready to import the lldb module. Below is a python | |
| script that will launch a program from the current working directory called | |
| `a.out`, set a breakpoint at `main`, and then run and hit the breakpoint, and | |
| print the process, thread and frame objects if the process stopped: | |
| ```python3 | |
| #!/usr/bin/env python3 | |
| import lldb | |
| import os | |
| def disassemble_instructions(insts): | |
| for i in insts: | |
| print(i) | |
| # Set the path to the executable to debug | |
| exe = "./a.out" | |
| # Create a new debugger instance | |
| debugger = lldb.SBDebugger.Create() | |
| # When we step or continue, don't return from the function until the process | |
| # stops. Otherwise we would have to handle the process events ourselves which, while doable is | |
| # a little tricky. We do this by setting the async mode to false. | |
| debugger.SetAsync(False) | |
| # Create a target from a file and arch | |
| print("Creating a target for '%s'" % exe) | |
| target = debugger.CreateTargetWithFileAndArch(exe, lldb.LLDB_ARCH_DEFAULT) | |
| if target: | |
| # If the target is valid set a breakpoint at main | |
| main_bp = target.BreakpointCreateByName( | |
| "main", target.GetExecutable().GetFilename() | |
| ) | |
| print(main_bp) | |
| # Launch the process. Since we specified synchronous mode, we won't return | |
| # from this function until we hit the breakpoint at main | |
| process = target.LaunchSimple(None, None, os.getcwd()) | |
| # Make sure the launch went ok | |
| if process: | |
| # Print some simple process info | |
| state = process.GetState() | |
| print(process) | |
| if state == lldb.eStateStopped: | |
| # Get the first thread | |
| thread = process.GetThreadAtIndex(0) | |
| if thread: | |
| # Print some simple thread info | |
| print(thread) | |
| # Get the first frame | |
| frame = thread.GetFrameAtIndex(0) | |
| if frame: | |
| # Print some simple frame info | |
| print(frame) | |
| function = frame.GetFunction() | |
| # See if we have debug info (a function) | |
| if function: | |
| # We do have a function, print some info for the function | |
| print(function) | |
| # Now get all instructions for this function and print them | |
| insts = function.GetInstructions(target) | |
| disassemble_instructions(insts) | |
| else: | |
| # See if we have a symbol in the symbol table for where we stopped | |
| symbol = frame.GetSymbol() | |
| if symbol: | |
| # We do have a symbol, print some info for the symbol | |
| print(symbol) | |
| ``` | |
| ## Expected Output | |
| Exact output varies by system, but you should see something like this: | |
| ``` | |
| Creating a target for './a.out' | |
| SBBreakpoint: id = 1, name = 'main', module = a.out, locations = 1 | |
| SBProcess: pid = 2593449, state = stopped, threads = 1, executable = a.out | |
| thread #1: tid = 2593449, 0x0000aaaaaaaa0714 a.out`main at test.c:3:9, name = 'test.o', stop reason = breakpoint 1.1 | |
| frame #0: 0x0000aaaaaaaa0714 a.out`main at test.c:3:9 | |
| SBFunction: id = 0x0000002e, name = main, type = main | |
| a.out[0x714]: mov w0, #0x0 ; =0 | |
| a.out[0x718]: ret | |
| ``` | |
| ## Troubleshooting | |
| You can use all the usual Python tools to debug scripts, and on top of that | |
| you can enable LLDB's log channels. To do this in the script shown above, add | |
| this line right after `debugger` has been assigned: | |
| ```python | |
| debugger.EnableLog("lldb", ["all"]) | |
| ``` | |
| `lldb` `all` enables a lot of different channels, so you will probably want | |
| to enable only a few channels once you know what you are interested in. | |
| This API call is the equivalent of `log enable lldb all` when using LLDB | |
| interactively. All channels available to `log enable` can be enabled using | |
| `EnableLog` too. |