The selfless debugger

Mark Dalrymple's Headshot
Mark Dalrymple

You know that feeling. You're on a deadline. It's 9:00 at night. You have a demo the next morning. Suddenly Xcode freaks out. Apps running half the time. Rebooting your phone. Rebooting your computer. Finally the phone decides to run your App for awhile.

That's happening to me right now on a Thursday night (gotta take a break for my blood pressure to return to normal, so might as well write something). I figured I'd pass on a trick for working around a particular bit of tool uncooperativeness: I can't look at any object data inside of some blocks.

With Xcode 4.4, both lldb and gdb are refusing to display instance variables inside of a couple of blocks (rdar:/12038473). Some work fine. Others don't. Perhaps it's just a deadline-sensitive implementation and everything will work after my time of trial. Breakpoints are working great and I can single-step, but the data display isn't working. I can't print out stuff in the console:

(gdb) <strong>print _iCloudAvailable</strong>
Cannot access memory at address 0x38

(gdb) <strong>call [self iCloudOn]</strong>
Unable to access variable "self"
$24 = <variable optimized away by compiler>

(gdb) <strong>print self</strong>
Unable to access variable "self"
$25 = <variable optimized away by compiler>

(Optimization is off.) Even worse, "hover over a variable" puts up a value, it just happens to be wrong:

Lying debugger

Notice at the very bottom - the print statement shows the proper value (1), while hover-and-display is showing a stale value. The left-hand debugger panel which shows the tree view of ivars shows the same stale data. It's not just gdb - I started this debugging rollercoaster with lldb, and it was having the exact same problem.

The problem I'm having is "self" is not available. All the interesting stuff in this chunk of code keys off of self, such as method calls and instance variable access. I'm very interested in what some of these methods return and what ivar values are. If only self were there, life would be good. So let's put it there.

I added an NSLog in the code which prints out self:

      NSLog (@"self is %p", self);

Which when run prints out something straightforward like

2012-08-02 20:58:37.595 ClassBuilder[199:707] self is 0x2b0800

If you're not familiar with it, %p is the "print the value of a pointer in hex without dereferencing it" format specifier.

Now copy and paste that value, and just print it.

(gdb) <strong>print 0x2b0800</strong>
$8 = 2820096

That puts the value of self into one of the debugger session variables. Everything you print something, you get a new debugger variable you can use as a shorthand to refer to that value. self can now be referenced by $8. Why $8? I had printed out seven other values before I needed to reference self in this session. Plus it matches the screen shot. I've replaced other $values with $XX just because the order of numbers isn't terribly interesting.

(gdb) <strong>call [$8 iCloudOn]</strong>
$XX = 1 '\001'

Dereferencing a raw address can be a pain:

(gdb) <strong>print $8->_iCloudAvailable</strong>
Attempt to extract a component of a value that is not a structure pointer.

You can cast it, though. "Hey debugger, treat this pointer as a ClassBuilderAppDelegate pointer first, and then dereference it.":

(gdb) <strong>print ((ClassBuilderAppDelegate *) $8)->_iCloudAvailable</strong>
$XX = 1 '\001'

But that's also a pain to type that cast over and over. So, just print it out again, casting it to the correct type:

(gdb) <strong>print ((ClassBuilderAppDelegate *) $8)</strong>
$9 = (ClassBuilderAppDelegate *) 0x2b0800

Now $9 hold a numeric value, but also has the type of my ClassBuilderAppDelegate pointer. $9 can new be dereferenced directly to peek at instance variables:

(gdb) <strong>print $9->_iCloudAvailable</strong>
$10 = 1 '\001'

Any place you'd use self in a debugger expression, you can use $9. These debugger variables aren't scoped, and live the duration of the debugging session, so you can exit the function or block where you were doing your investigation and still refer to the object in another context. Well, so long as the object is still alive, of course. Because I'm debugging my app delegate I know the object will be alive for the entire program.

It's not pretty, but can help you from having to go full-caveman and put logging after every line to see what's going on in a tricky debugging situation.

(Dig Debugging? Our Advanced Mac and Advanced iOS Bootcamps take deep dives into Debugging.)

Recent Comments

comments powered by Disqus