iOS 友盟崩溃日志分析——Exception information

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: iOS 友盟崩溃日志分析——Exception information

每个崩溃报告都包含异常信息。此信息部分告诉您流程是如何终止的,但它可能无法完全解释应用程序终止的原因。这些信息很重要,但往往被忽视。

友盟崩溃日志原文:

Exception Category: mach
Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x000065c21519ae70
Crashed Thread:  0


以下字段提供有关异常的信息:


Exception Category:异常分类


Mach是一个XNU的微内核核心,Mach异常是指最底层的内核级异常,被定义在下 。每一个thread,task,host都有一个异常端口数组,Mach的部分API暴露给了用户态,用户态的开发者能够直接经过Mach API设置thread,task,host的异常端口,来捕获Mach异常,抓取Crash事件。

全部Mach异常都在host层被ux_exception转换为相应的Unix信号,并经过threadsignal将信号投递到出错的线程。iOS中的 POSIX API就是经过Mach之上的 BSD层实现的。


Exception Type:异常类型


终止进程的Mach异常的名称,以及括号中相应BSD终止信号的名称。崩溃报告中的异常类型描述应用程序如何终止。这是指导如何调查问题根源的关键信息。

下面总结了异常类型:


1)EXC_BAD_ACCESS

此类型的Excpetion是我们最常碰到的Crash,通常用于访问了不该访问的内存导致。野指针引起的崩溃,访问了一个已经释放的内存而导致,向已经释放的对象或向它发送消息时,EXC_BAD_ACCESS就会出现。造成EXC_BAD_ACCESS最常见的原因是,在初始化方法中初始化变量时用错了所有权修饰符,这会导致对象过早地被释放。举个例子,在viewDidLoad方法中为UIViewController创建了一个包含元素的NSArray,却将该数组的所有权修饰符设成了assign而不是strong。现在在viewWillAppear中,若要访问已经释放掉的对象时,就会得到名为EXC_BAD_ACCESS的崩溃。

这个崩溃发生时,查看崩溃日志,却往往得不到有用的栈信息。还好,有一个方法用来解决这个问题:NSZombieEnabled。

这是一个环境变量,用来调试与内存相关的问题,跟踪对象的释放过程。启用了NSZombieEnabled的话,它会用一个僵尸实现来去你的默认的dealloc实现,也就是在引用计数降到0时,该僵尸实现会将该对象转换成僵尸对象。僵尸对象的作用是在你向它发送消息时,它会显示一段日志并自动跳入调试器。

所以,当在应用中启用NSZombie而不是让应用直接崩溃时,一个错误的内存访问就会变成一条无法识别的消息发送给僵尸对象。僵尸对象会显示接收到的消息,然后跳入调试器,这样你就可以查看到底哪时出了问题。 可以在Xcode的scheme页面中设置NSZombieEnabled环境变量。点击Product-Edit Scheme打开该页面,然后勾选Enable Zombie Objects复选框,如图所示:


1684483148065.png


僵尸在RAC出现以前作用很大。但自从有了ARC,如果你在对象的所有权方面比较注意,那么通常不会碰到内存相关的崩溃。

一般EXC_BAD_ACCESS后面的"()"还会带有补充信息。


SIGSEGV:通常由于重复释放对象导致,这种类型在切换了ARC以后应该已经很少见到了。

段错误信息(SIGSEGV)是操作系统产生的一个更严重的问题。当硬件出现错误、访问不可读的内存地址或向受保护的内存地址写入数据时,就会发生这个错误。

硬件错误这一情况并不常见。当要读取保存在RAM中的数据,而该位置的RAM硬件有问题时,你会收到SIGSEGV。SIGSEGV更多是出现在后两种情况。默认情况下,代码页不允许进行写操作。当应用中的某个指针指向代码页并试图修改指向位置的值时,你会收到SIGSEGV。当要读取一个指针的值,而它被初始化成指向无效内存地址的垃圾值时,你也会收到SIGSEGV。

SIGSEGV错误调试起来更困难,而导致SIGSEGV的最常见原因是不正确的类型转换。要避免过度使用指针或尝试手动修改指针来读取私有数据结构。如果你那样做了,而在修改指针时没有注意内存对齐和填充问题,就会收到SIGSEGV。


