iOS:如何获取未处理的std :: exception的堆栈跟踪?

如果抛出一个未处理的NSException,堆栈跟踪有一个这样的部分:

Last Exception Backtrace: 0 CoreFoundation 0x32bd688f __exceptionPreprocess + 163 1 libobjc.A.dylib 0x34b7b259 objc_exception_throw + 33 2 CoreFoundation 0x32bd65c5 -[NSException init] + 1 3 Foundation 0x37296bd7 -[NSObject(NSKeyValueCoding) valueForUndefinedKey:] + 263 ... 

但是,如果std :: exception被抛出,我只得到这个:

 Thread 0 Crashed: 0 libsystem_kernel.dylib 0x34f2632c __pthread_kill + 8 1 libsystem_c.dylib 0x31e4c208 pthread_kill + 48 2 libsystem_c.dylib 0x31e45298 abort + 88 3 libc++abi.dylib 0x33bcaf64 abort_message + 40 4 libc++abi.dylib 0x33bc8346 default_terminate() + 18 5 libobjc.A.dylib 0x349f4368 _objc_terminate + 164 6 libc++abi.dylib 0x33bc83be safe_handler_caller(void (*)()) + 70 7 libc++abi.dylib 0x33bc844a std::terminate() + 14 8 libc++abi.dylib 0x33bc981e __cxa_rethrow + 82 9 libobjc.A.dylib 0x349f42a2 objc_exception_rethrow + 6 10 CoreFoundation 0x329a5506 CFRunLoopRunSpecific + 398 11 CoreFoundation 0x329a5366 CFRunLoopRunInMode + 98 12 GraphicsServices 0x32af2432 GSEventRunModal + 130 13 UIKit 0x34f84cce UIApplicationMain + 1074 14 APP_NAME 0x00086b10 main (main.m:68) 15 APP_NAME 0x00071b98 start + 32 

我如何从这个崩溃日志得到确切的崩溃信息?

更新 –

我已经给了HockeyApp一个镜头,但它与iTunes崩溃日志具有相同的限制 – 它没有告诉我一个未处理的C ++exception的堆栈。

你看到的是AppKit和UIKit的一个不幸的怪癖。 iOS和OS X在CFRunLoop中都有一个exception处理程序,用于捕获所有未捕获的exception。 在OS X上,处理程序用对话框向用户显示exception,但在iOS上,处理程序只是重新抛出exception。

NSException实现的Objective-Cexception,在实际的“throw”发生之前,将它们的回溯保存在exception对象中,作为[NSException init] (或类似的初始化方法)的一部分。 不幸的是,C ++exception不会这样做。 通常情况下,C ++exception有一个回溯,因为运行时库检测到没有catch并立即调用std::terminate ,然后调用abort() ,使整个调用堆栈保持不变,如下所示:

 Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libsystem_kernel.dylib 0x00007fff93ef8d46 __kill + 10 1 libsystem_c.dylib 0x00007fff89968df0 abort + 177 2 libc++abi.dylib 0x00007fff8beb5a17 abort_message + 257 3 libc++abi.dylib 0x00007fff8beb33c6 default_terminate() + 28 4 libobjc.A.dylib 0x00007fff8a196887 _objc_terminate() + 111 5 libc++abi.dylib 0x00007fff8beb33f5 safe_handler_caller(void (*)()) + 8 6 libc++abi.dylib 0x00007fff8beb3450 std::terminate() + 16 7 libc++abi.dylib 0x00007fff8beb45b7 __cxa_throw + 111 8 test 0x0000000102999f3b main + 75 9 libdyld.dylib 0x00007fff8e4ab7e1 start + 1 

但是,当运行循环exception处理程序捕获exception时,执行将在catch块中正常恢复。 原始的回溯因此丢失。 重新抛出随后调用std::terminate ,但在这一点上,调用堆栈反映了运行循环exception处理程序。

要在这种情况下从C ++exception中回溯,必须抛出一个模仿NSException的exception对象,并将调用堆栈作为其构造函数的一部分读取,并确保在代码中抛出的所有exception都是相同的。 std::exception不会执行此操作,而且您使用的任何第三方代码也不会完全可能。

向Apple提出一个错误,要求他们移除CFRunLoopexception处理程序,或者至less提供一个closures它的API。 没有API,甚至没有SPI,因为现在这样做。

我已经有幸改善了mpipe3的答案。

