iOS:如何获取未处理的 std::exception 的堆栈跟踪?
Posted
技术标签:
【中文标题】iOS:如何获取未处理的 std::exception 的堆栈跟踪?【英文标题】:iOS: How to get stack trace of an unhandled std::exception? 【发布时间】:2012-11-26 11:29:42 【问题描述】:如果抛出未处理的 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++ 异常的堆栈。
【问题讨论】:
您是否使用第三方工具(如 crittercism 或 testflightapp)来获取报告的崩溃。您必须上传一个 dsym 文件,然后它将象征崩溃,它会向您显示崩溃发生的确切行。要查找 dsym 文件,您可以右键单击管理器窗口中的 .ipa 文件或存储它的位置,然后右键单击并显示内容。 我没有使用任何第三方工具。你能推荐一个吗?另外,我怀疑这些工具可能无法处理 C++ 异常——你知道有人这样做吗? 不确定。但是在这里,还有一个与您类似的问题:***.com/questions/11883069/… 【参考方案1】:您看到的是 AppKit 和 UIKit 的一个不幸的怪癖。 ios 和 OS X 在CFRunLoop
中都有一个异常处理程序,用于捕获所有未捕获的异常。在 OS X 上,处理程序通过对话框向用户显示异常,但在 iOS 上,处理程序只是重新抛出异常。
由NSException
实现的Objective-C 异常在实际“抛出”发生之前将它们的回溯保存在异常对象中,作为[NSException init]
的一部分(或类似的初始化方法)。不幸的是,C++ 异常不会做同样的事情。通常,C++ 异常有一个回溯,因为运行时库检测到没有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
但是,当运行循环异常处理程序捕获异常时,会在catch
块内正常恢复执行。因此,原始回溯丢失。重新抛出随后调用std::terminate
,但此时调用堆栈反映了运行循环异常处理程序。
在这种情况下,要从 C++ 异常中获取回溯,您必须抛出一个模仿 NSException
的异常对象,并将调用堆栈作为其构造函数的一部分读取,并确保代码中抛出的所有异常都执行相同的。 std::exception
不会这样做,而且您使用的任何第三方代码也不可能这样做。
向 Apple 提交一个错误,要求他们删除 CFRunLoop
异常处理程序,或者至少提供一个 API 来关闭它。目前没有 API,甚至没有 SPI 来执行此操作。
【讨论】:
有什么方法可以在 SIGABRT 的信号处理程序中获取基本的异常信息(如exception::what()
中的信息),而无需实际捕获异常?
有人向 Apple 提交过错误吗?如何抛出一个模仿 NSException 的 C++ 异常?
我向 Apple 提交了一个错误。打开雷达等效:openradar.me/radar?id=4943586562932736【参考方案2】:
我很幸运能改进 mpipe3 的答案。
在我的 main 方法中,我使用 try-catch 来获取 C++ 异常,然后在 dispatch_get_global_queue
上调用 dispatch_sync
。现在,带有行号的完整堆栈跟踪将显示在 Crashlytics(崩溃管理器)上。
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 抛出异常来避免 CFRunLoop 异常处理程序。
【讨论】:
似乎这个解决方案并不总是有效。但这是我想出的最好的。 我意识到这是 3 年后的事了......但是你有没有机会回忆一下你的方法在什么情况下不起作用?我也有类似的情况…… 我发现它很多时候都不喜欢 C++ 抛出的异常。但不确定为什么它有时会起作用,而其他时候不起作用。【参考方案3】:按照 Gwynne Raskind 的回答,您可以通过使用 GCD 同步调度对 C++ 代码块的调用来避免 CFRunLoop 异常处理程序:
namespace
void MyThrowingCode()
throw std::runtime_error("My Exception");
- (void)objcHandlerCalledFromARunLoop
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
MyThrowingCode();
);
您可能需要创建自己的队列,而不是使用单个共享队列。
崩溃日志如下所示:
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
【讨论】:
【参考方案4】:看起来异常是从 UIApplicationMain() 内部发生的...您可以尝试将 main.m 文件转换为 main.mm 文件,然后执行以下操作:
int main(...)
try
return UIApplicationMain(...);
catch( exception e )
cerr < e.what() ;
我实际上并没有尝试,但是...
【讨论】:
以上是关于iOS:如何获取未处理的 std::exception 的堆栈跟踪?的主要内容,如果未能解决你的问题,请参考以下文章