iOS Crash 四

多个handler共存带来的坑

在hook crash时,单独的app测试handle方法会生效,但是一旦将测试的代码加入到依赖很多第三方库的工程中,可能这段代码就不再生效了,不过也有别的可能,例如,当前代码有效,别的第三方库中的代码功能失效。导致这一现象出现的原因是,handle句柄没有正常传递。正常使用的规范是:

  • 拿到之前的句柄。

    方式:preHandler = NSGetUncaughtExceptionHandler();
  • 注册自己的handle。

    方式:NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
  • 处理完之后检测之前的handler是否存在,存在就将handle交给 NSSetUncaughtExceptionHandler

    if (preHandler != NULL) {
        NSSetUncaughtExceptionHandler(preHandler)
    }

上述的操作,手递手的将exception接入并传出,不影响别的工具的使用。如果在调试的时候有部分功能一直没有生效,可以试试的方法。hook handle入口,观察当前exception的流转。因为像signal这样的crash,无法通过断点的方式模拟demo的走向。

使用XCode 编译参数完成部分crash符号化工作

在符号化Crash的时候,一直存在一个疑问,有没有可能,ipa包自己带着dSYM文件,在产生crash的同时,像Xcode Debug一样直接产出一个符号化的结果,减少人为的解析呢?带着这样的疑问,查询了一些文章,结果一半一半吧。可以从Code setting中配置相关的参数,使得crash的时候堆栈中的信息可读。

配置选项中,选择了各种的混合方式,上面的配置没有丢弃任何选项编译资源。配置完成了之后,archive出来的size会变大,因为携带了编译信息。模拟crash之后产出的堆栈如下

0   CoreFoundation                      0x000000018308ada4 <redacted> + 252
1   libobjc.A.dylib                     0x00000001822445ec objc_exception_throw + 56
2   CoreFoundation                      0x0000000183023750 _CFArgv + 0
3   CoreFoundation                      0x0000000182f5705c <redacted> + 1412
4   TestCrash                           0x0000000102d8c72c -[ViewController function9] + 60
5   UIKit                               0x000000018cdf964c <redacted> + 96
6   UIKit                               0x000000018cf1a870 <redacted> + 80
7   UIKit                               0x000000018cdff700 <redacted> + 440
8   UIKit                               0x000000018cf351a8 <redacted> + 572
9   UIKit                               0x000000018ce7c9e0 <redacted> + 2428
10  UIKit                               0x000000018ce71890 <redacted> + 3160
11  UIKit                               0x000000018ce701d0 <redacted> + 340
12  UIKit                               0x000000018d651d1c <redacted> + 2340
13  UIKit                               0x000000018d6542c8 <redacted> + 4744
14  UIKit                               0x000000018d64d368 <redacted> + 152
15  CoreFoundation                      0x0000000183033404 <redacted> + 24
16  CoreFoundation                      0x0000000183032c2c <redacted> + 276
17  CoreFoundation                      0x000000018303079c <redacted> + 1204
18  CoreFoundation                      0x0000000182f50da8 CFRunLoopRunSpecific + 552
19  GraphicsServices                    0x0000000184f36020 GSEventRunModal + 100
20  UIKit                               0x000000018cf70758 UIApplicationMain + 236
21  TestCrash                           0x0000000102d8c804 main + 88
22  libdyld.dylib                       0x00000001829e1fc0 &lt;redacted&gt; + 4</string>

可以看到堆栈中的部分信息被符号话了,变的可读了,但是其他的堆栈没有被符号化。这样的信息在crash发生的时候能解决大部分的问题,但是对整个crash奔溃的过程没有很好的记录,还是不推荐的。

crash字段整理

之前上报的堆栈存在一些冗余字段,整理了一下,方便定位的crash文件应该具备的信息:

  • name
  • reason
  • 堆栈
  • 当前时间
  • 当前手机架构类型
  • 硬件型号
  • 软件版本
  • 应用版本
  • imageUUID
  • uuid
  • 当前topVC名称
  • userInfo

