Understanding and Analyzing Application Crash Reports

原文地址:https://developer.apple.com/library/content/technotes/tn2151/_index.html#//apple_ref/doc/uid/DTS40008184-CH1-INTRODUCTION

当应用程序crash,一个crash报告会被创建,它对我们了解引发crash的原因非常有用。这篇文档包含了关于如何符号化,理解和解释崩溃报告的重要的信息。

简介

获取崩溃和低内存报告

符号化崩溃报告

 位代码

 确定崩溃报告是否可符号化

 使用Xcode符号化崩溃报告

 使用atos符号化崩溃报告

 符号化故障排除

分析崩溃报告

 

 异常信息

 额外的诊断信息

 回朔

 线程状态

 二进制镜像

了解低内存报告

相关文档

文档修订历史


简介

当应用程序崩溃时,将创建一个崩溃报告并存储在设备上。崩溃报告描述了在什么情况下应用程序终止,在大多数情况下,包括每个执行线程的一个完整的回溯,并通常对应用中的问题调试非常有用。你应该看看这些崩溃报告以了解你的应用程序有什么崩溃,然后尝试修复它们。

包含回溯的崩溃报告需要符号化才可以分析。符号化使用人类可读的函数名称和行号取代内存地址。如果你通过Xcode的Devices窗口取出崩溃记录,然后他们在几秒以后将为你自动符号化。否则,你将需要自己通过将它导入到Xcode的Devices窗口来符号化.crash文件。 See Symbolicating Crash Reports for details. 

低内存报告不同于其他的崩溃报告,这种类型的报告没有回朔。当低内存崩溃发生时,你必须调查你的内存使用模式和对低内存警告的响应。此文档向您指示了多个可能会对您有用的内存管理引用。


获取崩溃和低内存报告

Debugging Deployed iOS Apps 描述了如何直接从iOS设备获取崩溃和低内存报告.

Analyzing Crash Reports 在 App Distribution Guide 描述了如何查看从TestFlight beta测试人员和那些从App Store下载应用程序的用户那里收集的崩溃报告 。

符号化崩溃报告

符号化是将回溯地址解析为源代码的方法或函数名的过程。没有符号化的崩溃报告难以确定crash发生的位置。

Note:低内存报告不需要符号化

Note:从macOS获取的崩溃报告通常在它们被生成的时候都被符号化的,或者部分被符号化。这部分集中符号化来自iOS, watchOS, 和 tvOS的crash报告,但整个过程类似于macOS.

Figure 1  Overview of the crash reporting and symbolication process.

1.当编译器将源代码翻译成机器代码时,它也会生成调试符号,这些调试符号将编译后的二进制中的每一个机器指令映射到产生它的源代码的行。根据调试信息格式(DEBUG_INFORMATION_FORMAT)编译设置,这些调试符号存储在二进制或在同伴的调试符号文件(dsym)。默认情况下,调试版本的应用程序将调试符号存储在编译的二进制而发布版本的应用程序将调试符号存储在同伴dsym文件以减少二进制大小。

调试符号文件和应用程序二进制通过基于每次构建的构建UUID捆绑在一起。你的应用程序的每次构建会生成一个新的UUID来唯一标识它。即使一个功能相同的可执行文件从相同的源代码重构,并具有相同的编译器设置,它将会有不同的构建UUID。调试符号文件的后续版本,甚至来自同一个源文件,不会与其他版本构建的二进制文件交互操作。

2.当你archive你的应用程序用来发布的时候,Xcode会收集应用程序二进制以及.dsym文件并存放在你个人文件夹的某个位置。你可以在Xcode的Organizer下的“Archived”部分找到所有你的存档应用 。有关创建存档的更多信息,可以参考 App Distribution Guide

Important:为了从测试员,App Review,和客户那里符号化crash报告,你必须保留你发布的应用的每个构建的归档.

3.如果你通过App Store发布你的app,或者通过Test Flight实施一个beta测试,你可以选择上传你的文件到iTunes Connect时包含dsym文件。在提交对话框中,选择“Include app symbols for your application…”。上传你的dsym文件对于接收来自TestFlight用户以及那些已选择分享诊断数据的用户的崩溃报告是必要的。有关崩溃报告服务的更多信息,可以参考 App Distribution Guide

Important: 从App Review收到的崩溃报告将是非符号化的,即使上传你的文件到iTunes Connect时包含了dSYM文件。你需要通过Xcode符号化任何来自于App Review的崩溃报告。参考 Symbolicating Crash Reports With Xcode.

4.当你的应用crash,一个非符号化的crash报告被创建并存储在设备上。

5.用户可以按照Debugging Deployed iOS Apps步骤直接从他们的设备检索崩溃报告。如果你的应用程序通过AdHoc或Enterprise发布,这是从你的用户获取崩溃报告的唯一途径。

6.从设备检索的崩溃报告是非符号化的并需要使用Xcode来符号化。Xcode使用您的应用程序的二进制相关联的dSYM文件来替换回溯的每个地址为它在源代码中的原始位置。其结果是一个符号化的崩溃报告。