在我的主要方法中,我使用try-catch来获取C ++exception,然后在dispatch_sync上调用dispatch_get_global_queue 。 现在,Crashlytics(crash manager)会显示带有行号的完整堆栈跟踪。

 int main(int argc, char *argv[]) { @autoreleasepool { @try{ return UIApplicationMain(argc, argv, nil, @"AppControllerClassName"); } @catch (NSException *exception) {/* Catch any uncaught exceptions and print out a friendly call stack. Instead of an ugly memory addresses. */ NSString * message = [NSString stringWithFormat:@"Uncaught exception %@ : %@\n %@", exception.name, exception.reason, [exception callStackSymbols]]; [LogManager e:@"main.m" Message:message Exception: exception]; @throw; } @catch (...){ //Attempt to get the correct stacktrace for C++ exceptions. dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ @throw; }); } return 0; } } 

这样可以避免使用GCD引发exception的CFRunLoopexception处理程序。

接下来从Gwynne Raskind的回答中,您可以通过使用GCD同步调用一个C ++代码块来避免CFRunLoopexception处理程序:

 namespace { void MyThrowingCode() { throw std::runtime_error("My Exception"); } } - (void)objcHandlerCalledFromARunLoop { dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ MyThrowingCode(); }); } 

您可能需要创build自己的队列,而不是使用单个共享队列。

崩溃日志看起来像这样:

 0 libsystem_kernel.dylib 0x3aa39350 __pthread_kill + 8 1 libsystem_c.dylib 0x3a9affb2 pthread_kill + 54 2 libsystem_c.dylib 0x3a9ec366 abort + 90 3 libc++abi.dylib 0x39f94dda abort_message + 70 4 libc++abi.dylib 0x39f92094 default_terminate() + 20 5 libobjc.A.dylib 0x3a545a70 _objc_terminate() + 168 6 libc++abi.dylib 0x39f92118 safe_handler_caller(void (*)()) + 76 7 libc++abi.dylib 0x39f921b0 std::terminate() + 16 8 libc++abi.dylib 0x39f9359a __cxa_throw + 118 9 Test 0x000fddfc MyThrowingCode() (MainViewController.mm:177) 10 Test 0x000fe38c __35-[MainViewController toolsClick:]_block_invoke (MainViewController.mm:184) 11 libdispatch.dylib 0x3a95f5d8 _dispatch_client_callout + 20 12 libdispatch.dylib 0x3a962776 _dispatch_sync_f_invoke + 22 13 Test 0x000fde90 -[MainViewController toolsClick:] (MainViewController.mm:183) 14 UIKit 0x34744082 -[UIApplication sendAction:to:from:forEvent:] + 66 15 UIKit 0x3474410c -[UIBarButtonItem(UIInternal) _sendAction:withEvent:] + 116 16 UIKit 0x34744082 -[UIApplication sendAction:to:from:forEvent:] + 66 17 UIKit 0x34744036 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 26 18 UIKit 0x34744010 -[UIControl sendAction:to:forEvent:] + 40 19 UIKit 0x347438c6 -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 498 20 UIKit 0x34743db4 -[UIControl touchesEnded:withEvent:] + 484 21 UIKit 0x3466c5f4 -[UIWindow _sendTouchesForEvent:] + 520 22 UIKit 0x346598dc -[UIApplication sendEvent:] + 376 23 UIKit 0x346591ea _UIApplicationHandleEvent + 6194 24 GraphicsServices 0x363715f4 _PurpleEventCallback + 588 25 GraphicsServices 0x36371222 PurpleEventCallback + 30 26 CoreFoundation 0x3281f3e4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 32 27 CoreFoundation 0x3281f386 __CFRunLoopDoSource1 + 134 28 CoreFoundation 0x3281e20a __CFRunLoopRun + 1378 29 CoreFoundation 0x32791238 CFRunLoopRunSpecific + 352 30 CoreFoundation 0x327910c4 CFRunLoopRunInMode + 100 31 GraphicsServices 0x36370336 GSEventRunModal + 70 32 UIKit 0x346ad2b4 UIApplicationMain + 1116 33 Test 0x00120462 main (main.mm:55) 34 libdyld.dylib 0x3a972b1c start + 0 

看起来像从UIApplicationMain()里面发生的exception…你可以尝试把你的main.m文件转换成一个main.mm文件,并且这样做:

 int main(...) { try { return UIApplicationMain(...); } catch( exception e ) { cerr < e.what() ; } } 

我没有尝试过 ,但是…