Introduction to iOS Debugging with LLDB.

Being a developer means that many times a day you will end up debugging code, wether is yours or someone else’s. According to this research, we spend 25% of time fixing bugs and 25% making code works, at the end, we spend 50% of our time debugging. But sometimes I found myself spending whole day trying to catch a pesky bug or getting along a new project. More often you will have to debug spaghetti code.

Fortunately Xcode comes with amazing tools to make this task easier: LLDB & Instruments.

LLDB is the default debugger in Xcode (used to be GDB), comes with handy commands, also you can write your own debugging scripts using python and extend it’s functionality.

To help you understand this post I made a test project in github. I’m using Xcode Version 5.1.1 (5B1008).

Start Using LLDB.

Add a breakpoint to FGAppDelegate.

App Delegate breakpoint

Run your app.

Navigate until you reach the breakpoint, you will see the LLDB console.

LLDB Console

 

Print object values.

The command “po” evaluates C/ObjC/C++ expressions in the current program content, using defined variables and variables that are currently in scope. Which means that you will access solely the variables from the current class.

Add a breakpoint in FGViewController.m:40

Breakpoint in class FGViewController

Run the app.
When you hit the breakpoint type:

(lldb) po _person

You will get: nil (because is not yet initialised, press F6 to continue)

(lldb) po _person
<FGPerson:0xc7762c0, ID:0 Name:John Doe>

po is useful to print objects, but if you need to print scalar values use (personID is an int property):

(lldb) p _person.personID
(int) $2 = 0

Also you can use self:

(lldb)po self.view
<UIView: 0xa08c030; frame = (0 0; 320 568); autoresize = RM+BM; layer = <CALayer: 0xa08c090>>

LLDB Console:

po & p commands

NOTE: You can print all the view elements in current view using:
(lldb) po [[[UIApp delegate] window] recursiveDescription]

Breakpoints

Remove previous breakpoint by right click at breakpoint->Delete

Add breakpoints

Add a breakpoint to a class and code line:

(lldb) b FGViewController.m:40

“Breakpoint 2:” = the id for the breakpoint, with this id you can add commands, delete, enable or modify a breakpoint.

Command Options (for more options type “help br” on LLDB prompt):

 s: Sets a breakpoint or set of breakpoints in the executable.
-n: Set the breakpoint by function name.
-S: Set the breakpoint by ObjC selector name.

Add a breakpoint to a class and selector without parameters:

(lldb) br s -n "-[FGViewController setLabels]"

Add a breakpoint to a class and selector with parameters:

(lldb) br s -n "-[FGViewController addPersonHandler:]"

Add a breakpoint to a selector no matter the class:

(lldb) br s -S viewDidLoad

List all current breakpoints; added from LLDB or Xcode:

(lldb) br list

LLDB Console:

Break point command console.

 

NOTE: All breakpoints added through LLDB will never be reflected in Xcode, if you want to create a Symbolic breakpoint that gets reflected in Debugging tab, then follow this.
  1. Open the Breakpoint Navigator
  2. Click on + button
  3. Add Symbolic Breakpoint
  4. In Symbol text box write: -[FGViewController setLabels]
  5. Uncheck “Automatically continue after evaluating”.

br_visual_command

Also you can read more about Xcode breakpoints in the post Xcode Breakpoint Wizardry from Big Nerd Ranch.

Conditional breakpoint

(lldb) b FGResizeViewController.m:53
Breakpoint 2: where = LLDBTest`-[FGResizeViewController resizeSquare:] + 198 at FGResizeViewController.m:53, address = 0x000029d6
(lldb) br mod -c "currentFrame.size.width > 200" 2

Adding a script to a breakpoint

For python scripts you have available these variables:

frame:  a lldb.SBFrame object for the frame which hit breakpoint.
bp_loc: a lldb.SBBreakpointLocation object that represents the breakpoint location that was hit.
dict:   the python session dictionary hit.

Adding a single line Python script that will print the current method for the breakpoint, this is really useful when you get into a new project and you want to trace where you’re passing while using the app:

(lldb) br s -S viewDidLoad
Breakpoint 2: 18 locations.
(lldb) br command add -s python 2 -o "function_name = frame.GetFunctionName(); print '%s' % function_name"

Adding a LLDB script:

(lldb) br command add -s command 2
Enter your debugger command(s). Type 'DONE' to end.
> frame info
> DONE
frame #0: 0x0034bea5 UIKit`-[UIViewController viewDidLoad]

Debugging is one of the main developer’s activities, using the right tools provided by Xcode will help you easy this often tedious task. Now you know how to inspect data without adding NSLogs to your class files and most important, without stopping the app, which will save you a lot of time. Breakpoints are a powerful feature that you can use to stop conditionally  or print more info about what is going on.

In LLDB type

(lldb) help

for more info about all commands available.

Further Reading.

Apple’s official LLDB quick start guide.

LLDB Tutorial.

Getting Started with LLDB from Apple Developer.

 

 

  • Waseem

    I am iOS developer and have same experience as mentioned by Fabian. Almost 40-50% time normally spent on debugging and getting exact flow for code and value details. lldb really a nice debugger build-in with Xcode and helps me a lot for many issues. NSLog is also a technique but i am not impressed with it because it does not provide more detail like we have in lldb and also most of the time there are other operations running simultaneously and we missed NSLogs output.