7.如果用户已经选择分享诊断数据给Apple,或者如果用户已经通过TestFlight安装一个beta版本的应用,crash报告会被上传到App Store。

8.App Store符号化这些crash报告并将它们归类。类似的崩溃报告的集合被称为崩溃点。

9.符号化的crash报告通过Xcode的 Crashes organizer提供给你。


位代码

位代码是一个编译程序的中间表示。当你通过bitcode enabled来archive一个应用,编译器产生的二进制文件包含bitcode而不是机器代码。一旦二进制已经上传到App Store,这些位代码可以被编译成机器码。App Store可以在未来再次编译这些位代码,利用未来的编译器的改进而不需要你做任何额外的工作。

Figure 2  Overview of the Bitcode compilation process.


因为你的二进制最后编译出现在App Store,你的Mac将不包含符号化从App Review或者从设备向你发送crash报告的用户的crash报告所需要的调试符号文件(dSYM)。虽然在你archive你的应用时会产生一个dSYM文件,它是为bitcode二进制而并不能用来符号化崩溃报告。App Store在bitcode编译的时候生成dSYM文件以供你下载,从Xcode或从iTunes Connect网站。你必须下载这些dSYM文件来符号化从App Review或者从设备向你发送crash报告的用户的crash报告。通过崩溃报告服务获得的crash报告将自动被符号化。

Important: App Store编译的二进制和原始提交的二进制有不同的UUIDs.

从Xcode下载dSYMs文件

1.在Archives organizer中,选择最初提交给App Store的存档文件。

2.点击Download dSYMs按钮。

Xcode下载dSYM文件并将其插入到选定的archive。

从 iTunes Connect网站下载dsyms文件

1.打开App Details页面。

2.点击Activity。

3.从All Builds列表中,选择一个版本。

4.点击Download dSYM链接。


确定crash报告是否可符号化

一个崩溃报告可能非符号化,完全或部分符号化。非符号化的崩溃报告在回溯中将不包含方法或函数的名字。相反,你有可执行代码的十六进制地址(在加载的二进制镜像里面)。在一个完全符号化的崩溃报告中,在每一行的回溯的十六进制地址被相应的符号代替。在部分符号化的崩溃报告,只有一些地址在回溯被相应的符号代替。

显然,你应该充分符号化任何收到的crash报告,它将提供关于崩溃的最深刻的见解。一部分符号化的崩溃报告可能包含足够的信息来了解crash,根据crash的类型和哪部分回溯被成功符号化。一个非符号化的崩溃报告几乎没用。

Figure 3  The same backtrace at various levels of symbolication.


使用Xcode符号化崩溃报告

Xcode会自动符号化它所遇到的所有的crash报告。所有你需要做的就是添加崩溃报告到Xcode的Organizer。

1.连接iOS设备到你的Mac

2.从“Window”菜单中选择“Devices”

3.在“DEVICES”的左一栏中,选择一个设备

4.在右侧面板上的“Device Information”部分中单击“View Device Logs”按钮

5.将您的崩溃报告拖到面板的左栏上

6.Xcode会自动符号化crash报告并显示结果

对符号化崩溃报告,Xcode需要能够找到以下文件:

1.崩溃的应用程序的二进制文件和dSYM文件。

2.应用链接的所有自定义框架的二进制文件和dSYM文件。那些通过来源构建的框架,他们的dSYM文件被复制到存档文件和应用的dSYM文件放在一起。框架是由一个第三方建立的,你需要问作者要求dSYM文件。

3.该应用程序在崩溃时运行的操作系统的符号。这些符号包含包含在一个特定的操作系统版本(如iOS 9.3.3)的框架的调试信息。操作系统的符号是特定于体系结构的-一个发布于iOS 64位设备不包括armv7符号。Xcode会从你连接到你的Mac的每个设备自动复制操作系统符号。

如果任一项缺失Xcode可能无法符号化crash报告,或可能只是部分符号化crash报告。

使用atos符号化crash报告

atos 命令将数字地址转换为它们的符号等价物。如果完整的调试符号信息可用,atos输出将包含文件名和行号信息的来源。atos命令可以用来在一个非符号化回溯,或部分符号化的crash报告中符号化个别地址。使用atos符号化crash的一部分:

1.在回溯中找到你想要符号化的行。请注意第二列中的二进制镜像的名称,以及第三列中的地址。

2.在崩溃报告底部的二进制镜像列表中寻找一个二进制镜像。注意二进制镜像的体系结构和负载地址。

Figure 4  Information from the crash report that is needed to use atos.

1.找到二进制文件的dSYM文件。你可以使用Spotlight找到二进制镜像的UUID相匹配的dSYM文件。参考Symbolication Troubleshooting部分。dSYM文件被捆绑在包含DWARF调试信息(构建时编译器生成的)的文件包中。你必须提供该文件的路径,而不是dSYM文件,当调用atos的时候。

2.使用上述信息,你可以使用atos命令符号化回溯中的地址。您可以指定要符号化的多个地址,用一个空格隔开。

atos -arch <Binary Architecture> -o <Path to dSYM file>/Contents/Resources/DWARF/<binary image name> -l <load address> <address to symbolicate>Listing 1  Example usage of the atos command following the steps above, and the resulting output.

