当一个应用程序崩溃, 会产生一个崩溃报告(crash report) ,并存储到那个设备。崩溃报告描述了应用程序崩溃的条件,大多数情况会包含每个执行线程的一个完整的回溯,对我们调试应用程序的问题是非常有用的。你应该查看崩溃报告来了解你应用程序有什么崩溃,然后尝试修复它们。
崩溃报告必须被符号化(symbolicated),才能够被分析。符号化就是将内存地址替换成人能够阅读的函数名和代码所处的行数(即第几行)。如果你通过Xcode的Devices窗口,从一个设备中获取崩溃日志,几秒钟后会自动为你完成符号化。否则,你需要将.crash文件导入到Xcode的Devices窗口,来将它符号化。
低内存报告(Low Memory report)和崩溃报告不一样,这类型的报告没有回溯。当一个低内存报告发生时,你必须检查你的内存使用模式以及对低内存警告的应对方法。这篇文章将提供几种内存管理参考,你可能会有用。
Debugging Deployed iOS Apps讨论怎样直接从设备获取崩溃和低内存报告。
App Distribution Guide中的Analyzing Crash Reports会讨论如何查看从TestFlight beta测试者以及从App Store下载你App的用户中收集的全部崩溃报告。
符号化是把回溯地址(即符号)还原成源码方法或者函数名的过程。不先把崩溃报告进行符号化,是很难判断崩溃发生在哪里。
Note:低内存报告不需要进行符号化。
Note:来至macOS的崩溃报告通常在它们产生的时候就已经符号化的,或者部分符号化的。这部分集中讨论来之iOS,watchOS和tvOS,但是总体过程和macOS相似。
Figure 1:崩溃的报告以及符号化过程的总览。
Debug Symbol file和应用程序的二进制文件是通过预构建基础(per-build-basis)的构建UUID来绑定的。每次构建应用程序都会生成一个新的UUID,来唯一确定这次构建。甚至使用相同的编译器设定,对同样的源代码,来进行功能上一模一样的执行重新构建,也会有不同的构建UUID。来自本构建的Debug Symbol文件,尽管是相同的源文件,也不会和其他构建的二进制文件互通。
Important:要符号化来之测试者,app审核以及用户的崩溃报告,你必须保存每次你发布应用程序的archive。
Important:来自App Review的崩溃报告会没法符号化,尽管当你上传到iTunes Connect的archive包含了dSYM文件。你需要通过Xcode来符号化来自App Review的崩溃报告。
当你的应用程序崩溃,一个未符号化的崩溃报告会产生并存储到设备上。
通过 Debugging Deployed iOS Apps
步骤,用户可以直接从他们的设备获取的崩溃报告。如果你是通过AdHoc发布你的应用程序,或者是企业发布(Enterprise distribution),这是唯一从你的用户获取崩溃报告的方法。
从一个设备获取的崩溃报告是没有被符号化的,需要使用Xcode来进行符号化。Xcode使用应用程序二进制文件关联的dSYM文件,把回溯中的每个地址替换为你源代码原来的位置。替换的结果就是符号化的崩溃报告。
如果用户选择分享诊断数据给Apple,或者如果用户通过TestFlight安装了你应用程序的一个beta版本,崩溃报告会被上传到App Store。
App store符号化那些崩溃报告,把相似的崩溃报告分组。这些全部相似崩溃报告,被称为崩溃点(Crash Point)。
已经符号化的崩溃报告可以在Xcode的Crashes organizer中找到和使用。
Bitcode 是代表一个已编译的程序的中间媒介。当Bitcode开启的时候,你archive一个应用程序,编译器产生包含bitcode二进制文件而不是机器码。一旦二进制已经上传到App Store,bitcode会被编译成机器码。App Store 可能在以后再次编译这个bitcode,用于未来编译器的提升,你这边不需要做任何事。
Figure 2:Bitcode编译过程。
因为你的二进制的最后编译是由App Store完成的,你的Mac不会包含dSYM文件,来符号化来自App Store崩溃报告或者用户从它们设备获取发送给你的崩溃报告。尽管当你archive你的App,会产生dSYM文件,但它是属于bitcode二进制文件的,不能用于符号化崩溃报告。App Store在进行bitcode编译会产程dSYM文件,你可以通过Xcode或者iTunes connect网页进行下载。你必须下载这些dSYM文件,才能符号化来自App Store崩溃报告或者用户从它们设备获取发送给你的崩溃报告。从崩溃报告服务获取的崩溃报告会被自动符号化。
Important: App Store编译的二进制文件的UUID会和你原来提交的二进制文件的UUID不同。
当你上传你的bitcode App到App Store,你可以在提交对话框里,不勾选选择"Upload your app‘s symbols to receive symbolicated reports from Apple",从而不发送应用程序的symbols。如果你选择不发送的App的符号信息给Apple,在发送你的App到iTunes Connect之前,Xcode会用一个混淆的symbols,如 "__hidden#109_"来代替你App的symbols。Xcode会创建原始symbols和"hidden" symbols的映射关系并存储这个映射关系到一个. bcsymbolmap文件到这个应用程序的archive中,每一个.dSYM文件都有对应的一个.bcsymbolmap文件。
在符号化崩溃报告之前,你需要反向混淆(还原)这个从iTunes Connect下载的文件。如果你使用Xcode来下载dSYM文件,会为你自动进行反向混淆。然而,如果你使用iTunes Connect网址下载的.dSYM文件,打开终端并使用下面的命令行来反向混淆你的symbols(使用你自己的archive 和从iTunes connect下载的dSYMs文件夹来代替下面范例)。
xcrun dsymutil -symbol-map ~/Library/Developer/Xcode/Archives/2017-11-23/MyGreatApp\ 11-23-17\,\ 12.00\ PM.xcarchive/BCSymbolMaps ~/Downloads/dSYMs/3B15C133-88AA-35B0-B8BA-84AF76826CE0.dSYM
你下载的dSYMs文件夹中的每一个. dSYM文件需要运行这个命令。
一个崩溃报告可能未被符号化,完全符号化,或者部分符号化。未被符号化的崩溃报告在回溯中不会包含方法或者函数名。取而代之的是已加载二进制镜像的可执行代码的16进制地址。在一个完全符号化的崩溃报告,回溯的每一行16进制地址都会被对应的符号代替。在部分符号化的崩溃报告,只有部分地址被对应的符号代替。
显然,你应该试着完全符号化崩溃报告,以便更深入地分析崩溃。一个部分符号化的崩溃报告,也许能包含足够信息来明白崩溃,这取决崩溃的类型以及哪部分回溯被成功符号化。一个未被符号化的崩溃报告很少被使用。
Figure 3: 同样的回溯在不同程度的符号化。
Xcode 会自动尝试符号化它遇到的所有崩溃报告。你只需要将崩溃报告添加到Xcode Organizer。
Note: Xcode不会接收不是以.crash为扩展名的崩溃报告。如果你接收到一个崩溃报告没有扩展名或者扩展名是.txt的,在进行以下步骤之前请将它重命名.crash扩展名。
要符号化一个崩溃报告,Xcode必须能找到以下信息:
atos命令转换数字地址到它们等价的符号。如果调试符号信息能够使用,atos的输出包含文件名和源码的行数信息。atos命令能够符号化未符号化或部分符号化的崩溃报告回溯中的单个地址。使用atos符号化崩溃报告的一部分:
atos -arch <Binary Architecture> -o <Path to dSYM file>/Contents/Resources/DWARF/<binary image name> -l <load address> <address to symbolicate>
Listing 1:按照上面步骤使用atos命令,以及输出的结果。
$ atos -arch arm64 -o TheElements.app.dSYM/Contents/Resources/DWARF/TheElements -l 0x1000e4000 0x00000001000effdc
-[AtomicElementViewController myTransitionDidStop:finished:context:]
如果Xcode不能完全符号崩溃报告,很可能是因为你的Mac没有应用程序二进制对应的aSYM文件,应用程序关联的Framework的对应的dSYM文件,或者应用程序运行崩溃所在的设备系统的symbols。下面的步骤展示怎样使用Spotlight来确定用于符号化回溯对应二进制镜像的dSYM文件是否存在在你的Mac上。
Figure 5:找到二进制镜像的UUID。
$ grep --after-context=1000 "Binary Images:" <Path to Crash Report> | grep <Binary Name>
将二进制镜像的UUID转化成一个被分割成8-4-4-4-12(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)组的32字节长字符串。注意所有字母必须是大写的。
使用mdfind命令行工具和"com_apple_xcode_dsym_uuids == <UUID>"(包含引号)查询来UUID。
Listing 3使用mdfind命令行工具来查找UUID对应的dSYM文件。
$ mdfind "com_apple_xcode_dsym_uuids == <UUID>"
如果Spotlight找到二进制对应的dSYM文件,但是Xcode根据那二进制镜像还是不能符号化地址,你应该提交一个bug。将崩溃报告和相关的dSYM文件附带至bug报告中。一个变通的办法,安装上面的介绍的方法使用atos手动符号化地址。
如果Spotlight没有找到二进制对应的dSYM文件,确定你有导致崩溃应用程序版本的Xcode archive,而这个archive处于Spotlight可以找到的地方(应该在你的home目录中)。如果你的应用程序开启bitcode,请确保你已经从App Store下载了最后编译的dSYM文件。
如果你认为你有正确对应二进制文件的dSYM文件,你可以使用dwarfdump命令进行打印匹配的UUIDs。你也可以使用dwarfdump命令来打印二进制文件的UUIDs。
xcrun dwarfdump --uuid <Path to dSYM file>
Note:你必须有提交到崩溃的App Store版本的应用程序的archive。dSYM文件和应用程序的二进制是在预构建基础上绑定在一起的。尽管使用相同的源码和构建配置进行新的archive,也不会产生一个和这个崩溃build互通的dSYM文件。
如果你已经没有这个archive了,你应该提交一个保存了archive的新版本的应用程序,然后你就能够符号化这个新版本的崩溃。
这部分将讨论一个标准崩溃报告的每一部分。
每个崩溃报告都从头部开始
Listing 4:一个崩溃报告的头部
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
大部分都自带解释,但有一些值得特别说明一下:
CrashReporter Key
。CrashReporter Key
。CFBundleExecutable
键的值。CFBundleVersion
和CFBundleVersionString
串联。不要被Objective-C/C++的异常迷惑了(尽管其中一个可能导致崩溃),这部分列出提供崩溃本质的信息的匹配异常类型(Exception Type)和相关字段。 不是所有字段都会出现在每一次崩溃。
Listing 5: 因为未知Objective-C异常而进程被终止产生的崩溃报告,其中异常代码的摘录。
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 0
Listing 6:因为取一个空指针的值(dereferenced a NULL pointer)而进程被终止产生的崩溃报告,其中异常代码的摘录。
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 Subtype
字段包含一个kern_return_t
值来描述错误和不正确访问的内存的地址。
以下是调试错误访问内存访问崩溃的提示:
objc_msgSend
或者objc_release
是在崩溃线程的回溯顶部附近,进程可能是尝试对一个已销毁的对象发送消息。你应该用Zombies instrumentgpus_ReturnNotPermittedKillClient
是在崩溃线程的回溯顶部附近,进程被杀是因为它尝试在后台使用OpenGL ES或者Metal来绘制。参考:QA1766: How to fix OpenGL ES application crashes when moving to the background。进程异常退出了。通常导致这种异常类型崩溃的绝大多数原因是未捕获的Objective-C/C++的异常,调用了abort()
。
App Extensions如果花费太多时间初始化,就会因为这种异常类型而被终止(一种看门狗的终止)。如果一个extension因为启动时挂起被杀死,产生的崩溃报告的Exception Subtype
字段值会是LAUNCH_HANG
。因为extensions没有main
函数,初始化的任何时间都发生在你的extension及其依赖库中的静态构造函数和+load
方法中。你应该尽可能地推迟这部分工作。
和异常退出类似,此异常旨在使附加调试器有机会在执行过程中的某个特定点中断进程。你可以在你的代码中使用__builtin_trap()
函数来触发这种异常。如果没有附加调试器,进程会被终止并产生一个崩溃报告。
低级别的库(例如libdispatch
)遇上一个致命错误的会捕获进程。关于错误的额外信息能够在崩溃报告的Additional Diagnostic Information(额外诊断信息,下文有介绍)部分或者设备的控制台中找到。
SWIFT代码如果在运行时遇到意外情况,将以该异常类型终止,如:
nil
值进程尝试去执行一个非法或者未定义的指令。进程由于错误配置的函数指针跳到一个无效地址。
在英特尔处理器,ud2操作码会导致EXC_BAD_INSTRUCTION
异常,但它通常用于捕捉进程的调试目的。在英特尔处理器中,Swift代码如果在运行时一个意外条件触发了,会以这种异常类型终止。细节请参看陷阱追踪。
该进程在另一个具有管理其生命周期的进程的请求下被终止。SIGQUIT
不意味着这个进程崩溃,但可能确实是被检测到行为不端。
在iOS,键盘extensions如果加载时间太长,会被主App停止。这样崩溃报告中的回溯很可能不会指向对应的代码。最有可能的是,在extension的启动路径上的一些其他代码花费很多时间才完成,但是在时间限制内完成了,而当extension退出时,代码运行到回溯显示的部分。你应该extension以便更好的了解启动的过程哪儿出现大部分的工作量,使用后台线程来执行这些工作量,或者推迟执行工作量(待extension已经加载后)。
进程因为系统请求被终止。查看Termination Reason
字段以便更好了解终止的原因。
Termination Reason
字段会包含一个命名空间,后面跟着一段代码。以下代码是基于watchOS的:
终止代码0xc51bad01
表示由于在执行后台任务时使用了太多的CPU时间,所以一个watch app被终止。为了解决这个问题,优化执行后台任务的代码以获得更高的CPU效率,或者减少App在后台运行的工作量。
终止代码0xc51bad02
表示由于在分配的时间内未能完成后台任务而终止了手表应用程序。要解决此问题,请减少在后台运行时应用程序执行的工作量。
终止代码0xc51bad03
表示一个watch app在分配的时间内未能完成后台任务,并且整体系统足够繁忙,以至于App可能没有接收足够CPU时间到执行后台任务。尽管App可以通过减少在后台任务中执行的工作量来避免此问题,但是0xc51bad03并不表示应用程序出错了。更可能的是,由于整体系统负载,App无法完成其工作。
进程违反了资源守护策略。系统的库会标记指定的文件描述符为guarded(被守护的),之后对这些描述符的正常操作会触发一个EXC_GUARD
异常(当它想要对这些文件描述符进行操作时,系统使用特殊的“守护”私有API)。这可以帮助您快速追踪问题,如关闭由系统库打开的文件描述符。例如,如果一个应用程序关闭了用于访问支持Core Data存储的SQLite文件的文件描述符,那么Core Data将在后面莫名其妙地崩溃。guard异常更快地注意到这些问题,从而使它们更易于调试。
来自较新版本的iOS的崩溃报告在 Exception Subtype
和Exception Message
字段中包含了导致EXC_GUARD
异常的操作的人为可读的细节, 引起EXC_GUARD
异常的操作的人类可读的细节。在来自macOS或较旧版本的iOS的崩溃报告中,该信息被编码为第一个异常代码,作为一个位字段,如下所示:
0x2
表示资源是一个文件描述符。F_DUPFD
或F_DUPFD_CLOEXEC
命令调用dup()
,dup2()
或fcntl()
。该进程超出了资源消耗限制。这是来自操作系统的通知,该进程正在使用太多的资源。具体的资源列在Exception Subtype
字段中。如果 Exception Note
字段包含NON-FATAL CONDITION
,那么即使生成崩溃报告,该过程也不会被终止。
异常子类型MEMORY
表示该进程已超过系统强加的内存限制。这可能是终止过量内存使用的先兆。
异常子类型WAKEUPS
表示进程中的线程每秒被唤醒的次数太多,这迫使CPU醒来的频率很高,并消耗电池寿命。
通常,这是由线程间通信(通常使用peformSelector:onThread:
或dispatch_async
)导致的,这些通信不知不觉地发生得比正常情况频繁得多。因为触发这种异常的通信发生得非常频繁,所以通常会有多个具有非常相似的回溯的后台线程 - 指示通信起源的位置。
某些崩溃报告可能包含一个未命名的异常类型,将以十六进制值打印(例如00000020
)。如果您收到一个这种崩溃报告,请直接查看Exception Codes
字段以获取更多信息。
0xbaaaaaad
表示日志是整个系统的堆栈快照,而不是崩溃报告。要拍摄堆栈快照,请同时按侧面按钮和两个音量按钮。这些日志通常是由用户意外创建的,并不表示一个错误。0xbad22222
表示一个VoIP应用程序已被iOS终止,因为它恢复得太频繁。0x8badf00d
表示应用程序因为看门狗超时已被iOS终止。应用程序启动,终止或响应系统事件花了太长时间。一个常见的原因是在主线程上进行同步网络操作。无论是什么操作,线程0都需要移动到后台线程,或者以不同的方式进行处理,这样就不会阻塞主线程。0xc00010ff
表示该App因响应热事件(如CPU温度过高)而被操作系统杀死。这可能是由于发生此次崩溃的特定设备或其所在的环境所引起的问题。有关使你的App更高效运行的提示,请参阅iOS Performance and Power Optimization with Instruments WWDC session。0xdead10cc
表示应用程序已被操作系统终止,因为它在挂起期间持有文件锁或sqlite数据库锁。如果您的应用程序在挂起时对锁定了的文件或sqlite数据库执行操作,则必须请求额外的后台执行时间才能完成这些操作,并在挂起之前放弃锁定。0x2bad45ec
表示应用程序由于安全违规而被iOS终止。终止描述“进程被检测到在安全模式下进行不安全的绘图”表示App尝试在不允许的情况下绘制到屏幕上,例如屏幕锁定的时候。用户可能不会注意到这个终止,因为屏幕关闭或终止时显示的是锁定屏幕。Note: 使用App切换器终止一个挂起的程序不会产生崩溃报告。一旦程序挂起了,它随时有可能被iOS终止,所有不会产生崩溃报告。
这部分包含特定于终止类型的额外诊断信息,其中可能包括:
应用程序的特定信息:在进程终止之前捕获的框架错误消息
内核消息:有关代码签名问题的详细信息
Dyld错误消息:由动态链接器发出的错误消息
dyld 是Apple 的动态链接器;在 xnu 内核为程序启动做好准备后,就会将 PC 控制权交给 dyld 负责剩下的工作 (dyld 是运行在 用户态的, 这里由 内核态 切到了用户态)。
从macOS Sierra,iOS 10,watchOS 3和tvOS 10开始,大部分信息现在都在异常信息下的Termination Reason
字段中报告。
您应该阅读本节以更好地了解进程终止的情况。
Listing 7:一个进程因为一个框架的链接无法找到而被终止的崩溃报告的应用程序特定信息部分的摘录。
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: 一个进程因为无法快速加载其初始视图控制器而被终止的崩溃报告的应用程序特定信息部分的摘录。
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)来profile您的应用程序,以消除不正确的内存管理的可能性。
如果一个异常没有被捕获,则被一个称为未捕获的异常处理函数(uncaught exception handler)的函数截获。默认的未捕获异常处理程序将异常消息记录到设备的控制台,然后终止该进程。只有异常回溯写入到生成的崩溃报告中的Last Exception Backtrace部分下,如Listing 10所示。崩溃报告中省略了异常消息。如果您收到带有Last Exception Backtrace的崩溃报告,则应从原始设备获取控制台日志,以便更好地了解导致该异常的条件。
Listing 10:一个未符号化的崩溃报告的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部分只包含十六进制地址,必须符号化以产生可用的回溯,如Listing 11所示。
Listing 11:一个已符号化的崩溃报告的Last Exception Backtrace部分的摘录。 这是在加载storyboard一个scene时引发了此异常。 连接到scene中一个元素的对应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设备的崩溃报告中线程状态部分的摘录。
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:在崩溃报告中二进制镜像部分中,应用程序的入口摘录。
Binary Images:
0x100060000 - 0x100073fff TheElements arm64 <2defdbea0c873a52afa458cf14cd169e> /var/containers/Bundle/Application/888C1FA2-3666-4AE2-9E8E-62E2F787DEC1/TheElements.app/TheElements
...
了解低内存报告
当检测到低内存条件时,iOS中的虚拟内存系统依靠应用程序的协作来释放内存。低内存通知发送到所有正在运行的应用程序和进程作为一个释放内存的请求,希望减少内存的使用。如果内存压力仍然存在,系统可能会终止后台进程以减轻内存压力。如果有足够的内存被释放,你的应用程序将继续运行。否则,您的应用程序将被iOS终止,因为没有足够的内存来满足应用程序的需求,而低内存报告会被生成并存储在设备上。
低内存报告的格式不同于其他崩溃报告,因为没有应用程序线程的回溯。低内存报告与崩溃报告的头部(Header)相似。下面的头部是列出系统范围内存统计信息的字段集合。记下Page Size字段的值。低内存报告中每个进程的内存使用情况以内存页面数量的形式报告。
低内存报告中最重要的部分是进程表。此表列出了生成低内存报告时所有正在运行的进程,包括系统守护进程。如果一个进程被“抛弃”,原因将被列在 [reason] 栏中。一个进程可能会被抛弃的原因有很多:
Note:Extensions每个进程的内存限制要低得多。某些技术(如map views和SpriteKit)具有较高的基线内存消耗,可能不适合用于Extensions。
Note:当vnodes(虚拟节点,系统中数据分片的单位)快用完时,系统可以避免杀死最前面的应用程序。这意味着当您的应用程序处于后台,即使它不是使用多余的vnode的源头,也可能被终止。
当你看到一个内存不足的崩溃时,你应该调查你的内存使用模式和你对低内存警告的应对,而不是关心终止时正在执行什么代码。在应用程序中查找内存问题(Locating Memory Issues in Your App)列出了如何使用泄漏工具(Leaks Instrument)发现内存泄漏的详细步骤,以及如何使用分配工具的标记堆(Allocations Instrument‘s Mark Heap)功能来避免废弃的内存。 内存使用性能指南讨论了响应低内存通知的正确方法,以及有效使用内存的许多技巧。还建议您查看WWDC 2010 session,使用工具进行高级内存分析(Advanced Memory Analysis with Instruments)。
Important:Leaks(泄漏)和Allocations(分配)工具不会跟踪所有内存使用情况。您需要使用VM Tracker工具(包含在工具分配的模板中)运行您的应用程序以查看您的总内存使用情况。VM Tracker默认是禁用的。要使用VM跟踪器来分析您的应用程序,请单击该工具,检查“Automatic Snapshotting(自动快照)”标志或手动按下“Snapshot Now(快照)”按钮。
有关如何使用工具僵尸模板修复关于内存过度释放的崩溃的信息,请参阅Eradicating Zombies with the Zombies Trace Template。
有关应用程序archiving的更多信息,请参阅App Distribution Guide。
有关解析崩溃日志的更多信息,请参阅Understanding Crash Reports on iPhone OS WWDC 2010 Session。
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、转自
作者:MrLittleWhite
链接:https://www.jianshu.com/p/574a5a6d00c5
原文:https://www.cnblogs.com/8335IT/p/10385135.html