内容简介:原文链接:翻译:CoderWangx当你的iOS应用崩溃的时候,我们需要去分析
原文链接: Understanding iOS Exception Types (PS.由于未知原因已失效,经检查,文章中引用的链接都还有效 :)
翻译:CoderWangx
当你的iOS应用崩溃的时候,我们需要去分析 异常日志 以定位根本原因。崩溃可能是 “低内存崩溃Low Memory Crash” 或者 “普通异常崩溃”。当碰到“异常”时,更好的理解“不同类型的异常”能够真正帮助我们快速定位问题所在。
在这篇文章中,我们将研究 iOS 应用可能碰到的不同类型的“异常”,例如 EXC_CRASH 、 EXC_BAD_ACCESS 、 EXC_RESOURCE 、 00000020 等。
崩溃日志中的“异常”
“异常”这个词在“崩溃日志”语境下更多与“Mach 异常”(以“
EXC_ 为前缀”)和 “
UNIX 信号
”(如: SIGSEGV
, SIGBUS
等)相关。在某些情况下(应该是有对应的dSYM符号文件时)系统会通过映射将底层的 Mach 异常
翻译为 UNIX 信号
。这就是为什么你能log中看到有用 “EXC_CRASH(SIGABRT)”
及 “EXC_BAC_ACCESS(SIGSEGV)”
作为
异常类型(Exception Type)
。
对于某些异常,还会附带一个关联的 处理器定制异常码(processor-specific Exception Code) 或者 异常子类型(Exception Subtype) ,用以包含更多问题相关信息。举例来说, “EXC_BAC_ACCESS” 类型异常可能有一行如“KERN_INVALID_ADDRESS at 0x80000010”作为“异常码”; “EXC_RESOURCE” 可能有一行"WAKEUPS"作为"异常子类别"。
UNIX 信号
iOS开发者常见的 UNIX 信号 如下:
UNIX 信号 | 注释 |
---|---|
SIGSEGV | 访问无效的内存地址。地址存在,但是应用程序无法访问。 |
SIGABRT |
程序崩溃。由 C函数 abort()
初始化。通常意味着系统检测到某些事务出错,例如 assert()
或者 NSAssert()
校验失败。 |
SIGBUS | 访问无效的内存地址。地址不存在,或对齐无效。(The address does not exist, or the alignment is invalid.) |
SIGTRAP | 调试器相关 |
SIGILL | 尝试执行非法的、有缺陷、未知的或者需要权限的指令。 |
更多 UNIX 信号 可以参考这里:Unix_signal。
Mach 异常
Mach 异常 | 描述 | 注释 |
---|---|---|
EXC_BAD_ACCESS | 错误内存访问 | 访问“错误”内存地址。“错误”可能指“地址不存在”或者“应用没有权限访问”。因此通常与 SIGBUS 及 SIGSEGV 相关联。 |
EXC_CRASH | 异常跳出 | 通常与 SIGABRT 相关联,意思是由于检测到代码抛出的未捕获异常而使应用程序异常退出。 |
EXC_BREAKPOINT | 跟踪/断点捕获 | 通用与 SIGTRAP 相关联。可以由你自己的代码或者 NSExceptions 抛出时触发。 |
EXC_GUARD | 违反了受保护资源的防护(Violated Guarded Resource Protection) | 由违背受保护资源防护触发,例如‘某些文件描述符’。 |
EXC_BAD_INSTRUCTION | 非法指令 | 通常与特定非法或未定义指令/操作数相关。 |
EXC_RESOURCE | 资源限制 | 应用由于达到资源消耗限制而退出。 |
00000020 | 十六进制异常类型 | 非 'OS Kernel' 异常。 |
查看完整 Mach 异常列表请参考这里 ( sys/osfmk/mach/exception_types.h
)的源码文件。
异常
EXC_BAD_ACCESS(错误内存访问)
“EXC_BAD_ACCESS” 是APP崩溃时最常见的异常之一。不幸的是,调试起来却不容易。
一般有两种可能性:
- 访问某些尚未初始化的对象。( SIGBUS )
-
访问已经被 ARC 释放(导致地址变为不可访问)的对象。如果是这个情况,你通常可以在崩溃日志中的 “Backtrace” 顶部附近看到
objc_release
。
示例如:
Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x6d783f44 ... Exception Type: EXC_BAD_ACCESS (SIGBUS) Exception Codes: KERN_PROTECTION_FAILURE at 0x00000011 复制代码
“EXC_BAD_ACCESS”也有关联的“异常码”以帮助提供额外信息。举例来说, KERN_PROTECTION_FAILURE
表示内存有效,但是不允许当前形式的访问, KERN_INVALID_ADDRESS
意思是地址当前无效。
查看这里的源码文件获取完整的可能值列表。
为了辅助调试 “EXC_BAD_ACCESS” 类型异常,你可以勾选 Xcode 中的 “Enable Zombie Objects” 后再尝试。
EXC_CRASH(异常跳出)
相较于 “EXC_BAD_ACCESS”,“EXC_CRASH" 更容易遇到。它通常发生在对象接收到未实现的消息时,如 Xcode 调试器中显示的 “unrecognized selector sent to instance 0x6a33840”。
一般情况里这个异常会与调试器一起发挥作用,因为调试器可以中断进程。如果没有附加调试器,会生成一个崩溃日志。
崩溃日志中展示的信息示例:
Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 ... ## Usually you will see a similar line in the "backtrace" part 2 CoreFoundation 0x36c02e02 -[NSObject(NSObject) doesNotRecognizeSelector:] + 166 复制代码
可能存在某些与“unrecognized selector”无关的特殊情况。如果碰到了,请注意到处都有可能发生这种事情。
另一个常见的“EXC_CRASH”情况是关于“应用扩展(App Extensions)”。应用扩展如果“花了太长时间来初始化”则会被系统终止。在这种情况下,
异常子类型(Exception Subtype)
显示为 LAUNCH_HANG
,附带一个得体的
异常消息(Exception Message)
:
Exception Type: EXC_CRASH (SIGABRT) Exception Subtype: LAUNCH_HANG Exception Message: The extension took too much time to initialize 复制代码
EXC_BREAKPOINT(跟踪捕获)
与“EXC_CRASH”非常相似, EXC_BREAKPOINT 也往往与调试器一起发挥作用,在测试阶段被捕获。 当使用 Swift 时,在以下情况这个异常会在运行时抛出:
- 一个非可选类型值为nil
- 强制类型转换失败
示例信息如:
Exception Type: EXC_BREAKPOINT (SIGTRAP) Exception Codes: 0x0000000000000002, 0x0000000000000000 复制代码
你可以在代码中手动调用 __builtin_trap()
来触发这个异常。
EXC_GUARD(违反了受保护资源的防护)
与其他所有“EXC_”前缀的异常不同,这个异常不是一个“原生”的 Mach 异常。事实上,它是为 XNU - 一个苹果开发的衍生操作系统内核 而添加的。
" XNU
" 代表 "X 不是 Unix"(X is Not Unix)。
“EXC_GUARD”的定义可以在这里- osfmk/mach/exception_types.h
找到。
这个异常的一个较好例子是应用程序在 Core Data 访问 SQLite 文件时关闭了它的“文件描述符(file descriptor)”。
在 iOS7 之前,这个异常会附带一部分“异常码(Exception Codes)”以帮助理解情况。异常码包含“两个”位域代码(如: 0x400000010000005e
)及 subcode
(如: 0x00007f8254a019c0
)。
位域代码部分可分解为“3”个区:
-
Guard Type
- 这个时候只有一种类型 - 受保护的文件描述符(guarded file descriptor(GUARD_TYPE_FD))
。值为
0x2
。如果你看到时 0x4 作为代码的前缀,则这个崩溃与“文件描述符”相关。 -
Flavor
- 当违反“受保护的文件描述符”时的不同条件:
如果设置了“第1”(
[32]: "1 << 0"
)位( kGUARD_EXC_CLOSE ),则它曾试图在“受保护的文件描述”上调用close()
。 如果设置了“第2”([33]: "1 << 1"
)位( kGUARD_EXC_DUP ),则它试图在“受保护的文件描述符”上使用F_DUPFD
或F_DUPFD_CLOEXEC
调用dup(2)
,dup2(2)
,fcntl(2)
。还包含了尝试使用/dev/fd/
打开“文件描述符”。 如果设置了“第3”([34]: "1 << 2"
)位( kGUARD_EXC_NOCLOEXEC ),则它试图关闭“文件描述符”上的“close-on-exec”标志。 如果设置了“第4”([35]: "1 << 3"
)位( kGUARD_EXC_SOCKET_IPC ),则它试图通过 套接字(socket)发送“受保护的文件描述符”。 如果设置了“第5”([36]: "1 << 4"
)位( GUARD_FILEPORT ),则它曾试图通过 套接字(socket)从“受保护的文件描述符”创建一个文件端口。 如果设置了“第6”([37]: "1 << 5"
)位( kGUARD_EXC_MISMATCH ),说明“受保护的文件描述符”与“守卫”不相符。 如果设置了“第7”([38]: << 6
)位( kGUARD_EXC_WRITE ),则它曾试图通过 套接字(socket)写入一个“受保护的文件描述符”。 - File Descriptor - 应用尝试操作的受保护的文件描述符。- subcode 部分包含“受保护的值”。
详细定义可以在这里( /bsd/sys/guarded.h
)找到。
从 iOS 7 开始,“Exception Codes”被提供更详细解释的“Exception Subtype”和“Exception Message”替代。
# iOS 6 Exception Type: EXC_GUARD Exception Codes: 0x400000010000005e, 0x00007f8254a019c0 # The type is "GUARD_TYPE_FD" (0x4), with "kGUARD_EXC_CLOSE". The FD is "94". # ------- # iOS 7 and above Exception Type: EXC_GUARD Exception Subtype: GUARD_TYPE_FD Exception Message: CLOSE on file descriptor 81 (guarded with 0x0000000017e6eed0) 复制代码
EXC_BAD_INSTRUCTION(非法指令)
“EXC_BAD_INSTRUCTION”,通常与“ SIGILL ”关联,是一个非常容易理解的异常 - 即你正在使用“错误”的指令或操作。然而,有时候也很难去调试。
以下是一些较常见的情况。 由于Xcode提供的调试信息,这个很容易识别 - 它是由于不安全的解包导致的。
## Usually show "EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)" in Xcode. “fatal error: unexpectedly found nil while unwrapping an Optional value” 复制代码
但是,像这样和这样(均为StackOverflow上的问题)的就不容易了 - 第一个是有关于 GCD 的使用,另一个是苹果的bug。 以下是崩溃日志中的显示格式:
Exception Type: EXC_BAD_INSTRUCTION (SIGILL) Exception Codes: 0x0000000000000001, 0x000000000000b6d2 复制代码
EXC_RESOURCE
“EXC_RESOURCE”意思是进程“达到资源消耗上限”。通常,当你的应用在一定时间内持续超出限制时会被触发。 这个异常包含“ Exception Subtype ”以帮助理解实际情况:
-
CPU
- 限制为
50%
,时间不超过180秒
。 -
WAKEUPS
- 表示线程每秒唤醒次数太多。限制为
150次/每秒
, 时间不超过300秒
。 - MEMORY - 没有相关文档描述限制信息。
与“EXC_GUARD”类似,它曾使用“位域”来传递信息,现在也使用“Exception SubType”和“Exception Message”。
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 )- 表示应用被用户强制关闭了。强制关闭发生于用户先按下电源键直到“滑动来关机”出现然后按住主屏幕按钮。
这些“十六进制”代码实际上是六音词 - 由我们开发者创建作为不容易忘记的魔法数字。
以上所述就是小编给大家介绍的《[译]理解 iOS 异常类型》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 理解 TypeScript 类型拓宽
- 理解 TypeScript 类型收窄
- 理解JavaScript变量和类型
- 一分钟理解 Java 包装类型
- 理解 MySQL 中的数字类型
- 反编译理解java枚举类型
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。