$ atos -arch arm64 -o TheElements.app.dSYM/Contents/Resources/DWARF/TheElements -l 0x1000e4000 0x00000001000effdc
-[AtomicElementViewController myTransitionDidStop:finished:context:]

符号化故障排除

如果Xcode未能充分符号化崩溃报告,这可能是因为你的Mac丢失了应用程序二进制的dSYM文件,一个或者多个应用程序相链接的框架的dSYM文件,或应用程序crash的时候正在运行的设备系统的符号。下面的步骤说明如何使用Spotlight来确定你需要的符号化二进制镜像中回溯地址的dSYM文件是否存在于你的Mac上。

Figure 5  Locating the UUID for a binary image.

1.在回溯中找到Xcode未能符号化的一行。请注意第二列中的二值镜像的名称。

2.在崩溃报告底部的二值镜像列表中寻找那个名字的一个二进制镜像。该列表包含crash时加载到进程的每个二进制镜像的UUID。

Listing 2  你可以使用grep命令行工具来快速查找二进制镜像列表的入口 

$ grep --after-context=1000 "Binary Images:" <Path to Crash Report> | grep <Binary Name>

3.将二进制镜像的UUID转换为一个32个字符的字符串分离成组8-4-4-4-12(xxxxxxxx -xxxx-xxxx-xxxx- xxxxxxxxxxxx)。请注意,所有的字母必须大写。

4.搜索的UUID使用mdfind命令行工具使用查询”com_apple_xcode_dsym_uuids = = < UUID >”(包括引号)。

Listing 3 使用 mdfind 命令行工具来查找指定UUID的dSYM文件

$ mdfind "com_apple_xcode_dsym_uuids == <UUID>"


5.如果Spotlight发现指定UUID的dSYM文件,mdfind将打印的文件的路径和可能它包含的文件。如果一个UUID的dsym文件没有被发现,mdfind将退出不打印任何东西。

如果Spotlight找到二进制的dSYM文件但是Xcode无法符号化二进制镜像内的地址,那么你应该提交一个bug。将crash报告和有关dSYM文件的附加到bug报告。作为一种变通方法,您可以使用atos手动符号化地址。参考Symbolicating Crash Reports With atos.

如果Spotlight没有找到的二进制镜像的dSYM文件,核实你还有你的崩溃的应用程序的Xcode归档的版本,并且这个归档是位于Spotlight下可以找到的位置(home目录中的任何位置应该可以)。如果你的应用程序在bitcode enabled下构建,确保你已经下载的App Store最终编译的dSYM文件。参考Downloading dSYM files.

如果你认为你有正确的二进制镜像的dSYM文件,你可以使用dwarfdump命令打印匹配的UUIDs。你也可以使用dwarfdump命令打印一个二进制的UUID。

xcrun dwarfdump --uuid <Path to dSYM file>

Note: 您必须有您崩溃的应用程序最初提交给App Store的版本的归档。dSYM文件和应用程序二进制是基于每一个构建绑在一起的。创建一个新的归档,甚至从同一来源和构建配置,将不会产生一个可以和崩溃的那个构建互操作的dSYM文件。

如果您不再拥有此归档,您应该提交一个您保留存档的应用程序的新版本。然后你就可以为这一新版本符号化崩溃报告。

分析崩溃报告

本节讨论在一个标准崩溃报告中发现的每一个部分。

每一个crash报告都以一个头开始. 

Listing 4  摘录自一个crash报告的头

Incident Identifier: B6FD1E8E-B39F-430B-ADDE-FC3A45ED368C
CrashReporter Key: f04e68ec62d3c66057628c9ba9839e30d55937dc
Hardware Model: iPad6,8
Process: TheElements [303]
Path: /private/var/containers/Bundle/Application/888C1FA2-3666-4AE2-9E8E-62E2F787DEC1/TheElements.app/TheElements
Identifier: com.example.apple-samplecode.TheElements
Version: 1.12
Code Type: ARM-64 (Native)
Role: Foreground
Parent Process: launchd [1]
Coalition: com.example.apple-samplecode.TheElements [402]
 
Date/Time: 2016-08-22 10:43:07.5806 -0700
Launch Time: 2016-08-22 10:43:01.0293 -0700
OS Version: iPhone OS 10.0 (14A5345a)
Report Version: 104

大部分字段是意思明显的,但是还有一部分值得特别注意

Incident Identifier: 报告的唯一标识符。两个报告将永远不会共享同一个事件标识符。

CrashReporter Key: 每个匿名设备的标识符。来自同一个设备的两个报告将包含相同的值。

Beta Identifier: 一个设备和崩溃的应用程序的供应商的组合的唯一标识符。两个来自同一个供应商和相同的设备的应用程序的报告将包含相同的值。这个字段仅存在于通过TestFlight发布的应用程序的崩溃报告,并取代CrashReporter Key字段。

Process: 崩溃的进程的可执行文件名。这和应用程序的信息属性列表的CFBundleExecutable关键字匹配。

Version: 崩溃的进程的版本。此字段的值和崩溃的应用程序的CFBundleVersionCFBundleVersionString相关联

