[译]理解 iOS 异常类型

栏目: IOS · 发布时间: 4年前

内容简介:原文链接:翻译:CoderWangx当你的iOS应用崩溃的时候,我们需要去分析

原文链接: Understanding iOS Exception Types (PS.由于未知原因已失效,经检查,文章中引用的链接都还有效 :)

翻译:CoderWangx

当你的iOS应用崩溃的时候,我们需要去分析 异常日志 以定位根本原因。崩溃可能是 “低内存崩溃Low Memory Crash” 或者 “普通异常崩溃”。当碰到“异常”时,更好的理解“不同类型的异常”能够真正帮助我们快速定位问题所在。

在这篇文章中,我们将研究 iOS 应用可能碰到的不同类型的“异常”,例如 EXC_CRASHEXC_BAD_ACCESSEXC_RESOURCE00000020 等。

崩溃日志中的“异常”

“异常”这个词在“崩溃日志”语境下更多与“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 错误内存访问 访问“错误”内存地址。“错误”可能指“地址不存在”或者“应用没有权限访问”。因此通常与 SIGBUSSIGSEGV 相关联。
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_DUPFDF_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 异常类型》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web容量规划的艺术

Web容量规划的艺术

阿尔斯帕瓦 / 叶飞、罗江华 / 机械工业出版社 / 2010-1 / 29.00元

《Web容量规划的艺术》由John Allspaw(F订ickr的工程运营经理)撰写,结合了他个人在F1ickr成长过程中的许多经历和很多其他产业中同行的洞察力。在衡量增长、预测趋势、成本效益等方面,他们的经验都会给你一些可靠并有效的指导。 网站的成功是以使用和增长来衡量的,而且网站类公司的成败(生死)是依赖于他们是否有能力来衡量决定他们的基础结构,从而适应不断增长的需求。作者通过自身实践给......一起来看看 《Web容量规划的艺术》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

SHA 加密
SHA 加密

SHA 加密工具