美文网首页APP & program
iOS 友盟崩溃日志分析——Exception informat

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

作者: anny_4243 | 来源:发表于2022-12-06 17:05 被阅读0次

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

    友盟崩溃日志原文:

    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复选框,如图所示:

    僵尸在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:引发崩溃的线程

    参考链接:
    https://www.meiwen.com.cn/subject/xboesttx.html

    https://wenku.baidu.com/view/21c92e69757f5acfa1c7aa00b52acfc788eb9f57.htmlwkts=1669946129880&bdQuery=ios+%E5%88%87%E6%8D%A2%E7%BD%91%E7%BB%9C%E5%B4%A9%E6%BA%83

    https://www.jianshu.com/p/6b950867cc00

    https://developer.apple.com/documentation/xcode/examining-the-fields-in-a-crash-report?language=objc#Diagnostic-messages

    https://developer.apple.com/documentation/xcode/understanding-the-exception-types-in-a-crash-report?language=objc

    相关文章

      网友评论

        本文标题:iOS 友盟崩溃日志分析——Exception informat

        本文链接:https://www.haomeiwen.com/subject/mrnbfdtx.html