Nov 8 2013

Xcode Breakpoint Wizardry

Every great developer should know how to use a debugger. Xcode has an excellent debugger UI that wraps LLDB (or, if you’re living in the past, GDB), giving you access to all the standard tricks like breakpoints, stepping in and out and around your code, and stack frame details.

Many times in your debugging adventures, the basics will suffice. But occasionally, a hairy problem will surface that demands more finesse. Imagine, for example, that you’re debugging something that only happens every five minutes on a timer, and then triggers some multi-threaded gobbledy-gook where timing is critical to reproducing a bug.

I don’t know about your work environment, but in mine, five minutes is an eternity, and I will get distracted by at least two shiny objects while waiting for the timer to fire. What I need is something to grab my attention again.

Audible Breakpoints!

When you want your breakpoint to have a bit more oomph, Xcode can play a sound and snap your focus back to the problem. To configure this, set a breakpoint on your favorite line of code (or method definition), right click on it and choose “Edit Breakpoint…” from the menu.

edit-breakpoint-menu

Xcode will pop up the breakpoint editor window, which has a ton of cool options.

edit-breakpoint-window

To make a breakpoint alert you with a sound, you’ll need to add an “action” to the breakpoint. Xcode offers several to choose from.

breakpoint-actions

Select the “Sound” action, and the window will update, allowing you to select from a list of sounds to be triggered when your breakpoint hits.

breakpoint-sounds

“Glass” is a nice choice since it’s pretty loud and clear. Remember, you want to be alerted here.

By default, your breakpoint will still pause the execution of your app after playing the sound. For timing-sensitive situations, or things that happen frequently, this may be undesirable.

Check the “Automatically continue after evaluation” box to have Xcode keep your app chugging along after the breakpoint is evaluated. Marvel at your now-audible alert that some point in your code has been hit!

Also keep in mind that the presence of such a breakpoint will introduce brief hiccups in your app’s performance, especially when running on an iOS device. When the debugger encounters this point in the code, it pauses execution of the thread long enough to switch contexts and execute whatever actions are attached to the breakpoint. This means that if you are debugging very time-sensitive code, your breakpoint actions could have adverse effects on the app’s ability to reproduce something consistently. A good rule of thumb is to keep the action’s execution as short as possible.

Another feature of breakpoint actions is the ability to execute a shell command when the breakpoint is hit. I’m sure you can think of tons of great uses for this feature, but in the spirit of keeping things audible, try this:

Command: say

Arguments: -v, Zarvox, "Your breakpoint %B has been hit %H times"

Backtraces: How Did We Get Here?

Sometimes you may find yourself debugging code in a method or function that has many entry points throughout the code. In these situations, you might wonder how the app got to that point (or why it’s getting to that point so darned often).

Under normal circumstances, a regular breakpoint that pauses the app will show the stack trace for the current thread in the Xcode debug navigator, and this may be all you need. Again, though, there arise situations where timing is critical or you don’t want the app to pause every time it hits the breakpoint.

This is where another breakpoint action can come in handy: Debugger Command. LLDB offers many commands, but one that is useful in knowing how you arrived is “bt”, which will print out the current thread’s stack trace (backtrace) to the console.

bt-debugger-command

Combine this with “Automatically Continue” and some particularly hot or confusing code path, and you can then see console output of where your code was hit, interspersed with any other output you may have, for example from NSLog.

