So it’s extremely important to make this 70% of our time as pleasant as possible. LLDB comes to rescue. Fancy Xcode Debugger UI shows you all available information without typing a single LLDB command. However, the console is still a big part of our workflow. Let’s break down some of the most useful LLDB tricks. I personally use them daily for debugging.

Where should we go first?

LLDB is an enormous tool and it has a lot of useful commands inside. I won’t describe them all. I’d like to walk you through the most useful commands instead. So here is our plan:

1. Explore variables value and state

Commands: expression, e, print, po, p

The basic function of a debugger is to explore and modify variables’ values. This is what expression or e is made for (and much more actually). You can evaluate basically any expression or command in a runtime.

Let’s assume you’re debugging some function valueOfLifeWithoutSumOf() which do a summation of two numbers and extract the result from 42.

Let’s also assume you keep getting the wrong answer and you don’t know why. So to find a problem you can do something like this:

Or… it’s better to use LLDB expression instead to change the value in the runtime. And find out where the problem has happened. First, set a breakpoint to a place you interested in. Then run your app.

To print the value of specific variable in LLDB format you should call:

(lldb) e <variable>

And the very same command is used to evaluate some expression:

(lldb) e <expression>

(lldb) e sum (Int) $R0 = 6 // You can also use $R0 to refer to this variable in the future (during current debug session)(lldb) e sum = 4 // Change value of sum variable(lldb) e sum (Int) $R2 = 4 // sum variable will be "4" till the end of debugging session

expression command also has some flags. To distinct flags and actual expression LLDB uses double-dash notation -- after expression command like this:

(lldb) expression <some flags> -- <variable>

expression has almost ~30 different flags. And I encourage you to explore them all. Write the command below in the terminal to get full documentation:

> lldb> (lldb) help # To explore all available commands> (lldb) help expression # To explore all expressions related sub-commands

Let’s say we have some object called logger. This object contains some string and structure as properties. For example, you want to explore first-level properties only. Just use -D flag with appropriate depth level to do so:

You can also explore object description with e -O -- or simply using alias po like in the example below:

(lldb) po logger<Logger: 0x608000087e90>

Not so descriptive, isn’t it? To get human-readable description you have to apply your custom class to CustomStringConvertible protocol and implement var description: String { return ...} property. Only then po returns you readable description.

At the beginning of this section, I’ve also mentioned print command.Basically print <expression/variable> is the same as expression -- <expression/variable>. Except print command doesn’t take any flags or additional arguments.

2. Get overall app’s state + language specific commands

bugreport, frame, language

How often have you copied and pasted and paste crash logs into your task manager to explore the issue later? LLDB has this great little command called bugreport which will generate a full report of current app’s state. It could be really helpful if you encounter some problem but want to tackle it a bit later. In order to restore your understand of app’s state, you can use bugreport generated report.

(lldb) bugreport unwind --outfile <path to output file>

The final report will look like example on the screenshot below:

Example of bugreport command output

Let’s say you want to get a quick overview of the current stack frame in current thread. frame command can help you with that:

Use snippet below to get a quick understanding where you are and what surrounding conditions are at the moment:

This information will be useful in breakpoint management later in the article.

LLDB has a couple of commands for a specific language. There are commands for C++, Objective-C, Swift and RenderScript. In this case, we’re interested in Swift. So here are these two commands: demangle and refcount.

demangle as written in its name just demangle mangled Swift type name (which Swift generates during compilation to avoid namespace problem). If you’d like to learn more on that I’d suggest you watch this WWDC14 session — “Advanced Swift Debugging in LLDB”.

refcount is also a pretty straightforward command. It shows you reference count for the specific object. Let’s see the output example with an object we used in the previous section — logger:

For sure, this could be quite useful if you are debugging some memory leaks.

3. Control app’s execution flow

process, breakpoint, thread

This part is my favorite. Because using these command from LLDB (breakpoint command in particular) you can automate a lot of routine stuff during debugging. Which eventually speed up your debugging process a lot.

