IOS的应用程序少不了crash,互联网统计分析工具友盟有一项目错误分析的功能,专门用于应用程序崩溃日志统计,最近研究友盟上统计到的崩溃日志,在此对崩溃日志做一个简单的总结。
IOS崩溃日志分类:
一、低内存崩溃:IOS设备检测到低内存时,虚拟内存系统发出通知请求应用释放内存。这些通知发送到所有正在运行的应用和进程,试图收回一些内存。如果内存使用依然居高不下,系统将会终止后台线程以缓解内存压力。如果可用内存足够,应用将能够继续运行而不会产生崩溃报告。否则,应用将被iOS终止,并产生低内存崩溃报告。低内存崩溃日志上没有应用线程的堆栈回溯。相反,上面显示的是以内存页数为单位的各进程内存使用量。(一个内存页的大小是4KB)被iOS因释放内存页终止的进程名称后面你会看到jettisoned 字样。如果看到它出现在你的应用名称后面,说明你的应用因使用太多内存而被终止了。
二、常见Exception Codes:
0x8badf00d: 读做 “ate bad food”该编码表示应用是因为发生watchdog超时而被iOS终止的。 通常是应用花费太多时间而无法启动、终止或响应用系统事件。这类crash有可能是在极差网络情况下并且有启动APP的时候同步网络阻塞主线程导致的崩溃,所以我们应该尽可能地把一些需要耗时间的任务如同步网络搬离主线程,使得应用程序能够正常启动。
0xbaaaaaad:用户按住Home键和音量键,获取当前内存状态,不代表崩溃
0xbad22222:VoIP 应用因为过于频繁重启而被终止。
0xc00010ff:因为太烫了被干掉,意为“cool off”
0xdead10cc:读做 “dead lock”!该代码表明应用因为在后台运行时占用系统资源,如通讯录数据库不释放而被终止
0xdeadfa11:读做 “dead fall”! 该代码表示应用是被用户强制退出的。根据苹果文档, 强制退出发生在用户长按开关按钮直到出现 “滑动来关机”, 然后长按 Home按钮。强制退出将产生 包含0xdeadfa11 异常编码的崩溃日志, 因为大多数是强制退出是因为应用阻塞了界面。
三、常见Exception types:
查看我们的crash分析报告邮件,会发现最经常遇到的错误类型是SEGV(Segmentation Violation,段违例),表明内存操作不当,比如访问一个没有权限的内存地址。
当我们收到SIGSEGV信号时,可以往以下几个方面考虑:
1)、访问无效内存地址,比如访问Zombie(僵尸)对象;
2)、尝试往只读区域写数据;
3)、引用空指针;
4)、使用未初始化的指针;
5)、栈溢出;
此外,还有其它常见信号:
SIGABRT:收到Abort信号,可能自身调用abort()或者收到外部发送过来的信号;
SIGBUS:总线错误。与SIGSEGV不同的是,SIGSEGV访问的是无效地址(比如虚存映射不到物理内存),而SIGBUS访问的是有效地址,但总线访问异常(比如地址对齐问题);
SIGILL:尝试执行非法的指令,可能不被识别或者没有权限;
SIGFPE:Floating Point Error,数学计算相关问题(可能不限于浮点计算),比如除零操作;
SIGPIPE:管道另一端没有进程接手数据;
三、解析崩溃日志:
获取一份crash崩溃日志,我们需要把十六进制地址等原始信息映射为源代码级别的方法名称和代码行数,使其对开发人员可读。这个过程称为符号化解析。要成功地符号化(Symbolication)解析一份crash日志,我们需要有对应的应用程序二进制文件以及符号(.dSYM)文件。
下面主要针对友盟上常见的crash log 进行解析
看一下,crash log中有这么一行崩溃日志
6 appname 0x350f3b appname + 3460923
同时堆栈信息后面还有一行:
dSYM UUID: B52242B9-C4FD-3750-8D3B-AB313091F44D
根据crash log,得到App的UUID。UUID是个字符串,由32个字符组成。得到了UUID,你才能知道是你的哪个版本在用户的iPhone上出了问题。
这样, 我们去我们的dSYM文件看看我们的UUID是否和崩溃日志上的UUID一致,只有dSYM文件的UUID和崩溃日志上的UUID一致,我们才能正确把crash 日志符号化。
dwarfdump是MAC上的一个免费小工具,可以用这个小工具去检测我们APP的UUID (以下过程都是终端上完成)
1、 命令是:dwarfdump --uuid golo.app.dSYM/ (先进入到我们的/Users/username/Documents/mydocment/appname.xcarchive/dSYMs/目录)
如果此时提示xcrun: error: active developer path ("/Volumes/Xcode/Xcode.app/Contents/Developer") does not exist, use xcode-select to change
2、这个时候,我们使用sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer/ 命令(这个命令的用意还有研究中)
3、完了我们再次键入命令:dwarfdump --uuid golo.app.dSYM/
此时就不会现出现1中的错误提示了,而是提示:UUID:B52242B9-C4FD-3750-8D3B-AB313091F44D (armv7) appname.app.dSYM/Contents/Resources/DWARF/appname
到这里, 我们就可以看到,我们的dSYM文件的UUID和我们的crash log的UUID一致,到这里,我们的准备工作结束
接下来,解析crash log:
4、在终端键入命令:dwarfdump --arch=armv7 --lookup 0x350f3b /Users/username/Documents/mydocment/appname.xcarchive/dSYMs/gaappname.app.dSYM/Contents/Resources/DWARF/appname
此时,不出意外的话,应该显示
Looking up address: 0x0000000000350f3b in .debug_info... found!
0x006e52dd: Compile Unit: length = 0x0000806a version = 0x0002 abbr_offset = 0x00000000 addr_size = 0x04 (next CU at 0x006ed34b)
0x006e52e8: TAG_compile_unit [1] *
AT_producer( "Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)" )
AT_language( DW_LANG_ObjC )
AT_name( "/Users/mydocment/myClass.m" )
AT_low_pc( 0x0034d8fc )
AT_stmt_list( 0x00181846 )
AT_comp_dir( "/Users/launch007/dev/golo" )
AT_APPLE_optimized( 0x01 )
AT_APPLE_major_runtime_vers( 0x02 )
0x006e997f: TAG_subprogram [60] *
AT_name( "__66-[myClass coustomMethod]_block_invoke" )
AT_decl_file( "/Users/mydocment/myClass.m"
AT_decl_line( 454 )
AT_prototyped( 0x01 )
AT_APPLE_isa( 0x01 )
AT_accessibility( DW_ACCESS_public )
AT_low_pc( 0x00350bd8 )
AT_high_pc( 0x0035110c )
AT_frame_base( r7 )
0x006e99c5: TAG_lexical_block [43] *
AT_low_pc( 0x00350c06 )
AT_high_pc( 0x003510ec )
0x006e99ce: TAG_lexical_block [42] *
AT_ranges( 0x0001fc90
[0x00350c06 - 0x00350f6a)
[0x003510d8 - 0x003510ec)
End )
0x006e99d3: TAG_lexical_block [42] *
AT_ranges( 0x0001fca8
[0x00350c0e - 0x00350f6a)
[0x003510d8 - 0x003510ec)
End )
0x006e99e4: TAG_lexical_block [42] *
AT_ranges( 0x0001fcc0
[0x00350c1a - 0x00350f5c)
[0x003510d8 - 0x003510de)
End )
0x006e99e9: TAG_lexical_block [42] *
AT_ranges( 0x0001fcd8
[0x00350c62 - 0x00350f5c)
[0x003510d8 - 0x003510de)
End )
Line table dir : ‘/Users/mydocment‘
Line table file: ‘myClass.m‘ line 486, column 0 with start address 0x0000000000350ef0
Looking up address: 0x0000000000350f3b in .debug_frame... found!
0x00046620: FDE
length: 0x0000000c
CIE_pointer: 0x00000000
start_addr: 0x00350bd8 __66-[myClass coustomMethod]_block_invoke
range_size: 0x00000534 (end_addr = 0x0035110c)
Instructions: 0x00350bd8: CFA=4294967295+4294967295
到这里,我们解析完全,已经定位到是myClass类coustomMethod方法中line 486这一行出现崩溃,去对代码修改代码就好了。。。