The selfless debugger
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
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) print _iCloudAvailable Cannot access memory at address 0x38 (gdb) call [self iCloudOn] Unable to access variable "self" $24 = <variable optimized away by compiler> (gdb) print self 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:
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) print 0x2b0800 $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? 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
$XX just because the order of numbers isn’t terribly interesting.
(gdb) call [$8 iCloudOn] $XX = 1 '\001'
Dereferencing a raw address can be a pain:
(gdb) print $8->_iCloudAvailable 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) print ((ClassBuilderAppDelegate *) $8)->_iCloudAvailable $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) print ((ClassBuilderAppDelegate *) $8) $9 = (ClassBuilderAppDelegate *) 0x2b0800
$9 hold a numeric value, but also has the type of my
$9 can new be dereferenced directly to peek at instance variables:
(gdb) print $9->_iCloudAvailable $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.