从容的对面程序崩溃
记得有一次面试的时候,面试官问我,如果有用户在生产环境中使用 App 的时候,突然偶发崩溃,但是无法重现,公司的测试在测试过程中也无法复现此问题,那么你要如何解决这样的问题,我当时想的是我们需要收集崩溃时的堆栈信息,然后发送到服务器,由于并不知道如何获取堆栈信息,因此这道题并没有答好;大多数时候,我们可能用的是像 Bugly ,听云这样第三方的 SDK 帮我们收集这些符号信息,然后找到某个时候的奔溃信息,但是并不知道具体的实现原理,最近又看到了一篇类似的博客,搜了一下之后,发现大家写出来的东西都是一样的(搜索国内的博客基本上都这样),而且并没有具体说明是如何操作的,所有就深入了解了下,当我们面对用户使用过程中的奔溃,需要如何去处理。
异常崩溃的种类和处理方法
出现崩溃会有两种原因,第一种是 NSException 捕获的异常 ,一种是由 系统发出的崩溃信号量 导致的。
-
NSException 官方描述是,程序在进行正常流程时由于特殊的原因导致的程序终止的对象。 这类崩溃的原因可能有:调用不存在的方法,数组越界等
该类的头文件中有两个函数 `NSSetUncaughtExceptionHandler` 和 `NSUncaughtExceptionHandler` 分别用来设置和获取当遇到崩溃时,我们希望在程序终止前可以自定义的操作;在这些操作中我们可以从 NSException 中获取到发生异常时我们需要的信息 点击到 NSException 类中,我们可以看到 name , reason, userInfo 和 callStackSymbols等信息,我们通过记录这些数据,就可以大致的定位到崩溃发生的地方;(在具体实现的过程中,大家都喜欢把 callStackSymbols 堆栈符号信息也放到 userInfo 里面)
-
系统发出的崩溃信号量,这类就需要我们去注册系统已经设置好的信号量,然后在对应的自定义操作时获取当前线程的堆栈信息,通过分析堆栈来定位崩溃原因。常用的信号量如下有 SIGABRT(由于abort()函数调用发生的程序中止信号), SIGILL (由于非法指令产生的程序中止信号),SIGSEGV (由于无效内存的引用导致的程序中止信号),SIGFPE(由于浮点数异常导致的程序中止信号),SIGBUS (由于内存地址未对齐导致的程序中止信号),SIGPIPE (通过端口发送消息失败导致的程序中止信号)
在注册了这些信号量之后,我们就可以在系统发送对应信号量的时候通过自定义的操作来打印或者存储当前堆栈信息,具体的方法是使用 execinfo 头文件里面的 backtrace , backtrace_symbols 和 backtrace_symbols_fd 三个函数,具体这三个函数做了哪些事情可以查看 找到了堆栈信息后就可以定位到问题了。
如果有想深入了解的,可以看下KSCrash的使用以及阅读KSCrash 的源码,可能会让你对异常的捕获有更加深入的认识。
参考链接:
1. 使用signal让app能够在从容崩溃
2. 使用backtrace获取堆栈信息
3. ios 的异常处理
4. 漫谈iOS Crash收集框架
5. 获取任意线程调用栈的那些事
网友评论