SIGABRT:收到Abort信号退出,通常Foundation库中的容器为了保护状态正常会做一些检测,例如插入nil到数组中等会遇到此类错误。

SIGABRT代表SIGNAL ABORT(中止信号)。当操作系统发现不安全的情况时,它能够对这种情况进行更多的控制;必要的话,它能要求进程进行清理工作。在调试造成此信号的底层错误时,并没有什么妙招。Cocos2d或UIKit等框架通常会在特定的前提条件没有满足或一些糟糕的情况出现时调用C函数abort(由它来发送此信号)。当SIGABRT出现时,控制台通常会输出大量的信息,说明具体哪里出错了。由于它是可控制的崩溃,所以可以在LLDB控制台上键入bt命令打印出回溯信息。


SEGV:(Segmentation Violation),代表无效内存地址,比如空指针,未初始化指针,栈溢出等。


SIGBUS:总线错误,与 SIGSEGV 不同的是,SIGSEGV 访问的是无效地址,而 SIGBUS 访问的是有效地址,但总线访问异常(如地址对齐问题)。

总线错误信号(SIGBUG)代表无效内存访问,即访问的内存是一个无效的内存地址。也就是说,那个地址指向的位置根本不是物理内存地址(它可能是某个硬件芯片的地址)。


SIGILL:尝试执行非法的指令,可能不被识别或者没有权限

SIGILL代表signal illegal instruction(非法指令信号)。当在处理器上执行非法指令时,它就会发生。执行非法指令是指,将函数指针会给另外一个函数时,该函数指针由于某种原因是坏的,指向了一段已经释放的内存或是一个数据段。有时你收到的是EXC_BAD_INSTRUCTION而不是SIGILL,虽然它们是一回事,不过EXC_*等同于此信号不依赖体系结构。


2)EXC_BREAKPOINT(SIGTRAP)、EXC_BAD_INSTRUCTION(SIGILL):跟踪陷阱中断了该过程。

断点异常类型表示跟踪陷阱中断了进程。跟踪陷阱使附加的调试器有机会在进程执行的特定点中断进程。在ARM处理器上,这显示为EXC_BREAKPOINT(SIGTRAP)。在x86_64处理器上,显示为EXC_BAD_INSTRUCTION(SIGILL)。

Swift运行时对特定类型的不可恢复错误使用跟踪陷阱。一些较低级别的库(如Dispatch)会在遇到不可恢复的错误时捕获具有此异常的进程,并在崩溃报告的“附加诊断信息”部分中记录有关错误的附加信息。

如果您想在自己的代码中使用相同的技术处理不可恢复的错误,请调用__builtin_trap()函数。这允许系统生成带有线程回溯的崩溃报告,显示您如何到达不可恢复的错误。


EXC_BAD_INSTRUCTION


此类异常通常由于线程执行非法指令导致。

1.在代码中修改了storyboard与outlet的对应关系,但是storyboard没有更新时发生过此crash。

2.与第三方库中方法冲突时发生过此crash。

3.调用系统方法时传入了不恰当的指针类型。


3)EXC_ARITHMETIC

崩溃的线程执行了无效的算术运算,例如除以零或浮点错误。


4)EXC_CRASH (SIGABRT) 该进程终止,因为它收到了一个SIGABRT。

EXC_CRASH (SIGABRT)表示进程因为收到SIGABRT信号而终止。通常,发送此信号是因为进程中的函数调用了abort(),例如当应用程序遇到未捕获的 Objective-C 或 C++ 语言异常时。

如果没有Last Exception Backtrace指示语言异常触发了崩溃,请查看崩溃线程的回溯以确定进程中的代码是否调用了abort().

当应用程序扩展需要太多时间来初始化时,操作系统会向SIGABRT应用程序扩展进程发送一个SIGABRT。这些崩溃包括一个值为 的Exception Subtype字段。因为扩展没有主函数,所以初始化所花费的时间都发生在扩展和依赖库中的静态构造函数和load()方法中。


5)EXC_CRASH (SIGKILL)

表示操作系统终止了进程。崩溃报告包含“终止原因”字段,其中包含解释崩溃原因的代码。在以下示例中,该代码为0xdead10cc:

