If you've ever used Xcode's console to debug, then you're probably familiar with at least a few LLDB commands. You can do things like print objects (using
po ...), set breakpoints (using
breakpoint set ...) or show the general purpose registers (using
What you might not know is that you can create your own custom commands, and that these commands can do some powerful things. This post will describe how to create a basic LLDB command, and then will walk you through some of the setup needed to get the command showing up in Xcode.
Your command's guts will be written in Python. You won't be doing anything especially difficult, and you can probably feel your way through this post by copying the example code. If you'd like to learn the basics of Python before you begin, I recommend the official Python tutorial.
At the end of this post, you’ll be able to enter your new command at the LLDB prompt and see the message being printed by your Python function:
Step 1: Create your function
In this step, You'll write your Python function.
Open your text editor of choice and create a new Python file. Name it my_commands.py and save it somewhere convenient (you’ll tell LLDB exactly where to find it in Step 2).
Inside my_commands.py, add a function named
printworld. This is the function that LLDB will call when you run your command. Be sure that your function takes four parameters. You won't use them here, but your command will fail if they're missing. Inside your function, print a single line of text:
def printworld(debugger, command, result, internal_dict):
Step 2: Import your script
In this step, you'll tell LLDB where to find your Python file.
Enter the LLDB command line, either by running and then pausing one of your projects in Xcode, or by running
lldb in your terminal. Either way, you should see a prompt like this:
You load new Python modules (i.e. Python files) into LLDB using the
command script import command (confusing naming, I know). This command takes a single argument - the path to the module you'd like to import. I saved my file to my home directory (
~/my_commands.py), so my command looks like this:
(lldb) command script import ~/my_commands.py
After running this command, LLDB will know where your file is saved. However, you still need a way to call the function inside the file.
Step 3: Add your command
Here’s where you name your LLDB command and map it to your Python function. You do this using the
command script add command. I'll show you the full command first, and then will describe the parts:
(lldb) command script add -f my_commands.printworld hello
-fparameter indicates that you want to bind a Python function to your command.
my_commands.printworldis the full name of your Python function. You must prefix the module name (
my_commands) to the function name (
printworld). LLDB doesn't warn you if the function name you provide doesn't exist. If you see a message like "error: unable to execute script function" when you try to run your command, double-check your command name for typos.
hellois the name of your new command.
At this point, you've got a fully-functioning command. Try it out, and you should see the text printed by the Python function:
If you stop now, you'll need to repeat steps 2 and 3 every time you launch LLDB. To automatically load the script when Xcode launches, you've got one more step to complete.
Step 4: Automatically load your script when Xcode launches
In this step, you'll configure LLDB to load your command every time you start a new session. You do this by taking advantage of the .lldbinit file.
.lldbinit is the file that LLDB looks for and loads each time a new session is started. Create this file in your home directory (or open this file now). Somewhere in the file, add the
command script import and
command script add commands from steps 2 and 3:
command script import ~/my_commands.py
command script add -f my_commands.printworld hello
Now, when you start a new LLDB session, your
hello command will be available right away.
This is a trivial example, but LLDB provides a Python library that gives you a ton of functionality. In Part 2, I'll use some of this functionality to fill out my new command.