在日常工作中,开发者最怕的应该便是线上的溃散了。线上的溃散不像咱们开发中遇到的溃散,能够在 Xcode 的 log 中直观的看到溃散信息。
不过,线上的溃散也并不是线索全无,让咱们卖虾的不拿秤 — 抓瞎。
每逢 App 产生溃散时,体系会自动生成一个后缀 ips 的溃散陈述。咱们能够经过溃散陈述来进行问题定位。但溃散陈述的内容繁多,新手看很容易一脸懵。所以本文先讲解一下陈述中各字段的意义,后边再说陈述符号化。
废话不多说,让咱们开始吧!
前期准备
首要,陈述解读咱们需求先生成一个 crash 陈述。
1、新建一个项目,在 ViewController 中写下面的代码:
NSString *value;
NSDictionary *dict = @{@"key": value}; // 字典的 value 不可为 nil,所以会溃散
2、在真机上运转项目,然后去设置 – 隐私与安全性 – 剖析与改善 – 剖析数据,拿去生成的 crash 陈述(陈述的姓名与项目姓名共同,比如我的项目名为:CrashDemo,溃散陈述的名则为:CrashDemo-2023-05-30-093930.ips)。
留意:连着 Xcode 运转时不会产生溃散陈述,需求真机拔掉数据线再次运转 app 才会生成溃散陈述。
拿到陈述,接下来便是解读了。
陈述内容解读
官网的示例图:
Header
首要来看 Header:
Incident Identifier: 9928A955-FE71-464F-A2AF-A4593A42A26B
CrashReporter Key: 7f163d1c67c5ed3a6be5c879936a44f10b50f0a0
Hardware Model: iPhone14,5
Process: CrashDemo [45100]
Path: /private/var/containers/Bundle/Application/6C9D4CF7-4C16-4B50-A4A5-389BED62C699/CrashDemo.app/CrashDemo
Identifier: cn.com.fengzhihao.CrashDemo
Version: 1.0 (1)
Code Type: ARM-64 (Native)
Role: Foreground
Parent Process: launchd [1]
Coalition: cn.com.fengzhihao.CrashDemo [3547]
Date/Time: 2023-05-30 09:39:29.6418 +0800
Launch Time: 2023-05-30 09:39:28.5579 +0800
OS Version: iPhone OS 16.3.1 (20D67)
Release Type: User
Baseband Version: 2.40.01
Report Version: 104
Header 首要描绘了方针设备的软硬件环境。比如上图能够看出:是 iphone 14 的设备,体系版本是16.3,产生溃散的事件是 2023-05-30 09:39:29 等等。
需求留意的是 Incident Identifier
相当于当时陈述的 id,陈述和 Incident Identifier 是一一对应的关系,绝对不会存在两份不同的陈述 Incident Identifier 相同的状况。
Exception information
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
这一部分首要是告知咱们 app 是因为什么错误而导致的溃散,但不会包括完整的信息。
能够看到当时的 Type 为:EXC_CRASH (SIGABRT),这代表当时进程因收到了 SIGABRT 信号而导致溃散,这是一个很常见的类型,字典 value 为nil或许归于越界等都会是此类型。更多的 Exception Type 解释请参见此处。
Diagnostic messages
Application Specific Information:
abort() called
操作体系有时包括额定的诊断信息。此信息运用多种格局,具体取决于溃散的原因,并且不会出现在每个溃散陈述中。
本次的溃散原因是因为调用了 abort() 函数。
接下来,便是陈述的重点了。
Backtraces
这部分记载了当时进程的线程的函数调用栈,咱们能够经过调用栈来定位出问题的代码。
溃散进程的每一条线程都会被捕获成回溯。回溯会展示当时线程被中止时的线程的函数调用栈。如果溃散是由于言语异常形成的,会额定有一个Last Exception Backtrace
,位于榜首个线程之前。
比如咱们示例中的溃散便是由于言语异常形成的,所以溃散陈述中会有 Last Exception Backtrace。
Last Exception Backtrace:
0 CoreFoundation 0x191560e38 __exceptionPreprocess + 164
1 libobjc.A.dylib 0x18a6f78d8 objc_exception_throw + 60
2 CoreFoundation 0x191706078 -[__NSCFString characterAtIndex:].cold.1 + 0
3 CoreFoundation 0x1917113ac -[__NSPlaceholderDictionary initWithCapacity:].cold.1 + 0
4 CoreFoundation 0x19157c2b8 -[__NSPlaceholderDictionary initWithObjects:forKeys:count:] + 320
5 CoreFoundation 0x19157c158 +[NSDictionary dictionaryWithObjects:forKeys:count:] + 52
6 CrashDemo 0x104a69e0c -[ViewController touchesBegan:withEvent:] + 152
.... 中间内容省掉
25 CrashDemo 0x104a6a0c4 main + 120
26 dyld 0x1afed0960 start + 2528
以下是上述每一列元素的意义:
- 榜首列:栈帧号。仓库帧按调用顺序排列,其间帧 0 是在履行暂停时正在履行的函数。第 1 帧是调用第 0 帧函数的函数,依此类推
- 第二列:包括正在履行函数的二进制包名
- 第三列:正在履行的机器指令的地址
- 第四列:在彻底符号化的溃散陈述中,正在履行的函数的称号。出于隐私原因,函数称号有时限制为前 100 个字符
- 第五列(+ 号后边的数字):函数入口点到函数中当时指令的字节偏移量
经过第 6 行咱们能够推断出问题是由 NSDictionary 引起的。
但大部分时分咱们得到的陈述都是未符号化的,咱们需求对陈述进行符号化来获得更多的信息。关于符号化的相关内容能够看这里。
Thread state
Thread 0 crashed with ARM Thread State (64-bit):
x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x0000000000000000
...中间内容省掉
far: 0x00000001e4d30560 esr: 0x56000080 Address size fault
溃散陈述的线程状态部分列出了应用程序停止时溃散线程的 CPU 寄存器及其值。
Binary images
0x1cf074000 - 0x1cf0abfeb libsystem_kernel.dylib arm64e <c76e6bed463530c68f19fb829bbe1ae1> /usr/lib/system/libsystem_kernel.dylib
...中间内容省掉
0x18b8ca000 - 0x18c213fff Foundation arm64e <e5f615c7cc5e3656860041c767812a35> /System/Library/Frameworks/Foundation.framework/Foundation
以下是上述每一列元素的意义:
- 榜首列:二进制镜像在进程中的地址规模
- 第二列:二进制镜像的称号
- 第三列:操作体系加载到进程中的二进制映像中的 CPU 架构
- 第四列:唯一标识二进制映像的构建 UUID。符号化溃散陈述时运用此值定位相应的 dSYM 文件
- 第五列:二进制文件在磁盘上的途径
至此,陈述上的一切 section 都已经解读完。期望大家看完这篇文章后,再剖析溃散日志的时分能更加得心应手。