前言
我們一般使用友盟等第三方平臺來收集程序崩潰信息,但是一般情況下收集到的崩潰信息是我們無法看懂的,都是一些16進制的內(nèi)存地址,無法準(zhǔn)確定位到出錯的代碼政敢。通常像這樣的情況是由于我們沒有上傳符號文件到第三方平臺,所以無法解析出具體的代碼胚迫。
.dSYM為后綴的文件就是符號文件喷户,里面存儲著程序的符號表,也就是函數(shù)和內(nèi)存地址的對應(yīng)關(guān)系访锻,有了這個符號文件褪尝,我們就可以通過崩潰信息中的內(nèi)存地址解析出具體的出錯代碼,每次打包App生成的符號文件都可能不一樣朗若,所以說崩潰發(fā)生在哪個App版本就使用哪個版本的符號文件解析崩潰代碼恼五。
因為打包好之后的App安裝包和符號文件是分開來的(出于安裝包大小的考量)昌罩,所以App包中沒有符號文件哭懈,因此線上版本的App產(chǎn)生崩潰時只能得到崩潰地址,具體的解析工作還需拿到對應(yīng)的dSYM符號文件之后才能進行茎用。本地開發(fā)時符號文件就在本地遣总,所以出現(xiàn)崩潰時Xcode打印出來的崩潰信息能看到具體的崩潰代碼睬罗。
-
dSYM符號文件的位置
Xcode -> window -> Organizer -> 選擇對應(yīng)的包然后show in finder -> 顯示包內(nèi)容 -> dSYMs文件夾里存放的文件就是符號文件
-
自己收集崩潰信息
通過自己收集崩潰信息,我們能夠理解更多問題
1.首先在AppDelegate文件的didFinishLaunchingWithOptions方法中設(shè)置崩潰回調(diào)函數(shù),程序出現(xiàn)崩潰時會調(diào)用該函數(shù)旭斥,我們在該函數(shù)里面收集崩潰信息
2.實現(xiàn)該函數(shù)handleException()
3.獲取程序slide偏移值 (現(xiàn)在的App在運行到內(nèi)存以后容达,會在前面隨機空出一部分內(nèi)存,而slide值就是這個隨機空白段的長度垂券,由于我們打包生成的符號文件是不加這個空白段的花盐,所以我們收集到的崩潰地址需要減去slide值,才能在符號文件中解析出正確的信息)
崩潰的收集代碼如下
#import "AppDelegate.h"
#import <mach-o/dyld.h> //需要導(dǎo)入這個頭文件 使用獲取偏移值的api
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//1菇爪、設(shè)置異乘阈荆回調(diào)函數(shù)
NSSetUncaughtExceptionHandler(handleException);
return YES;
}
//重要,用于獲取偏移值
long calculate(void) {
long s = 0;
for (uint32_t i = 0; i < _dyld_image_count(); i++) {
if (_dyld_get_image_header(i)->filetype == MH_EXECUTE) {
long slide = _dyld_get_image_vmaddr_slide(i);
s = slide;
break;
}
}
return s;
}
/**
* 2凳宙、異澄踝幔回調(diào)函數(shù)實現(xiàn)
*/
void handleException(NSException *exception){
//2.設(shè)備型號
//3.系統(tǒng)版本
NSString *OSVersion = [NSString stringWithFormat:@"iOS %@",[UIDevice currentDevice].systemVersion];
//4.崩潰名
NSString *exceptionName = [exception name];
//5.崩潰原因
NSString *reason = [exception reason];
//6.函數(shù)調(diào)用棧信息(重要)
NSArray *array = [exception callStackSymbols]; //調(diào)用棧信息 拼接成字符串
NSString *callStackStr = @"";
for (NSString *str in array) {
callStackStr = [NSString stringWithFormat:@"%@\n%@",callStackStr,str];
}
//7.崩潰前畫面app畫面截圖(可選,沒有太多參考價值)
//8.崩潰時間
//9.app版本
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
NSString *AppVersion = [infoDict objectForKey:@"CFBundleShortVersionString"];
//10.偏移值(重要)
NSString *slide = [NSString stringWithFormat:@"%ld",calculate()];
//11.上傳崩潰信息到自己的后臺
...
exit(0);// 需要手動殺死程序
}
-
分析崩潰信息
上面就是我獲得的函數(shù)調(diào)用棧callStackSymbols信息氏涩,第一列為調(diào)用順序届囚,第二列就是對應(yīng)的庫,第三列是對應(yīng)方法函數(shù)對應(yīng)的內(nèi)存地址是尖。從第二列可以看出意系,大部分都是系統(tǒng)庫調(diào)用,這是我們無法修改的饺汹,所以不用管昔字。我們只關(guān)注項目名所對應(yīng)的那幾行,那才是我們自己編寫的代碼首繁,所以只需要關(guān)注5作郭、6、22行的信息弦疮。由于棧的調(diào)用順序夹攒,我們只需要分析最根層的方法即可,因為那才是崩潰發(fā)生的根源胁塞,也就是說只需要分析第5行咏尝。
由上面的崩潰信息可以看到第5行的內(nèi)存地址是0x00000001012f9d90,我們可以通過命令行去dSYM符號文件中尋找該內(nèi)存地址所對應(yīng)的方法啸罢,這樣就可以分析發(fā)生崩潰的方法了编检。
但是現(xiàn)在的iOS系統(tǒng)增加了一些安全措施,僅僅通過這個地址我們還無法從dSYM文件中解析出有用信息扰才,因為每次運行程序時允懂,系統(tǒng)都會在程序內(nèi)存前面加上一段隨機的空白段,所以我們需要用當(dāng)前得到的地址0x00000001012f9d90減去隨機空白段的長度才能得到最終的地址衩匣,只有這個地址才可以從dSYM文件中解析出正確的方法信息蕾总。這個偏移值我們可以通過系統(tǒng)的api獲取粥航,在上面的崩潰收集方法里已經(jīng)收集了slide偏移值。
-
解析方法
將第5行需要解析的地址減去偏移值slide得到最終地址(地址是16進制表示的生百,而偏移值是十進制的递雀,注意轉(zhuǎn)換)
1??使用命令行解析
- 打開終端,cd到dSYM文件所在的目錄
- 在終端運行如下命令即可打印顯示崩潰所在的類蚀浆、方法和行數(shù)信息
$atos -o 你的項目名.app.dSYM/Contents/Resources/DWARF/你的項目名 最終地址
2??使用DSYMTools工具解析
參考文章
沒有dsym符號文件如何解析崩潰
用命令和.dSYM 文件查找錯誤日志
偏移值的獲取和符號地址的計算
dSYMTools
iOS自己捕獲異常定位錯誤代碼