写点什么

ReactNative 进阶(五十):IOS 系统 Crash 日志分析实战

  • 2022 年 2 月 03 日
  • 本文字数:5932 字

    阅读完需:约 19 分钟

ReactNative进阶(五十):IOS 系统 Crash 日志分析实战

一、前言

当应用程序在IOS 设备上崩溃(例如,闪退)时,一份“Crash 崩溃报告”将在该设备上创建并存储起来。崩溃报告描述了应用程序是在何种条件下崩溃的,大部分情况下包含一份当前正在运行线程的完整堆栈跟踪。


如果设备就在身边,可以连接设备,打开Xcode - Window - Organizer,在左侧面板中选择Device Logs(可以选择具体设备的Device Logs或者Library下所有设备的Device Logs),然后根据时间排序查看设备上的crash日志。这是开发、测试阶段最经常采用的方式。


如果应用程序已经提交到App Store发布,用户已经安装使用了,那么开发者可以 通过iTunes ConnectManage Your Applications - View Details - Crash Reports)获取用户的crash日志。不过这并不是 100%有效的,而且大多数开发者并不依赖于此,因为这需要用户设备同意上传相关信息。


产生崩溃日志的原因


  • 应用违反操作系统规则,包括在启动、恢复、挂起、退出时 watchdog 超时、用户强制退出和低内存终止等。

  • 应用中有Bug


从多任务窗口中终止一个暂停的应用程序不会产生崩溃日志。Apple官方认为一旦一个应用被暂停,它有资格被iOS在任何时间终止,因此不会产生崩溃日志。

二、Crash 崩溃报告分析实战

IOS设置-隐私-分析与改进-分析数据中查找当前日期的应用崩溃日志mrcs-2021-08-31-091354.ips,日志内容大致如下:


{"app_name":"mrcs","timestamp":"2021-08-31 09:13:54.00 +0800","app_version":"2.1.7","slice_uuid":"85fda4ca-6eae-3600-91c8-6fa83b827d7a","adam_id":0,"build_version":"9","platform":2,"bundleID":"com.*.*","share_with_app_devs":0,"is_first_party":0,"bug_type":"109","os_version":"iPhone OS 14.6 (18F72)","incident_id":"3ADDB9E3-657A-4ECC-B1B1-377A79F793BB","name":"mrcs"}Incident Identifier: 3ADDB9E3-657A-4ECC-B1B1-377A79F793BB  //崩溃报告的唯一标识符CrashReporter Key:   7643d5953a8b0562ded233540a11c1beabbb8b58 //设备标识相对应的唯一键值(并非真正的设备的UDID,为保护隐私iOS6以后已无法获取)Hardware Model:      iPhone10,3 //发生Crash的设备类型Process:             mrcs [8968] //Crash的进程名称,通常都是我们的App的名字, []里面是当时进程的IDPath:                /private/var/containers/Bundle/Application/A09D22A5-324F-4686-964B-51AE831927B7/mrcs.app/mrcs //可执行程序在手机上的存储位置,注意路径是到x.app/x,x.app其实是作为一个Bundle的,真正的可执行文件其实是Bundle里面的xIdentifier:          com.*.* //App的Indentifier,通常为“com.xxx.yyy”Version:             9 (2.1.7) //App的版本号,由Info.plist中Code Type:           ARM-64 (Native) //App的CPU架构Role:                ForegroundParent Process:      launchd [1] //当前进程的父进程,由于iOS中App通常都是单进程的,一般父进程都是launchdCoalition:           com.*.ccmsm [421]