2013-10-14 14:28:07.047 MyStuff[62951:a0b] About to call super viewDidLoad
* thread #1: tid = 0x3cccc1, 0x00003076 MyStuff`-[BNRMasterViewController viewDidLoad](self=0x08988fa0, _cmd=0x009bad27) + 102 at BNRMasterViewController.m:35, queue = 'com.apple.main-thread, stop reason = breakpoint 1.1
    frame #0: 0x00003076 MyStuff`-[BNRMasterViewController viewDidLoad](self=0x08988fa0, _cmd=0x009bad27) + 102 at BNRMasterViewController.m:35
    frame #1: 0x003409a8 UIKit`-[UIViewController loadViewIfRequired] + 696
    frame #2: 0x00340c44 UIKit`-[UIViewController view] + 35
    frame #3: 0x0036b339 UIKit`-[UINavigationController rotatingSnapshotViewForWindow:] + 52
    frame #4: 0x00694910 UIKit`-[UIClientRotationContext initWithClient:toOrientation:duration:andWindow:] + 420
    frame #5: 0x00270ea2 UIKit`-[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:] + 1495
    frame #6: 0x002708c6 UIKit`-[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:] + 82
    frame #7: 0x00270798 UIKit`-[UIWindow _setRotatableViewOrientation:updateStatusBar:duration:force:] + 117
    frame #8: 0x00270820 UIKit`-[UIWindow _setRotatableViewOrientation:duration:force:] + 67
    frame #9: 0x0026f8ba UIKit`__57-[UIWindow _updateToInterfaceOrientation:duration:force:]_block_invoke + 120
    ...
2013-10-14 14:28:07.048 MyStuff[62951:a0b] Called super viewDidLoad

If you are debugging a multi-threaded part of your application, you can also use the command bt all to show the stack trace for every thread when the breakpoint is hit. You can also print out a limited number of stack frames by adding a number to the command, like bt 10.

Symbolic Breakpoints

Sometimes you want to set a breakpoint that will hit in a certain place in the app, but you may not have direct access to the code in question. Imagine trying to debug a third-party library for which you only have a binary, or something in the iOS frameworks. Without access to the code, you can’t set a breakpoint based on file names or line numbers, because you don’t know them.

This is where symbolic breakpoints can really save your bacon. Using one of these, you can set a breakpoint based on a symbol, like a method or function name, regardless of where that name might appear in the code.

Imagine you would like to know when viewDidLoad is called on any UIViewController instance. You can set up a symbolic breakpoint for -[UIViewController viewDidLoad] and it will be triggered any time that method is called. That will be, for example, any time a subclass implementation calls [super viewDidLoad].

To create a symbolic breakpoint in Xcode, click the + button in the lower-left toolbar within the Breakpoint Navigator and choose “Add Symbolic Breakpoint…”

add-breakpoint-menu

The new breakpoint will be added to the list, and a window will pop up asking you to fill in the details. Add the name of your symbol and press return to create the new breakpoint.

UIViewController-viewDidLoad-symbolic-breakpoint

If you run an iOS app with this breakpoint enabled, it will pause each time that method is called. You may find out some interesting details about the guts of iOS using breakpoints like this.

You can add symbolic breakpoints that refer to a method (selector) in a specific class, as above, or you can be more generic with your symbol and just provide a name, like viewDidLoad. In this case, Xcode will do something amazing: after you add the breakpoint, it will look for all matches for that name in the app and linked libraries and make them “child” breakpoints of the one you just created.

Here’s what it looks like when you create a symbolic breakpoint for viewDidLoad:

viewDidLoad-symbolic-breakpoint

Fascinating! Look at all of those “hidden” subclasses of UIViewController that live within the frameworks. We didn’t even have to break out class-dump!

Watchpoints

Watchpoints are a tool you can use to monitor the value of a variable or memory address for changes and trigger a pause in the debugger when changes happen. They can be very helpful in identifying problems with the state of your program that you might not know precisely how to track down.

For example, imagine you have built a user interface with two buttons and two labels. When you click one of the buttons, the label should update to reflect the number of times you have clicked that button.

click-counting-ui

To track the click count for each button, you’ll use a property like button1ClickCount. For each button, you’ll have an action method that increments the count property and updates the appropriate label. You create these properties and write the action methods.

- (IBAction)button1Clicked:(id)sender {
    self.button1ClickCount++;
    self.button1ClickCountLabel.text = [self textForButton:1 clickCount:self.button1ClickCount];
}