Code Type: 崩溃的进程的目标架构。这将是一个x86-64,ARM-64,ARM,或x86。

Role: 在终止时分配给进程的 task_role .

OS Version:操作系统版本, 包含crash时的构建号码.

异常信息

不可与Objective-C和C++异常混淆(尽管其中一个可能是crash的原因),这部分列出了有关crash的性质的信息的Mach异常类型及相关字段。不是所有的字段都会出现在每一个崩溃报告。

Listing 5  摘录自一个crash报告产生的异常代码部分,当因为一个未捕获的Objective-C的异常导致进程终止时

Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 0

Listing 6  摘录自一个crash报告产生的异常代码部分,当因为它dereferenced一个NULL指针导致进程终止时。

Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000
Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [0]
Triggered by Thread: 0

在本节中可能出现的字段的解释如下. 

Exception Codes: 有关异常的处理器的具体信息的一个或多个64位十六进制数的编码。通常情况下,这个字段不会出现因为崩溃报告会将异常代码解析成人类可读的描述并放在其他字段。

Exception Subtype: 异常代码的人类可读的名称.

Exception Message: 从异常代码中提取的额外的人类可读的信息.

Exception Note: 对于一个异常类型不特定的附加信息。如果这个字段包含了SIMULATED (this is NOT a crash),那么这个进程并没有崩溃,但被系统要求杀掉,通常是看门狗。

Termination Reason: 进程终止时确定的退出原因信息。关键的系统组件,无论是进程的内部和外部,将终止进程在遇到一个致命的错误时(例如,一个坏的代码签名,一个丢失的依赖库,或访问隐私敏感的信息在没有合法权限的时候)。MacOS的Sierra,iOS 10,WatchOS 3和TVOS 10已经采用新的基础设施来记录这些错误,这些操作系统生成的崩溃报告会在Termination Reason字段列出错误信息。

Triggered by Thread:异常起源的线程。

下面的部分解释了一些最常见的异常类型。

