1

I recently encountered the following error while running my application:

EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0)

How can I debug this specific error? Are there other methods that I haven't tried yet?

This specific crash happens when we open up a chat conversation UITableView. The configuration of this UITableView involves several elements like nib loading of registered UITableView cells as well as some asynchronous loading of images into these cells with the help of dispatch_async

I tried some common tricks to getting more useful information from the debugger all without success:


Enabling NSZombies

This appeared to give more information on the only visible function call in any of the threads (other than the main itself) which was previously +[ASIHTTPRequest runRequests] though I'm quite reluctant to believe that the real problem lies with the implementation of ASIHttpRequest especially since we haven't changed it in years... (I realize the lib is long since deprecated)

enter image description here


Execution of the bt command

Running bt seemed to give some semi-useful data. It at least indicates that the issue may be related to UITableView configuration.

(lldb) bt
* thread #1: tid = 0x854b7, 0x0503da6b libobjc.A.dylib`objc_exception_throw, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0503da6b libobjc.A.dylib`objc_exception_throw
    frame #1: 0x028fe86d CoreFoundation`+[NSException raise:format:] + 141
    frame #2: 0x03998d7c QuartzCore`CA::Layer::set_position(CA::Vec2<double> const&, bool) + 190
    frame #3: 0x03998f2a QuartzCore`-[CALayer setPosition:] + 56
    frame #4: 0x039995a7 QuartzCore`-[CALayer setFrame:] + 752
    frame #5: 0x03b4a20c UIKit`-[UIView(Geometry) setFrame:] + 305
    frame #6: 0x03c85432 UIKit`-[UIImageView _setViewGeometry:forMetric:] + 228
    frame #7: 0x03c8563a UIKit`-[UIImageView setFrame:] + 63
    frame #8: 0x03b4c0cf UIKit`-[UIView(Geometry) _applyAutoresizingMaskWithOldSuperviewSize:] + 967
    frame #9: 0x03b4ce11 UIKit`-[UIView(Geometry) _resizeWithOldSuperviewSize:] + 301
    frame #10: 0x03b4ce9e UIKit`-[UIView(Geometry) resizeWithOldSuperviewSize:] + 121
    frame #11: 0x03b4b9ed UIKit`__46-[UIView(Geometry) resizeSubviewsWithOldSize:]_block_invoke + 87
    frame #12: 0x02824e33 CoreFoundation`__53-[__NSArrayM enumerateObjectsWithOptions:usingBlock:]_block_invoke + 99
    frame #13: 0x028244cf CoreFoundation`-[__NSArrayM enumerateObjectsWithOptions:usingBlock:] + 239
    frame #14: 0x03b4b97d UIKit`-[UIView(Geometry) resizeSubviewsWithOldSize:] + 149
    frame #15: 0x03be9df2 UIKit`-[UITableView resizeSubviewsWithOldSize:] + 98
    frame #16: 0x03b4d13f UIKit`-[UIView(Geometry) setBounds:] + 537
    frame #17: 0x03b6b2a7 UIKit`-[UIScrollView setBounds:] + 1071
    frame #18: 0x03bea257 UIKit`-[UITableView setBounds:] + 260
    frame #19: 0x03b4caae UIKit`-[UIView(Geometry) _applyISEngineLayoutValues] + 348
    frame #20: 0x03b4cd6b UIKit`-[UIView(Geometry) _resizeWithOldSuperviewSize:] + 135
    frame #21: 0x042a1fb6 UIKit`-[UIScrollView(_UIOldConstraintBasedLayoutSupport) _resizeWithOldSuperviewSize:] + 73
    frame #22: 0x03b4ce9e UIKit`-[UIView(Geometry) resizeWithOldSuperviewSize:] + 121
    frame #23: 0x03b4b9ed UIKit`__46-[UIView(Geometry) resizeSubviewsWithOldSize:]_block_invoke + 87
    frame #24: 0x02824e33 CoreFoundation`__53-[__NSArrayM enumerateObjectsWithOptions:usingBlock:]_block_invoke + 99
    frame #25: 0x028244cf CoreFoundation`-[__NSArrayM enumerateObjectsWithOptions:usingBlock:] + 239
    frame #26: 0x03b4b97d UIKit`-[UIView(Geometry) resizeSubviewsWithOldSize:] + 149
    frame #27: 0x04250c39 UIKit`-[UIView(AdditionalLayoutSupport) _is_layout] + 166
    frame #28: 0x03b512bf UIKit`-[UIView(Hierarchy) _updateConstraintsAsNecessaryAndApplyLayoutFromEngine] + 697
    frame #29: 0x03b5137c UIKit`-[UIView(Hierarchy) layoutSubviews] + 57
    frame #30: 0x03b5edd1 UIKit`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 608
    frame #31: 0x05053771 libobjc.A.dylib`-[NSObject performSelector:withObject:] + 70
    frame #32: 0x039a228f QuartzCore`-[CALayer layoutSublayers] + 152
    frame #33: 0x03996115 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 397
    frame #34: 0x03995f70 QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 26
    frame #35: 0x038f43c6 QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 284
    frame #36: 0x038f578c QuartzCore`CA::Transaction::commit() + 392
    frame #37: 0x038f5e58 QuartzCore`CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 92
    frame #38: 0x028219de CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
    frame #39: 0x02821920 CoreFoundation`__CFRunLoopDoObservers + 400
    frame #40: 0x0281735a CoreFoundation`__CFRunLoopRun + 1226
    frame #41: 0x02816bcb CoreFoundation`CFRunLoopRunSpecific + 443
    frame #42: 0x028169fb CoreFoundation`CFRunLoopRunInMode + 123
    frame #43: 0x067db24f GraphicsServices`GSEventRunModal + 192
    frame #44: 0x067db08c GraphicsServices`GSEventRun + 104
    frame #45: 0x03ad38b6 UIKit`UIApplicationMain + 1526


