什么是崩潰日志
iOS的App在崩潰時,系統(tǒng)會記錄下當前的每個線程的調用棧信息等等,并保存到設備中兔簇。這些信息匯總起來就是我們所說的崩潰日志塘装。
iOS崩潰日志收集的幾種方式
- 通過Xcode的
Window > Devices
進入設備管理,使用View Device Logs查看 - 通過第三方SDK上傳用戶的崩潰日志备图,比如使用友盟灿巧,BugHD,Bugly等等
- 通過Apple后臺搜集崩潰日志揽涮,可以在Organizer里直接查看線上的崩潰日志抠藕。但是如果用戶選擇不上傳診斷信息,就看不到日志了蒋困。
為什么要符號化
從iOS設備采集的原始崩潰日志記錄的都是調用棧地址
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0:
0 libsystem_kernel.dylib 0x000000019959cb3c 0x19959c000 + 2876
1 libsystem_platform.dylib 0x0000000199679534 0x199674000 + 21812
2 libobjc.A.dylib 0x0000000198c5b200 0x198c44000 + 94720
3 libobjc.A.dylib 0x0000000198c6443c 0x198c44000 + 132156
4 CoreFoundation 0x0000000184190cbc 0x184064000 + 1232060
......
這樣的崩潰日志是無法使用的盾似,通過符號化可以將它轉換成可讀的日志
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0:
0 libsystem_kernel.dylib 0x000000019959cb3c syscall_thread_switch + 8
1 libsystem_platform.dylib 0x0000000199679534 _os_lock_handoff_lock_slow + 120
2 libobjc.A.dylib 0x0000000198c5b200 spinlock_t::lockTwo(spinlock_t*, spinlock_t*) + 52
3 libobjc.A.dylib 0x0000000198c6443c objc_storeWeakOrNil + 120
4 CoreFoundation 0x0000000184190cbc _NSObjectStoreWeak + 80
5 CoreFoundation 0x00000001841167f8 -[_CFXNotificationObjcObserverRegistration initWithObserver:parent:] + 92
......
怎樣符號化
大多數(shù)iOS程序員應該都知道使用View Device Logs查看設備日志時會自動符號化崩潰日志,但有時候可以符號化雪标,但有時候卻不行零院。
為什么呢?除了通過View Device Logs自動符號化文件村刨,還有沒有其他方式可以手動符號化崩潰日志呢告抄?下面會一一解答這些問題。
符號化的原理
dSYM文件
dSYM文件(符號文件)是存儲了類名和方法名等調試信息的文件嵌牺。在release模式下打洼,build后會有.app.dSYM文件。
debug模式下調試信息在可執(zhí)行文件中逆粹,所以不會生成dSYM文件募疮。符號化就是通過在dSYM文件查找地址對應的方法名來實現(xiàn)的。
Xcode的View Device Logs符號化時如何尋找符號文件
每個Crash日志都記錄了它所對應符號文件的uuid僻弹,打開一個crash文件酝锅,可以看到類似于下面這行的信息
Binary Images:
0x10006c000 - 0x100073fff Demo1 arm64 <2cf1790547ff3a1cac055152319617ba> /var/mobile/Containers/Bundle/Application/7E6DE925-0B33-4699-89F7-05381876AD81/Demo1.app/Demo1
例子中的App名字是Demo1,符號文件所對應的uuid就是2cf1790547ff3a1cac055152319617ba
奢方。當符號化這個崩潰日志的時候搔扁,符號化工具會通過這個uuid去尋找對應的dSYM文件爸舒,如果找到了就可以正確的符號化了。
由此我們就可以知道稿蹲,使用View Device Logs查看崩潰日志時扭勉,有時不能符號化的原因就是沒有查到對應的dSYM文件。因為每次build都會導致uuid發(fā)生變化苛聘,所以大部分的崩潰日志所對應的dSYM文件其實都被覆蓋掉了涂炎。
通過符號化命令行工具深入了解符號化的過程
在Xcode7.0中可以在一下路徑找到符號化命令行工具
/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
可以為這個工具設置alias:
alias symbolicatecrash="/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash"
這時候直接調用的話會報
Error: "DEVELOPER_DIR" is not defined at ...
這樣的錯誤,只要設置環(huán)境變量DEVELOPER_DIR
就行了 设哗。
export 'DEVELOPER_DIR'="/Applications/Xcode.app/Contents/Developer"
后面我都會直接使用symbolicatecrash
進行命令行下的符號化
Xcode就是通過這個工具符號化崩潰日志的
在符號化之前我們先準備好幾樣東西:
- 原始的崩潰日志
- dSYM文件
- symbolicatecrash可執(zhí)行文件
我們先查看crash日志是否和符號文件是匹配的唱捣,下面的命令將提取dSYM文件的uuid
xcrun dwarfdump --uuid Demo1.app.dSYM/Contents/Resources/DWARF/Demo1
我提取出來的uuid是:
UUID: 2CF17905-47FF-3A1C-AC05-5152319617BA (arm64) Demo1.app.dSYM/Contents/Resources/DWARF/Demo1
崩潰日志的uuid是:
0x10006c000 - 0x100073fff Demo1 arm64 <2cf1790547ff3a1cac055152319617ba> /var/mobile/Containers/Bundle/Application/7E6DE925-0B33-4699-89F7-05381876AD81/Demo1.app/Demo1
都是 2cf1790547ff3a1cac055152319617ba
,所以使用這個dSYM文件既可以符號化上面的崩潰日志。
用symbolicatecrash來符號化崩潰日志
symbolicatecrash demo1.crash Demo1.app.dSYM -o processed.crash
符號化后的crash文件將會被寫入processed.crash
中,
然后我們來看看如果把Demo1.app.dSYM
去掉會怎樣,為了看到符號化的過程网梢,我們可以加上 -v
symbolicatecrash demo1.crash -o processed.crash -v
我們主要看Demo1的符號化過程震缭,其他系統(tǒng)調用其實也會在這個過程中被符號化。#后面是我添加的注釋
-- [2cf1790547ff3a1cac055152319617ba] fetching symbol file for Demo1 #開始尋找Demo1的符號文件
Running mdfind "com_apple_xcode_dsym_uuids == 2CF17905-47FF-3A1C-AC05-5152319617BA" #使用Spotlight 搜索uuid為2CF17905-47FF-3A1C-AC05-5152319617BA的dsym文件
#接下來是使用file,lipo,otool等來分析dSYM的相關信息
.
.
-- [2cf1790547ff3a1cac055152319617ba] MATCH (spotlight): ... #確認找到對應的dSYM文件
.
.
atos -arch arm64 -l 0x10006c000 -o '..../Demo1.app.dSYM/Contents/Resources/DWARF/Demo1' 0x00000001000706e8 0x0000000100070a80
我們看到最后會發(fā)現(xiàn)實際上symbolicatecrash
是使用atos
來尋找調用棧地址對應的調試符號的战虏。
我們來看一下atos
所使用的參數(shù):
- -arch 所運行設備的架構拣宰,有arm64,armv7等等
- -l 二進制鏡像運行時加載的地址
- -o 后面是符號文件或者含有調試符號的可執(zhí)行文件(debug編譯所產生的可執(zhí)行文件默認是包含調試符號的)烦感。
- 再后面就是需要符號化的調用棧地址巡社,
5 Demo1 0x00000001000706e8 0x10006c000 + 18152
,0x00000001000706e8
就是其中的一個地址。
二進制鏡像運行時加載的地址通過如下方式獲得
Binary Images:
0x10006c000 - 0x100073fff Demo1 arm64 <2cf1790547ff3a1cac055152319617ba> /var/mobile/Containers/Bundle/Application/7E6DE925-0B33-4699-89F7-05381876AD81/Demo1.app/Demo1
中的0x10006c000
就是這個崩潰日志所對應的二進制鏡像加載地址手趣。
綜上所述晌该,symbolicatecrash
主要幫我們做了兩件事情,匹配到對應的dSYM文件绿渣,使用atos
符號化每個調用棧地址朝群。