多个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 <redacted> + 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字段.这样在解析的时候可以从字典中直接取值,不需要做额外的操作。