Exception Type:  EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Termination Reason: Namespace RUNNINGBOARD, Code 0xdead10cc


6)EXC_CRASH (SIGQUIT)

EXC_CRASH(SIGQUIT)表示在另一个具有管理其生存期权限的进程请求时终止的进程。SIGQUIT并不意味着进程崩溃,但它可能会以可检测的方式出现错误。

对于iOS和iPadOS键盘扩展,如果加载时间过长,主机应用程序会终止键盘扩展。尽管看门狗终端中的异常信息不同,但使用与寻址看门狗终端相同的技术调查EXC_CRASH(SIGQUIT)。


7)EXC_GUARD


EXC_GUARD表示进程违反了受保护的资源保护。尽管有多种类型的受保护的系统资源,但大多数受保护的资源崩溃都来自受保护的文件描述符,这些描述符在异常子类型字段中具有GUARD_TYPE_FD值。操作系统将文件描述符标记为受保护的,以便正常的文件描述符API无法修改它们。例如,如果一个应用程序关闭了用于访问支持核心数据存储的SQLite文件的文件描述符,那么核心数据可能会在很长一段时间后神秘地崩溃。保护文件描述会在这些问题发生时识别这些问题,使其更容易识别和解决。

异常消息字段包含特定违规:

  • CLOSE  进程试图对受保护的文件描述符调用close()。
  • DUP  进程试图在受保护的文件描述符上使用F_DUPFD或F_DUPFD_CLOEXEC命令调用dup()、dup2()或fcntl()。
  • NOCLOEXEC  进程试图从受保护的文件描述符中删除FD_CLOEXEC标志。
  • SOCKET_IPC  进程试图通过套接字发送受保护的文件描述符。
  • FILEPORT  该进程试图获得受保护文件描述符的Mach发送权限。
  • WRITE  进程试图写入受保护的文件描述符。

异常消息字段还标识进程试图修改的特定受保护文件描述符。要了解触发此异常的上下文,请查阅崩溃线程的回溯。


8)EXC_RESOURCE


EXC_RESOURCE是来自操作系统的进程超过资源消耗限制的通知。如果“异常注释”字段包含“非致命条件”,则即使操作系统生成了崩溃报告,进程也不会终止。异常消息字段描述特定时间间隔内消耗的资源量。

崩溃报告在Exception Subtype字段中列出了特定资源:

  • CPU and CPU_FATAL  进程中的线程在短时间内使用了过多的CPU。
  • MEMORY  进程超出了系统设置的内存限制。这可能是过量内存使用终止的前兆。
  • IO  该过程在短时间内导致过多的磁盘写入。
  • WAKEUPS  进程中的线程每秒唤醒次数太多,这会消耗电池寿命。线程到线程的通信API,如perform(_:on:with:waitUntilDone:)async(execute:)dispatch_async,在无意中调用的频率远远超过预期时,会导致这种情况。由于触发此异常的通信非常频繁,因此通常会有多个后台线程,这些线程具有非常相似的回溯,指示线程通信的起源。有关如何更有效地管理并发工作负载,请参阅现代化大中心调度使用。

Exception Codes:异常代码

有关异常的处理器特定信息编码为一个或多个64位十六进制数。

