How To Debug Swift Code with LLDB

This guide contains the following content to ease your journey to become an lldb ninja:

— Object inspection

— Code modification

— Breakpoints manipulation

— Tips and tricks

Object inspection

In this section, we will use “po”, “p” and “v” commands to inspect any suspicious objects in your debugging journey.

(lldb) po 

Definition

This very famous command prints an object’s description. It is a compiled expression that has full access to the language so you can execute expressions inside the scope of a breakpoint.

Usage


(lldb) po

And you can print an object child property value using dot notation

(lldb) po <object_name>.child

customizing printed message:

The object should conform to CustomDebugStringConvertible protocol which requires one computed property (get only) called debugDescription and returns String.

extension <type_name>: CustomDebugStringConvertible {
var debugDescription: String {
return "This is custom description for <type_name>"
}
}

(lldb) p 

Definition

This is a compiled expression that has full access to the language and output LLDB-formatted description (DataFormatter)

Usage

(lldb) p <object_name>

Dynamic type resolution

This is the process of verifying the type at runtime. 

Let’s consider the following example:

protocol CanWalk {
func walk()
}

struct Person: CanWalk {
let name: String

func walk() { ... }
}

let aPerson: CanWalk = Person(name: "John")

Notice in code the static type of aPerson is CanWalk but at runtime the variable will have an instance type of Person , which is its dynamic type?..

p only computes the static type on the result, this is so, because similar to po, it compiles and then executes the code.

(lldb) v

The LLDB debugger has a new command alias ( since Xcode 10.2 ), v, which is an alias for frame variable command to print variables in the current stack frame.

(lldb) v aPerson

will deal with aPerson as an instance of Person which is its dynamic type.

The v command is significantly faster because it doesn’t execute or compile any commands at all.

Customizing lldb DataFormatter

However, in certain cases, you may want to associate a different style to the display for certain data types. To do so, you need to give hints to the debugger as to how variables should be displayed.

The LLDB type command allows you to do just that. Using it you can change your visualization.

The type command has five subcommands:

— type format

— type summary

— type filter

— type synthetic

— type category

Each of the commands (except type category) has four subcommands available:

add: associates a new printing option to one or more types
delete: deletes an existing association
list: provides a listing of all associations
clear: deletes all associations

We are going to focus on type filter and type summary for today’s article.

Filters

Filters are a solution to the display of complex classes. At times, classes have many member variables but not all of these are actually necessary for the user to see.

A filter will solve this issue by only letting the user see those member variables he cares about. Of course, the equivalent of a filter can be implemented easily using synthetic children, but a filter lets you get the job done without having to write Python code.

For instance, if your class Foobar has member variables named A thru Z, but you only need to see the ones named B, H and Q, you can define a filter:

adding filter

(lldb) type filter add Foobar --child B --child H --child Q
(lldb) v aFoobarObject

will print only variables B, H and Q

If you need to delete a custom filter simply type type filter delete followed by the name of the type to which the filter applies. To delete ALL filters, use type filter clear. To see all the filters defined, use type filter list.

String summaries

type summary works by extracting information from classes and structures, and arranging it in a user-defined format.

adding summary


(lldb) type summary add Foobar --summary-string "has one property called A and its value is ${var.A}"
(lldb) v aFoobarObject

will print:


(Foobar) aFoobarObject = "has one property called A and its value is 15"

Code modification

In this section we will use an expression command to make changes to your code only inside the debugging session.

(lldb) expression

This allows you to execute arbitrary code in the debugger.

Usage

(lldb) expression isThisreal = false

Breakpoints manipulation

In this section you'll learn how to create, modify, and delete breakpoints.

Create

(lldb) breakpoint set

This command deletes all breakpoints.

Usage

(lldb) breakpoint set -n "-[NSView hitTest:]" -C "po $rdi" -G1

this command says to create a breakpoint on -[NSView hitTest:], have it execute the "po $rdi" command which instructs LLDB to print out the contents of the object at the memory address referenced by what’s stored in the RDI assembly register., then automatically continue after executing the command.

