亲宝软件园·资讯

展开

iOS之异常与信号使用场景分析

好_好先生 人气:0

正文

Crash的主要原因是你的应用收到了未处理的信号。 未处理的信号可能来源于三个地方:kernel(系统内核)、其他进程、以及App本身。 因此,crash异常也分为三种:

异常

Exception Type:

异常的type固定是SIGABRT,其实是CrashReporter在捕获Exception之后,再调用abort()发出的信号类型。这个机制也决定了如果是Exception Crash,堆栈就看这里的Last Exception Backtrace:, Crash Thread 里面固定是handleException的堆栈,没有查看的意义。

Exception Codes:

一般就是 0 at 0x18ac2378,后面这个地址就是发生异常的对象的地址

特殊的 Exception Code

我们在挂起之前持有文件锁或 SQLite 数据库锁。我们应该在挂起之前释放锁

通过侧面和两个音量按钮对整个系统进行了 stackshot。

可能是 VOIP 应用被频繁唤起导致的崩溃。也可以注意一下我们的后台调用网络的代码。 如果我们的TCP连接被唤醒太多次(例如 300 秒内唤醒 15 次),就会导致此崩溃。

我们的应用程序执行状态更改(启动、关闭、处理系统消息等)花费了太长时间。与看门狗的时间策略发生冲突(超时)并导致终止。最常见的罪魁祸首是在主线程上进行同步的网络连接。

系统检测的设备发烫而终止了我们的 App。如果只在少量设备上(几个)发生,那就可能是由于硬件的问题,而不是我们 App 问题。但是如果发生在其他设备上,我们应该使用 Instruments 去检查我们 App 的耗电量问题。

发生安全冲突。 如果 Termination Description 显示为 Process detected doing insecure drawing while in secure mode,则意味着我们的应用尝试在不允许的情况下进行绘制,例如在锁定屏幕的情况下。

Triggered by Thread:

发生Crash的线程

Application Specific Infomation:

Exception的信息,这个是定位异常的关键信息

Last Exception Backtrace:

抛出异常的代码堆栈,如果是异常,就看这个堆栈

主要信号

主要信号有 SIGTERM、SIGABRT、SIGSEGV、SIGBUS、SIGILL、SIGFPT、SIGKILL、SIGTRAP

程序结束(terminate)信号,与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出。iOS中一般不会处理到这个信号

SIGABRT原因

场景

全局变量赋值的代码段,被多线程调用同时赋值,上一次赋的值就可能被多个线程释放

解决方案

删掉类似的赋值操作或者加锁

SIGSEGV原因:

场景:

SDWebImageDownloaderOperation 生命周期的管理和错误回调是在2个queue, 有可能 self 已经进入释放逻辑,再访问 self.completeBlock, 再访问就是无效的。

原因:

多线程访问或者操作对象、栈溢出。

SIBBUS原因:

char *p, tmp;
NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"txt"];
int fd = open(path.UTF8String, O_RDWR);
p = (char*)mmap(NULL,FILESIZE, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0); signal(SIGBUS, handle_sigbus);
getchar();
for(int i=0;i<FILESIZE;i++){
	tmp = p[i];
}
printf("ok\n");

场景:

mmap 映射了⼀个⽂件的内存,写入时越界。

对比:

SIGSGV 访问的是⽆效的内存,就是该内存不属于我们的进程,或者没有权限,SIGBUS指的是 CPU ⽆法操作该地址,⼤部分是没有对⻬导致的。

SIGILL原因:

典型场景:

iOS 上该问题有可能会随机产⽣在任何动态库、静态库的⽅法中,⼀旦出现之后,应⽤会⼀直崩溃

解决方案:

app级别的代码没有修改可执⾏段的权限,⽆法污染代码段,判断是苹果增量的问题,用户重启⼿机

定义:

程序结束接收中⽌信号,⼀般exit()会发⽣这个信号,当前应用不能捕获,也无法忽略,OOM,Watchdog最终都是这个异常信号。

SIGTRAP原因:

很多系统库例如 WebKit,libdispatch 等使⽤了__builtin_trap() ⽅法去触发断点异常,在debug 模式下,会触发调试器断点,这样开发可以实时查看问题,在 release 模式下,应⽤就会崩溃,然后产⽣ SIGTRAP 信号。

典型场景:

dispatch_group_enter 和 dispatch_group_leave 调⽤不匹配,如果dispatch_group_leave 多调⽤了,会触发 DISPATCH_CLIENT_CRASH,在DISPATCH_CLIENT_CRASH 内部会调⽤__builtin_trap() 触发调式陷阱 。

Mach 异常

Mach异常的好处就是可以捕获更多的Crash,⽐如循环递归导致的堆栈溢出crash。原本信号的⽅式回调会在崩溃的线程⾥⾯,但是因为循环递归已经堆栈溢出了,已经没有环境来执⾏crash捕获的逻辑了,但是Mach异常捕获可以定义单独的线程来处理Mach异常逻辑。 如何区分我们看到的⽇志是Mach异常的呢? Exception Type: 是EXC_打头的话,就是Mach异常了,后⾯的Exception Subtype:其实是根据Exception Type:转了⼀下

Exception Type:

Abort

Abort 包含哪些场景?

Abort目标

Abort推导规则

需要根据可能导致客户端崩溃的原因设计推导规则,并基于线上⽤户的 Abort 数据快速聚合,从而发现并解决影响线上稳定性的问题

加载全部内容

相关教程
猜你喜欢
用户评论