With process you can basically control debug process and attach to a specific target or detach a debugger from it. But since Xcode does the process attachment for us automatically (LLDB is attached by Xcode every time you run a target) I won’t stop on that. You can read how to attach to a target using terminal in this Apple guide — “Using LLDB as a Standalone Debugger”.

Using process status you can explore a current place where the debugger is waiting for you:

Now let’s tackle the most interesting part — breakpoint automation. Did you know you can set a specific action which will execute as soon as breakpoint occurs? Yes, you can! Do you use print() in the code to explore values you’re interested in for debugging? Please don’t do that, there is a better way. 🙂

With breakpoint command, you can setup commands which will execute right when the breakpoint is hit. You can even make “invisible” breakpoints which won’t interrupt execution. Well, technically these “invisible” breakpoints will interrupt execution but you won’t notice it if you add continue command at the end of commands chain.

With thread command and its subcommands you can fully control execution flow: step-over, step-in, step-out and continue. These are direct equivalent of flow control buttons on Xcode debugger toolbar.

There is also a predefined LLDB shortcut for these particular commands:

(lldb) thread step-over(lldb) next // The same as "thread step-over" command(lldb) n // The same as "next" command(lldb) thread step-in(lldb) step // The same as "thread step-in"(lldb) s // The same as "step"

In order to get more information about the current thread just call info subcommand:

Honorable mentions

command, platform, gui

In the LLDB you can find a command for managing other commands. Sounds weird but in practice, it’s quite useful little tools. First, it allows you to execute some LLDB commands right from the file. So you can create a file with some useful commands and execute them at once as if it would be a single LLDB command. Here is a simple example of the file:

thread info // Show current thread infobr list // Show all breakpoints

There is also a downside, unfortunately, you can’t pass any argument to the source file (unless you’ll create a valid variable in the script file itself).

If you need something more advanced you can always use script sub-command. Which will allow you to manage (add, delete, import and list) custom Python scripts. With the script a real automation becomes possible. Please check out this nice guide on Python scripting for LLDB. Just for the demo, let’s create a script file script.py and write a simple command print_hello() which will just print “Hello Debugger!” in the console:

Then we need to import a Python module and start using our script command normally:

Well, you can’t use LLDB GUI mode in the Xcode, but you can always do it from the terminal.

(lldb) gui// You'll see this error if you try to execute gui command in Xcodeerror: the gui command requires an interactive terminal.

This is how LLDB GUI mode looks like

Conclusion:

In this article, I just scratched the surface of true LLDB’s power. Even though LLDB here with us for ages, there are still many people who don’t use its full potential. I have made a quick overview of basic functions and how LLDB can automate debugging process. I hope it was useful.

So much LLDB functions were left behind. There are also some view debugging techniques which I didn’t even mention. If you are interested in such topic, please leave a comment below. I’d be more than happy to write about it.

I strongly encourage you to open a terminal, enable LLDB and just type help. This will show you a full documentation. And you can spend hours reading it. But I guarantee this would be a reasonable time investment. Because knowing your tools is the only way for engineers to become truly productive.

References and useful articles on LLDB

Official LLDB site — you’ll find here all possible materials related to LLDB. Documentation, guides, tutorials, sources and much more.

LLDB Quick Start Guide by Apple — as usual, Apple has a great documentation. This guide will help you to get started with LLDB really quickly. Also, they’ve described how to do debugging with LLDB without Xcode.

How debuggers work: Part 1 — Basics — I enjoyed this series of articles a lot. It’s Just fantastic overview how debuggers really work. Article describes all underlying principles using code of hand-made debugger written in C. I strongly encourage you to read all parts of these great series (Part 2, Part 3).

WWDC14 Advanced Swift Debugging in LLDB — great overview what’s new in LLDB in terms of Swift debugging. And how LLDB helps you be more productive with an overall debugging process using built-in functions and features.

Dancing in the Debugger. A Waltz with LLDB — a clever introduction to some LLDB basics. Some information is a bit outdated (like (lldb) thread return command, for example. Unfortunately, it doesn't work with Swift properly because it can potentially bring some damage to reference counting). Still, it’s a great article to start your LLDB journey.