iOS Crash log符号化庖丁解牛

项目在做zombie内存监测的时候有把zombie调用栈和oc对象释放栈报上来,由于我们的crash组件是用的第三方组件,zombie栈没法和crash log一起符号化,要自己对栈进行符号化,研究了一下CrashLog的还原原理和方法。

Crash Log符号还原方法

使用xcode

这是最简单的方法,要使用xcode进行符号还原需要下面三个文件:

Crash Reports(.crash文件)
符号文件 (.dsymb文件)
应用程序文件 (appName.app文件)

把这3个文件放到同一个目录下,打开Xcode的Window菜单下的organizer,然后点击Devices tab,然后选中左边的Device Logs。然后把.crash文件拖到Device Logs或者选择下面的import导入.crash文件。

使用symbolicatecrash

symbolicatecrash是xcode自带的工具
将“.app“, “.dSYM”和 “.crash”文件放到同一个目录下,终端设置如下环境:

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

然后输入下面命令

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash appName.crash appName.app > appName.log

使用atos命令行

前面两种方法足够简单,但都不适合脚本自动化,并且我们也没有原始.crash文件,
本文重点介绍这种方法和内部的原理。
atos命令可以对指定的地址进行符号化

NAME
atos -- convert numeric addresses to symbols of binary images or processes
SYNOPSIS
atos [-o <binary-image-file>] [-p <pid> | <partial-executable-name>] [-arch architecture][-l <load-address>] [-s <slide>] [-printHeader] [-v] [-D] [-f <address-input-file>] [<address> ...]
DESCRIPTION
The atos command converts numeric addresses to their symbolic equivalents. If full debug symbol infor-mation informationmation is available, for example in a .app.dSYM sitting beside a .app, then the output of atos will
include file name and source line number information.

符号还原原理

首先来看下需要还原的栈长啥样

0   AppName                     0x00000001009e3110 _ZNSt3__111char_traitsIcE2eqEcc + 7884972
1   AppName                     0x0000000100620f04 _ZNSt3__111char_traitsIcE2eqEcc + 3944096
2   CoreFoundation              0x0000000188e58f60 <redacted> + 132
3   CoreFoundation              0x0000000188d5280c _CF_forwarding_prep_0 + 92

看栈可以知道每个frame包括image名、代码地址、代码地址对应的符号信息,现在代码地址对应的符号是一串奇怪东东,我们要做的就是把这串奇怪的东东还原成可读的信息,包括函数名、原文件名、代码行。
要还原符号信息必须解决下面问题

  • 从哪里找函数名、原文件名、代码行这些信息
  • 怎么找这些信息

从哪里找函数名、原文件名、代码行这些信息

dSYM和DWARF

dSYM(debugging SYMbol)是从Mach-O文件中抽取调试信息而得到的文件目录,发布的时候为了安全和减小安全,一般会把调试信息存储在单独的文件,dSYM实际是一个文件目录,其目录结构如下:

 |--AppName.app.dSYM
    |--Contents
      |--info.plist
      |--Resources
        |--DWARF
          |--AppName

dSYM符号信息实际存储在DWARF文件里面,DWARF (DebuggingWith Arbitrary Record Formats)是起源贝尔实验室的一种调试信息文件格式,是ELF和Mach-O等文件格式中用来存储和处理调试信息的标准格式。
DWARF文件包含所有调试信息,并且以section的形式进行存储,DWARF使用DIE(Debugging Information Entry)来存储具体信息,DIE通过树结构组织,DIE可以有兄弟节点和子节点。DWARF文件包括下面section:

.debug_abbrev              Abbreviations used in the .debug_info section
.debug_aranges             A mapping between memory address and compilation
.debug_frame               Call Frame Information
.debug_info                The core DWARF data containing DIEs
.debug_line                Line Number Program
.debug_loc                 Macro descriptions
.debug_macinfo             A lookup table for global objects and functions
.debug_pubnames            A lookup table for global objects and functions
.debug_pubtypes            A lookup table for global types
.debug_ranges              Address ranges referenced by DIEs
.debug_str                 String table used by.debug_info

其中主要信息存储在debug_info和debug_line里,debug_info存储了函数信息、变量信息等,debug_line存储了对应源代码行数信息。可以用dwarfdump工具读取dwarf文件里的section。使用dwarfdump读取下面demo dSYM文件的section

@interface DSYMDemo : NSObject
@property (nonatomic, strong) NSString* var1;
- (NSString*)test;
@end
debug_info

使用dwarfdump读取DWARF文件debug_info信息

dwarfdump -e --debug-info DSYMDemo.app.dSYM/Contents/Resources/DWARF/DSYMDemo > debug-info.txt
0x00034d29:     function [119] *
                low pc( 0x0000000100006adc )
                high pc( 0x0000000100006b14 )
                frame base( reg29 )
                object pointer( {0x00034d49} )
                name( "-[DSYMDemo setVar1:]" )
                decl file( "/Users/haishengding/Desktop/test/DSYMDemo/DSYMDemo/DSYMDemo.h" )
                decl line( 13 )
                prototyped( 0x01 )
                artificial( 0x01 )
                APPLE optimized( 0x01 )