The following registers are used as parameters when a function is called in x64 assembly. Try and commit these to memory, as you’ll use these frequently in the future:

— First Argument: RDI

— Second Argument: RSI

— Third Argument: RDX

— Fourth Argument: RCX

— Fifth Argument: R8

— Sixth Argument: R9

-G0: says to the breakpoint to not automatically resume execution after the action has been performed

-G1: says to the breakpoint to automatically resume execution after the action has been performed.

(lldb) breakpoint set --one-shot true --name "-[NSView hitTest:]"

this command says to create a one shot breakpoint on -[NSView hitTest:] will be deleted automatically.

Modify

(lldb) breakpoint modify

This command modifies existing breakpoints.

Usage

(lldb) breakpoint modify -c '(BOOL)[NSStringFromClass((id)[$rdi class])
containsString:@"IDESourceEditorView"]' -G0

This command modifies all existing breakpoints in your debugging session and creates a condition which gets evaluated every time -[NSView hitTest:] fires. If the condition evaluates to true, then execution will pause in the debugger. This condition checks that the instance of the NSView is of type IDESourceEditorView. The final -G0 says to modify the breakpoint to not automatically resume execution after the action has been performed.

Delete

(lldb) breakpoint delete

This command deletes all breakpoints.

Usage

(lldb) breakpoint delete

Tips and tricks

This section contains some tips and tricks I'd like to use to enhance my debugging workflow.

(lldb) continue

This command will resume execution.

Usage

(lldb) continue

or c for short

(lldb) c

assembly registers

I do forget this assembly registers all the time but fortunately there is a way to not use them.

(lldb) po $arg1

arg1 -> rdi

arg2 -> rsi

arg3 -> rdx

arg4 -> rcx

arg5 -> r8

arg6 -> r9

There is a special case for arg2 which represents the selector, because lldb doesn't know the type of the arguments you need to explicitly typecast it.

(lldb) po (SEL)$arg2

Replacing code

I can't count how many times I needed to replace a line of code and put another.

(lldb) thread jump --by 1

This will skip one line.

(lldb) expression isThisreal = false

New tab for Debug

This is very useful feature in Xcode, you can make xcode create new tab for debug automatically

goto xcode menu >> preferences >> behaviors >> Pauses >> check Show tab named [Debug] in [Active window]

UI manipulation

— Get the memory address for this view.

(lldb) expression -l objc -o -- [`self.view` recursiveDescription]

— Use view's memory address to modify its properties.

(lldb) expression -l objc -o -- 0x7fb3afc40e90

there is another way to do this

(lldb) po unsafeBitCast(, to: <type_name>.self)

now you can modify any property

(lldb) po unsafeBitCast(, to: <type_name>.self).center.y = 300

— Finally update the screen's frame buffer

(lldb) expression CATransaction.flush()

Aliases

This is very useful to shortening long commands, po in objective c is a very good example.

(lldb) command alias poc expression -l objc -o --

And now, I can do this:

(lldb) poc 0x7fb3afc40e90

More info

Related posts

The latest articles from Andela.

Visit our blog

Overcoming the Challenges of Working With a Mobile FinTech API

Andela community member Zzwia Raymond explores why, despite the potential of the MTN Mobile Money platform and its API, there are technical hurdles, from complex documentation to enhancing functionality.

How Andela Transformed Tech Hiring in 10 Years

Celebrating 10 years of transforming tech hiring by unlocking global talent across Africa, Latin America and beyond, Andela has surpassed its original goal by training nearly 110,000 technologists and assembling one of the world's largest remote tech talent marketplaces.

What GPT-4o and Gemini releases mean for AI

The latest generative AI models from OpenAI (GPT-4) and Google (Gemini 1.5 Pro, Veo, etc.) promise improved capabilities, lower costs, and transformative applications across various industries by integrating advanced AI technologies into business operations.

We have a 96%+
talent match success rate.

The Andela Talent Operating Platform provides transparency to talent profiles and assessment before hiring. AI-driven algorithms match the right talent for the job.