崩溃捕获原理:定义自己的信号处理函数,代替内核的默认处理,需要监听从当mach微内核发出mach异常(EXC_BAD_ACCES,EXC_CRASH等,在这一瞬间先暂停所以子线程(除捕获线程外),捕获到各个线程的backtrace,backtrace只能以16进制地址信息存储、
产生异常的线程、捕获发生的时间、可以获取其他硬件信息、获取当前进程加载的二进制镜像文件包括镜像开始地址,结束地址,镜像名字)、app info,崩溃的uuid,Last Exception Backtrace等;与之对应mach异常转换为unix信号量(SIGSEGV,SIGBUS、SIGILL、SIGTRAP等)
最后向各个线程发送crash指令,退出进程。
1、苹果官方使用的基本方式:从手机中导出奔溃的.ips文件,使用symbolicatecrash工具,XXX.app的可执行文件,XXX.app.dSY文件进行还原栈信息,需要3者文件中的UUID完全一样;
1.1、可以直接在Xcode -> Devices and Simulators中 View Device Logs中查看,可以自动帮你符号化,包括其它奔溃的app如:微信,支付宝(这种类型只能符号化崩溃在系统的16进制
鉴于此,让我联想到PLCrashReporter,KSCrash系统未被符号化的16进制地址,也可以做到与Bugly一样的效果);
1.2、Xcode -> Organizer的Crashes种查看用户上传的crash log 这里出现的用户crash频率最高,时间最近;
2、使用IDA反编译Bugly发现与github开源的PLCrashReporter:https://github.com/plausiblelabs/plcrashreporter 和 KSCrash:https://github.com/kstenerud/KSCrash文件组织结构,命名方式几乎相同;
2.1、bugly目前拿到的堆栈和PLCrashReporter拿到的堆栈几乎完全一样(bugly目前本地符号化比PLCrashReporter本地符号化更详细,后来推测,放到服务器端符号化缺少的系统符号表);
2.2、bugly目前拿到crash thread获取到的文件行号不太正确(包括PLCrashReporter);
2.3、bugly使用buglySymboliOS.jar工具提取XXX.app.dSY,并生成符号表,根据.ips文件中的奔溃的16进制地址对照符号表,最后还原成对应的文件名,类名,函数名,行号;
2.4、KSCrash能获取到mach异常,c++异常,oc异常,主线程死锁,fatal signals,自定义crashs(本地符号化没有比bugly更详细);
疑惑:bugly符号化的时候,没有采用苹果官方使用的方式如XXX.app/XXX,XXX.app.dSY,.ips,symbolicatecrash,但在网站上需要上传符号表文件,在没上传有的crahsh已经做到完全符号化,所以此时不需要上传符号表文件;
对于这种疑惑,推测bugly使用本地符号化策略,在运行时采用不精准的启用方式和符号数据,一些符号化依赖于runtime内部运行知识(使用 ive-C元数据查找方法和类名,这依赖于 ive-C运行时数据的详细解析,
包括未定义的标志和其他运行时内部。 因此,如果运行时更改不兼容,它可能会返回不正确的数据),这些知识在未来ios版本中可能会发生变化,DWARF符号化数据更加准确(也就是苹果官方推荐使用XXX.app.dSY文件符号化),强烈建议仅对非发布版本启用本地符号。
本地符号化的原理:根据崩溃地址先找到崩溃的镜像如:(.dylib,app可执行文件,这些为mach-o文件,可以使用MachOView打开,也可以使用某种规则解析二进制流文件),然后找到基础段位置+每个镜像都会有一定的偏移,但都不会相同slide(ASLR地址空间布局随机),从struct symtab_command结构体中,根据相应规则读取符号表,字符串表(每一个mach-o文件都会存在),再根据给定的地址找到最接近的64位的struct nlist_64或者32的位struct nlist实体变量,最后在符号表中找到nlist实体变量的索引,在字符串表中的找到相应的字符信息。
本地符号在崩溃时会执行更多代码,存在风险,并且本地符号化有一部分不会被符号化。
一般我遇到的是系统框架无法被符号化,所以我猜了哈,未符号化的那部分16进制地址放到后台去符号化,鉴于此需要找到崩溃的设备的CPU架构,设备版本,然后找到对应的
系统符号表/Users/vincentwgao/Library/Developer/Xcode/iOS DeviceSupport/11.0 (15A372)/Symbols/usr/lib/system/libdyld.dylib
11.0 (15A372)对应的OS Version,鉴于此,iOS系统版本需要几十种,相关介绍:https://github.com/Zuikyo/iOS-System-Symbols;
libdyld.dylib 对应系统版本,在奔溃一瞬间,会有很多系统库,需要找到对应的版本,系统库,否则,符号化的数据不正确;
使用命令:atos -arch arm64 -o libdyld.dylib -l 0x183cd7000(libdyld.dylib二进制镜像的开始地址) 0x0000000183cd856c(需要符号化的地址);
还原系统16进制地址,可以有两种方式实现:
第一种:在服务端使用MAC OS平台,完全可以写一个shell脚本或者使用phython,将需要的参数传入,结合atos命令,将被符号化的字符串写入到关系数据库,最后达到和bugly完全一样的效果。
第二种:推测bugly使用方式,使用符号表工具iOS版本buglySymboliOS.jar工具针对指定系统版本的系统符号表如:libdyld.dylib 生成关联映射的符号表
包含三个字段(开始地址,结束地址,函数名称)系统符号表目前未提供行号,最后存入到关系数据库中,最后通过16进制地址查询进行还原;当然buglySymboliOS.jar工具也可以对我们
XXX.app.dSY生成相应的符号表,包含(开始地址,结束地址,函数名称,文件名:行号)然后存入到数据库。附上:(另外利用runtime的特色,在奔溃之前,根据16进制地址,去相应元数据也能找到对应的类名,函数名,为本地符号化提供基础)。
后来推测,后期bugly维护针对最新iOS设备需要加入新的系统符号框架;如果要达到精准的(app如果开启bitcode,每次需要从apple下载可执行文件,XXX.app.dSY文件;app如果关闭bitcode,在编译时保存好XXX.app.dSY文件)。
3、这里会出现bugly拿到其他线程堆栈和苹果生成的crash report其他线程堆栈会不一致的现象,这种现象是由于获取奔溃处理异常,有先后顺序,通常bugly,PlCrashReporter优先于系统捕获(通常时间间隔1秒);
4、bugly根据不同的日志级别进行上报,这一点在客户端写死了日志级别,我们可以做到在云端控制日志级别上报;
鉴于以上几点,可以做到和bugly一样效果的SDK。
继续阅读与本文标签相同的文章
下一篇 :
vim
-
美团携手世界粮食计划署共推“拒绝隐性饥饿”健康饮食倡导行动
2026-05-18栏目: 教程
-
圆通回应“承诺达”解散:由直营模式改回加盟商授权经营
2026-05-18栏目: 教程
-
2019 年度 “CCF 杰出会员” 公布,清华北大等86人当选
2026-05-18栏目: 教程
-
3步轻松搞定Spring Boot缓存
2026-05-18栏目: 教程
-
5G机皇已来 三星Galaxy Note10+5G正式登陆中国
2026-05-18栏目: 教程
