文章目录1.SystemCrash2.处理信号以下是信号描述的一些要点3.实战4.CrashCallstack分析-进一步分析5.Demo地址6.参考前言今天在ios进阶群里,有有朋友问iOS异常捕获,之前没研究过,所以趁这个机会研究一下。并写了一个demo,有需要的可以在文末下载。看文章之前,建议大家看完这篇文章,再去看看漫无边际的iOSCrash收集框架,了解其中的原理。解决Crash问题一直是iOS应用开发中的难题。crash分为两种,一种是EXC_BAD_ACCESS引起的,原因是访问了不属于进程的内存地址,可能是已经释放的内存;另一个是未捕获的Objective-C异常(NSException),通过向自身发送SIGABRT信号导致程序崩溃。事实上,我们有办法记录未捕获的Objective-C异常。如果日志记录得当,大部分的崩溃问题都可以解决。这里分别对UI线程和后台线程进行说明。SystemCrash对于系统崩溃导致的程序异常退出,可以通过UncaughtExceptionHandler机制捕获;也就是说,程序中除了catch之外的内容,都是由系统自己的错误处理捕获的。我们所要做的就是用自定义函数替换ExceptionHandler。2.Handlesignal使用Objective-C的异常处理是无法获取到Signal的。如果我们要对其进行处理,需要使用unix标准的信号机制,在SIGABRT、SIGBUS、SIGSEGV等发生时注册信号处理函数。在这个函数中,我们可以输出堆栈信息、版本信息和我们想要的一切。以下是一些信号说明1)SIGHUP当用户终端连接(正常或异常)结束时发送该信号,通常是终端控制进程结束时,通知同一个会话中的各个作业,然后它们就不再连接了关联的控制终端。登录Linux时,系统会为登录的用户分配一个终端(Session)。所有运行在这个终端上的程序,包括前台进程组和后台进程组,一般都属于这个Session。当用户退出Linux时,前台进程组和输出到终端的后台进程都会收到SIGHUP信号。这个信号默认的动作是终止进程,所以前台进程组和后台有终端输出的进程都会被终止。但是你可以捕捉到这个信号。比如wget可以捕获SIGHUP信号并忽略它,这样即使你退出了Linux,wget也可以继续下载。此外,该信号用于在与终端断开连接时告诉守护进程重新读取配置文件。2)SIGINT程序终止(中断)信号是在用户键入INTR字符(通常为Ctrl-C)时发出的,用于通知前台进程组终止进程。3)SIGQUIT和SIGINT类似,但是是由QUIT字符(通常是Ctrl-)控制的。进程在收到SIGQUIT退出时会生成一个core文件,在这个意义上类似于一个程序错误信号。4)SIGILL执行了非法指令。通常是因为可执行文件本身有错误,或者是试图执行数据段。该信号也可能在堆栈溢出时产生。5)SIGTRAP由断点指令或其他陷阱指令产生。它由调试器使用。6)SIGABRT调用abort函数产生的信号。7)SIGBUS非法地址,包括内存地址对齐错误。比如访问一个四字整数,但是它的地址不是4的倍数。它和SIGSEGV的区别在于后者是由对合法存储地址的非法访问触发的(比如访问不属于到自己的存储空间或只读存储空间)。8)当发生致命的算术运算错误时发出SIGFPE。不仅包括浮点运算错误,还包括溢出、被0除等所有其他算术错误。9)SIGKILL用于立即结束程序的运行。该信号不能被阻塞、处理和忽略。如果管理员发现一个进程无法终止,他可以尝试发送这个信号。10)SIGUSR1为用户保留。11)SIGSEGV试图访问没有分配给自己的内存,或者试图向一个没有写权限的内存地址写入数据。12)SIGUSR2为用户保留。13)SIGPIPE管道损坏。该信号通常在进程间通信中产生。例如,两个使用FIFO(管道)通信的进程,如果读管道没有打开或意外终止,就会向管道写入数据,写入进程会收到SIGPIPE信号。另外,两个进程使用Socket通信,当写进程正在写Socket时,读进程已经终止。14)SIGALRM时钟计时信号,计算实际时间或时钟时间。报警功能使用此信号。15)SIGTERM程序结束(terminate)信号,与SIGKILL不同的是这个信号可以被阻塞处理。通常用于要求程序正常退出,shell命令kill默认产生该信号。如果无法终止进程,我们将尝试SIGKILL。17)SIGCHLD当子进程结束时,父进程会收到这个信号。如果父进程不处理这个信号,不等待子进程,子进程虽然终止,但仍会在内核进程表中占有一个表项。这时的子进程就称为僵尸进程。我们应该避免这种情况(父进程要么忽略SIGCHILD信号,要么捕获它,要么等待它派生的子进程,要么父进程先终止,子进程的终止由init进程自动接管).18)SIGCONT允许一个停止(stopped)的进程继续执行。这个信号不能被屏蔽。处理程序可用于让程序在从停止状态变为继续执行时完成特定工作。例如重新显示提示19)SIGSTOP停止(stopped)进程的执行。注意它和terminate、interrupt的区别:进程还没有结束,只是暂停执行。不能阻止、处理或忽略此信号。20)SIGTSTP停止进程的运行,但是这个信号可以被Process忽略。当用户键入SUSP字符(通常是Ctrl-Z)时发送此信号21)SIGTTIN当后台作业要从用户终端读取数据时,作业中的所有进程都会收到SIGTTIN信号。默认情况下,这些进程将停止执行。22)SIGTTOU与SIGTTIN类似,只是在写终端(或修改终端模式)时收到。23)SIGURG在“紧急”数据或带外数据到达套接字时产生。24)SIGXCPU超出CPUTime资源限制。可以通过getrlimit/setrlimit读取/更改此限制。25)SIGXFSZ当进程试图扩展文件以致超出文件大小资源限制时。26)SIGVTALRM虚拟时钟信号。与SIGALRM类似,但计算的是进程占用的CPU时间。27)SIGPROF类似于SIGALRM/SIGVTALRM,但包括进程使用的CPU时间和系统调用的时间。28)SIGWINCH窗口大小改变时发出。29)SIGIO文件描述符准备好进行输入/输出操作。30)SIGPWRPowerfailure31)SIGSYS非法系统调用。重点:上面列出的信号中,不能被程序捕获、阻塞或忽略的信号有:SIGKILL、SIGSTOP不能恢复到默认动作的信号有:SIGILL、SIGTRAP默认会引起进程的信号中止是:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ将导致进程退出的默认信号是:SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM默认会导致进程停止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU默认进程忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH此外SIGIO退出SVR4和在4.3BSD中被忽略;SIGCONT在进程挂起时继续,否则忽略,无法阻塞。三.实战1.AppDelegate.m中-(BOOL)application:(UIApplication*)applicationdidFinishLaunchingWithOptions:(NSDictionary*)launchOptions{//Overridepointforcustomizationafterapplicationlaunch.InstallSignalHandler();//信号量检测InstallUncaughtExceptionHandler();//系统异常返回}YES;捕获2.SignalHandler.m的实际voidSignalExceptionHandler(intsignal){NSMutableString*mstr=[[NSMutableStringalloc]init];[mstrappendString:@"Stack:\n"];void*callstack[128];inti,frames=backtrace(callstack,128);char**strs=backtrace_symbols(callstack,frames);for(i=0;i[mstrappendFormat:@"%s\n",strs[i]];}[SignalHandlersaveCreash:mstr];}voidInstallSignalHandler(无效){信号(SIGHUP,SignalExceptionHandler);信号(SIGINT,SignalExceptionHandler);信号(SIGQUIT,SignalExceptionHandler);信号(SIGABRT,SignalExceptionHandler);信号(SIGILL,SignalExceptionHandler);信号(SIGSEGV,SignalExceptionHandler);信号(SIGFPE,SignalExceptionHandler);信号(SIGBUS,SignalExceptionHandler);信号(SIGPIPE,标志alExceptionHandler);}错误类型可以参考上面的描述。SignalExceptionHandler是信号错误时的回调。当出现信号错误时,可以回调该方法。3.UncaughtExceptionHandler.m的实现voidHandleException(NSException*exception){//异常Stack信息NSArray*stackArray=[exceptioncallStackSymbols];//异常原因NSString*reason=[exceptionreason];//异常名称NSString*name=[exceptionname];NSString*exceptionInfo=[NSStringstringWithFormat:@"Exceptionreason:%@\nExceptionname:%@\nExceptionstack:%@",name,reason,stackArray];NSLog(@"%@",exceptionInfo);[UncaughtExceptionHandlersaveCreash:exceptionInfo];}voidInstallUncaughtExceptionHandler(void){NSSetUncaughtExceptionHandler(&HandleException.test)};–踩坑的关键是这里最关键的一步,SignalHandler不要在debug环境下测试。因为系统的debug会先被拦截。我们要运行一次后,关闭调试状态。您应该直接单击我们构建的应用程序以在模拟器上运行。而UncaughtExceptionHandler可以在调试状态下被捕获——(IBAction)buttonClick:(UIButton*)sender{//1。信号量测试*pTest={1,2};free(pTest);//SIGABRT引起的错误,因为内存中根本就没有这个空间,哪里来的free,只是栈中的对象pTest->a=5;}-(IBAction)buttonOCException:(UIButton*)sender{//2.ioscrashNSArray*array=@[@"tom",@"xxx",@"ooo"];[arrayobjectAtIndex:5];}4.CrashCallstack分析——进一步分析属性描述长期以来,它的意思是“吃了不好的食物”。0xdeadfa11用户强制退出,意思是“死掉”。(当系统无反应时,用户按下电源开关和HOME)0xbaaaaaad用户按下Home键和音量键获取当前内存状态,不代表死机。0xbad22222VoIP应用程序因过于频繁的恢复而崩溃。killedwhenhot,意思是“cooloff”0xdead10cc因为在后台还占用系统资源(比如通讯录)被杀死,意思是“死锁”5.Demo地址iOSCrashUncaught下载https://github.com/xcysuccess/iOSCrashUncaught6.参考资料1.程序崩溃后的调试技巧2.iOS开发socket程序被SIGPIPE信号终止的问题3.美女念前4.如何定位Obj-C野指针随机崩溃(一):首先增加野指针Crash率五、如何定位Obj-C野指针随机崩溃(二):让非强制性崩溃变成强制性六、如何定位Obj-C野指针随机崩溃(三):加点黑科技使Crash自我报告