Date/Time: 2021-08-31 09:13:53.8890 +0800 //Crash发生的时间Launch Time: 2021-08-31 09:13:18.1164 +0800 //系统登陆时间OS Version: iPhone OS 14.6 (18F72) //系统版本,括号内的数字代表的时Bulid号Release Type: UserBaseband Version: 6.71.01Report Version: 104
Exception Type: EXC_CRASH (SIGABRT) //异常类型Exception Codes: 0x0000000000000000, 0x0000000000000000Exception Note: EXC_CORPSE_NOTIFYTriggered by Thread: 21
Application Specific Information:abort() called...................................Thread 21 name: Dispatch queue: com.facebook.react.ShadowQueueThread 21 Crashed://编号 二进制库名 调用方法的地址 基本地址 + 偏移0 libsystem_kernel.dylib 0x00000001d73517b0 0x1d732a000 + 1617121 libsystem_pthread.dylib 0x00000001f39dc9c0 0x1f39d2000 + 434562 libsystem_c.dylib 0x00000001b4243a44 0x1b41d0000 + 4736683 mrcs 0x0000000102dd4e18 0x102c08000 + 18877684 mrcs 0x0000000102dd4d9c 0x102c08000 + 18876445 mrcs 0x0000000102ddd034 0x102c08000 + 19210766 mrcs 0x0000000102ddd0d0 0x102c08000 + 19212327 mrcs 0x0000000102ddd0d0 0x102c08000 + 19212328 mrcs 0x0000000102ddd0d0 0x102c08000 + 19212329 mrcs 0x0000000102ddd0d0 0x102c08000 + 192123210 mrcs 0x0000000102ddd0d0 0x102c08000 + 192123211 mrcs 0x0000000102dda324 0x102c08000 + 190954012 mrcs 0x0000000102dd8908 0x102c08000 + 190285613 mrcs 0x0000000102ddda4c 0x102c08000 + 192366014 mrcs 0x0000000102dd96e8 0x102c08000 + 190640815 mrcs 0x0000000102dd8908 0x102c08000 + 190285616 mrcs 0x0000000102ddda4c 0x102c08000 + 192366017 mrcs 0x0000000102dd96e8 0x102c08000 + 190640818 mrcs 0x0000000102dd8908 0x102c08000 + 190285619 mrcs 0x0000000102ddda4c 0x102c08000 + 192366020 mrcs 0x0000000102dd96e8 0x102c08000 + 190640821 mrcs 0x0000000102dd8908 0x102c08000 + 190285622 mrcs 0x0000000102dddf38 0x102c08000 + 192492023 mrcs 0x0000000102dd9e14 0x102c08000 + 190824424 mrcs 0x0000000102dd8908 0x102c08000 + 190285625 mrcs 0x0000000102ddb598 0x102c08000 + 191426426 mrcs 0x0000000102dd8908 0x102c08000 + 190285627 mrcs 0x0000000102dda940 0x102c08000 + 191110428 mrcs 0x0000000102dd8908 0x102c08000 + 190285629 mrcs 0x0000000102dda940 0x102c08000 + 191110430 mrcs 0x0000000102dd8908 0x102c08000 + 190285631 mrcs 0x0000000102ddb598 0x102c08000 + 191426432 mrcs 0x0000000102dd8908 0x102c08000 + 190285633 mrcs 0x0000000102dda940 0x102c08000 + 191110434 mrcs 0x0000000102dd8908 0x102c08000 + 190285635 mrcs 0x0000000102dda940 0x102c08000 + 191110436 mrcs 0x0000000102dd8908 0x102c08000 + 190285637 mrcs 0x0000000102dda940 0x102c08000 + 191110438 mrcs 0x0000000102dd8908 0x102c08000 + 190285639 mrcs 0x0000000102dda940 0x102c08000 + 191110440 mrcs 0x0000000102dd8908 0x102c08000 + 190285641 mrcs 0x0000000102ddb598 0x102c08000 + 191426442 mrcs 0x0000000102dd8908 0x102c08000 + 190285643 mrcs 0x0000000102dda940 0x102c08000 + 191110444 mrcs 0x0000000102dd8908 0x102c08000 + 190285645 mrcs 0x0000000102dda940 0x102c08000 + 191110446 mrcs 0x0000000102dd8908 0x102c08000 + 190285647 mrcs 0x0000000102dda940 0x102c08000 + 191110448 mrcs 0x0000000102dd8908 0x102c08000 + 190285649 mrcs 0x0000000102dda940 0x102c08000 + 191110450 mrcs 0x0000000102dd8908 0x102c08000 + 190285651 mrcs 0x0000000102dda940 0x102c08000 + 191110452 mrcs 0x0000000102dd8908 0x102c08000 + 190285653 mrcs 0x0000000102ddc09c 0x102c08000 + 191708454 mrcs 0x0000000102cfe104 0x102c08000 + 100787655 mrcs 0x0000000102cf2e10 0x102c08000 + 96206456 mrcs 0x0000000102d0b800 0x102c08000 + 106291257 mrcs 0x0000000102d0f860 0x102c08000 + 107939258 mrcs 0x0000000102cd0efc 0x102c08000 + 82303659 libdispatch.dylib 0x00000001ab2562b0 0x1ab1f6000 + 39390460 libdispatch.dylib 0x00000001ab257298 0x1ab1f6000 + 39797661 libdispatch.dylib 0x00000001ab233344 0x1ab1f6000 + 25069262 libdispatch.dylib 0x00000001ab233e2c 0x1ab1f6000 + 25348463 libdispatch.dylib 0x00000001ab23d66c 0x1ab1f6000 + 29246064 libsystem_pthread.dylib 0x00000001f39dd5bc 0x1f39d2000 + 4652465 libsystem_pthread.dylib 0x00000001f39e086c 0x1f39d2000 + 59500Thread 22:0 libsystem_pthread.dylib 0x00000001f39e0864 0x1f39d2000 + 59492//Crash时发生时刻,线程的状态(寄存器中的值)Thread 21 crashed with ARM Thread State (64-bit): x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x0000000000000000 x4: 0x000000010378c20c x5: 0x000000016dc98930 x6: 0x000000000000000a x7: 0x0000000102dd7248 x8: 0x000000016dc9f000 x9: 0xef6382e5d8fc5092 x10: 0x0000000000000002 x11: 0x00000000fffffffd x12: 0x0000010000000000 x13: 0x0000000000000000 x14: 0x0000000000000000 x15: 0x0000000000000000 x16: 0x0000000000000148 x17: 0x0000000000000002 x18: 0x0000000000000000 x19: 0x0000000000000006 x20: 0x0000000000006e4b x21: 0x000000016dc9f0e0 x22: 0x0000000000000000 x23: 0x0000000144f1ff80 x24: 0x0000000000000002 x25: 0x0000000000000000 x26: 0x0000000000000002 x27: 0x0000000000000000 x28: 0x0000000000000001 fp: 0x000000016dc98880 lr: 0x00000001f39dc9c0 sp: 0x000000016dc98860 pc: 0x00000001d73517b0 cpsr: 0x40000000 esr: 0x56000080 Address size fault//Crash时刻App加载的所有库,其中第一行是Crash发生时我们App可执行文件的信息,可以看出为arm64,可执行文件包的uuid为c0f……cd65,解析Crash的时候dsym文件的uuid必须和这个一样才能完成Crash的符号化解析。Binary Images:0x102c08000 - 0x103933fff mrcs arm64 <85fda4ca6eae360091c86fa83b827d7a> /var/containers/Bundle/Application/A09D22A5-324F-4686-964B-51AE831927B7/mrcs.app/mrcs0x103d60000 - 0x103dcbfff dyld arm64 <c5d2aaed4aeb3e18a5d2c8b681a2d6eb> /usr/lib/dyld.....................EOF
复制代码


