今天我們來探討一下dSYM文件。我們在使用第三方工具來檢測APP在運(yùn)行的過程中的崩潰情況的時(shí)候,往往會被要求上傳一個(gè)dSYM文件(比如Firebase)。那么dSYM文件究竟是什么、又有什么作用呢?
初識 dSYM
-
dSYM:保存按DWARF格式保存調(diào)試信息的文件。 -
DWARF:是一種被眾多編譯器和調(diào)試器使用的用于支持源代碼級別調(diào)試的調(diào)試文件格式。
那么我們的調(diào)試信息是怎么生成dSYM文件的呢?
i: 讀取debug map
ii: 從.o文件中加載__DWARF
iii: 重新定位所有地址
iiii: 最后將全部的DWARF打包成dSYM Bundle
有了dSYM后,我們就擁有了最標(biāo)準(zhǔn)的DWARF的文件,任何可以dwarf讀取工具(可以處理Mach-O二進(jìn)制文件)都可以處理該標(biāo)準(zhǔn)DWARF。
-
首先我們來看一下這樣一個(gè)文件:
執(zhí)行下面的指令:
clang -g -c main.m -o main.o
/*
-g 代表生成調(diào)試信息
*/
之后我們會得到一個(gè)__DWARF的Section(這里面保存的就是調(diào)試信息);也就是說編譯器在編譯的時(shí)候會將調(diào)試信息存放在__DWARF里面,但是當(dāng)鏈接的時(shí)候,會將__DWARF段刪除,將調(diào)試信息放到符號表里面。
在終端執(zhí)行objdump --macho --private-headers main.o指令,可以在終端查看.o文件的內(nèi)容。

- 生成
dSYM文件
clang -g1 main.m -o main
/*
-g1 生成可執(zhí)行文件的時(shí)候,同時(shí)生成dSYM文件
*/

- 查看
dSYM文件
dwarfdump main.dSYM

可以看到
dSYM文件里面保存了符號對應(yīng)的一些信息,比如:地址,名稱等等。這就是按一定的格式保存的調(diào)試信息。其實(shí)這就是將我們符號表里的調(diào)試信息,抽取出來,按照DWARF的格式,從新生成了dSYM文件。
dSYM 在實(shí)際開發(fā)中的應(yīng)用
首先我們來看一下我們的案例:

看到上面的代碼,大家都清楚,運(yùn)行起來會崩潰,因?yàn)閿?shù)組越界。
那么我們來看一下Xcode的打印信息:

可以看到Xcode可以精準(zhǔn)的打印出崩潰的代碼信息。這是因?yàn)閄code保存了我們的調(diào)試符號信息。
正常的開發(fā)過程中,APP上線之后我們并不能像這樣再Xcode里面查看崩潰信息,通常我們拿到的是一個(gè)crash文件。此時(shí)我們可以通過控制臺來模擬一下這個(gè)情況:

-
大家也知道,我們APP在上架的時(shí)候會進(jìn)行脫符號,這個(gè)時(shí)候就看不到具體的崩潰信息了。如下:
脫符號 - 此時(shí)我們添加一個(gè)腳本來生成我們的
dSYM文件:
rm -rf -- "${SRCROOT}/../dSYM" ///刪除dSYM目錄
mkdir -p -- "${SRCROOT}/../dSYM" ///生成dSYM目錄
cp -Rv -- ${BUILT_PRODUCTS_DIR} "${SRCROOT}/../dSYM" ///把編譯的產(chǎn)物,拷貝到指定的目錄下
/// 如果只想拷貝dSYM文件,可以這樣寫
/// cp -Rv -- ${BUILT_PRODUCTS_DIR}/*.dSYM "${SRCROOT}/../dSYM"

-
我們再來運(yùn)行一下,看一下此時(shí)的控制臺的內(nèi)容:
控制臺信息
可以看到,此時(shí)控制臺并沒有清楚的打印出崩潰的符號信息,只剩下一個(gè)地址。
這里要補(bǔ)充一個(gè)知識點(diǎn):
ASLR地址空間配置隨機(jī)加載。也就是說我們的在運(yùn)行時(shí)調(diào)試到的地址實(shí)際上是這樣的:
調(diào)試地址 = 虛擬地址 +ASLR
通過上面我們知道,
dSYM文件是編譯鏈接的時(shí)候生成的,而ASLR所產(chǎn)生的偏移地址是dyld在調(diào)用程序的時(shí)候才加上去的。所以dSYM里面保存的是偏移前的虛擬內(nèi)存地址。
在我們的
Mach-o中,第一個(gè)地址的起始地址是:
那么在Mach-o中進(jìn)行偏移的話,也是以Mach-o為單位進(jìn)行偏移的。
-
我們再來看一下控制臺信息,獲取偏移地址量:
偏移地址
錯(cuò)誤信息地址
那么偏移前的內(nèi)存地址就是:
/// 偏移前的內(nèi)存地址 = 偏移后的地址 - 偏移量
0x0000000100001E70 = 0x0000000101ae6e70 - 0x1ae5000
- 下面我們通過計(jì)算出來的地址,在
dSYM文件中查找一下我們要的崩潰信息:
dwarfdump --lookup 0x0000000100001E70 TestInject.app.dSYM

這樣就得到了我們需要的崩潰信息。這也就是我們在使用第三方工具的時(shí)候,能夠精準(zhǔn)定位到我們的報(bào)錯(cuò)代碼的原因。
如何查找Xcode幫我們生產(chǎn)的dSYM文件
- 打包時(shí)
我們的每一次Archive都會生成一條記錄。找到記錄所在的目錄,在.achive目錄下就可以找到dSYM文件。 - debug
在debug的時(shí)候,如果不想通過上面腳本的方式將dSYM文件拷貝出來,可以在編譯產(chǎn)物里面查找:
另外我們還可以選擇是否生成dSYM File:

- tips
我們在進(jìn)行算術(shù)運(yùn)算的時(shí)候,可以通過(lldb)終端命令來計(jì)算:
--:表示后面的不再是e的指令
d:代表十進(jìn)制
b:代表二進(jìn)制
x:代表十六禁止
如果想要進(jìn)制轉(zhuǎn)換,直接帶上數(shù)字就可以了:
(lldb) e -f x -- 10
(int) $2 = 0x0000000a
這樣我們在Xcode的控制臺就可以很方便的進(jìn)行算術(shù)運(yùn)算了。