- (IBAction)button2Clicked:(id)sender {
    self.button2ClickCount++;
    self.button2ClickCountLabel.text = [self textForButton:2 clickCount:self.button2ClickCount];
}

- (NSString *)textForButton:(NSUInteger)buttonNumber
                 clickCount:(NSUInteger)clickCount {
    return [NSString stringWithFormat:@"Button %d clicked %d times", buttonNumber, clickCount];
}

To make the action methods trigger in response to the button clicks, you dutifully wire them up in Interface Builder. Then you run your app, and click each button a few times. But, there’s a problem: no matter which button you click, you see only the first button’s count update!

How can you debug this? In this trivial example, of course you could set breakpoints in each of the action methods and ensure that they are getting triggered appropriately. But imagine that the code that is incrementing the counts is really some complex logic that lives in another class with a much more complicated API. When you don’t know where or when a variable is being changed, you can set a watchpoint.

It makes sense here to set up a watchpoint for the value of the button 1 click count. Watchpoints can be set on the value of variables, but button1ClickCount was set up as a property. How can you watch it? You can rely on the automatic synthesized instance variable that backs up the property: _button1ClickCount.

To set the watchpoint, you need to be paused in the debugger within a stack frame that has the variable you want to watch in scope. For ease of demonstration, put a breakpoint in the button1Clicked: action method and trigger it by clicking the first button. Xcode will pause the app and then you can create the watchpoint in one of two ways: using the Xcode GUI or the LLDB console.

To use the GUI, navigate to the Variables View, expose the variable you want to watch, and right-click (control-click) on the name of it. In the menu that appears, select “Watch _button1ClickCount”.

set-watchpoint-menu

If you prefer, you can instead type in the LLDB console to set the watchpoint:

(lldb) watch set variable _button1ClickCount
Watchpoint created: Watchpoint 1: addr = 0x0b9a1ca4 size = 4 state = enabled type = w
    declare @ 'MyStuff/MyStuff/BNRDetailViewController.m:52'
    watchpoint spec = '_button1ClickCount'
    new value: 10

By default, a watchpoint watches for “write” occurrences on the variable you specify. This means that when the variable’s value is set, the watchpoint will hit.

With this watchpoint set, you can click either of the buttons and see it get triggered:

watchpoint-hit

You can also see that LLDB helpfully spits out the old and new values of the variable in the console:

Watchpoint 1 hit:
old value: 10
new value: 11

In this contrived example, the root cause of the problem is that button 2′s ”Touch Up Inside” action was wired incorrectly to the button1Clicked: method. It should be apparent, however, that watchpoints can help you in situations where changes are happening and you don’t quite have a handle on why.

You can learn more about watchpoints in the LLDB Tutorial and by typing help watch in the LLDB console. One important detail to note: watchpoints are not saved between executions of your program, so if you need to set a watchpoint repeatedly you should save the command for setting it in another file and paste it into the debugger console when appropriate.

You can also use a combination of techniques discussed here to help: set a breakpoint that hits early in the program’s execution where the variable you want to watch is in scope, and make the action of that breakpoint an LLDB command to set the watchpoint.

Getting to the (Break)Point

With Xcode and LLDB, a wealth of debugging options are at your disposal. Knowing when and how to use these powerful tools can really take the bite out of difficult bugs in your code.

Do you have any other great debugger tips and tricks? Feel free to post them in the comments!

3 Comments

  1. Reid

    So useful! Thanks so much!! I can’t believe I’ve never explored any of these options…but now I know.

  2. Konstantin

    Thanx for the article.

    Do you know, how to set an address breakpoint in xcode? Something, what is equal to lldb `breakpoint set -a 0×00001234`.
    This type of breakpoint worked in gdb (needs to type in `*0×00001234` Symbol field). But it don’t in lldb.

Leave a Comment

Join the discussion. Do not worry, your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>