如何手动解析CrashLog
http://www.cocoachina.com/ios/20150803/12806.html
解决崩溃问题是移动应用开发者最日常的工作之一。如果是开发过程中遇到的崩溃,可以根据重现步骤调试,但线上版本就无能为力了。好在目前已经有很多不错的第三方CrashLog搜集平台(如友盟、Crashlytics等)为我们做好了解析工作,甚至在Xcode7里苹果也跟进了解析线上版本崩溃日志的功能,为开发者减轻了不少负担。尽管通常已经不需要我们手工处理CrashLog,了解CrashLog的还原原理和方法还是有必要的。
一、.dSYM
.dSYM(debugging SYMbols)又称为调试符号表,是苹果为了方便调试和定位问题而使用的一种调试方案,本质上使用的是起源于贝尔实验室的DWARF(Debugging With Attributed Record Formats),其在.xcarchive目录中的层次结构为:
|
1
2
3
4
5
6
|
.xcarchive--dSYMs |--Your.app.dSYM |--Contents |--Resources |--DWARF |
关于DWARF的具体内容以后有机会再说。我们能解析CrashLog全靠.dSYM文件,解析方式见后文。
二、确定符号表和崩溃日志的一致性
有了符号表文件,有了崩溃日志文件,在解析之前一定要确保二者的对应关系,否则就算按照下述步骤解析出内容也肯定是不准确的。二者的对应关系可以通过UUID来确定。
1、从崩溃日志中获取UUID
崩溃日志比较靠下的位置有个Binary Images模块,其第一行内容如下:
|
1
2
|
Binary Images:0xa2000 - 0x541fff Your armv7 /var/mobile/Containers/Bundle/Application/645D3184-4C20-4161-924B-BDE170FA64CC/Your.app/Your |
从中可以看到关于你应用的若干信息:
代码段的起终地址为:0xa2000 – 0x541fff
运行你应用的CPU指令集为:armv7
应用的UUID为:a5c8d3cfda65396689e4370bf3a0ac64(不区分大小写)
2、从符号表中获取UUID
执行以下命令从符号表中提取UUID:
|
1
|
dwarfdump --uuid Your.app.dSYM |
或者:
|
1
|
dwarfdump --uuid Your.app.dSYM/Contents/Resources/DWARF/Your |
执行结果为:
|
1
|
UUID: A5C8D3CF-DA65-3966-89E4-370BF3A0AC64 (armv7) Your.app.dSYM/Contents/Resources/DWARF/Your |
由此得到armv7指令集的UUID为:A5C8D3CF-DA65-3966-89E4-370BF3A0AC64(如果你的二进制文件支持多个指令集,这里会列出每个指令集对应符号表的UUID),通过和崩溃日志中的对比发现二者一致,才可进行进一步的解析操作。
三、计算崩溃符号表地址
以下面的崩溃堆栈为例:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
Thread 0:0 libobjc.A.dylib 0x33f10f60 0x33efe000 + 776641 Foundation 0x273526ac 0x2734a000 + 344762 Foundation 0x27355c3e 0x2734a000 + 481903 UIKit 0x29ef9d1c 0x29bbc000 + 33989404 UIKit 0x29ef9c9a 0x29bbc000 + 33988105 UIKit 0x29ef954c 0x29bbc000 + 33969406 UIKit 0x29c3a16a 0x29bbc000 + 5164587 UIKit 0x29e4b8e6 0x29bbc000 + 26851588 UIKit 0x29c3a128 0x29bbc000 + 5163929 Your 0x000f0846 0xa2000 + 32160610 UIKit 0x29e90fb2 0x29bbc000 + 296952211 UIKit 0x29e91076 0x29bbc000 + 296971812 UIKit 0x29e867cc 0x29bbc000 + 292654013 UIKit 0x29c9e8ea 0x29bbc000 + 92797814 UIKit 0x29bc8a6a 0x29bbc000 + 5181815 QuartzCore 0x295f0a08 0x295e4000 + 5172016 QuartzCore 0x295ec3e0 0x295e4000 + 3376017 QuartzCore 0x295ec268 0x295e4000 + 3338418 QuartzCore 0x295ebc4c 0x295e4000 + 3182019 QuartzCore 0x295eba50 0x295e4000 + 3131220 QuartzCore 0x295e5928 0x295e4000 + 644021 CoreFoundation 0x266d0d92 0x26604000 + 83905822 CoreFoundation 0x266ce44e 0x26604000 + 82849423 CoreFoundation 0x266ce856 0x26604000 + 82952624 CoreFoundation 0x2661c3bc 0x26604000 + 9926025 CoreFoundation 0x2661c1ce 0x26604000 + 9876626 GraphicsServices 0x2da1a0a4 0x2da11000 + 3702827 UIKit 0x29c2a7ac 0x29bbc000 + 45252428 Your 0x0024643a 0xa2000 + 172140229 libdyld.dylib 0x34484aac 0x34483000 + 6828 |
1、 符号表堆栈地址计算方式
要想利用符号表解析出崩溃对应位置,需要计算出符号表中对应的崩溃堆栈地址。而从上述堆栈中第9行可以看到,应用崩溃发生在运行时地址0x000f0846,该进程的运行时起始地址是0xa2000,崩溃处距离进程起始地址的偏移量为十进制的321606(对应十六进制为0x4E846)。三者对应关系:
|
1
|
0x000f0846 = 0xa2000 + 0x4E846 |
对应的公式为:
|
1
|
运行时堆栈地址 = 运行时起始地址 + 偏移量 |
崩溃堆栈中的起始地址和崩溃地址均为运行时地址,根据虚拟内存偏移量不变原理,只要提供了符号表TEXT段的起始地址,再加上偏移量(这里为0x4E846)就能得到符号表中的堆栈地址,即:
|
1
|
符号表堆栈地址 = 符号表起始地址 + 偏移量 |
2、获取符号表中的TEXT段起始地址
符号表中TEXT段的起始地址可以通过以下命令获得:
|
1
|
$otool -l Your.app.dSYM/Contents/Resources/DWARF/Your |
运行结果中的片段如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
Load command 3 cmd LC_SEGMENT cmdsize 736 segname __TEXT vmaddr 0x00004000 vmsize 0x00700000 fileoff 0 filesize 0 maxprot 0x00000005 initprot 0x00000005 nsects 10 flags 0x0 |
其中的vmaddr 0x00004000字段即为TEXT段的起始地址。
3、计算符号表地址
由公式:
|
1
|
符号表堆栈地址 = 符号表起始地址 + 偏移量 |
可得:
|
1
|
0x52846 = 0x4E846 + 0x4000 |
即符号表中的崩溃地址为0x52846,接下来就可以根据这个地址解析出崩溃位置了。
四、崩溃信息还原
有了符号表的崩溃地址,有以下几种方式解析崩溃信息:
1、dwarfdump
命令如下:
|
1
|
$dwarfdump --arch armv7 Your.app.dSYM --lookup 0x52846 | grep 'Line table' |
需要注意的是:
这里的armv7是运行设备的CPU指令集,而不是二进制文件的指令集
比如armv7指令集的二进制文件运行在arm64指令集的设备上,这个地方应该写arm64。
—lookup后面跟的一定是经过准确计算的符号表中的崩溃地址
使用dwarfdump解析的结果较杂乱,因此使用grep命令抓取其中关键点展示出来
运行结果如下:
|
1
2
|
Line table dir : '/data/.../Src/OBDConnectSetting/Controller'Line table file: 'OBDFirstConnectViewController.m' line 882, column 5 with start address 0x000000000052768 |
其中第一行是编译时文件目录,第二行包含了崩溃发生的文件名称以及文件中具体行号等信息,有了这些信息就能准确定位崩溃原因啦。
2、atos
atos是另一种更加简洁的崩溃日志解析方法,使用方式如下:
|
1
|
$atos -o LuBao -arch armv7 0x52846 |
其执行结果如下:
|
1
|
-[OBDFirstConnectViewController showOilPricePickerView] (in Your) (OBDFirstConnectViewController.m:882) |
相对dwarfdump命令的解析结果,更加简洁直观的指出了崩溃发生的位置。
3、无需符号表崩溃地址的解析方式
实际上,atos还提供了另外一种无需计算崩溃地址对应的符号表地址的方式,命令格式如下:
|
1
|
$atos -o Your.app.dSYM/Contents/Resources/DWARF/Your -arch armv7 -l 0xa2000 0x000f0846 |
其中-l选项指定了二进制文件在运行时的起始地址0xa2000(获取方式见Binary Images相关内容),后面跟的是崩溃发生的运行时地址0x000f0846,解析结果和使用计算得到的符号表中崩溃地址一致:
|
1
|
-[OBDFirstConnectViewController showOilPricePickerView] (in Your) (OBDFirstConnectViewController.m:882) |
五、参考文档
如何手动解析CrashLog的更多相关文章
- json手动解析详解
项目中有时候仅仅需要一个或者多个字段时可以使用这种解析方式,省去创建实体类. 1.首先讲解下最基本的数据格式. 例如: String json="{'name':'小明','action': ...
- 如何手动解析vue单文件并预览?
开头 笔者之前的文章里介绍过一个代码在线编辑预览工具的实现(传送门:快速搭建一个代码在线编辑预览工具),实现了css.html.js的编辑,但是对于demo场景来说,vue单文件也是一个比较好的代码组 ...
- 手动解析Excel获取文件元数据
工作中有遇到需要获取上传的Excel文件的列明.最大行数.大小等元数据信息.通常做法是通过Apache的POI工具加载文件然后再读取行列进行处理.这种方法很大的弊端就是需要把excel文件加载到内存, ...
- Java - 手动解析不带引号的JSON字符串
目录 1 需求说明 2 解析代码 2.1 实现思路 2.2 详细代码 2.3 测试样例 1 需求说明 项目中遇到了一批不带引号的类JSON格式的字符串: {Name:Heal,Age:20,Tag:[ ...
- JVM(五)手动解析.class文件
一:不同进制之间的转换 二进制:逢2进1,数字0-1. 八进制:逢8进1,数字0-7.三位二进制表示一位八进制.三位二进制最大为111,最大为7. 十进制:逢10进1,数字0-9.四位二进制表示一位十 ...
- IOS 解析crashlog
1.需要log.crash. dSYM和xcode自带的symbolicatecrash 放到一个文件夹下面 2.终端cd 到文件夹下面 运行命令 export DEVELOPER_DIR=/Appl ...
- php下载远程图片方法总结(curl手动解析header)curl跳转问题解决
常用方法一般有:. file_get_contents file_put_contents readfile($file) //效率很高. 一般代码: /** * 抓取远程图片 * * @param ...
- 手动解析网易云音乐MP3真实地址
火狐打开音乐播放页面 然后按F12 点击网络选项卡 点击音乐播放按钮 然后过滤输入“url” 选中筛选出来的结果 点击右边的相应选项卡 下面的url里面就是真实的预约MP3地址
- 零基础逆向工程17_PE结构01_PE头解析_手动
PE文件的两种状态 1.在硬盘中 节省硬盘空间 硬盘对齐 内存对齐 2.在内存中 3.PE磁盘文件与内存映像结构图 PE文件为什么要分节 -- 手动解析:PE文件 分析软件:飞鸽传书http://ww ...
随机推荐
- 2019-4-17-从-dotnet-core-3.0-的特性让-WPF-布局失效讨论-API-兼容
title author date CreateTime categories 从 dotnet core 3.0 的特性让 WPF 布局失效讨论 API 兼容 lindexi 2019-4-17 1 ...
- python中os的常用方法
1.os模块:os模块在python中包含普遍的操作系统功能,下面列出了一些在os模块中比较有用的部分. os.sep可以取代操作系统特定的路径分隔符.windows下为 “\\” os.name字符 ...
- Leetcode563.Binary Tree Tilt二叉树的坡度
给定一个二叉树,计算整个树的坡度. 一个树的节点的坡度定义即为,该节点左子树的结点之和和右子树结点之和的差的绝对值.空结点的的坡度是0. 整个树的坡度就是其所有节点的坡度之和. 示例: 输入: 1 / ...
- 用惯了jquery, 想用angularjs 还真不好理解
jquery 比较直白,什么都是操作dom 节点. angularjs 就好比 thinkphp, ci 等框架,有自己约定的格式和方式.需要遵循它的规则,研究中... 比如说我,用了很长事件的jqu ...
- UVA10905 Children's Game
题意:给定n个正整数,把它们连接成一个最大的整数.比如,123,124,556,90有24种连接方法,最大的结果为9 056 124 123. 贪心.一开始就想用string水过.注意不能直接用str ...
- draft.js开发富文本编辑器
写在前头的话 在react中去寻找一个好用的富文本编辑器网上很少有推荐的,搜到的也只有一些个人不成熟的作品,慢慢发现网上比较推荐的一个东东叫做draft.js. 这个东西在网上可以找到的教程也是手指头 ...
- PHP的安全性问题,你能说得上几个?
一.SQL注入 所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令.具体来说,它是利用现有应用程序,将(恶意)的SQL命令 ...
- 使用JSP渲染Web视图
Pom文件引入以下依赖 注意,创建SpringBoot整合JSP,一定要为war类型,否则会找不到页面 不要把jsp页面存放在Resources目录下,resources目录是给springboot打 ...
- Promise对象和async函数
Promise对象 //1开始 function fna(){ console.log('1开始'); var p = new Promise(function(resolve, reject){ / ...
- python实例 输出字符串和数字
但有趣的是,在javascript里我们会理想当然的将字符串和数字连接,因为是动态语言嘛.但在Python里有点诡异,如下: #! /usr/bin/python a=2 b="test&q ...