iOS Crash文件分析

本文详细介绍如何获取iOS应用的崩溃日志(Crashlog),并提供三种解析Crashlog的方法,包括使用Xcode的可视化解析功能、symbolicatecrash命令及atos命令。此外,文章还解释了Crashlog中ExceptionType和ExceptionCode的含义,并介绍了如何通过Crashlog定位到具体的代码文件及行号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.获取崩溃日志(Crash log)

1.1获取开发环境的Log

方法a.

方法b:在~/Library/Logs/CrashReporter/MobileDevice/<DEVICE_NAME>

1.2获取发布环境的Log

注意:app已经上架AppStore

作为开发者,你想要获取到你的用户的崩溃日志的话就得通过 iTunes Connect ,在 iTunes Connect 上的 Manage Your Applications -> View Details -> Crash Reports

这种方式有个前提,就是用户设备同意上传相关信息,打开了诊断与用量这个选项设置->隐私->诊断与用量

2.Crash Log的符号化

2.1认识Log文件

2.2解析Log文件

目前有三种方法:

a.使用Xocde自带可视化解析功能

b.使用Xcode提供的解析命令symbolicatecrash

c.使用系统命令atos

2.2.1 XCode自带可视化解析功能(适合开发环境下做符号还原)

要使用Xcode符号化 crash log,你需要下面所列的3个文件:

  1. crash报告(.crash文件) 
  2. 符号文件 (.dsymb文件)
  3. 应用程序文件 (appName.app文件,把IPA文件后缀改为zip,然后解压,Payload目录下的appName.app文件), 这里的appName是你的应用程序的名称。

这里先介绍一下dsymb文件:

DWARF(DebuggingWith Arbitrary Record Formats),是ELF和Mach-O等文件格式中用来存储和处理调试信息的标准格式,.dSYM中真正保存符号表数据的是DWARF文件。DWARF中不同的数据都保存在相应的section(节)中,ELF文件里所有的section名称都以".debug_"开头,Mach-O中关于section的命名和ELF稍有区别,把名称前的.换成了_,例如.debug_info变成了_debug_info

符号表文件.dSYM实际上是从Mach-O文件中抽取调试信息而得到的文件目录,实际用于保存调试信息的文件符号表文件目录中的DWARF文件

.dSYM文件的生成方式有两种:

  1. Xcode自动生成,在与AppName同一目录下
  2. 使用dsymutil命令从Mach-O中提取

dsymutil /Users/<username>/Library/Developer/Xcode/DerivedData/Test-bympsyamgkietlhflbioakqjyemv/Build/Products/Release-iphoneos/AppName.app/Test -o /Users/<user-name>/Desktop/myDevice.dSYM/

保存在DAWARF中的信息是高度压缩的,可以通过dwarfdump命令从中提取出可读信息。

把这3个文件放到同一个目录下,打开Xcode的Window菜单下的organizer,然后点击Devices tab,然后选中左边的Device Logs。

然后把.crash文件拖到Device Logs或者选择下面的import导入.crash文件。

这样你就可以看到crash的详细log

2.2.2 使用Xcode提供的解析命令symbolicatecrash

首先找到symbolicatecrash可执行文件,执行以下命令:

sudo find / -name "symbolicatecrash"

得到结果:

Password:

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

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

这里是因为本地安装了多个版本的Xcode,所以找到了多个路径,具体使用哪一个,要看App的编译版本

将对应版本的symbolicatecrash命令拷贝到系统命令文件夹下,执行以下命令:

cp /Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash /usr/local/bin

命令使用crash文件,应用的dSYMcrash文件中崩溃地址对应的方法符号表, 在处理之前,请依然将“.app“, “.dSYM”和 ".crash"文件放到同一个目录下,然后执行以下命令:

export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"

symbolicatecrash Test.crash Test.app.dSYM/ > crash.log 

或者(symbolicatecrash) -d Test.app.dSYM/ -o

结果就会输出在crash.log,如图:

2.2.3 使用系统命令atos

如果你有多个“.ipa”文件,多个".dSYMB"文件,你并不太确定到底“dSYMB”文件对应哪个".ipa"文件,那么,这个方法就非常适合你。
特别当你的应用发布到多个渠道的时候,你需要对不同渠道的crash文件,写一个自动化的分析脚本的时候,这个方法就极其有用。
UUID:
每一个可执行程序都有一个build UUID来唯一标识。Crash日志包含发生crash的这个应用(app)的 build UUID以及crash发生的时候,应用加载的所有库文件的[build UUID]。
那如何知道crash文件的UUID呢?
可以用:
grep "appName armv" *crash
可以得到类似如下的结果:

0x10003c000-0x100043fff AppName arm64 <b282de86d259354d8965dae67f535817> /var/mobile/Containers/Bundle/Application/5B6BF767-65A2-4293-872B-11418E1C8113/AppName.app/AppName

(请注意这里的0x10003c000,是模块的加载地址,后面用atos的时候会用到)


如何找到App的UUID
进入App所在的目录,然后执行命令:
xcrun dwarfdump --uuid appName.app/appName
结果如下:

UUID: 2915A551-2818-3288-981B-7F5E1933FF64 (armv7) AppName.app/AppName

UUID: B282DE86-D259-354D-8965-DAE67F535817 (arm64) AppName.app/AppName


这个app有2个UUID,表明它是一个fat binnary,它能利用最新硬件的特性,又能兼容老版本的设备。对比上面crash文件和app文件的UUID,发现它们是匹配的(这一点很重要)
用atos命令来符号化某个特定模块加载地址
命令是:
atos [-o AppName.app/AppName] [-l loadAddress] [-arch architecture]
下面3种都可以:

// atos命令使用指定模块(-o参数) 模块加载地址(-l参数) 函数栈地址来解析出符号

atos -o UIKit -arch arm64  -l 0x18b4cc000 0x100040584(还原系统符号)

atos -o UIKit -arch arm64  -l 0x18b4cc000 0x18b4dcc84(还原系统符号)

atos -o dysm文件路径 -l 模块load地址 -arch cpu指令集种类 调用方法的地址(还原App符号)

例如执行命令:

atos -o Test.app.dSYM/Contents/Resources/DWARF/Test -l 0x10003c000 0x100040884

得到结果:

注意,关于系统模块,需要根据运行环境,指定在Xcode安装的目录的对应的iOS版本的库文件,例如UIKit:

3.总结

以上三种方法适用于不同的场景,需要根据情况使用

4.补充

Exception Type

1)EXC_BAD_ACCESS

此类型的Excpetion是我们最长碰到的Crash,通常用于访问了不改访问的内存导致。一般EXC_BAD_ACCESS后面的"()"还会带有补充信息。
SIGSEGV: 通常由于重复释放对象导致,这种类型在切换了ARC以后应该已经很少见到了。
SIGABRT: 收到Abort信号退出,通常Foundation库中的容器为了保护状态正常会做一些检测,例如插入nil到数组中等会遇到此类错误。
SEGV:(Segmentation Violation)代表无效内存地址,比如空指针,未初始化指针,栈溢出等;
SIGBUS:总线错误,与 SIGSEGV 不同的是,SIGSEGV 访问的是无效地址,而 SIGBUS 访问的是有效地址,但总线访问异常(如地址对齐问题)
SIGILL:尝试执行非法的指令,可能不被识别或者没有权限

2)EXC_BAD_INSTRUCTION

此类异常通常由于线程执行非法指令导致
3)EXC_ARITHMETIC

除零错误会抛出此类异常


Exception Code


0xbaaaaaad 此种类型的log意味着该Crash log并非一个真正的Crash,它仅仅只是包含了整个系统某一时刻的运行状态。通常可以通过同时按Home键和音量键,可能由于用户不小心触发
0xbad22222 当VOIP程序在后台太过频繁的激活时,系统可能会终止此类程序
0x8badf00d 程序启动或者恢复时间过长被watch dog终止
0xc00010ff 程序执行大量耗费CPU和GPU的运算,导致设备过热,触发系统过热保护被系统终止
0xdead10cc 程序退到后台时还占用系统资源,如通讯录被系统终止
0xdeadfa11 前面也提到过,程序无响应用户强制关闭

获取崩溃代码所在文件以及行号

上述介绍dsym文件的时候,所述的那些section中,定位CrashLog只需要用到.debug_info和.debug_line。由于解析出来的数据量较大,为了方便查看,就将其保存在文本中。两个section的数据提取方式如下:

_debug_info:

dwarfdump -e --debug-info YourPath/YourApp.dSYM/Contents/Resources/DWARF > info-e.txt
_debug_line:

dwarfdump -e --debug-line YourPath/YourApp.dSYM/Contents/Resources/DWARF > line-e.txt
(命令中的-e可以增加解析结果的可读性;其它section的提取方式类似)

举例:例如我们的文件是:

那看看在info-e.txt文件的信息:

0x00034ed7:     function [113] *
                low pc( 0x0000000100004848 ) // 方法在栈中的低地址
                high pc( 0x0000000100004894 ) // 方法在栈中的高地址
                frame base( reg29 )
                object pointer( {0x00034ef6} )
                name( "-[Model showModelName]" ) // 方法名
                decl file( "/Users/uwei/WorkSpace/Demo/Objective-C/iOS/Test/Test/Model.m" ) // 方法所在文件
                decl line( 14 ) // 方法在文件中的起始行号
                prototyped( 0x01 )
                APPLE optimized( 0x01 )

 那看看在line-e.txt文件的信息:

Address               Line   File
------------------    ------ ------------------------------
0x0000000100004848     14    /Users/uwei/WorkSpace/Demo/Objective-C/iOS/Test/Test/Model.m
0x0000000100004854     15
0x000000010000486c     17
0x0000000100004884     18

 这里的每一个行号都对应着代码在编译执行的时候的内存中的地址,可以从CrashLog中找到对应的地址信息,就可以对应上代码在文件中的行号

参考:符号化iOS Crash文件的三种方法iOS调试之Crash分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值