代码是以下值之一:

  • 0xbaaaaaad 此种类型的log意味着该Crash log并非一个真正的Crash,它仅仅只是包含了整个系统某一时刻的运行状态。通常可以通过同时按Home键和音量键,可能由于用户不小心触发。
  • 0xbad22222 当VOIP程序在后台太过频繁的激活时,系统可能会终止此类程序。
  • 0x8badf00d (发音为“ate bad food”)。操作系统的监视器终止了应用程序。程序启动或者恢复时间过长被watch dog终止。
  • 0xc00010ff (发音为“cool off”)。由于热事件,操作系统终止了应用程序。这可能是发生此崩溃的特定设备或其运行环境的问题。程序执行大量耗费CPU和GPU的运算,导致设备过热,触发系统过热保护被系统终止。
  • 0xdead10cc (发音为“dead lock”)。操作系统终止了应用程序,因为它在挂起期间持有文件锁或SQLite数据库锁。使用beginBackgroundTask(withName:expirationHandler:)在主线程上请求额外的后台执行时间。在开始写入文件之前发出此请求,以便在应用程序挂起之前完成这些操作并放弃锁定。在应用程序扩展中,使用 beginActivity(options:reason:)
    管理此工作。程序退到后台时还占用系统资源,如通讯录被系统终止。
  • 0xbaadca11(发音为“bad call”)。操作系统因未能响应PushKit通知报告CallKit调用而终止应用程序。
  • 0xdeadfa11 程序无响应用户强制关闭。
  • 0xbaddd15c(发音为“bad disc”)。操作系统终止了删除缓存的应用程序,试图回收磁盘空间。许多因素导致磁盘空间不足。您可以通过最小化向磁盘写入的内容并管理文件的整个生命周期来提供帮助。
  • 0xc51bad01  watchOS终止了应用程序,因为它在执行后台任务时占用了太多的CPU时间。优化执行后台任务的代码以提高CPU效率,或减少应用程序在后台运行时执行的工作量以解决此崩溃。
  • 0xc51bad02  watchOS终止了应用程序,因为它未能在分配的时间内完成后台任务。减少应用程序在后台运行时执行的工作量,以解决此崩溃。
  • 0xc51bad03  watchOS终止了应用程序,因为它未能在分配的时间内完成后台任务,但系统总体上非常繁忙,应用程序可能没有收到太多CPU时间来执行后台任务。虽然您可以通过减少应用程序在后台任务中执行的工作量来避免此问题,但0xc51bad03并不表示应用程序做错了什么。更可能的是,由于系统整体负载,该应用程序无法完成其工作。

更多开发中遇到的错误码可以参考链接https://en.wikipedia.org/wiki/Hexspeak

对于某些异常,还会附带一个关联的 处理器定制异常码(processor-specific Exception Code) 或者 异常子类型(Exception Subtype),用以包含更多问题相关信息。举例来说, “EXC_BAC_ACCESS” 类型异常可能有一行如“KERN_INVALID_ADDRESS at 0x80000010”作为“异常码”; “EXC_RESOURCE” 可能有一行"WAKEUPS"作为"异常子类别"。

Crashed Thread:引发崩溃的线程

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
27天前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
34 2
|
8天前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
|
15天前
|
安全 Linux Android开发
探索安卓与iOS的安全性差异:技术深度分析
本文深入探讨了安卓(Android)和iOS两个主流操作系统平台在安全性方面的不同之处。通过比较它们在架构设计、系统更新机制、应用程序生态和隐私保护策略等方面的差异,揭示了每个平台独特的安全优势及潜在风险。此外,文章还讨论了用户在使用这些设备时可以采取的一些最佳实践,以增强个人数据的安全。
|
15天前
|
缓存 监控 算法
分析慢日志文件来优化 PHP 脚本的性能
分析慢日志文件来优化 PHP 脚本的性能
08-06-06>pe_xscan 精简log分析代码 速度提升一倍
08-06-06>pe_xscan 精简log分析代码 速度提升一倍
|
1月前
|
IDE 开发工具 Android开发
安卓与iOS开发环境对比分析
本文将探讨安卓和iOS这两大移动操作系统在开发环境上的差异,从工具、语言、框架到生态系统等多个角度进行比较。我们将深入了解各自的优势和劣势,并尝试为开发者提供一些实用的建议,以帮助他们根据自己的需求选择最适合的开发平台。
29 1
|
2月前
|
存储 消息中间件 监控
Java日志详解:日志级别,优先级、配置文件、常见日志管理系统ELK、日志收集分析
Java日志详解:日志级别,优先级、配置文件、常见日志管理系统、日志收集分析。日志级别从小到大的关系(优先级从低到高): ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF 低级别的会输出高级别的信息,高级别的不会输出低级别的信息
|
2月前
|
算法 关系型数据库 程序员
第一周算法设计与分析:A : log2(N)
这篇文章介绍了解决算法问题"输入一个数N,输出log2N(向下取整)"的三种编程思路,包括使用对数函数和幂函数的转换方法,以及避免浮点数精度问题的整数逼近方法。
|
7天前
|
IDE Android开发 iOS开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。
|
16天前
|
开发框架 数据可视化 Java
iOS开发-SwiftUI简介
iOS开发-SwiftUI简介
下一篇
无影云桌面