XCode 5 LLDB Debug Assertions

How to avoid long investigations while debugging

One situation when a debugger might not be the first option is when you are not sure where does a program has a bug. One way you can check whether your assumptions are truly what is happening in your code is by using assertions.

What assertions let you do is express one particular assumption and you believe this is not only unlikely but is actually impossible. Assertions stop your app in situations that should be impossible:

NSAssert(_dictionary != nil, @"_dictionary should be initialized");

Another case when assertions can be useful is if you have internal APIs between components and they have contracts between them that shouldn’t be violated. You can also use assertions to enforce contracts between components:

NSAssert((buffer != nil) || (length == 0), @"empty buffer with nonzero length");

These contracts should not be violated. What the assertion will do, if the contract is violated, the application will crash right there. Remember, these are for internal APIs. For external APIs use error handling.

Having your application crash deliberately isn’t exactly a recipe for good reviews on the App Store. So you wanna make sure that the assertions are disabled when you build a release. In XCode default project template, assertions are disabled for release builds:

NS_BLOCK_ASSERTIONS disables assertions in release builds.

There is another thing about assertions you have to be aware of. When you have an assertion compiled into your code, the condition is evaluated only if the assertion is there. So your condition should not do useful work, otherwise, when you build release, that useful work is gone:


NSAssert(myString = [myDictionary objectForKey:@"key"], @"'key' not in dict");

Log effectively with ASL

Let’s say you don’t have a clear red flag that says “something’s gone wrong”. You might have behaviors in your app where over a period of time you…

Logging lets you review an execution of your code after the fact that you want to investigate, and ASL let’s you review the execution of your code under console.app on your Mac. One useful feature of ASL is that you can indicate how severe the logging message are that you are sending out. You can distinguish between absolute emergencies and just debugging information that you want to communicate, to let the programmer or user know what is going on:

ASL_LEVEL_DEBUG ASL_LEVEL_EMERG

Above are just the two extremes of logging severity, but there are a lot of gradients in between that you can use.

Common Breakpoint Scenarios

(lldb) breakpoint set –file MyView.m –line 4
(lldb) b MyView.m:4

Another way to stop at a method:

(lldb) breakpoint set –name “-[MyViewA drawRect:]”
(lldb) b “-[MyViewA drawRect:]”

The third way:

(lldb) breakpoint set –selector drawRect:
(lldb) b drawRect:

And so on…

Switching between your app and XCode is tedious. Breakpoint commands run each time a breakpoint is hit. In order to avoid frustrating back and forth from your application to debugging console and so on, XCode 5 + LLDB introduces breakpoint commands.

Breakpoint commands are a way to tell LLDB “I want you to do this set of actions each time you hit a breakpoint”. Those actions can collect some data for you, in this case, when you need to redisplay a rect you print the rect and get a backtrace on the current thread to tell you who told you about this. Another cool thing you can do is, you can actually continue your process after you hit that breakpoint. This is really handy because it means you can do all your app interaction that triggers these events and then go to XCode afterwards and see all your output from your commands. There’s a way to do this in XCode.

There is another situation when breakpoints are frustrating to use. -> breakpoint conditions (most of the time you don’t care; but you have a case that you care)

(lldb) p id $myModel = self;
(lldb) expression id $myModel = self
(lldb) b “-[MyModel dealloc]”
(lldb) br m -c “self == $myModel”
(lldb) breakpoint modify –condition “self == $myModel”
(lldb) bt
* thread #1: tid = 0x16aeb, 0x00008dae SimpleDemoCalculator`-[CalculatorController viewWillAppear:](self=0x08d0fe10, _cmd=0x0074bb7f, animated=’\0′) + 382 at CalculatorController.m:36, queue = ‘com.apple.main-thread, stop reason = breakpoint 11.1
frame #0: 0x00008dae SimpleDemoCalculator`-[CalculatorController viewWillAppear:](self=0x08d0fe10, _cmd=0x0074bb7f, animated=’\0’) + 382 at CalculatorController.m:36
frame #1: 0x0012b81f UIKit`-[UIViewController _setViewAppearState:isAnimating:] + 226
frame #2: 0x0012bca8 UIKit`-[UIViewController __viewWillAppear:] + 114
frame #3: 0x0012cc87 UIKit`-[UIViewController viewWillMoveToWindow:] + 387
frame #4: 0x00082123 UIKit`-[UIView(Hierarchy) _willMoveToWindow:withAncestorView:] + 628
frame #5: 0x0008dd38 UIKit`-[UIView(Internal) _addSubview:positioned:relativeTo:] + 471
frame #6: 0x00081741 UIKit`-[UIView(Hierarchy) addSubview:] + 56
frame #7: 0x000645cb UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 500
frame #8: 0x000647b4 UIKit`-[UIWindow _setHidden:forced:] + 312
frame #9: 0x00064a1e UIKit`-[UIWindow _orderFrontWithoutMakingKey] + 49
frame #10: 0x0006eb17 UIKit`-[UIWindow makeKeyAndVisible] + 65
frame #11: 0x00008a49 SimpleDemoCalculator`-[ApplicationDelegate application:didFinishLaunchingWithOptions:](self=0x0a3196b0, _cmd=0x0072d0c1, application=0x0a30a990, launchOptions=0x00000000) + 601 at ApplicationDelegate.m:40
frame #12: 0x000255f9 UIKit`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 309
frame #13: 0x00025daa UIKit`-[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1536
frame #14: 0x000271dc UIKit`-[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 794
frame #15: 0x0003cc6c UIKit`-[UIApplication handleEvent:withNewEvent:] + 3447
frame #16: 0x0003d1d9 UIKit`-[UIApplication sendEvent:] + 85
frame #17: 0x00028e55 UIKit`_UIApplicationHandleEvent + 736
frame #18: 0x02192ce2 GraphicsServices`_PurpleEventCallback + 776
frame #19: 0x021927ed GraphicsServices`PurpleEventCallback + 46
frame #20: 0x021af8d5 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 53
frame #21: 0x021af60a CoreFoundation`__CFRunLoopDoSource1 + 522
frame #22: 0x021da032 CoreFoundation`__CFRunLoopRun + 1650
frame #23: 0x021d959f CoreFoundation`CFRunLoopRunSpecific + 415
frame #24: 0x021d93eb CoreFoundation`CFRunLoopRunInMode + 123
frame #25: 0x00026960 UIKit`-[UIApplication _run] + 804
frame #26: 0x00028b6b UIKit`UIApplicationMain + 1225
frame #27: 0x000087e2 SimpleDemoCalculator`main(argc=1, argv=0xbffff174) + 114 at main.m:14
(lldb)

Ok. Enough for today. We’ll get back on this. Happy coding. 🙂

 
 
Filed in Apple Programming, Blog, Debugging. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *