Xcode支持崩溃日志自动符号化,前提是本地有当时Build/Archive生成的dSYM文件,iOS崩溃日志符号化后,可以帮助开发者更好的定位问题,但如果dSYM文件丢失或拿到的崩溃日志不是标准的crash log,如何定位crash呢,笔者结过尝试发现一样可以定位到具体函数。

  在无dSYM文件情况下,之所以无法解析出崩溃地址对应的函数名,是因为Xcode在导出ipa时会去除Symbol Table(符号表)的非系统符号部分。这时address无法对应函数名,所以无法确定是在哪个函数或block中出了问题。因此解析日志的关键是要恢复符号表,国内已有大神做过研究 杨君的小黑屋,本文基于此完成解析目标。

我们以测试程序CrashTest的崩溃为例,介绍一下具体解析步骤

如图,

我们拿到了崩溃日志,这是一个arm64架构的崩溃日志,从最后的backtrace我们知道程序在访问数组元素时异常终止,但由于本地没有对应的dSYM文件,Xcode没有将红框内3,4两行符号化为具体的函数名,本文的工作就是要将这2行符号化

开始之前,先解释一下这几行地址的含义

:左边这一列是崩溃时的调用栈地址(虚拟内存地址)

基址:基址指向的地址是CrashTest这个模块加载到内存中的起始地址

偏移:左边栈地址 = 基址 + 偏移 (注意是10进制的)

什么?你拿到的崩溃日志不是这样的! (老司机请绕过)

像这样,只有一堆地址

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Triggered by Thread: Last Exception Backtrace:
(0x186e9de48 0x1975dc0e4 0x186d83a54 0x10000c00c 0x10000bf7c 0x18b6810f8 0x18b66a22c 0x18b680a94 0x18b680720 0x18b679c74 0x18b64d38c 0x18b8ec1b4 0x18b64b8f4 0x186e560e8 0x186e5538c 0x186e5343c 0x186d811f4 0x18ff0f5a4 0x18b6b2784 0x10000c574 0x197c4aa08) Thread name: Dispatch queue: com.apple.main-thread
Thread Crashed:
libsystem_kernel.dylib 0x0000000197d63270 0x197d48000 +
libsystem_pthread.dylib 0x0000000197e01224 0x197dfc000 +
libsystem_c.dylib 0x0000000197cdab14 0x197c78000 +
libc++abi.dylib 0x0000000196dad414 0x196dac000 +
libc++abi.dylib 0x0000000196dccb88 0x196dac000 +
libobjc.A.dylib 0x00000001975dc3bc 0x1975d4000 +
libc++abi.dylib 0x0000000196dc9bb0 0x196dac000 +
libc++abi.dylib 0x0000000196dc9738 0x196dac000 +
libobjc.A.dylib 0x00000001975dc290 0x1975d4000 +
CoreFoundation 0x0000000186d812a0 0x186d78000 +
GraphicsServices 0x000000018ff0f5a0 0x18ff04000 +
UIKit 0x000000018b6b2780 0x18b63c000 +
CrashTest 0x000000010000c570 0x100004000 +
libdyld.dylib 0x0000000197c4aa04 0x197c48000 +

没有关系,其实和上面是一样的,我们来找找基址和偏移

往下找到 Binary Images段,这里显示的就是崩溃程序当时加载的所有库的快照

Binary Images:
0x100004000 - 0x10000ffff CrashTest arm64 <5fc8820b297631d087e5e665b261ed0c> /var/mobile/Containers/Bundle/Application/D8F09771-5B65--A19C-CE77DAF32623/CrashTest.app/CrashTest // 这里第一行便是我们要找的
0x120070000 - 0x120097fff dyld arm64 <f958ba064181388a9658f927da42e9e7> /usr/lib/dyld
0x185678000 - 0x18580bfff AVFoundation arm64 <0c542593e3613f82b7e860cb5beeeed6> /System/Library/Frameworks/AVFoundation.framework/AVFoundation
0x18580c000 - 0x185870fff libAVFAudio.dylib arm64 <c9d296cb28c73570aaf8355b05f1adee> /System/Library/Frameworks/AVFoundation.framework/libAVFAudio.dylib
0x1858b4000 - 0x1858b4fff Accelerate arm64 <e9ba7838f51634a7b59ed392be50e86f> /System/Library/Frameworks/Accelerate.framework/Accelerate
0x1858cc000 - 0x185aebfff vImage arm64 <da44067fc79931c7aef1b7e88bf82a83> /System/Library/Frameworks/Accelerate.framework/Frameworks/vImage.framework/vImage
0x185aec000 - 0x185b93fff libBLAS.dylib arm64 <e5276e7784ef34a4baca480264978ea0> /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/libBLAS.dylib

然后找到我们的应用 CrashTest

0x100004000 - 0x10000ffff CrashTest arm64  <5fc8820b297631d087e5e665b261ed0c> /var/mobile/Containers/Bundle/Application/D8F09771-5B65-4403-A19C-CE77DAF32623/CrashTest.app/CrashTest

我们看到行首有个地址区间 0x100004000 - 0x10000ffff , 这便是崩溃程序的内存区,起始地址(基址)为 0x100004000,OK基址找到了。

然后我们再看看崩溃时的栈

Last Exception Backtrace:
(0x186e9de48 0x1975dc0e4 0x186d83a54 0x10000c00c 0x10000bf7c 0x18b6810f8 0x18b66a22c 0x18b680a94 0x18b680720 0x18b679c74 0x18b64d38c 0x18b8ec1b4 0x18b64b8f4 0x186e560e8 0x186e5538c 0x186e5343c 0x186d811f4 0x18ff0f5a4 0x18b6b2784 0x10000c574 0x197c4aa08)

其中位于地址区间 0x100004000 - 0x10000ffff 的,有2个 0x10000c00c 0x10000bf7c,这就是崩溃程序的调用栈,其余地址为系统库函数调用栈。

OK开始动手

1. 下载符号恢复工具

修改权限

chmod a+x restore-symbol 

2. 恢复符号

我们拿到的崩溃日志来自arm64机器,所以先将二进制文件 CrashTest.app/CrashTest 瘦身 (必须正确选择目标CPU架构类型,否则解析出来也是错的)

lipo -thin arm64 CrashTest -output CrashTest-arm64

接着用工具恢复符号表

./restore-symbol -o CrashTest-symbol CrashTest-arm64

现在我们得到了一个恢复了符号表的二时制文件 CrashTest-symbol

3. 使用苹果自带命令行工具atos,将崩溃地址解析成具体函数

atos -arch arm64 -o CrashTest-symbol -l 0x100030000 0x100034340 0x1000342b0
# 简单解释一下这个命令,atos -arch CPU架构 -o 进制文件 -l 起始地址 ...一系列内存地址
# -l 后面跟的是模块的起始地址,再后面可以罗列很多地址,该命令会依次解析出具体函数

得到如下输出

-[ViewController getChild:] (in CrashTest-symbol) +
-[ViewController crashOnFunc:] (in CrashTest-symbol) +

至此,完成了解析。

本篇涉及的崩溃是普通函数中的崩溃,如果崩溃发生在block中,则需要借住反编译工具,请参考 恢复二进制文件中的block符号表

PS:再多说一点,上面的解析输出我们看到在函数后面有一个 + 64, 这个+ 64、+ 44是什么意思呢,我开始也不太明白,仔细观察Hopper/IDA解析出来的的汇编代码,才明白原来这也是个偏移,是指调用地址相对于函数的起始地址的偏移,并非.m文件中的代码行数。

PS2: 当然还有更直接的方法,用Hopper打开二进制文件,然后根据崩溃偏移量,直接定位到汇编代码

详解没有dSYM文件 如何解析iOS崩溃日志的更多相关文章

  1. iOS崩溃日志ips文件解析

    iOS崩溃日志ips文件解析  一 简介 测试组的同事在进行稳定性测试时,通常会遇到一些崩溃,然后他们会将这些崩溃日志(一般是ips格式的文件)反馈给开发进行分析,但是这些ips文件中的内容通常是如下 ...

  2. SpringBoot Profile使用详解及配置源码解析

    在实践的过程中我们经常会遇到不同的环境需要不同配置文件的情况,如果每换一个环境重新修改配置文件或重新打包一次会比较麻烦,Spring Boot为此提供了Profile配置来解决此问题. Profile ...

  3. Spring配置文件详解 – applicationContext.xml文件路径

    Spring配置文件详解 – applicationContext.xml文件路径 Java编程                 spring的配置文件applicationContext.xml的默 ...

  4. iOS 崩溃日志分析(个人总结,最实用)

    iOS 崩溃日志分析(个人总结,最实用) 要分析奔溃日志需要三个文件:crash日志,symbolicatecrash分析工具,.dSYM符号集 0. 在桌面创建一个crash文件夹 1. 需要Xco ...

  5. 常用获取Android崩溃日志和IOS崩溃日志的几种方法

    一:前言 在日常测试app时,经常会遇到崩溃问题,测试快速抓取到崩溃日志可以有效方便开发进行定位,快速解决问题所在测试做到测试分析,定位是非常重要的,这也是判断一个测试能力指标的一大维度. 二:And ...

  6. iOS崩溃日志

    今天看crash report ,有这样两个crash: 调用 stopUpdatingLocation 函数的是一个CLLocationManager 类型的对象,为什么报错的时候会把这个对象转成N ...

  7. extern的使用详解(多文件编程)——C语言

    extern——关键字 extern是C语言中的一个关键字,一般用在变量名前或函数名前,作用是用来说明“此变量/函数是在别处定义的,要在此处引用”,extern这个关键字大部分读者应该是在变量的存储类 ...

  8. 如何看iOS崩溃日志

    重点:Triggered by Thread这句话后边的线程号,快速定位问题出现在那个线程,是否是你的锅:Triggered by Thread所指的线程表示导致异常.崩溃的线程 下边内容转自简书 简 ...

  9. iOS 崩溃日志 Backtrace的符号化

    iOS的崩溃日志配合dsym文件可以找到崩溃时的backtrace,这是解决崩溃的最重要的信息. 如果是在同一台mac上打包, 导入crash log时候会自动将backtrace符号化,可以看到方法 ...

随机推荐

  1. Your Prediction Gets As Good As Your Data

    Your Prediction Gets As Good As Your Data May 5, 2015 by Kazem In the past, we have seen software en ...

  2. 最好用的xshell替代软件----FinalShell工具

    2017年8月份NetSarang公司旗下软件家族的官方版本被爆被植入后门着实让我们常用的Xshell,Xftp等工具火了一把,很长时间都是在用Xshell,不过最近发现了一款同类产品FinalShe ...

  3. MongoDB探索之路(二)——系统设计之CRUD

    1.构造实体类 using System; using System.Collections.Generic; using System.Linq; using System.Text; using ...

  4. JavaScript继承详解(一)

    面向对象与基于对象 几乎每个开发人员都有面向对象语言(比如C++.C#.Java)的开发经验. 在传统面向对象的语言中,有两个非常重要的概念 - 类和实例. 类定义了一类事物公共的行为和方法:而实例则 ...

  5. int,char指针探究

    #include<iostream> using namespace std; int main() { /* 思路: 1.关于int指针,不可以直接往指针里传值 例:int *a = 4 ...

  6. python垃圾回收三之标记清除

    #第一组循环引用# a = [1,2] b = [3,4] a.append(b) b.append(a) del a ## #第二组循环引用# c = [4,5] d = [5,6] c.appen ...

  7. deeplearning.ai学习seq2seq模型

    一.seq2seq架构图 seq2seq模型左边绿色的部分我们称之为encoder,左边的循环输入最终生成一个固定向量作为右侧的输入,右边紫色的部分我们称之为decoder.单看右侧这个结构跟我们之前 ...

  8. 洛谷 P3749: LOJ 2146: [SHOI2017]寿司餐厅

    题目传送门:LOJ #2146. 题意简述: 有 \(n\) 种寿司,第 \(i\) 种寿司的类型为 \(a_i\). 如果你吃了第 \(i\) 种到第 \(j\) 种寿司,你会得到 \(d_{i,j ...

  9. springboot日志框架

    Spring Boot日志框架Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starters启动器,Spring Boot将使用L ...

  10. 【BZOJ2059】Buying Feed 购买饲料

    题面 约翰开车来到镇上,他要带V吨饲料回家.如果他的车上有X吨饲料,每公里就要花费X^2元,开车D公里就需要D* X^2元.约翰可以从N家商店购买饲料,所有商店都在一个坐标轴上,第i家店的位置是Xi, ...