Bad Memory Access [EXC_BAD_ACCESS // SIGSEGV // SIGBUS]

进程试图访问无效的内存,或试图访问内存的方式是内存的保护级别不允许的(例如,写入到只读存储器)。Exception Subtype字段包含一个kern_return_t描述错误以及被错误访问的内存的地址。

下面是调试错误内存访问崩溃的一些信息:

如果objc_msgsend或objc_release在靠近崩溃线程的 Backtraces顶部,进程可能试图给一个已经释放的对象发送消息。你应该使用 Zombies instrument 来profile应用程序以更好的了解这个crash。

如果gpus_ReturnNotPermittedKillClient在靠近崩溃线程的 Backtraces顶部,这个进程被杀死的原因是它试图在后台使用OpenGL ES或者Matal来渲染。参考 QA1766: How to fix OpenGL ES application crashes when moving to the background

运行您的应用程序启用Address Sanitizer。address sanitizer在你编译代码的内存访问中增加了额外的仪器。当你的应用程序运行时,Xcode会提醒你内存是否以一种可能导致崩溃的方式访问。

Abnormal Exit [EXC_CRASH // SIGABRT]

进程异常退出。该异常类型的crash最常见的原因是未捕获的Objective-C/C++异常并调用abort()。

应用程序扩展将被终止于这个异常类型,如果他们需要太多的时间来初始化(一个看门狗终止)。如果一个扩展是在启动挂起时被杀死,所产生的崩溃报告的Exception Subtype将是LAUNCH_HANG。因为扩展没有main函数,任何花费在初始化的时间都在你的扩展以及依赖库中的静态构造器和+load方法。你应该尽可能地推迟这项工作。

Trace Trap [EXC_BREAKPOINT // SIGTRAP]

类似于Abnormal Exit,此异常的目的是给一个附加的调试器在其执行过程中的一个特定的点中断进程的机会。你可以触发这个异常通过在您自己的代码中使用__builtin_trap()函数。如果没有连接调试器,则该进程被终止,并生成一个崩溃报告。

低层库(例如,libdispatch)将因为一个致命的错误困住进程。有关该错误的附加信息可以在崩溃报告的 Additional Diagnostic Information 部分或设备控制台中找到。

如果在运行时遇到以下意外情况,Swift代码将终止于此异常类型,如:

一个不可选类型赋值nil

一个失败的强制类型转换

看 Backtraces确定意外情况发生的位置。额外的信息可能也被打印到设备的控制台。你应该修改崩溃的位置的代码,以优雅地处理运行时故障。例如,使用Optional Binding,而不是强制展开一个可选的变量。

Illegal Instruction [EXC_BAD_INSTRUCTION // SIGILL]

进程试图执行一个非法或未定义的指令。进程可能试图跳到一个无效的地址通过一个错误的函数的指针。

英特尔处理器的ud2操作码会导致EXC_BAD_INSTRUCTION异常,但是它常用于调试进程。Swift代码在英特尔处理器上在此异常类型下将终止,如果运行时遇到未捕获的情况。详情见 Trace Trap

Guarded Resource Violation [EXC_GUARD]

进程违反了一个有保护的资源的保护措施。系统库可能将某些文件描述符标志为guarded,正常操作这些描述符后会触发一个EXC_GUARD异常(当它想要操作这些文件描述符,操作系统采用特殊的 'guarded' 私有APIs)。这可以帮助您快速地跟踪关闭系统库打开的文件描述符之类的问题。例如,如果一个应用程序关闭用于访问支持Core Data存储的SQLite文件的文件描述符,Core Data就会在稍后神秘的崩溃。守卫异常使这些问题得到注意,因此使他们更容易调试。

新版本的iOS崩溃报告在Exception Subtype 和 Exception Message字段包含了关于导致EXC_GUARD异常的操作的人类可读的详细信息。在MacOS或老版本的iOS的崩溃报告中,这个信息作为一个位域被编码为第一个Exception Code可分解如下:

[63:61] - Guard Type: 保护资源的类型。值0x2表示资源是一个文件描述符。

[60:32] - Flavor: 违反被触发的条件。

如果第1位 (1 << 0)被置为1, 进程试图在有保护的文件描述符上调用close()。

如果第2位 (1 << 1)被置为1,进程试图在有保护的文件描述符上使用 F_DUPFD 或 F_DUPFD_CLOEXEC 命令调用 dup()dup2(), 或 fcntl()

如果第3位 (1 << 2)被置为1,进程试图通过一个socket发送一个有保护的文件描述符。

如果第4位 (1 << 4)被置为1, 进程试图写入一个有保护的文件描述符。

[31:0] - File Descriptor: 进程试图修改的有保护的文件描述符。

Resource Limit [EXC_RESOURCE]

进程超出了资源消耗限制。这是一个来自操作系统的通知,该进程正在使用太多的资源。透支资源被列在Exception Subtype字段中。如果Exception Note字段包含NON-FATAL CONDITION,那么进程并没有被杀死,即使崩溃报告被生成。

异常子类型MEMORY指示该进程已越过该系统所规定的内存限制。这可能是内存使用过多时终止的前兆。

异常子类型WAKEUPS表明进程中的线程每秒被唤醒次数过多,迫使CPU频繁唤醒并消耗电池寿命。

通常,这是由于不经意间发生的超出它预期的线程对线程的通信导致(通常使用peformselector:onthread:或dispatch_async)。因为触发这种异常的这种通讯发生得太频繁,经常会有很多有相同Backtraces(指示通讯的原始位置)的后台线程。

Other Exception Types

一些崩溃报告可能包含一个未命名的Exception Type,它将被打印成一个十六进制的值(如00000020)。如果您接收到这样一个崩溃报告,直接看Exception Codes字段以获得更多信息。

异常代码0xbaaaaaad表明日志是一个整个系统的stackshot,而不是一个crash报告。要获取stackshot可以按Home键和音量键。通常这些日志是由用户无意创建的,并不显示错误。

异常代码0xbad22222表明VoIP应用已被iOS终止因为恢复太频繁。

异常代码0x8badf00d指示应用程序已被iOS终止因为看门狗超时发生。应用程序花费太长的时间启动、终止或响应系统事件。一个常见的原因synchronous networking on the main thread。任何在Thread 0 上的任何操作都需要移动到后台线程,或者不同的处理,以至于它不会阻塞主线程。

异常代码0xc00010ff指示应用程序在响应热事件时由操作系统杀。这可能是由于在特殊设备或者它操作的环境的问题。让你的应用更有效地运行技巧,参考 iOS Performance and Power Optimization with Instruments WWDC 部分.。

异常代码0xdead10cc指示应用程序已被终止,因为它在后台运行时持有系统资源(如地址簿数据库)。

异常代码0xdeadfa11表明应用程序被用户强制退出。强制退出发生在用户第一次按住On/Off按钮,直到“slide to power off”出现,然后按住Home按钮。这是合理的假设用户已经这样做,因为应用程序已经变得没有响应,但它不保证-强制退出将工作在任何应用程序。

Note: 通过从多任务托盘移除它来终止一个暂停的应用程序不会产生一个崩溃报告。一旦一个应用程序已经暂停,iOS在任何时间有资格终止它,所以不会产生崩溃报告。

额外的诊断信息


本节包括特定于终止类型的额外诊断信息,其中可能包括:

Application Specific Information: 在进程终止之前捕获的框架错误消息

Kernel Messages: 关于代码签名问题的详细信息

Dyld Error Messages: 由动态链接库发出的错误消息

从watchOS 3, MacOS Sierra, iOS 10和tvOS 10开始, 大部分信息记录在Exception Information下的Termination Reason字段。

你应该阅读这一部分,以更好地了解进程终止的情况。

Listing 7  引用应用程序因为找不到它链接的框架而终止时生成的crash报告的特定信息部分

Dyld Error Message:
Dyld Message: Library not loaded: @rpath/MyCustomFramework.framework/MyCustomFramework
  Referenced from: /private/var/containers/Bundle/Application/CD9DB546-A449-41A4-A08B-87E57EE11354/TheElements.app/TheElements
  Reason: no suitable image found.

Listing 8  引用应用程序因为它无法快速加载它的初始视图控制器而终止时生成的crash报告的特定信息部分。

Application Specific Information:
com.example.apple-samplecode.TheElements failed to scene-create after 19.81s (launch took 0.19s of total time limit 20.00s)
 
Elapsed total CPU time (seconds): 7.690 (user 7.690, system 0.000), 19% CPU
Elapsed application CPU time (seconds): 0.697, 2% CPU

回溯

一个崩溃的报告最有趣的部分是其终止时每个进程的线程的回溯。这些追溯是在你用调试器暂停进程时看到的类似的东西。

Listing 9  摘录自一个完全符号化的崩溃报告的回溯部分。

Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   TheElements                   	0x000000010006bc20 -[AtomicElementViewController myTransitionDidStop:finished:context:] (AtomicElementViewController.m:203)
1   UIKit                         	0x0000000194cef0f0 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 312
2   UIKit                         	0x0000000194ceef30 -[UIViewAnimationState animationDidStop:finished:] + 160
3   QuartzCore                    	0x0000000192178404 CA::Layer::run_animation_callbacks(void*) + 260
4   libdispatch.dylib             	0x000000018dd6d1c0 _dispatch_client_callout + 16
5   libdispatch.dylib             	0x000000018dd71d6c _dispatch_main_queue_callback_4CF + 1000
6   CoreFoundation                	0x000000018ee91f2c __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
7   CoreFoundation                	0x000000018ee8fb18 __CFRunLoopRun + 1660
8   CoreFoundation                	0x000000018edbe048 CFRunLoopRunSpecific + 444
9   GraphicsServices              	0x000000019083f198 GSEventRunModal + 180
10  UIKit                         	0x0000000194d21bd0 -[UIApplication _run] + 684
11  UIKit                         	0x0000000194d1c908 UIApplicationMain + 208
12  TheElements                   	0x00000001000653c0 main (main.m:55)
13  libdyld.dylib                 	0x000000018dda05b8 start + 4
 
Thread 1:
0   libsystem_kernel.dylib        	0x000000018deb2a88 __workq_kernreturn + 8
1   libsystem_pthread.dylib       	0x000000018df75188 _pthread_wqthread + 968
2   libsystem_pthread.dylib       	0x000000018df74db4 start_wqthread + 4
 
...

第一行列出了当前正在执行的调度队列的线程号和标识符。其余行详细列出了回溯中的个别堆栈帧。从左到右:

堆栈帧号。堆栈帧按照调用顺序呈现,其中帧号零执行停止时的正在执行的函数。第一帧是在帧号零调用的函数,以此类推。

堆栈帧的执行函数驻留的二进制的名称。

对于帧零,当执行停止时所执行的机器指令的地址。对于剩余的堆栈帧,当控件返回到堆栈帧时,下一个执行的机器指令的地址。

在符号化的崩溃报告,在堆栈帧的函数的方法名称。

异常

异常在Objective-C中用来表示程序在运行时检测到的错误如通过一个越界的索引来访问数组,试图改变不可变对象,未实现协议所要求的方法,或发送接收者无法识别的消息。

Note: 向已释放的对象发送消息会引发NSInvalidArgumentException而不是内存错误访问导致的程序崩溃。这发生在一个新的对象被分配在之前已被已释放对象占用的内存。如果你的应用是由于未捕获的NSInvalidArgumentException崩溃(在异常回溯查询-[NSObject(NSObject) doesNotRecognizeSelector:] ),考虑使用 Zombies instrument 来分析您的应用程序来消除不当的内存管理的可能性。

如果一个异常没有被捕获,它是由一种称为未捕获的异常处理程序的函数截获。默认的未捕获的异常处理程序记录异常信息到设备的控制台,然后终止进程。仅仅异常回溯被写到生成的崩溃报告的最后Last Exception Backtrace部分,如Listing10所示。异常消息被从崩溃报告中省略了。如果你收到一个有Last Exception Backtrace的crash报告你应该从源设备获取控制台崩溃报告日志以更好地了解导致异常的条件。

Listing 10  从一个未符号化的crash报告摘录自Last Exception Backtrace部分.

Last Exception Backtrace:
(0x18eee41c0 0x18d91c55c 0x18eee3e88 0x18f8ea1a0 0x195013fe4 0x1951acf20 0x18ee03dc4 0x1951ab8f4 0x195458128 0x19545fa20 0x19545fc7c 0x19545ff70 0x194de4594 0x194e94e8c 0x194f47d8c 0x194f39b40 0x194ca92ac 0x18ee917dc 0x18ee8f40c 0x18ee8f89c 0x18edbe048 0x19083f198 0x194d21bd0 0x194d1c908 0x1000ad45c 0x18dda05b8)

一个仅包含十六进制地址的含有Last Exception Backtrace的崩溃日志必须被符号化来产生一个可用的回溯,如Listing11所示。

Listing 11  从一个符号化的crash报告摘录自Last Exception Backtrace部分.。这异常在从app的storyboard加载一个场景时引发。场景中的元素对应的相关联的IBOutlet未找到.

Last Exception Backtrace:
0   CoreFoundation                	0x18eee41c0 __exceptionPreprocess + 124
1   libobjc.A.dylib               	0x18d91c55c objc_exception_throw + 56
2   CoreFoundation                	0x18eee3e88 -[NSException raise] + 12
3   Foundation                    	0x18f8ea1a0 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 272
4   UIKit                         	0x195013fe4 -[UIViewController setValue:forKey:] + 104
5   UIKit                         	0x1951acf20 -[UIRuntimeOutletConnection connect] + 124
6   CoreFoundation                	0x18ee03dc4 -[NSArray makeObjectsPerformSelector:] + 232
7   UIKit                         	0x1951ab8f4 -[UINib instantiateWithOwner:options:] + 1756
8   UIKit                         	0x195458128 -[UIStoryboard instantiateViewControllerWithIdentifier:] + 196
9   UIKit                         	0x19545fa20 -[UIStoryboardSegueTemplate instantiateOrFindDestinationViewControllerWithSender:] + 92
10  UIKit                         	0x19545fc7c -[UIStoryboardSegueTemplate _perform:] + 56
11  UIKit                         	0x19545ff70 -[UIStoryboardSegueTemplate perform:] + 160
12  UIKit                         	0x194de4594 -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1352
13  UIKit                         	0x194e94e8c -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 268
14  UIKit                         	0x194f47d8c _runAfterCACommitDeferredBlocks + 292
15  UIKit                         	0x194f39b40 _cleanUpAfterCAFlushAndRunDeferredBlocks + 560
16  UIKit                         	0x194ca92ac _afterCACommitHandler + 168
17  CoreFoundation                	0x18ee917dc __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
18  CoreFoundation                	0x18ee8f40c __CFRunLoopDoObservers + 372
19  CoreFoundation                	0x18ee8f89c __CFRunLoopRun + 1024
20  CoreFoundation                	0x18edbe048 CFRunLoopRunSpecific + 444
21  GraphicsServices              	0x19083f198 GSEventRunModal + 180
22  UIKit                         	0x194d21bd0 -[UIApplication _run] + 684
23  UIKit                         	0x194d1c908 UIApplicationMain + 208
24  TheElements                   	0x1000ad45c main (main.m:55)
25  libdyld.dylib                 	0x18dda05b8 start + 4

Note: 如果你发现在你应用程序安装的一个异常处理域抛出的异常没有被捕获,请确认你在构建你的应用程序或库时没有指定-no_compact_unwind标识.

64位iOS使用“zero-cost”异常实现。在一个“zero-cost”系统中,每一个函数都有额外的数据来描述在函数中抛出异常的情况下如何展开堆栈。如果一个异常通过没有展开的数据的一个堆栈帧被抛出,那么异常处理不能进行并且进程停止 。在堆栈中可能有一个异常处理程序,但如果没有一个帧的展开数据,那么从异常被抛出的堆栈帧中也没有办法到达那里。指定 -no_compact_unwind标志意味着你获取那些代码未展开的表,所以你不能通过这些函数抛出异常。

另外,如果你在您的应用程序或库包含了普通的C代码,您可能需要指定-funwind-tables标识来展开代码中所有函数表.

线程状态


本节列出了崩溃线程的线程状态。这是一个在执行停止的时候的寄存器列表和它们的值 。了解线程状态在读一个崩溃报告时是没有必要的,但你可能能够使用这些信息,以更好地了解崩溃的条件。

Listing 12  从ARM64设备的crash报告摘录的线程状态的部分.

Thread 0 crashed with ARM Thread State (64-bit):
    x0: 0x0000000000000000   x1: 0x000000019ff776c8   x2: 0x0000000000000000   x3: 0x000000019ff776c8
    x4: 0x0000000000000000   x5: 0x0000000000000001   x6: 0x0000000000000000   x7: 0x00000000000000d0
    x8: 0x0000000100023920   x9: 0x0000000000000000  x10: 0x000000019ff7dff0  x11: 0x0000000c0000000f
   x12: 0x000000013e63b4d0  x13: 0x000001a19ff75009  x14: 0x0000000000000000  x15: 0x0000000000000000
   x16: 0x0000000187b3f1b9  x17: 0x0000000181ed488c  x18: 0x0000000000000000  x19: 0x000000013e544780
   x20: 0x000000013fa49560  x21: 0x0000000000000001  x22: 0x000000013fc05f90  x23: 0x000000010001e069
   x24: 0x0000000000000000  x25: 0x000000019ff776c8  x26: 0xee009ec07c8c24c7  x27: 0x0000000000000020
   x28: 0x0000000000000000  fp: 0x000000016fdf29e0   lr: 0x0000000100017cf8
    sp: 0x000000016fdf2980   pc: 0x0000000100017d14 cpsr: 0x60000000

二进制镜像


本节列出在终止时加载到进程中的二进制镜像。

Listing 13  摘录自应用crash报告的二进制镜像部分的入口

Binary Images:
0x100060000 - 0x100073fff TheElements arm64 <2defdbea0c873a52afa458cf14cd169e> /var/containers/Bundle/Application/888C1FA2-3666-4AE2-9E8E-62E2F787DEC1/TheElements.app/TheElements
...

每行包括一个单一的二进制镜像的以下细节:

  • 进程内的二进制镜像的地址空间。

  • 二进制名称或二进制的bundle identifier(仅MacOS)。在MacOS的崩溃报告,如果二进制是操作系统的一部分一个(+)会放在前面。

  • (仅MacOS)二进制的short version string 和 bundle version,用虚线隔开。

  • (仅iOS)二进制镜像的架构。一个二进制可能包含多个“slices”,一个用于每个它支持的每一个架构。这些slices中只有一个被加载到进程。

  • 一个唯一标识二进制镜像的UUID。每次编译二进制这个值都会变化,它被用来找出符号化crash报告时对应的dSYM文件。

  • 磁盘上的二进制路径。

了解低内存报告

当内存不足的情况被发现,在iOS系统的虚拟内存依赖于应用程序释放内存。低内存通知被发送到所有运行的应用程序和进程,作为一个释放内存的请求,希望减少内存使用量。如果内存压力仍然存在,系统可能会终止后台进程以减轻内存压力。如果有足够多的内存可以释放,您的应用程序将继续运行。如果没有的话,你的应用会被iOS终止因为没有足够的内存来满足应用的需求,然后低内存报告将被生成并存储在设备上。

格式化的低内存报告不同于其他崩溃报告,它没有应用程序线程的回溯。低内存报告开始于一个和crash类似的 Header。下面的头是一个系统范围内存统计的字段列表集合。注意Page Size字段的值。 在一个低内存报告中的每个进程的内存使用根据内存页面的数量被记录。

低内存报告中最重要的部分是进程表。此表列出低内存报告生成时所有正在运行的进程,包括系统守护进程。如果一个进程被“抛弃”,原因会在 [reason]列出。一个进程被被抛弃的原因有很多:

[per-process-limit]: 进程超出它的系统强加的内存限制。Per-process limits的由系统来为每个应用程序建立。超出这个限制,使进程适当的被终止。

Note: 扩展有低得多的每个进程内存限制。某些技术,如地图视图和SpriteKit,携带一个高底线的内存花销,可能不适用于扩展。

[vm-pageshortage]/[vm-thrashing]/[vm]: 由于内存压力,这个进程被杀死了。

[vnode-limit]: 打开的文件太多。

Note: 系统在vnodes几乎耗尽的时候避免杀死前台的应用。这意味着你的应用程序,当运行在后台时,可能被终止,即使它不是超额使用vnode的根源。

[highwater]: 一个系统守护进程越过它的内存使用高水位。

[jettisoned]: 这个进程因为一些其他原因被抛弃。

如果你没有看到一个理由列出在你的应用程序/扩展进程旁边,那么崩溃的原因不是内存压力。查看.crash文件(在前面的章节中描述)以获取更多的信息。

当你看到一个低内存崩溃,而不是关注终止时你的代码的执行在哪一部分,你应该调查你的内存使用模式和对低内存警告的响应。Locating Memory Issues in Your App列出了如何使用Leaks Instrument来发现内存泄漏的详细步骤,以及如何使用Allocations Instrument的 Mark Heap功能来避免被遗弃的内存。Memory Usage Performance Guidelines讨论了适当的方法来响应低内存通知以及有效利用内存的许多技巧。建议您查看的WWDC 2010部分, Advanced Memory Analysis with Instruments.

Important: Leaks和Allocations工具不会跟踪所有内存使用情况。您需要运行您的应用程序使用 VM Tracker工具(包含在 Instruments Allocations模板)来查看您的总内存使用。 VM Tracker默认情况下是禁用的。使用VM Tracker来profile你的应用程序,单击instrument,手动勾选“Automatic Snapshotting”标志或按“Snapshot Now”按钮。

相关文档

有关如何使用Instruments Zombies模板解决内存过度释放的crash ,参考Eradicating Zombies with the Zombies Trace Template.

有关应用程序归档的更多信息,参阅App Distribution Guide

关于解析崩溃日志的更多信息,请参阅 Understanding Crash Reports on iPhone OS WWDC 2010 Session


文档修订历史


Date Notes
2016-09-02

为iOS 10更新。扩展了符号化崩溃报告的讨论。

2015-07-21

为Xcode 6更新. 扩展了 crash 报告的讨论.

2012-12-13

添加了关于exception codes更多的信息

2012-03-28

添加了关于低内存crash报告和更多exception codes的信息. 为 Xcode 4更新.

2011-03-01

更新以反映iOS 4及以后的变化.

2010-07-06

修复文档的bug

2010-05-18

更新以反映iPhone OS 3.2 SDK 和 Xcode 3.2.2的变化.

2009-06-01

添加关于不仅需要保存.dSYM文件,而且需要保存应用程序二进制的强调重点。

2009-04-30

更新iTunes Connect崩溃日志服务。

2009-02-18

更新用于引入一个工作区的问题以防止应用程序代码被符号化。

2009-01-29

新文档向开发者表述如何符号化的重要信息,理解和解释的崩溃报告


猜你喜欢

转载自blog.csdn.net/junjun150013652/article/details/53338830
今日推荐