敞开生长之旅!这是我参与「日新方案 12 月更文应战」的第3天,点击检查活动概况

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

  • Mach反常:是指底层的内核级反常。用户态的开发者能够直接经过Mach API设置Thread、task、host的反常端口,来捕获Mach反常。
  • Unix信号:又称BSD信号,假如开发者没有捕获Mach反常,则会被host层的办法ux_exception()将反常转换为对应的UNIX信号,并经过办法threadsignal()将信号投递到犯错的线程。能够经过办法singnal(x, SignalHandler)来捕获single。
  • NSException:应用级反常,它是未被捕获的Objective-C反常,导致程序向本身发送了SIGABRT信号而溃散,对于未捕获的Objective-C反常,是能够经过try catch来捕获的,或许经过NSSetUncaughtExceptionHandler()机制来捕获。

反常

Exception Type:

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

Exception Codes:

一般便是 0 at 0x18ac2378,后面这个地址便是产生反常的方针的地址

特殊的 Exception Code
  • 0xdead10cc – Deaklock

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

  • 0xbaaaaaad – Bad

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

  • 0xbad22222 – Bad too (two) many times

或许是 VOIP 应用被频频唤起导致的溃散。也能够留意一下咱们的后台调用网络的代码。 假如咱们的TCP连接被唤醒太多次(例如 300 秒内唤醒 15 次),就会导致此溃散。

  • 0x8badf00d – Ate (eight) bad food

咱们的应用程序履行状况更改(启动、关闭、处理体系音讯等)花费了太长时刻。与看门狗的时刻战略产生冲突(超时)并导致终止。最常见的元凶巨恶是在主线程上进行同步的网络连接。

  • 0xc00010ff – Cool Off

体系检测的设备发烫而终止了咱们的 App。假如只在少量设备上(几个)产生,那就或许是因为硬件的问题,而不是咱们 App 问题。但是假如产生在其他设备上,咱们应该使用 Instruments 去检查咱们 App 的耗电量问题。

  • 0x2bad45ec – Too bad for security

产生安全冲突。 假如 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

SIGTERM

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

SIGABRT

原因
  • double free指针,void *ptr = malloc(256); free(ptr);free(ptr);// 重复开释会导致SIGABRT过错
  • free没有初始化的地址或许过错的地址,void ptr – (void)0x0000100; free(ptr);//开释未初始化的地址导致SIGABRT过错
  • 内存越界,char str2[10]; char *str1 = “askldfjadslfjsalkjfsalkfdj”; strcpy(str2, str1);
  • 直接调用abort()
  • 直接调用assert()

场景

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

解决方案

删掉相似的赋值操作或许加锁

SIGSEGV

原因:
  • invalid memory access(segmentation fault)
  • 无效的内存地址引用信号(常见的野指针拜访,拜访了没有权限的内存地址,体系内存地址等)
  • 非ARC形式下,iOS中常常会出现在Delegate方针野指针拜访
  • ARC形式下,iOS常常会出现在Block代码块内强持有或许开释的方针
场景:

SDWebImageDownloaderOperation 生命周期的管理和过错回调是在2个queue, 有或许 self 现已进入开释逻辑,再拜访 self.completeBlock, 再拜访便是无效的。

原因:

多线程拜访或许操作方针、栈溢出。

SIBBUS

原因:
  • mmap 内存映射拜访超出了⼤⼩
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");
  • 拜访未对⻬的内存地址, int pi = (int)(0x00001111); *pi = 17;
场景:

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

比照:

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

SIGILL

原因:
  • 执⾏⾮法指令
  • 仓库溢出
典型场景:

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

解决方案:

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

界说:

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

原因:
  • ⻓时刻占⽤太多 CPU 资源,被体系杀掉
  • 应⽤启动的时分,在主线程做⻓时刻操作,或许卡死,导致被 watchdog 杀死
  • 线程切换过于频频,被体系杀掉
  • 应⽤占⽤过多内存,被 jetsam 杀掉

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:
  • EXC_BAD_ACCESS:内存不能拜访,对应SIGBUS和SIGSEGV
  • EXC_BAD_INSTRUCTION:⾮法的指令,对应捕获到的SIGILL问题
  • EXC_ARITHMETIC:算术运算犯错,对应SIGFPE
  • EXC_EMULATION:对应SIGEMT
  • EXC_SOFTWARE:软件犯错,对应SIGSYS,SIGPIPE,SIGABRT,SIGKILL
  • EXC_BREAKPOINT:对应SIGTRAP
  • EXC_SYSCALL:不常⽤
  • EXC_MACH_SYSCALL:不常⽤
  • EXC_RPC_ALERT:不常⽤
  • EXC_CRASH:对应SIGBART
  • EXC_GUARD:⼀般是⽂件句柄防护,⽐如close到了⼀个内核的fd.
  • EXC_RESOURCE:遇到了⼀些体系资源的约束,⼀般是CPU过载,线程调度太频频,⽐如iOS中每秒⼦线程唤醒次数不能超过150

Abort

Abort 包含哪些场景?
  • 内存使⽤量过⾼、短时刻内请求⼤量内存,体系发送signal9(signal9⽆法经过信号捕获)强制杀死进程(相似于Android端上的OOM),便是⼤家常说的Jetsam事情
  • 主线程发⽣卡死超过⼀定时刻watchdog强制杀死进程(不同体系版别卡死时刻不同)
  • 启动超时、后台切前台resume超时
  • 部分死循环、递归等造成的栈溢出
Abort方针
  • 现场及上下⽂捕获
  • 定位 Abort 的事务场景、发⽣原因
  • 基于现场及上下⽂捕获数据、Abort发⽣原因的⽅法构成⾼效的⼯具链,⽤于快速定位线上溃散率发⽣的主因

Abort推导规矩

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