由以上崩溃报告内容可知,Thread * 表示发生Crash的线程调用栈,从上到下分别代表调用顺序,最上面的一个表示抛出异常的位置,依次往下可以看到API的调用顺序。重点由以下内容定位 Crash 异常类型、错误码、导致异常发生的线程。


Exception Type:  EXC_CRASH (SIGABRT) //异常类型Exception Codes: 0x0000000000000000, 0x0000000000000000 //异常类型代码Exception Note:  EXC_CORPSE_NOTIFYTriggered by Thread:  21
复制代码


有以上可知,线程 21 导致应用崩溃问题产生。


注意⚠️:包含堆栈跟踪的崩溃报告需要先进行符号化(symbolicated)才可以进行分析。符号化的过程是将内存地址替换为便于阅读的函数名称和行号。如果通过XcodeOrganizer窗口获取崩溃日志,那么该报告将在几秒钟后自动进行符号化。否则需要将.crash文件导入到XcodeOrganizer进行符号化。

三、常见 Exception Type

  • EXC_BAD_ACCESS 通常由于访问了不该访问的内存导致。一般EXC_BAD_ACCESS后面的"()"还会带有补充信息。

  • SIGSEGV: 通常由于重复释放对象导致,这种类型在切换ARC之后已经很少见到。当我们收到 SIGSEGV 信号时,可以往以下几个方面考虑:

  1. 访问无效内存地址,比如访问 Zombie 对象;

  2. 尝试往只读区域写数据;

  3. 引用空指针;

  4. 使用未初始化的指针;

  5. 栈溢出;

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

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

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

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

  • EXC_BAD_INSTRUCTION,此类异常通常由于线程执行非法指令导致;

  • EXC_ARITHMETIC,除零错误会抛出此类异常;

  • SIGFPE:Floating Point Error,数学计算相关问题(可能不限于浮点计算),比如除零操作;

  • SIGPIPE:管道另一端没有进程接收数据;


由以上分析内容可知,异常代码是SIGABRT。通常, SIGABRT 异常是由于某个对象接收到未实现的消息引起的。 或者,简单来说,在某个对象上调用了不存在的方法。


这种情况一般不会发生,因为 A 对象调用了 B 方法,如果 B 方法不存在,编译器会报错。但是,如果你是使用selector间接调用方法的,编译器则无法检测对象是否存在该方法了。


此外,比较常见的崩溃基本都源于代码bug,比如数组越界插空多线程安全性访问野指针发送未实现的 selector 等。

四、拓展阅读

发布于: 2022 年 02 月 03 日阅读数: 9
用户头像

No Silver Bullet 2021.07.09 加入

岂曰无衣 与子同袍

评论

发布
暂无评论
ReactNative进阶(五十):IOS 系统 Crash 日志分析实战