可以看到DIE里面包括了函数开始地址、结束地址、函数名、原文件名、开始地址在原文件的行数。对于给定的地址,找到函数开始地址和结束地址之间包含改地址的DIE,则可以还原函数名和原文件名。

debug_line

通过debug_info还原了函数名、原文件名,剩下原文行数则通过debug_line进行还原

dwarfdump -e --debug-line DSYMDemo.app.dSYM/Contents/Resources/DWARF/DSYMDemo > debug-line.txt
Address                Line  File
------------------ ------ ------------------------------
0x0000000100006ac0     13 ~/Desktop/test/DSYMDemo/DSYMDemo/DSYMDemo.m
0x0000000100006ac0     14
0x0000000100006acc     13 ~/Desktop/test/DSYMDemo/DSYMDemo/DSYMDemo.h
0x0000000100006acc     13
0x0000000100006adc     13
0x0000000100006aec      0
0x0000000100006b14     11 ~/Desktop/test/DSYMDemo/DSYMDemo/DSYMDemo.m
0x0000000100006b14     11
0x0000000100006b28     11

可以看到debug_line里面包含了每个代码地址对应的行数。

怎么找这些信息

已经知道对于指定的地址,通过dSYM文件可以还原符号信息,但怎么拿到这个地址呢?开始的函数栈frame有一个地址,就是这个地址吗?当然没这么简单,我们知道image加载的时候都会相对基地址进行重定位,并且每次加载的基地址都不一样,函数栈frame的地址是重定位后的绝对地址,我们要的是重定位前的相对地址。

Binary Images

Binary Images:
0x100050000 - 0x101c07fff +AppName arm64 <ab90a1c5646f35dca8f8cf1ce74c767c> /var/containers/Bundle/Application/175ED3FA-7329-49DE-B54A-88EEC120412C/AppName.app/AppName
0x187ee6000 - 0x187eeffff  libsystem_pthread.dylib arm64 <d8480fc3a35d3475b0d12553c761d8cb> /usr/lib/system/libsystem_pthread.dylib
0x187e04000 - 0x187e28fff  libsystem_kernel.dylib arm64

可以看到Crash Log的Binary Images块包含每个image加载起止地址、image名、arm架构、uuid、image路径。

frame
0   AppName   0x00000001009e3110 _ZNSt3__111char_traitsIcE2eqEcc + 7884972
Binary Image
0x100050000 - 0x101c07fff +AppName arm64 <ab90a1c5646f35dca8f8cf1ce74c767c> /var/containers/Bundle/Application/175ED3FA-7329-49DE-B54A-88EEC120412C/AppName.app/AppName

frame0的地址0x00000001009e3110-0x100050000 就是函数的相对地址,使用改地址通过dSYM文件就可以还原符号。

UUID

符号还原的时候必须通过匹配的dSYM,dSYM和image是通过UUID进行关联的,两者的UUID必须一样才能正确还原,image的UUID在Binary Images可以拿到,dSYM 的UUID可以通过dwarfdump读取

dwarfdump -u -arch arm64 AppName.app.dSYM/Contents/Resources/DWARF/AppName
UUID: AB90A1C5-646F-35DC-A8F8-CF1CE74C767C (arm64) AppName.app.dSYM/Contents/Resources/DWARF/AppName

可以看到读取的跟image里的是一样的。

使用atos命令行

上面讲了符号还原的原理,实际上atos工具帮我们做了这些事情,只要通过简单的命令就看还原了

atos -o AppName.app.dSYM/Contents/Resources/DWARF/AppName -arch arm64 -l 0x100050000 0x00000001009e3110
currentCallStack (in AppName) (xxx.m:17)

其中0x100050000是image加载地址,* 0x00000001009e3110*是需要符号还原的绝对地址,atos自己会转成相对地址

系统符号

dSYM文件只有我们自己代码的符号,系统函数则必须通过系统符号文件进行还原,系统符号一般存储在~/Library/Developer/Xcode/iOS DeviceSupport目录

0x187ee6000 - 0x187eeffff  libsystem_pthread.dylib arm64 <d8480fc3a35d3475b0d12553c761d8cb> /usr/lib/system/libsystem_pthread.dylib
OS Version: iPhone OS 10.2 (14C92)

通过Crash Log文件Bianry Images和OS Version信息可找到对应的符号文件。

脚本化

知道怎么还原具体frame栈桢的符号了,通个脚本解析每个栈桢就可以自动化还原整个栈了,像buggly平台应该也是用类似的方案。

以上内容为本人工作学习中所得,如有错误之处,还请指出!

参考文件

Symbolicating Your iOS Crash Reports
Understanding and Analyzing Application Crash Reports
Introduction to the DWARF Debugging Format

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,565评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,021评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,003评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,015评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,020评论 5 370
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,856评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,178评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,824评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,264评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,788评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,913评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,535评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,130评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,102评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,334评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,298评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,622评论 2 343

推荐阅读更多精彩内容