Analyzing Crash Reports(分析崩溃日志)
下面是关于分析一个标准的崩溃日志报告。
Header (头部) 每一个崩溃日志都是有一个开头的 【崩溃日志摘要】
解析:
Incident Identifier: 报告的标示符,两个报告将永远不会有相同的标示符
CrashReporter Key: 一个匿名的每设备标识符。来自同一设备的两个报告包含相同的值。
Beta Identifier: 崩溃应用程序的设备和供应商组合的唯一标识符。来自同一供应商和同一设备的应用程序的两个报告将包含相同的值。这场是目前唯一在分布式应用程序的崩溃报告通过TestFlight产生,并取代当机记录器关键字段(CrashReporter Key)。
Process: 可执行的名字对于程序的崩溃。 是CFBundleExecutable 关键字段的匹配的值在应用信息的属性列表中。
Version: 进程崩溃的版本, 这个值是一列串的应用的CFBundleVersion 和CFBundleVersionString
Code Type: 进程崩溃的目标架构,,这个可能是下面中的一个: ARM-64, ARM, x86-64, or x86
Role: 终止的时候分配给进程的task_role .
OS Version: 操作系统的版本,包括建立的数字
**Exception Type : **这里就显示了异常的信息;
Exception Codes: 中央处理器指定信息关于错误异常的编码 进入一个或者多个64位16进制数据。 典型的,这个字段将不会显示因为崩溃的日志报告对异常代码进行语法分析显示他们为人可读的。
Exception Subtype: 人可读的异常的代码的名字
Exception Message: 额外的人可读的信息从异常中扩展。
Exception Note: 额外的信息没有指定位一个异常类型。如果这个区域包括模拟器(不是崩溃),然后进程不是崩溃,但是,将会被杀掉在请求的系统,典型的watchdog。
Termination Reason: (终止的原因) 进程终止的原因。关键系统的部分,包括内部和外部的进程,将终止进程在遇到问题的时候。(例如:一个坏代码的信号量,错误的依赖库,或者访问隐私敏感信息没有合适的权利)macOS Sierra, iOS 10, watchOS 3, and tvOS 10 这些系统采取了新的架构去记录这些错误,和崩溃日志的产生通过这些操作系统列表的错误信息在终端错误的原因区域。
Triggered by Thread: 引起异常的线程。##(这个很重要)##
Bad Memory Access [EXC_BAD_ACCESS // SIGSEGV // SIGBUS] (地址的访问问题)
进程尝试去访问没有效的内存或者它尝试去访问的内存方式是不允许因为内存在保护的等级。 **Exception Subtype **包括了kern_return_t 的错误描述和内存地址的错误访问。
这是调试bad memory access 的一些技巧
1) 如果objc_msgSend 或objc_release 靠近backtraces 的顶部崩溃线程,进程可能尝试发信息结束对象。如果你测试应用使用Zombies instrument 最好明白崩溃的控制环境。 (可能就是出现了僵尸进程等等,以及对象创建和释放出现了问题)
2)gpus_ReturnNotPermittedKillClient 靠近backtraces 的顶部崩溃线程 , 进程会被杀死因为它尝试去渲染用openGL ES 或者基础元素子背后。
See QA1766: How to fix OpenGL ES application crashes when moving to the background. 【也就是这个是图形图形渲染的问题了】
3)运行程序勾选上 Address Sanitizer , 地址净化添加额外的instrumentation 关于地址访问在你的编译代码。当你的应用运行的时候,xcode 将警告你如果内存被访问就会导致崩溃。
(这里就会设计到使用instrument等等的配置的调试了)
Abnormal Exit [EXC_CRASH // SIGABRT](进程异常退出)
造成这个问题的最主要原因是:没有捕获到的oc/c++ 异常 、 调用abort() 。
app 扩展被终止通过这个异常的类型如果他们使用太多的时间去初始化(a watchdog termination).如果一个扩展kill掉因为悬挂启动,**Exception Subtype **产生的类型是LAUNCH_HANG ,因为扩展没有main函数,任何时候使用初始化放生在静态构造器和 +load方法显示在你的扩展或者依赖库。你应该尽可能地推迟这项工作.。【也即是不要在分类中或者库中使用load这些舒适化的方法】
Trace Trap [EXC_BREAKPOINT // SIGTRAP] (追踪陷入)
类似的异常退出,这个异常是计划去给一个连接的调试器去终端进程在一个指定的异常。可以触发这个异常从你的代码中使用__builtin_trap() 方法。如果没有debugger 被连接,进程将会被终止和崩溃报告差产生。
低级库(e.g, libdispatch) 将会陷入进程当遇到致命的错误的时候。额外的信息包括关于错误被在Additional Diagnostic Information 中。额外的信息包括错误被找到再崩溃中或者设备终端。 (Additional Diagnostic Information将会在下面介绍)
swift代码可能会出现这个异常中断:
(1)不是可选类型的值是nil
(2)错误的强转类型
查看backtraces 去决定不期望的状况遇到了。额外的信息可能也有被日志到设备的console中。你应该移动代码在崩溃的位置去小心处理runtime的错误。例如:使用optional binding 替代强转没有解包的可选类型。
Illegal Instruction [EXC_BAD_INSTRUCTION // SIGILL] (非法指令)
进程尝试执行一个非法或者没有定义的指令。今晨更可能有尝试去跳转到一个无效的地址通过一个错误配置的函数指针。
一个intel 处理器,ud2 操作码造成一个EXC_BAD_INSTRUCTION 异常但是是普遍用于陷入到进程去调试的。swift代码在intel处理器将终止因为这个异常类型如果一个不期望状况在运行时遇到。看追踪陷入的细节。
Guarded Resource Violation [EXC_GUARD] (警告资源的违例)
一个继承违例了警告资源的保护。系统库可能标志了确定的文件描述为警告,当普通的操作在描述符上的时候,将会触发EXC_GUARD 异常(当它想要去操作这些文件描述符,系统使用指定的guarded 私有API)。这个帮助我们快速追溯到问题,例如关闭文件描述符关于文件被打开的系统库。例如,如果你的一个应用关闭了文件描述符用于访问SQLite文件而不是core data存储。core data 可能然后出现了神秘的崩溃。这个警告奔溃获得这些问题通知然后,这样就可以容易找到问题了。
崩溃日志来自于一个ios的新版本中包括人们的可读细节关于操作,将会造成EXC_GUARD 异常在Exception Subtype 和 Exception Message区域。奔溃日志来自**macOS 或者老板的iOS **,这个信息将会编入到第一个 **Exception Code **作为位区域,造成如下:
Guard Type : 警告资源类型,0x2的值表示资源是一个文件描述符
**Flavor **:违例的条件被触发
如果第一 (1 << 0)位被设置,进程试图去调用close() 在一个警告文件的描述符
如果第二(1 << 1) 位被设置,进程尝试调用dup(), dup2(), or fcntl() 用F_DUPFD 或 F_DUPFD_CLOEXEC 命令在一个警告文件描述符上;
如果第三 (1 << 2)位被设置,进程尝试去发送一个警告文件描述符通过socket。
如果第四 (1 << 4) 为被设置,进程尝试去写到一个警告的文件描述符。
文件描述符: 警告文件描述符是进程试图去移除。
Resource Limit [EXC_RESOURCE] (进程超出资源限制)
进程使用太多资源的时候就会发送通知,这个资源被精确地列出来到**Exception Subtype 区域。
如果Exception Note **(或message)区域包括NON-FATAL CONDITION 字段,虽然产生了了崩溃日志,但是进程并没有被kill掉。
(1)exception subtype 是MEMORY 表示为进程使用跨越了系统强加内存的限制。这个可能是终止进程的预兆。
(2)exception subtype 是 WAKEUPS 表示该线程被唤醒太多次(秒级),强制要求cpu频繁地去唤醒和消耗电磁寿命。
一般,线程交流(普通:使用peformSelector:onThread: 或 dispatch_asyn)无意发生更多。因为简短的线程交流会运行触发器发生异常,在backtraces中显示了后台多线程的原始交流。
Other Exception Types (其他异常类型)
一些崩溃报告可能包括没有命名的异常类型(Exception Type)(eg:00000020),如果你接收到这样的一个报告,直接看Exception Codes 区域可以获取到更多信息。
(1)异常代码0xbaaaaaad 显示了日志是一个堆栈快照在整个系统中,不是一个崩溃日志。一个堆栈快照,按下home按钮和其他按钮。将行这些日志可能偶尔发生是因为人而不是错误。
(2)异常代码0xbad22222 显示了一个VoIP 应用被中断是因为被继续太频繁。
(3)异常代码0x8badf00d 显示了一个应用被中断时因为watchdog 超时发生的。这个应用用太长时间去启动、终止或者响应系统事件。一个共同的原因曹成和这个是 synchronous networking on the main thread (网络同步在主线程上)。操作在线程0上的需要一到后台线程或者进程不同,所以,这样就没哟停止主线程。
(4)异常代码0xc00010ff 显示应用被杀掉因为操作系统响应了一个热的事件(应该是手机太热了?)。这个可能因为一个问题就是特殊的设备造成崩溃或者它的运行环境。为了让程序更加强健就看iOS Performance and Power Optimization with Instruments ,
(5)异常代码0xdead10cc 显示了被中断是以为它拥有了一个文件,文件被锁住了或者sqlite数据库被锁住了在暂停。如果你的应用显示在操作加锁的文件或者数据库在暂停的事假,它必须request additional background execution time 去完成这些操作和放弃锁在暂停之前。
(6)异常代码0xdeadfa11 显示了一种应用被用户强制退出。强制退出发生当用户第一拥有on/off 按钮知道"slide to power off" (关机界面)出现。然后按下holds或home按钮。它是明智去评估用户所做的行为因为应用变成了没有反应,但是没有保证。强制退出将会工作在任意应用中。
注意:终止一个暂停的应用通过移除多任务托盘不会产生崩溃日志。一旦应用悬挂,有权限终止的iOS在任何时间,所以不会产生崩溃报告。
watchdog (看门狗:监听系统空间等等的一个程序)
Linux 自带了一个 watchdog 的实现,用于监视系统的运行,包括一个内核 watchdog module 和一个用户空间的 watchdog 程序。[1] http://baike.baidu.com/link?url=yqm4oZn_GOk9Y6N7YVnDtb_M6sgLyarlt22yhdY3L6M7jhq7ushk8WwTPDTSAgCZ7h1taK49p4pXBfr4q6ZedVUOUfm48WXqPo-5VSYIKa3
Additional Diagnostic Information (额外的诊断信息)
这个段包括了额外的诊断信息指定改了终端的类型,包括:
Application Specific Information:库错误信息被捕获在进程终止之前
**Kernel Messages : **细节关于代码信号的问题
Dyld Error Messages: 哭的错误信息触发因为动态链接器
在macOS Sierra, iOS 10, watchOS 3, and tvOS 10 系统上开始,最多的信息现在被上报到终止信息区域在Exception Information 中,你应该阅读这个段最好是明白状况进程被终止的原因。
**Backtraces **(回溯)
崩溃报告最有兴趣的一部分是在进程终止的时间在每个进程的回溯山。这些回溯中的每个都是相同于你将会看到当使用debugger停止的时候。
最有兴趣的一部分关于崩溃报告是在回溯对于每一个进程的线程的时间它终止。
第一个行列出了线程数字和当前执行适配队列的标识。余下的行列出每一个堆栈帧在回溯的细节。从左到右:
(1) stack frame number(堆栈帧数字),显示了调用顺序,当帧0 是函数从执行停止,帧1是函数在帧0中被调用等等。
(2)二进制的名字执行函数对于堆栈帧里。
(3)从帧0 ,机器指令的地址被执行当执行停止。关于余留的堆栈帧,机器指令的地址将会下一个执行当控制返回堆栈帧。
(4)在符号化的崩溃报告中,方法名是对应着堆栈帧的函数名。
PS: 这里说明了调用的先后顺序,关于函数堆栈的内容。
**Exceptions **(异常)
异常在oc中是被用来来显示程序错误查明运行时例如造成数组越界,视图改变不可改变的对象,诶呦实现要求的接口方法或者发送信息接受者并没有识别(调用了没有的方法)
注意:信息在过去被释放对象将会提示一个NSInvalidArgumentException 异常替代了崩溃程序带有内存访问违例。这个造成了当一个新的对象被分配在一个内存已经被占用通过释放对象。如果你的应用崩溃因为没有NSInvalidArgumentException(look for -[NSObject(NSObject) doesNotRecognizeSelector:],考虑概述你的应用带有Zombies instrument 去消除内存管理不当的原因。
如果一个异常没有被捕获,它将会被函数拦截调用没有捕获的异常处理。默认的没有捕获异常处理打出异常信息到设备控制台然后结束程序。只有异常回溯被写到产生崩溃报告在 **Last Exception Backtrace 区域。例如下图:这个异常信息被删除从崩溃日志。如果你接受了一个崩溃报告带有Last Exception Backtrace **你应该获取到控制台日志从原始的设备到较好明白造成异常的状况。
注意:如果你找到一个异常抛出在异常处理内动态建立通过应用将不会被捕获,确认你没有指定-no_compact_unwindflag 当你建立你的应用或者二进制。
64位** iOS 系统用了“zero-cost” 异常实现。在“zero-cost”系统中,每个函数有额外的数据用于描述怎么解开堆栈如果一个异常抛出通过函数。如果一个异常被抛出通过一个栈帧没有解开数据然后异常处理不能够执行或者进程停止。这个可能是异常处理更多的堆栈,但是如果没有解开数据对于帧然后将没有方式获取到栈帧当一场抛出的时候。指定-no_compact_unwindflag 意味着不获取解开的表。所以你不能够抛出异常通过这些函数。
另外,如果你包括了c**代码在你的程序或者库中,你可能需要指定-funwind-tables 标示包括解开表对于所有的函数在代码中。
线程状态
这个段列出了崩溃线程的线程状态。这个注册列表和他们的值同时执行终止。明白线程装填不需要当读取一个崩溃报告但你可能需要使用这个信息去更好的明白崩溃的装填。
**Binary Images **(二进制映像)
这个段列出了二进制印象,它加载进程的终止时间。
每一行包括了下面鞋机对于一个单独的二进制映像
(1)二进制映像地址空间在进程内
(2)二进制名字或二进制的bundle标示符(只是macOS)。从macOS的崩溃日志报告中,一个(+)是有些爱你考虑的如果二进制是操作系统的一部分。
(3)只是macos系统,一个二进制的短版本字符串和bundle版本被一个空格隔开。
(4)(只是ios系统)二进制映像架构。一个二进制可能包括多个片,对于每一个架构的支持,只有这些片段中的一个被加载到进程。
(5)一个UUID是用来标识一个二进制映像。这些值改变以为每个二进制的创建和用于定位响应的dsym文件当符号化崩溃日志。
(6)二进制在硬盘上的路径。
**Understanding Low Memory Reports **(明白低内存报告)
当一个低内存状态被检测,虚拟内存系统在iOS 上以阿里应用的合作去分发内存。低内存通知被发送到所有运行应用和进程作为一个请求去释放内存,希望去减少内存使用而数量。如果内存压力还在,系统可能终止后台进程去减少内存压力。如果内存足够被释放,你的应用将继续运行。如果没有,你的应用将被终止通过ios因为不足够内存去满足应用的需求和低内存报告将被生成和存储在设备上。
低内存格式报告区别了其他的崩溃报告因为它没有backtraces对于应用线程。一个低内存报告开始在头部和崩溃报告类似。头接下来是系统狂赌和内存数据的收集区域。注意page size区域的值,在低内存报告中的每个进程的内存使用将会被报告以内存数字的方式。低内存最重要的一部分是进程表。这个表列出了所有运行进程。包括系统守护进程,在这个时候低内存报告生成。如果进程是"jettisoned”,原因将会被诶写到【reason】列。一个进程可能被丢弃因为很多原因。(很大可能是因为使用内存过多)
下面的几种情况:
如果没有看到原因被列在app/extension 进程中,奔溃的原因就不是内存压力。查看.crash 文件查看更多额原因。
当你看到低内存崩溃,而不是关注你的代码哪一部分被执行当中断的时候。你应该调查你的内存使用正则和你的响应到一个低内存警告。定位应用内存问题累出细节步骤怎么使用leaks instrument 去发先内存泄露和怎么使用allocations instruments 去避免内存遗弃。
Memory Usage Performance Guidelines(内存使用显示指导) 讨论了很是的方式去相应低内存通知和很多技巧有效地使用内存。它也推荐你价差2010会议Advanced Memory Analysis with Instruments 。
重点:Leaks and Allocations instruments 不会回溯所有的内存使用。你需要运行你的应用使用VM Tracker instrument(包括alloction例子)去查看IDE所有的内存使用。VM 默认是不可用。去概述你的应用用VM Tracker ,点击instrument ,检查 "Automatic Snapshotting" 标记或手动按下 "Snapshot Now”按钮。