整理了之后上报的数据结构变成了这样:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>appInfo</key>
    <string>iPhone10,3</string>
    <key>appVersion</key>
    <string>1</string>
    <key>arch</key>
    <string>arm64</string>
    <key>currentVC</key>
    <string>ViewController</string>
    <key>imageUUID</key>
    <string>01E3CC6E-EAA9-3363-BA5A-29C88FA3D4EB</string>
    <key>name</key>
    <string>NSInvalidArgumentException</string>
    <key>reason</key>
    <string>*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil</string>
    <key>stackInfo</key>
    <array>
        <dict>
            <key>imageAddress</key>
            <string>0x182F45000</string>
            <key>name</key>
            <string>CoreFoundation</string>
            <key>strStackAddress</key>
            <string>0x000000018308ada4</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18223C000</string>
            <key>name</key>
            <string>libobjc.A.dylib</string>
            <key>strStackAddress</key>
            <string>0x00000001822445ec</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x182F45000</string>
            <key>name</key>
            <string>CoreFoundation</string>
            <key>strStackAddress</key>
            <string>0x0000000183023750</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x182F45000</string>
            <key>name</key>
            <string>CoreFoundation</string>
            <key>strStackAddress</key>
            <string>0x0000000182f5705c</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x104F60000</string>
            <key>name</key>
            <string>TestCrash</string>
            <key>strStackAddress</key>
            <string>0x0000000104f67ba0</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18CC53000</string>
            <key>name</key>
            <string>UIKit</string>
            <key>strStackAddress</key>
            <string>0x000000018cdf964c</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18CC53000</string>
            <key>name</key>
            <string>UIKit</string>
            <key>strStackAddress</key>
            <string>0x000000018cf1a870</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18CC53000</string>
            <key>name</key>
            <string>UIKit</string>
            <key>strStackAddress</key>
            <string>0x000000018cdff700</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18CC53000</string>
            <key>name</key>
            <string>UIKit</string>
            <key>strStackAddress</key>
            <string>0x000000018cf351a8</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18CC53000</string>
            <key>name</key>
            <string>UIKit</string>
            <key>strStackAddress</key>
            <string>0x000000018ce7c9e0</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18CC53000</string>
            <key>name</key>
            <string>UIKit</string>
            <key>strStackAddress</key>
            <string>0x000000018ce71890</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18CC53000</string>
            <key>name</key>
            <string>UIKit</string>
            <key>strStackAddress</key>
            <string>0x000000018ce701d0</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18CC53000</string>
            <key>name</key>
            <string>UIKit</string>
            <key>strStackAddress</key>
            <string>0x000000018d651d1c</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18CC53000</string>
            <key>name</key>
            <string>UIKit</string>
            <key>strStackAddress</key>
            <string>0x000000018d6542c8</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18CC53000</string>
            <key>name</key>
            <string>UIKit</string>
            <key>strStackAddress</key>
            <string>0x000000018d654628</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18CC53000</string>
            <key>name</key>
            <string>UIKit</string>
            <key>strStackAddress</key>
            <string>0x000000018d64d368</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x182F45000</string>
            <key>name</key>
            <string>CoreFoundation</string>
            <key>strStackAddress</key>
            <string>0x0000000183033404</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x182F45000</string>
            <key>name</key>
            <string>CoreFoundation</string>
            <key>strStackAddress</key>
            <string>0x0000000183032c2c</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x182F45000</string>
            <key>name</key>
            <string>CoreFoundation</string>
            <key>strStackAddress</key>
            <string>0x000000018303079c</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x182F45000</string>
            <key>name</key>
            <string>CoreFoundation</string>
            <key>strStackAddress</key>
            <string>0x0000000182f50da8</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x184F2B000</string>
            <key>name</key>
            <string>GraphicsServices</string>
            <key>strStackAddress</key>
            <string>0x0000000184f36020</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x18CC53000</string>
            <key>name</key>
            <string>UIKit</string>
            <key>strStackAddress</key>
            <string>0x000000018cf70758</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x104F60000</string>
            <key>name</key>
            <string>TestCrash</string>
            <key>strStackAddress</key>
            <string>0x0000000104f67c78</string>
        </dict>
        <dict>
            <key>imageAddress</key>
            <string>0x1829E1000</string>
            <key>name</key>
            <string>libdyld.dylib</string>
            <key>strStackAddress</key>
            <string>0x00000001829e1fc0</string>
        </dict>
    </array>
    <key>systemVersion</key>
    <string>11.4.1</string>
    <key>time</key>
    <string>2018-08-16</string>
    <key>userInfo</key>
    <string>unknown</string>
</dict>
</plist>

这里对堆栈的信息也做了拆解,每条堆栈收集的信息从一条String变成字典中的imageName imageAddress stackAddress字段.这样在解析的时候可以从字典中直接取值,不需要做额外的操作。