comment 1

#6 Basic LLDB tips

Long story short

For a few years of programming iOS apps, my use of LLDB debugger has been minimal:

That was all of it. Nothing to be proud of. Hitting a breakpoint and using a po command. I have known that po stands for ‘print object’ and that it can evaluate expressions. The more complex problems I had to deal with became, the more advanced toolbelt I needed.

Let’s start!

Discovering LLDB workshop

What you can always do is to type a help command:

You will already notice that even an apropos command is interesting. Let’s use LLDB’s ‘intelligence’ to suggest us commands for breakpoints:

Not bad LLDB, not bad.

Do I have to use Xcode to work with LLDB?

Absolutely… not 🙂 We don’t have to rely on Xcode’s Debug Area. We can easily launch LLDB from terminal. Before we do that we need to build an app for a simulator environment. From your project directory type:

You will now have your *.app file ready for debugging at: project_dir/build/Release-iphonesimulator/appname.app.

Now, without changing the directory you can launch LLDB in Terminal:

Then you need to create a test target like that:

Finally, you launch a process to start our session:

Now you are at the same point when we run debugging in Xcode.

If you try to quit it with a standard Ctrl+C command, that won’t work. Use a quit command instead:

Why should I use a command line LLDB?

If you have looked through the results of(lldb) apropos breakpoint, you must have noticed plenty of possibilities. For more targeted help regarding breakpoints type:

Although Xcode has a subset of debugging features implemented in UI:

  • Breakpoint Navigator (⌘ + 7)
  • Debug Navigator (⌘ + 6)
  • Debug Area (⌘ + Shift + Y)
  • Debug menu item

when using LLDB from a command line we will get more detailed information.

Take a list of breakpoints, for example. In Debug Navigator you will see a method name, a line of code and information whether the breakpoint is active or not:

Debug Navigator
Debug Navigator

Athough this looks nice, LLDB command breakpoint list gives you more information like a hit count or an address:

We can get even more verbose output if we add params like -v. For a full list of params type:

If you fancy practising more, try to play with breakpoint enable and breakpoint disable subcommands:

Setting simple and complex breakpoints

When using Xcode we are used to set breakpoints in a particular file at a given line. The command line LLDB offers not only this kind of breakpoints but much more. Let’s take a look at some examples, each showing a full and a short version of the same functionality:

  • Breakpoint in file at a given line:

  • Breakpoint(s) set at each selector method:

  • Breakpoint(s) set at each function name:

  • Breakpoint(s) set at method names matching regexp:

  • Breakpoint that is deleted after the first stop:

Clearly this is just a small subset of commands available. For more information about setting breakpoint type:

Navigating in debug session

You are well accustomed to using ‘continue’, ‘step in’, ‘step out’ and ‘step over’ buttons while debugging in Xcode. How would that look like as LLDB commands?

As always, you can find more params by looking into thread’s help.

Other selected LLDB tricks

What else can we achieve with the command line LLDB? Well, let’s see.

Type formatting

You are probably familiar with ‘View Value As’ in Debug Area. It is used to quickly change displaying format of a given value:

View Value As
View Value As

What if you would always like to see boolean values in a decimal format? Take a look at LLDB way to achieve this:

An extra thing we can learn here is that Swift’s Bool is Builtin.Int1. I have to admit that Swift’s types do not work best with LLDB’s type formatting. A type name must be fully qualified. Older Cocoa objects and Obj-C/C are supported much better.

Type summary

In our post about print debugging we have learnt how to use CustomStringConvertible to get a meaningful debug description. A similar thing can be achieved in LLDB. To make it simple, we will use Int. What we usually see in Debug Area is this:

Int in Debug Area
Int in Debug Area

Let’s add a type summary and display it in a console:

When we check Debug Area again we will see our custom description:

Custom Int in Debug Area
Custom Int in Debug Area
Multiline expression mode

Note that we have a fully equipped compiler in our hands when debugging with LLDB. The best way to use its potential is to enter a multiline mode of an expression command. In LLDB type expression, then hit enter:

Thread backtrace

When debugging threads we often use Debug Navigator (⌘ + 5):

Debug Navigator
Debug Navigator

Similar, but even more verbose thread’s backtrace can be printed with this one-liner:

Unwind on error

An extra thing to remember is that expressions executed in LLDB actually affect our executed code. If you change variable’s value in LLDB it will remain modified when continuing the execution.

What is more, some of those expressions may cause crashes. By default program state is cleaned up if the expression causes the crash. However, sometimes we want to see it.

Imagine decrementing var integer:UInt = 10 variable:

We get an error printed but we can easily continue the execution.

To change that behaviour we will set an --unwind-on-error=0 parameter to our expression:

Now we get the actual crash.

Lookup of unformatted variable

In the type summary example above we have been doing our best to have a nicely formatted output and used a frame variable command to display it. Sometimes we want just the opposite – a raw value. Let’s see the difference:

Now, that also explains how many Swift’s struct types are built. They contain a value variable. Digging deeper we can see an actual type that is wrapped in Bool type:

It is an Int1 type – a one-bit integer. If you would like to know more about unboxing Swift’s Bool and other types take a look at [SwiftUnboxed][blog](https://swiftunboxed.com/open-source/Bool/?utm_source=swifting.io&utm_medium=web&utm_campaign=blog%20post). The --raw parameter is also very useful for looking inside Swift optionals and nested optionals.

That’s it!

Hopefully, you have noticed benefits of using LLDB console commands. Scope of this post is just a tip of the iceberg. You can read documentation of lldb on LLDB Documentation. Check out also their tutorial.
Apple has a quick start on the developer portal. There are also many WWDC videos available:

Finally, a nice summary made by objc.io.

Update 21/01/2016

Do I have to type all that stuff over and over again?

If you are still not convinced about usefullness of command line LLDB, you may want to look at .lldbinit. It is a file that you place in your home directory. It contains a set of LLDB commands executed with each launch of LLDB. To create one for yourself execute the following in the Terminal:

Then you can fill the .lldbinit file with a set of commands to be executed on every LLDB launch. For example:

Above commands set breakpoints at malloc and free functions and give them a common name ‘memory’. They are disabled, so that they do not disturb you, but are at hand if you will be debugging some memory related issues. To enable it just modify the last line in the file or type the following at the runtime:

Take a look at the WWDC 2015: What’s new in LLDB video for more details.

More useful commands

In the WWDC talk mentioned above, you can find useful commands like:

  • A type lookup command:

  • Setting an exception breakpoint for Objc/Swift language:

  • Setting an exception breakpoint just for a specific type:

  • Finally something for a really low level stuff lovers. A memory command:

It’s true power is revealed when you lookup C arrays or char* strings.

1 Comment so far

  1. Pingback: [譯] LLDB 基礎 - 程序員的後花園

Leave a Reply


*