一、概述
“异常”这个词在“崩溃日志”语境下更多与“Mach 异常”(以 EXC_ 为前缀)和 UNIX 信号(如: SIGSEGV, SIGBUS等)相关。
1、UNIX信号,详见Unix_signal
常见的有:
SIGSEGV
,访问无效的内存地址。
地址存在,但是应用程序无法访问。SIGABRT
,程序崩溃。
由 C函数abort()
初始化。通常意味着系统检测到某些事务出错,例如assert()
或者NSAssert()
校验失败。SIGBUS
,访问无效的内存地址。
地址不存在,或对齐无效。(The address does not exist, or the alignment is invalid.)
2、Mach 异常,详见 Mach-EXC (sys/osfmk/mach/exception_types.h
)的源码文件。
常见的有:
-
EXC_BAD_ACCESS
,错误内存访问 -
EXC_CRASH
,异常跳出 -
SIGBUS
,访问无效的内存地址。地址不存在,或对齐无效。 -
EXC_BREAKPOINT
,跟踪/断点捕获
通用与 SIGTRAP 相关联。可以由你自己的代码或者 NSExceptions 抛出时触发。 -
EXC_GUARD
,违反了受保护资源的防护(Violated Guarded Resource Protection)
由违背受保护资源防护触发,例如‘某些文件描述符’。 -
EXC_BAD_INSTRUCTION
,非法指令,通常与特定非法或未定义指令/操作数相关。 -
EXC_RESOURCE
,资源限制,应用由于达到资源消耗限制而退出。 -
00000020
,十六进制异常类型,非 'OS Kernel' 异常。
二、EXC_BAD_ACCESS
1、概述
开发中经常遇到EXC_BAD_ACCESS
的崩溃信息,如果使用lldb的bt
打印堆栈,能对位具体代码是比较容易解决问题的。但也会遇到一些死在系统库,莫名错误引起的,就很难定位和排查问题。
它到底是什么问题引起的呢?具体要怎么分析?从哪里入手分析呢?带着疑问,找资料,学习汇总,以便查阅。
苹果官方给出EXC_BAD_ACCESS
的解释是,其是一个能被捕获语言异常,是关于内存访问方面的错误。
the EXC_BAD_ACCESS exception rules out that a crash is due to an uncaught language exception.
The crash is due to a memory access issue.
EXC_BAD_ACCESS
的堆栈错误
thread #39, queue = 'com.apple.root.default-qos', stop reason = EXC_BAD_ACCESS (code=2, address=0x13001c040)
EXC_BAD_ACCESS
的崩溃日志
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x00000011
我们发现EXC_BAD_ACCESS(code=2, address=0x13001c040)
错误信息给出两个字段,一个是address
代表出错的内存地址;code
是啥意思呢?
2、EXC_BAD_ACCESS
常见的错误子类型
上面错误中这个code
代表的是异常的子类型,具体是啥呢?常见有以下几种,详见源码
-
KERN_INVALID_ADDRESS == 1
The crashed thread accessed unmapped memory, either by accessing data or an instruction fetch.
由访问未映射内存的线程引起的,它可能由数据访问或指令提取触发。访问已经被 ARC 释放(导致地址变为不可访问)的对象。如果是这个情况,你通常可以在崩溃日志中的 “Backtrace” 顶部附近看到
objc_release
。
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x6d783f44
-
KERN_PROTECTION_FAILURE == 2
The crashed thread tried to use a valid memory address that’s protected. Some types of protected memory include read-only memory regions, or nonexecutable memory regions.
访问受保护的内存地址,例如制度内存区域 或者 不可执行的内存区域。
经常导致的原因就是访问某些尚未初始化的对象(SIGBUS
) ,如下
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x00000011
-
KERN_MEMORY_ERROR
The crashed thread tried to access memory that couldn’t return data at that moment, such as a memory-mapped file that became unavailable.
访问无法返回数据的内存区域,例如无效非法的文件。
3、如何定位EXC_BAD_ACCESS
更多信息
- 在debug运行是打开内存管理的
Zombie Objects
可以获得有效的调试信息。
三、EXC_CRASH(异常跳出)
它通常发生在对象接收到未实现的消息时,如 Xcode 调试器中显示的 “unrecognized selector sent to instance 0x6a33840”。
另一个常见的“EXC_CRASH”情况是关于“应用扩展(App Extensions)”。
应用扩展如果“花了太长时间来初始化”则会被系统终止。在这种情况下,异常子类型显示为LAUNCH_HANG
Exception Type: EXC_CRASH (SIGABRT)
Exception Subtype: LAUNCH_HANG
Exception Message: The extension took too much time to initialize
四、EXC_RESOURCE
“EXC_RESOURCE”意思是进程“达到资源消耗上限”。通常,当你的应用在一定时间内持续超出限制时会被触发。
这个异常包含“Exception Subtype”以帮助理解实际情况:
- CPU - 限制为 50%,时间不超过 180秒。
- WAKEUPS - 表示线程每秒唤醒次数太多。限制为 150次/每秒, 时间不超过 300秒。
- MEMORY - 没有相关文档描述限制信息。
Exception Type: EXC_RESOURCE
Exception Subtype: CPU
Exception Message: (Limit 50%) Observed 85% over 180 secs
---
Exception Type: EXC_RESOURCE
Exception Subtype: WAKEUPS
Exception Message: (Limit 150/sec) Observed 206/sec over 300 secs
---
Exception Type: EXC_RESOURCE
Exception Subtype: MEMORY
Exception Message: Crossed High Water Mark
五、00000020
与“EXC_”异常不同,这个“异常类型”实际上不能告诉你任何信息。取而代之,你应该查看“异常代码”获取更多详情。
-
0x8badf00d
(读作 ate bad food)- 表示由于 watchdog 出现超时而导致应用被操作系统终止。通常意味着应用程序花了太长时间启动、关闭或者响应系统事件。一个非常典型的情况是“在主线程上做同步网络请求”。 -
0xbaaaaaad
(读作 “plooookhy”)- 表示日志是整个系统的堆栈,而不是崩溃报告。 -
0xc00010ff
(读作 cool off(冷静))- 表示应用程序被系统关闭以响应热事件。 -
0xbad22222
- 表示操作系统终止了一个VoIP程序,因为它过于频繁的执行恢复。 -
0xdead10cc
(读作 dead lock(死锁))- 表示应用在后台运行时保持了系统资源。 -
0xdeadfa11
(读作 deadfall)- 表示应用被用户强制关闭了。强制关闭发生于用户先按下电源键直到“滑动来关机”出现然后按住主屏幕按钮。
参考
在exc_bad_access中,代码=1和代码=2有什么区别?
Diagnosing Issues Using Crash Reports and Device Logs
Understanding the Exception Types in a Crash Report
Investigating Memory Access Crashes
译:理解 iOS 异常类型
iOS Abort问题系统性解决方案