解决崩溃问题是移动应用开发者最日常的工作之一。如果是开发过程中遇到的crash,可以按照复现步骤调试,但是网络版就无能为力了。幸运的是,目前已经有很多不错的第三方CrashLog采集平台(如友盟、Crashlytics等)为我们做了分析工作。甚至在Xcode7中,苹果也跟进了分析在线版崩溃日志的功能,为开发者减轻了很多负担。虽然我们通常不需要手动处理CrashLog,但是了解CrashLog恢复的原理和方法还是很有必要的。1..dSYM.dSYM(debuggingSYMbols),也称为调试符号表,是Apple为了方便调试和定位问题而使用的一种调试方案。本质上,它使用源自贝尔实验室的DWARF(DebuggingWithAttributedRecord)。Formats),其在.xcarchive目录下的层次结构为:.xcarchive--dSYMs|--Your.app.dSYM|--Contents|--Resources|--DWARFDWARF的具体内容后面会讲到。我们可以全部通过.dSYM文件来解析CrashLog,解析方法见下文。2.判断符号表和crashlog的一致性。有了符号表文件和crashlog文件,在解析之前一定要保证两者一一对应。否则,即使按照下面的步骤解析内容,也肯定是不准确的。.两者的对应关系可以通过UUID来确定。1.从crashlog中获取UUID在crashlog的下方有一个BinaryImages模块,***行内容如下:BinaryImages:0xa2000-0x541fffYourarmv7/var/mobile/Containers/Bundle/Application/645D3184-4C20-4161-924B-BDE170FA64CC/Your.app/你可以看到你的应用程序的一些信息:代码段的起始地址和结束地址是:0xa2000–0x541fff运行你的应用程序的CPU指令集是:armv7应用程序的UUID为:a5c8d3cfda65396689e4370bf3a0ac64(不区分大小写)2.从符号表中获取UUID执行以下命令从符号表中提取UUID:dwarfdump--uuidYour.app.dSYM或:dwarfdump--uuidYour.app.dSYM/Contents/Resources/DWARF/Your的执行结果是:UUID:A5C8D3CF-DA65-3966-89E4-370BF3A0AC64(armv7)Your.app.dSYM/Contents/Resources/DWARF/Your从而得到UUID的armv7指令集:A5C8D3CF-DA65-3966-89E4-370BF3A0AC64(如果你的二进制文件支持多个instructionsets,这里会列出每个指令集对应的符号表的UUID)。与crashlog对比后,发现两者是一致的,接下来可以进行进一步的解析操作。三、计算崩溃符号表地址以下面的崩溃堆栈为例:Thread0:0libobjc.A.dylib0x33f10f600x33efe000+776641Foundation0x273526ac0x2734a000+344762Foundation0x27355c3e0x2734a000+481903UIKit0x29ef9d1c0x29bbc000+33989404UIKit0x29ef9c9a0x29bbc000+33988105UIKit0x29ef954c0x29bbc000+33969406UIKit0x29c3a16a0x29bbc000+5164587UIKit0x29e4b8e60x29bbc000+26851588UIKit0x29c3a1280x29bbc000+5163929Your0x000f08460xa2000+32160610UIKit0x29e90fb20x29bbc000+296952211UIKit0x29e910760x29bbc000+296971812UIKit0x29e867cc0x29bbc000+292654013UIKit0x29c9e8ea0x29bbc000+92797814UIKit0x29bc8a6a0x29bbc000+5181815QuartzCore0x295f0a080x295e4000+5172016QuartzCore0x295ec3e00x295e4000+3376017QuartzCore0x295ec2680x295e4000+3338418QuartzCore0x295ebc4c0x295e4000+3182019QuartzCore0x295eba500x295e4000+3131220QuartzCore0x295e59280x295e4000+644021CoreFoundation0x266d0d920x26604000+83905822CoreFoundation0x266ce44e0x26604000+82849423CoreFoundation0x266ce8560x26604000+82952624CoreFoundation0x2661c3bc0x26604000+9926025CoreFoundation0x2661c1ce0x26604000+9876626GraphicsServices0x2da1a0a40x2da11000+3702827UIKit0x29c2a7ac0x29bbc000+45252428Your0x0024643a0xa2000+172140229libdyld.dylib0x34484aac0x34483000+68281、符号表堆栈地址计算方式要想利用符号表解析出崩溃对应位置,需要计算出符号表中对应的崩溃堆栈地址而从上述堆栈中第9行Itcanbeseenthattheapplicationcrashoccursattheruntimeaddress0x000f0846,theruntimestartaddressoftheprocessis0xa2000,andtheoffsetfromthecrashpointtotheprocessstartaddressis321606indecimal(correspondingto0x4E846inhexadecimal).Correspondencebetweenthethree:0x000f0846=0xa2000+0x4E846Thecorrespondingformulais:runtimestackaddress=runtimestartaddress+offsetThestartaddressandcrashaddressinthecrashstackarebothruntimeaddresses,accordingtothevirtualmemoryoffsetTheprincipleofconstantquantity,aslongasthestartaddressoftheTEXTsegmentofthesymboltableisprovided,plustheoffset(0x4E846here),thestackaddressinthesymboltablecanbeobtained,thatis:thestackaddressofthesymboltable=thestartaddressofthesymboltable+Offset2.ObtainthestartaddressoftheTEXTsegmentinthesymboltableThestartaddressoftheTEXTsegmentinthesymboltablecanbeobtainedthroughthefollowingcommand:$otool-lYour.app.dSYM/Contents/Resources/DWARF/YourrunningresultsThefragmentisasfollows:Loadcommand3cmdLC_SEGMENTcmdsize736segname__TEXTvmaddr0x00004000vmsize0x00700000fileoff0filesize0maxprot0x00000005initprot0x00000005nsects10flags0x0Thestartaddressofthevmaddr0x00004000segmentistheTEXTsegment.3.Calculatethesymboltableaddressbytheformula:symboltablestackaddress=symboltablestartaddress+offset:0x52846=0x4E846+0x4000,thatis,thecrashaddressinthesymboltableis0x52846,andthenyoucananalyzeitaccordingtothisaddressCrashlocation.4、崩溃信息的恢复有了符号表的崩溃地址,分析崩溃信息的方法有以下几种:1、dwarfdump命令如下:$dwarfdump--archarmv7Your.app.dSYM--lookup0x52846|grep'Linetable'需要注意的是:这里的armv7是运行设备的CPU指令集,不是二进制文件的指令集。比如armv7指令集的二进制文件运行在arm64指令集的设备上。这个地方应该写arm64。--lookup后面必须是准确计算出的符号表中的崩溃地址。用dwarfdump分析结果比较乱,所以用grep命令抓取关键点显示出来。运行结果如下:Linetabledir:'/data/.../src/OBDConnectSetting/Controller'Linetablefile:'OBDFirstConnectViewController.m'line882,column5withstartaddress0x000000000052768其中,第一行是编译时的文件目录,第二行包含发生崩溃的文件名和文件中的具体行号等,有了这些信息就可以准确定位崩溃的原因。2、atosatos是另一种更简洁的crashlog解析方式,使用方法如下:$atos-oLuBao-archarmv70x52846执行结果如下:-[OBDFirstConnectViewControllershowOilPricePickerView](inYour)(OBDFirstConnectViewController.m:882)对比解析dwarfdump命令的结果,更简洁直观地指出了崩溃的位置。3、不用符号表解析崩溃地址的方法其实atos还提供了另一种方法,不需要计算崩溃地址对应的符号表地址。命令格式如下:$atos-oYour.app.dSYM/Contents/Resources/DWARF/Your-archarmv7-l0xa20000x000f0846其中-l选项指定运行时二进制文件的起始地址0xa2000(参见BinaryImages相关内容howtoobtainit),后面是crash的runtimeaddress0x000f0846,通过分析结果,使用计算得到和符号表中的crash地址一致:-[OBDFirstConnectViewControllershowOilPricePickerView](inYour)(OBDFirstConnectViewController.m:882)