Diffing logs and reviewing commits

This is what I'm doing right now but the error occurs during a somewhat infrequent use case so we're not sure when it was actually broken.


Set up a memory debugger

I didn't end up needing to do this but it may be helpful for others. See Tim's answer below for more details.


Thanks in advance for any feedback/suggestions!

Community
  • 1
  • 1
alexgophermix
  • 4,189
  • 5
  • 32
  • 59

2 Answers2

1

Unfortunately iOS doesn't come with a decent memory debugger. You can build clang from source yourself and create a custom Xcode compiler to use it. Read about that here.

Once done, if you pass -fsanitize=address in both the CFLAGS and LFLAGS then clang will automatically check for common memory errors (like Valgrind redux.)

I know it is a lot of work, but once you've set it up, it is pretty helpful to have handy as a tool.

Tim
  • 4,560
  • 2
  • 40
  • 64
  • Thanks for the suggestion Tim. I will definitely keep that in mind for future, especially in case a solution is not easy to find. I actually lucked out and the erroneous change was not too far back in the log. I'll post the problem and solution now. – alexgophermix Jan 22 '15 at 22:17
1

I luckily managed to find the issue by scraping through the change logs. A few commits ago I changed our UITableView configuration behaviour slightly.

As this pertains to a chat conversation UITableView we want to scroll down to the last message when the table is loaded. Previously we had our scrolling functionality set up like so:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear];
    // ...
    [self.chatTableView scrollToBottomAnimated:YES];
}

where scrollToBottomAnimated: is defined as such:

- (void)scrollToBottomAnimated:(BOOL)animated {
    NSInteger lastSection = -1;
    NSInteger lastRow = -1;
    lastSection = [self numberOfSections] - 1;
    if (lastSection >= 0) {
        lastRow = [self numberOfRowsInSection:lastSection] - 1;
    }
    if (lastRow >= 0) {
        NSIndexPath* path = [NSIndexPath indexPathForRow:lastRow inSection:lastSection];
        [self scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionBottom animated:animated];
    }
}

This caused a split second load+scroll to bottom animation which wasn't very nice. In order to prevent this undesirable animation I switched to scrolling in viewWillAppear instead of viewDidAppear. As the table hasn't yet properly loaded at this point, I could no longer use scrollToBottom so I used the content offset code suggested here instead.

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // ...
    [self.chatTableView.conversationTableView setContentOffset:CGPointMake(0, CGFLOAT_MAX)];
}

This worked great as far as I could tell! The table was already scrolled to bottom as soon as the UIViewController was loaded BUT if there happened to be an image chat message in the conversation this would cause a EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0) crash.

What seems to be happening is that when the UITableView is loaded all the cells are initialized including the image chat bubble cell. This triggers an asynchronous load of the contained image but since the table is instantly scrolled away from this cell when the async image download is completed and it goes to write the new image to the imageview this crash occurs.

I ended up switching to the scrolling method suggested here which fix my crashes and still provided the desired scrolling behaviour (albiet by using an expensive reloadData call) AKA:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // ...
    [self.chatTableView reloadData];
    [self.chatTableView scrollToBottomAnimated:NO];
}

I will investigate further and update this post if more info is found.

tl;dr - changing our UITableView to scroll instantly in viewWillAppear rather than viewDidAppear causes a crash. This crash seems to happen when our asynchronous image loading completes and tries to set the UIImageView of a custom UITableViewCell that was initialized but never properly displayed because it was instantly scrolled offscreen.

Community
  • 1
  • 1
alexgophermix
  • 4,189
  • 5
  • 32
  • 59