iOS崩溃日志分析

作者: handyTOOL | 来源:发表于2016-09-04 23:24 被阅读1456次

    什么是崩溃日志

    iOS的App在崩溃时,系统会记录下当前的每个线程的调用栈信息等等,并保存到设备中。这些信息汇总起来就是我们所说的崩溃日志。

    iOS崩溃日志收集的几种方式

    1. 通过Xcode的Window > Devices进入设备管理,使用View Device Logs查看
    2. 通过第三方SDK上传用户的崩溃日志,比如使用友盟,BugHD,Bugly等等
    3. 通过Apple后台搜集崩溃日志,可以在Organizer里直接查看线上的崩溃日志。但是如果用户选择不上传诊断信息,就看不到日志了。

    为什么要符号化

    从iOS设备采集的原始崩溃日志记录的都是调用栈地址

    Thread 0 name:  Dispatch queue: com.apple.main-thread
    Thread 0:
    0   libsystem_kernel.dylib          0x000000019959cb3c 0x19959c000 + 2876
    1   libsystem_platform.dylib        0x0000000199679534 0x199674000 + 21812
    2   libobjc.A.dylib                 0x0000000198c5b200 0x198c44000 + 94720
    3   libobjc.A.dylib                 0x0000000198c6443c 0x198c44000 + 132156
    4   CoreFoundation                  0x0000000184190cbc 0x184064000 + 1232060
    ......
    

    这样的崩溃日志是无法使用的,通过符号化可以将它转换成可读的日志

    Thread 0 name:  Dispatch queue: com.apple.main-thread
    Thread 0:
    0   libsystem_kernel.dylib          0x000000019959cb3c syscall_thread_switch + 8
    1   libsystem_platform.dylib        0x0000000199679534 _os_lock_handoff_lock_slow + 120
    2   libobjc.A.dylib                 0x0000000198c5b200 spinlock_t::lockTwo(spinlock_t*, spinlock_t*) + 52
    3   libobjc.A.dylib                 0x0000000198c6443c objc_storeWeakOrNil + 120
    4   CoreFoundation                  0x0000000184190cbc _NSObjectStoreWeak + 80
    5   CoreFoundation                  0x00000001841167f8 -[_CFXNotificationObjcObserverRegistration initWithObserver:parent:] + 92
    ......
    

    怎样符号化

    大多数iOS程序员应该都知道使用View Device Logs查看设备日志时会自动符号化崩溃日志,但有时候可以符号化,但有时候却不行。
    为什么呢?除了通过View Device Logs自动符号化文件,还有没有其他方式可以手动符号化崩溃日志呢?下面会一一解答这些问题。

    符号化的原理

    dSYM文件

    dSYM文件(符号文件)是存储了类名和方法名等调试信息的文件。在release模式下,build后会有.app.dSYM文件。
    debug模式下调试信息在可执行文件中,所以不会生成dSYM文件。符号化就是通过在dSYM文件查找地址对应的方法名来实现的。

    Xcode的View Device Logs符号化时如何寻找符号文件

    每个Crash日志都记录了它所对应符号文件的uuid,打开一个crash文件,可以看到类似于下面这行的信息

    Binary Images:    
    0x10006c000 - 0x100073fff Demo1 arm64  <2cf1790547ff3a1cac055152319617ba> /var/mobile/Containers/Bundle/Application/7E6DE925-0B33-4699-89F7-05381876AD81/Demo1.app/Demo1
    

    例子中的App名字是Demo1,符号文件所对应的uuid就是2cf1790547ff3a1cac055152319617ba。当符号化这个崩溃日志的时候,符号化工具会通过这个uuid去寻找对应的dSYM文件,如果找到了就可以正确的符号化了。
    由此我们就可以知道,使用View Device Logs查看崩溃日志时,有时不能符号化的原因就是没有查到对应的dSYM文件。因为每次build都会导致uuid发生变化,所以大部分的崩溃日志所对应的dSYM文件其实都被覆盖掉了。

    通过符号化命令行工具深入了解符号化的过程

    在Xcode7.0中可以在一下路径找到符号化命令行工具

    /Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
    

    可以为这个工具设置alias:

    alias symbolicatecrash="/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash"
    

    这时候直接调用的话会报Error: "DEVELOPER_DIR" is not defined at ...这样的错误,只要设置环境变量DEVELOPER_DIR就行了 。
    export 'DEVELOPER_DIR'="/Applications/Xcode.app/Contents/Developer"

    后面我都会直接使用symbolicatecrash进行命令行下的符号化

    Xcode就是通过这个工具符号化崩溃日志的

    在符号化之前我们先准备好几样东西:
    1. 原始的崩溃日志
    2. dSYM文件
    3. symbolicatecrash可执行文件

    我们先查看crash日志是否和符号文件是匹配的,下面的命令将提取dSYM文件的uuid

    xcrun dwarfdump --uuid Demo1.app.dSYM/Contents/Resources/DWARF/Demo1
    

    我提取出来的uuid是:

    UUID: 2CF17905-47FF-3A1C-AC05-5152319617BA (arm64) Demo1.app.dSYM/Contents/Resources/DWARF/Demo1
    

    崩溃日志的uuid是:

    0x10006c000 - 0x100073fff Demo1 arm64  <2cf1790547ff3a1cac055152319617ba> /var/mobile/Containers/Bundle/Application/7E6DE925-0B33-4699-89F7-05381876AD81/Demo1.app/Demo1
    

    都是 2cf1790547ff3a1cac055152319617ba,所以使用这个dSYM文件既可以符号化上面的崩溃日志。

    用symbolicatecrash来符号化崩溃日志
    symbolicatecrash demo1.crash Demo1.app.dSYM -o processed.crash
    

    符号化后的crash文件将会被写入processed.crash中,
    然后我们来看看如果把Demo1.app.dSYM去掉会怎样,为了看到符号化的过程,我们可以加上 -v

    symbolicatecrash demo1.crash -o processed.crash -v
    

    我们主要看Demo1的符号化过程,其他系统调用其实也会在这个过程中被符号化。#后面是我添加的注释

    -- [2cf1790547ff3a1cac055152319617ba] fetching symbol file for Demo1    #开始寻找Demo1的符号文件    
    Running mdfind "com_apple_xcode_dsym_uuids == 2CF17905-47FF-3A1C-AC05-5152319617BA"    #使用Spotlight 搜索uuid为2CF17905-47FF-3A1C-AC05-5152319617BA的dsym文件    
    #接下来是使用file,lipo,otool等来分析dSYM的相关信息
    .
    .
    -- [2cf1790547ff3a1cac055152319617ba] MATCH (spotlight): ...  #确认找到对应的dSYM文件
    .
    .
    atos -arch arm64 -l 0x10006c000 -o '..../Demo1.app.dSYM/Contents/Resources/DWARF/Demo1' 0x00000001000706e8 0x0000000100070a80
    

    我们看到最后会发现实际上symbolicatecrash是使用atos来寻找调用栈地址对应的调试符号的。

    我们来看一下atos所使用的参数:

    1. -arch 所运行设备的架构,有arm64,armv7等等
    2. -l 二进制镜像运行时加载的地址
    3. -o 后面是符号文件或者含有调试符号的可执行文件(debug编译所产生的可执行文件默认是包含调试符号的)。
    4. 再后面就是需要符号化的调用栈地址,5 Demo1 0x00000001000706e8 0x10006c000 + 18152,0x00000001000706e8就是其中的一个地址。

    二进制镜像运行时加载的地址通过如下方式获得

    Binary Images:        
    0x10006c000 - 0x100073fff Demo1 arm64  <2cf1790547ff3a1cac055152319617ba> /var/mobile/Containers/Bundle/Application/7E6DE925-0B33-4699-89F7-05381876AD81/Demo1.app/Demo1
    

    中的0x10006c000就是这个崩溃日志所对应的二进制镜像加载地址。

    综上所述,symbolicatecrash主要帮我们做了两件事情,匹配到对应的dSYM文件,使用atos符号化每个调用栈地址。

    相关文章

      网友评论

        本文标题:iOS崩溃日志分析

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