mach异常
EXC_BREAKPOINT (SIGTRAP) EXC_BAD_INSTRUCTION (SIGILL)
打断进程的陷阱;在swift遇到无法恢复的错误,会抛出此异常
EXC_BAD_ACCESS
内存访问问题
EXC_CRASH (SIGABRT)
进程收到SIGABRT信号而终止
- 进程中函数调用了abort()
- app扩展花费太多时间初始化,操作系统会发送SIGABRT信号(Subtype LAUNCH_HANG)
EXC_CRASH (SIGKILL)
操作系统终止了进程。此崩溃的Termination Reason会出现Code码
- 0x8badf00d (pronounced “ate bad food”). 操作系统watchdog终止了进程。
- 0xc00010ff (pronounced “cool off”). 操作系统因为发热杀死了App。
- 0xdead10cc (pronounced “dead lock”). The operating system terminated the app because it held on to a file lock or SQLite database lock during suspension. Request additional background execution time on the main thread with beginBackgroundTask(withName:expirationHandler:). Make this request well before starting to write to the file in order to complete those operations and relinquish the lock before the app suspends. In an app extension, use beginActivity(options:reason:) to mange this work.
- 0xbaadca11 (pronounced “bad call”). The operating system terminated the app for failing to report a CallKit call in response to a PushKit notification.
- 0xbad22222. The operating system terminated a VoIP application because it resumed too frequently.
- 0xbaddd15c (pronounced “bad disc”). The operating system terminated the app to delete caches in an attempt to reclaim disk space. Many factors contribute to low disk space. You may be able to help by minimizing what you write to disk and managing the entire life cycle of your files.
- 0xc51bad01. watchOS terminated the app because it used too much CPU time while performing a background task. Optimize the code performing the background task to be more CPU efficient, or decrease the amount of work that the app performs while running in the background to resolve this crash.
- 0xc51bad02. watchOS terminated the app because it failed to complete a background task within the allocated time. Decrease the amount of work that the app performs while running in the background to resolve this crash.
- 0xc51bad03. watchOS terminated the app because it failed to complete a background task within the allocated time, but the system was sufficiently busy overall that the app may not have received much CPU time to perform the background task. Although you may be able to avoid the issue by reducing the amount of work your app performs in background tasks, 0xc51bad03 doesn’t indicate that the app did anything wrong. More likely, the app wasn’t able to complete its work because of overall system load.
EXC_CRASH (SIGQUIT)
进程因其他进程(生命周期管理进程)的要求而终止。不意外崩溃,但是进程可能存在问题。 宿主app杀死键盘扩展,因为该扩展花费太长时间来加载。
EXC_GUARD
进程违反了资源保护的守护 (Subtype GUARD_TYPE_FD) app关闭了访问sqlite文件的文件描述符,core data会在稍微点崩溃,此中错误让问题更容易定位。
EXC_RESOURCE
进程超过了资源最大消耗限制 Subtype CPU and CPU_FATAL MEMORY IO WAKEUPS
EXC_ARITHMETIC.
线程执行了无效的数学运算,比如除0或者浮点错误。(ExceptionNote NON-FATAL CONDITION 并不一定崩溃)
bsd信号
SIGSEGV
SIGSEGV是当一个进程执行了一个无效的内存引用,或发生段错误时发送给它的信号。意味着指针所对应的地址是无效地址,没有物理内存对应该地址。
(1)官方说法是:
SIGSEGV --- Segment Fault. The possible cases of your encountering this error are:
1.buffer overflow --- usually caused by a pointer reference out of range.
2.stack overflow --- please keep in mind that the default stack size is 8192K.
3.illegal file access --- file operations are forbidden on our judge system.
KERN_INVALID_ADDRESS 访问了未映射的内存,通过虚拟内存信息确定是否为段之外内存。 KERN_INVALID_ADDRESS arm指针认证失败(大端位错误设置,也会导致指针认证失败,但其实是app中内存冲突) KERN_PROTECTION_FAILURE 使用了受保护的内存地址,通过虚拟内存信息确定是否为受保护的内存。 EXC_ARM_DA_ALIGN 访问未对齐的内存(包括即未对齐又未映射的内存)。
- invalid memory access (segmentation fault) 不合法的内存访问(内存段错误)
- 访问已经释放的内存, 无效的内存地址引用信号(常见的野指针访问,所指向的对象被释放或者收回,但是该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址。这个指针就是野指针)
- 非ARC模式下,iOS中经常会出现在 Delegate对象野指针访问
- ARC模式下,iOS经常会出现在Block代码块内 强持有可能释放的对象
- 试图对只读映射区域进行写操作
SEGV_ACCERR, 对映射的对象没有权限(invalid permission for mapped object), 可能是野指针, 此地址的对象已经释放, 然后又调用对象的某个方法.
SEGV_MAPERR, 地址没有映射到对象(address not mapped to object)### 可能的原因是dangling pointer或者overflow,
比如
-
ptr1和ptr2指向同一段内存,但是某个线程某个时刻用ptr1将内存delete了,如果因为错误的设计或者假设导致认为ptr2还是指向合法的内存,使用时就会出错;
-
某个数组有1个元素,但是传入的数组大小却是2,如果我们要用2作为长度来遍历这个数组,那当访问第二个元素时就会出错;
xcode提供出的一些工具来协助复现: Xcode特性Address Sanitizer_gcs的博客-CSDN博客
SIGBUS
总线错误, 意思是该地址有效,但是总线不能读取,具体错误信息有以下三种 KERN_MEMORY_ERROR 访问当时无法返回数据的内存,例如内存映射文件变无效。
-
BUS_OBJERR , 硬件故障,不用说,程序员最常碰上的肯定不是这种情形。
-
BUS_ADRERR, 不存在的物理地址, Linux平台上执行malloc(),如果没有足够的RAM,Linux不是让malloc()失败返回,而是向当前进程分发SIGBUS信号。
-
BUS_ADRALN 未对齐的内存, ARM不支持非对齐的内存访问,要求对齐访问,否则向当前进程分发SIGBUS信号。
SIGBUS与SIGSEGV信号一样,可以正常捕获。SIGBUS的缺省行为是终止当前进程并产生core dump。
- SIGBUS(Bus error)意味着指针所对应的地址是有效地址,但总线不能正常使用该指针。通常是未对齐的数据访问所致。
试图访问一块无文件内容对应的内存区域,比如超过文件尾的内存区域,或者以前有文件内容对应,现在为另一进程截断过的内存区域。
- SIGSEGV(Segment fault)意味着指针所对应的地址是无效地址,没有物理内存对应该地址。
参考资料: 关于SIGSEGV错误及处理方法(转) - 鸭子船长 - 博客园
关于SIGBUS的总结_CodeJoker的专栏-CSDN博客_sigbus
SIGINT
- external interrupt, usually initiated by the user
- 通常由用户输入产生的中断信号, 比如唤起键盘
- 在iOS中一般不会处理到该信号
SIGILL
- invalid program image, such as invalid instruction, (无效的程序映像,例如无效指令)
- 不管在任何情况下得杀死进程的信号
- 由于iOS应用程序平台的限制,在iOS APP内禁止kill掉进程,所以一般不会处理
- 执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。
SIGABRT
- abnormal termination condition, as is e.g. initiated by abort()
- 通常由于异常引起的中断信号,异常发生时系统会调用abort()函数发出该信号,有可能是NSException也有可能是Mach异常
- 多线程访问可变数组, 可变字典, 多线程添加/删除 元素导致,
- 一种是由于方法调用错误(调用了不能调用的方法)
- 一种是由于数组访问越界, 这样一般是先抛异常, 从异常就可以明显看出错误原因
SIGFPE, EXC_ARITHMETIC
- erroneous arithmetic operation such as divide by zero
- 浮点数异常的信号通知
- 一般是由于 除数为0引起的
SIGTERM
- 程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出。
- iOS中一般不会处理到这个信号
SIGPIPE
管道破裂。程序Socket发送失败中止信号, 还有可能在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止。
比如客户端程序向服务器端程序发送了消息,然后关闭客户端,服务器端返回消息的时候就会收到内核给的SIGPIPE信号。 TCP的全双工信道其实是两条单工信道,client端调用close的时候,虽然本意是关闭两条信道,但是其实只能关闭它发送的那一条单工信道,还是可以接受数据,server端还是可以发送数据,并不知道client端已经完全关闭了。
详细参考: Linux SIGPIPE信号产生原因与解决方法_自己的学习笔记-CSDN博客_sigpipe
SIGTRAP, EXC_BREAKPOINT
由断点指令或其它trap指令产生. 由debugger使用。
TRAP 是陷阱的意思。它并不是一个真正的崩溃信号。它会在处理器执行 trap 指令时发送。LLDB调试器通常会处理此信号,并在指定的断点处停止运行。如果你收到了原因不明的 SIGTRAP,先清除上次的输出,然后重新进行构建通常能解决这个问题。
该异常是由于打算给一个附加的调试器在执行特定的断点来中断进程时触发。你可以在自己的代码中使用__builtin_trap()方法来触发此异常。如果没有被调试器所附加,那么进程将会结束并且生成一份崩溃报告。
SIGKILL -- 程序结束接收中止信号, 此信号不可被拦截, 一般是OOM信号, 系统内存不足
OOM 其实是Out Of Memory的简称,指的是在 iOS 设备上当前应用因为内存占用过高而被操作系统强制终止,在用户侧的感知就是 App 一瞬间的闪退,与普通的 Crash 没有明显差异。但是当我们在调试阶段遇到这种崩溃的时候,可以从 设置- 隐私 -分析与改进中 是找不到普通类型的崩溃日志,只能够找到Jetsam开头的日志,这种形式的日志其实就是 OOM 崩溃之后系统生成的一种专门反映内存异常问题的日志。那么下一个问题就来了,什么是Jetsam?
Jetsam是 iOS 操作系统为了控制内存资源过度使用而采用的一种资源管控机制。不同于MacOS,Linux,Windows等桌面操作系统,出于性能方面的考虑,iOS 系统并没有设计内存交换空间的机制,所以在 iOS 中,如果设备整体内存紧张的话,系统只能将一些优先级不高或占用内存过大的进程直接终止掉。
Jetsam机制清理策略可以总结为下面两点:
- 单个 App 物理内存占用超过上限
- 整个设备物理内存占用收到压力按照下面优先级完成清理:
-
- 后台应用>前台应用
- 内存占用高的应用>内存占用低的应用
- 用户应用>系统应用
翻阅XNU源码的时候我们可以看到在Jetsam机制终止进程的时候最终是通过发送SIGKILL异常信号来完成的。
#define SIGKILL 9 kill (cannot be caught or ignored)
从系统库 signal.h 文件中我们可以找到SIGKILL这个异常信号的解释,它不可以在当前进程被忽略或者被捕获. iOS性能优化实践:头条抖音如何实现OOM崩溃率下降50%
SIGSYS ---- 非法的系统调用。
EXC_BAD_INSTRUCTION --- 线程试图访问非法/无效的指令或将无效的参数(操作数)传递给指令
EXC_ARITMETHIC ---- 除以0或整数溢出/下溢引发的异常
EXC_SYSCALL , EXC_MACH_SYSCALL -- 应用程序访问内核服务(如文件I/O)或网络访问时发出
SIGALRM ---- 程序超时信号
SIGHUP-- 程序终端中止信号
SIGSTOP--程序键盘中止信号, 此信号不可被拦截
信号signal中断crash经常出现的位置
PAC
Apple在A12开始支持了arm64e指令集,提供了指令地址加密功能,即PAC(Pointer Authentication Code的缩写, 指针身份验证),
- 在比较新的机器上(一般iOS系统版本也比较高), Crash的概率比较大, PAC验证没通过
- 通过App Connect搜集到的原始Crash Log中Crash的Mach异常转化后的Signal是不一样的
PAC是什么
PAC是ARMv8.3 新增的功能,因为虽然系统是64位的,但是arm64指令地址根本用不满,所以把高位的部分(upper bits)拿来存一个指针地址的签名。
PAC指针验证码就是在CPU执行指令前先拿指针的高位签名和低位的实际地址部分坐下校验,失败了直接抛出异常
为了实现PAC, arm64e新增了两个指令:
- PACIASP 计算 PAC 加密并加到指针地址上
- AUTIASP 校验加密部分,并还原指针地址
PAC参考文章: iOS开发之Crash追踪之旅(一) - 简书