iOS crash log 解析

iOS开发中,经常遇到App在开发及测试时不会有问题,但是装在别人的设备中会出现各种不定时的莫名的 crash,因为iOS设备会保存应用的大部分的 crash Log,所以可以通过 crash Log 来定位 crash 原因。

一. 获取iOS设备上的 crash log

1. 将iOS设备连接到电脑上,打开 Xcode -> Organizer -> Devices,找到该台设备,在 Device logs 中找到 crash log(后缀为 .crash 的 log 文件),将其导出即可。

2. 如果你的应用已经上架App Store,那么开发者可以通过iTunes Connect(Manage Your Applications - View Details - Crash Reports)获取用户的crash log。不过这并不是100%有效的,而且大多数开发者并不依赖于此,因为这需要用户设备同意上传相关信息,详情可参见iOS: Providing Apple with diagnostics and usage information摘要。

二. 解析iOS crash log

获取到的 crash log 中的相关信息都是 16 进制的内存地址,并不能定位崩溃的代码,所以需要将 16 进制的内存地址解析为对应的类及方法。

解析 crash log 需要使用上传应用时所发送的 .app 及 .sYSM 两个文件(所以每次上传新版本时都要保存这两个文件,不然没法解析 crash log),可以将 .app .dYSM 及crash log文件拷贝到同一个文件夹下,使用 Symbolicatecrash 进行解析。

获取 .app 及 .dYSM 文件:在iOS开发中,需要使用Xcode打包生成 .xcarchiver 文件,可以在Xcode - > Organizer - > archive 中进行管理并导出相应的 .xcarchiver 文件,.xcarchiver 文件中就包含 .app 及 .dYSM 文件。

Symbolicatecrash 是一个隐藏文件,并且独立于Xcode,位置在:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash

在利用 Symbolicatecrash 进行解析之前,要检查 .app .dYSM 及 crash log 三个文件的uuid是否一样,只有三者都一样才能进行解析。查看三个文件的 uuid 方法如下:

1. 查看xx.app文件的uuid的方法,在 terminal.app 中输入:

$ dwarfdump --uuid xxx.app/xxx (xxx工程名)

2. 查看xx.app.dSYM文件的uuid的方法,在命令行输入:

$ dwarfdump --uuid xxx.app.dSYM (xxx工程名)

3. 查看 crash log 文件的 uuid的方法:

在 crash log 文件中,找到 Binary Images: 项目名后面第一个尖括号中的一串码就是改 crash log 文件的 uuid。

利用 Symbolicatecrash 进行解析:

在 terminal.app 中输入如下命令:

$ ./symbolicatecrash xxx.crash xxx.app.dSYM > test.log

该命令会将 crash 文件解析成 test.log 文件,test.log 就是可读的函数文件。

输入上述命令可能会出现Error: "DEVELOPER_DIR" is not defined at ./symbolicatecrash line 53.这个错误。

如果出现上述错误,输入命令:export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer,

然后继续执行./symbolicatecrash xxx.crash xxx.app.dSYM > test.log可以成功

三. 其他解析(根据地址解析):

解析某一个地址的内容

方法一:

$ xcrun atos -o xxx.app/xxx -arch armv7 0x38ad42f9 0x38ad42f9 0x38ad42f9(多个16进制地址,使用空格分开)

方法二:

$ dwarfdump -–lookup 0x000036d2 -–arch armv6 xxx.app.dSYM

如果根据以上两个方法操作,均提示找不到地址的话,可以使用方法三:

方法三:

查找以下内容所对应的地址:

(当前代码行地址 = 当前地址 + 地址偏移量,地址偏移量为十进制数值)

10  TestTransform                    0x00057132 0x4f000 + 33074

1. 先说第一种比较简单的方法利用 "当前地址" 和 "当前代码行地址"

$ xcrun atos -o TestTransform.app/TestTransform -arch armv7 0x3d000 0x0004fc5c

//输出结果:

bddeMacBook-Pro:1 baidu$ xcrun atos -o /Users/baidu/Desktop/1/TestTransform.app/TestTransform -l 0x3d000 0x0004fc5c

got symbolicator for /Users/baidu/Desktop/1/TestTransform.app/TestTransform, base address 4000

main (in TestTransform) (main.m:16)

2. 第二种相对复杂一点,涉及到地址的偏移,首先查看起始地址,即使每次iOS app启动都会加载(main module)主模块在不同的内存地址,但是dSYM文件假设你的main module加载在地址0x1000(大多数情况是这个,也有0x4000的)。

获取此地址的方法:

$ otool -arch armv7 -l /Users/cnstar-tech/crash/xxx.app/xxx  | grep -B 1 -A 10 "LC_SEGM" | grep -B 3 -A 8 "__TEXT"

调用以上方法返回结果如下:

Load command 1

cmd LC_SEGMENT

cmdsize 600

segname __TEXT

vmaddr 0x00004000

vmsize 0x00014000

fileoff 0

filesize 81920

maxprot 0x00000005

initprot 0x00000005

nsects 8

flags 0x0

看到vmaddr显示为0x00004000。

然后查看  "TestTransform 0x00057132 0x4f000 + 33074"  使用 起始地址+地址偏移量如:

0x4000 + 33074 = 0xc132   (注意:前面为前面为16进制后面为10进制相加,要都转换成10进制相加,把得出的结果转换成16进制)

就可以使用 "0xc132" 地址查看内容了:

//方法一:

$ dwarfdump --lookup 0xc132 --arch armv7 TestTransform.app.DSYM

//输出结果:

----------------------------------------------------------------------

File: TestTransform.app.DSYM/Contents/Resources/DWARF/TestTransform (armv7)

----------------------------------------------------------------------

Looking up address: 0x000000000000c132 in .debug_info... found!

0x00003947: Compile Unit: length = 0x00007b6e  version = 0x0002  abbr_offset = 0x00000000  addr_size = 0x04  (next CU at 0x0000b4b9)

0x00003952: TAG_compile_unit [1] *

AT_producer( "Apple LLVM version 5.1 (clang-503.0.38) (based on LLVM 3.4svn)" )

AT_language( DW_LANG_ObjC )

AT_name( "/Users/baidu/Desktop/TestTransform/TestTransform/ViewController.m" )

AT_low_pc( 0x0000a950 )

AT_stmt_list( 0x0000089f )

AT_comp_dir( "/Users/baidu/Desktop/TestTransform" )

AT_APPLE_major_runtime_vers( 0x02 )

0x00003c22:    TAG_subprogram [39] *

AT_name( "__29-[ViewController aaaaaaaaaa:]_block_invoke" )

AT_decl_file( "/Users/baidu/Desktop/TestTransform/TestTransform/ViewController.m" )

AT_decl_line( 191 )

AT_prototyped( 0x01 )

AT_APPLE_isa( 0x01 )

AT_accessibility( DW_ACCESS_public )

AT_low_pc( 0x0000c09c )

AT_high_pc( 0x0000c182 )

AT_frame_base( r7 )

0x00003c46:        TAG_lexical_block [34] *

AT_low_pc( 0x0000c0d6 )

AT_high_pc( 0x0000c17e )

Line table dir : '/Users/baidu/Desktop/TestTransform/TestTransform'

Line table file: 'ViewController.m' line 193, column 0 with start address 0x000000000000c11e

Looking up address: 0x000000000000c132 in .debug_frame... found!

0x00000160: FDE

length: 0x0000000c

CIE_pointer: 0x00000000

start_addr: 0x0000c09c __29-[ViewController aaaaaaaaaa:]_block_invoke

range_size: 0x000000e6 (end_addr = 0x0000c182)

Instructions: 0x0000c09c: CFA=4294967295+4294967295

//方法二:

$ xcrun atos -o /Users/baidu/Desktop/1/TestTransform.app/TestTransform 0xc132

//输出结果:

__29-[ViewController aaaaaaaaaa:]_block_invoke (in TestTransform) (ViewController.m:193)

四. 如何判断两个 crash log 文件的 crash 是同一个原因:

1. 可以先比较 Triggered by Thread 看看是否为同一个线程,不同,则不是同一原因。

2. 如果有 Last Exception Backtrace,可以先比较 Last Exception Backtrace 中地址的行数,行数不同则原因不同,行数如果相同可以将两个 Last Exception Backtrace 中的地址逐个进行比较,(一般最开始几行和最后几行都是调用系统函数,所以地址都是一样的),直到出现第一个不一样的地址,可以用 dwarfdump 分别解析两个地址,如果解析得到的方法不一样,则 crash 原因可不一样。

五. Xcode 中 Organizer 自动解析:

如果项目是在自己机器上打包的,可以将iOS设备连接到电脑上,这样该设备中的 crash log 就会被 Organizer 自动进行解析,如果没有自动解析,可以 右击 crash log 选择 re-symbolicate 进行解析。(之所以 Organizer 可以进行自动解析,是因为在打包的时候会建立 .app 及 .dYSM 两个文件的索引,所以可以自动解析 crash log 文件)

也可以进行手动建立索引,即将该 App 的 .app 及 .dYSM 两个文件拷贝到同一个文件夹中,然后再用 midimport foldername 命令进行手动建立索引,然后,将 crash log 文件导入 Xcode Organizer 中,就会进行自动解析。(可以批量导入 crash log 文件,批量进行解析)

六. 使用专门的 crash log 解析工具进行解析:

比如QuincyKit, Crashlytics, Flurry等。

posted @ 2015-08-09 18:55  smallcake  阅读(612)  评论(0编辑  收藏  举报