深入理解debuginfo(轉(zhuǎn)載)

一那先、關(guān)于debuginfo的疑惑
程序員應(yīng)該都知道,為了能夠使用gdb跟蹤程序赡艰,需要在編譯期使用gcc的-g選項(xiàng)售淡。而對(duì)于系統(tǒng)庫(kù)或是Linux內(nèi)核,使用gdb調(diào)試或使用systemtap探測(cè)時(shí)慷垮,還需要安裝相應(yīng)的debuginfo包揖闸。

what is systemtap ?
https://segmentfault.com/a/1190000010774974

例如glibc及它的debuginfo包為:

image.png

...

于是我不禁有如下這些疑問:

glibc-debuginfo中包含了什么信息?

glibc-debuginfo是如何創(chuàng)建出來的料身?

gdb或systemtap汤纸,是如何把glibc與glibc-debuginfo關(guān)聯(lián)起來的?

本文將通過一些例子芹血,來解答這些問題贮泞。

二楞慈、debuginfo中包含了什么信息?
讓我們來看看glibc-debuginfo中啃擦,包含有什么內(nèi)容:

[yunkai@fedora t]$ rpm -ql glibc-debuginfo-2.18-12.fc20.x86_64

/usr/lib/debug

/usr/lib/debug/.build-id

/usr/lib/debug/.build-id/00

/usr/lib/debug/.build-id/00/a32f1b9405f5fcd41a7618f3c2c895ee4aab09

/usr/lib/debug/.build-id/00/a32f1b9405f5fcd41a7618f3c2c895ee4aab09.debug

…

/usr/lib/debug/lib64/libthread_db.so.1.debug

/usr/lib/debug/lib64/libutil-2.18.so.debug

/usr/lib/debug/lib64/libc-2.18.so.debug

…

/usr/src/debug/glibc-2.18/wcsmbs/wcwidth.h

/usr/src/debug/glibc-2.18/wcsmbs/wmemchr.c

/usr/src/debug/glibc-2.18/wcsmbs/wmemcmp.c

由上可見抖部,glibc-debuginfo大致有三類文件:

存放在/usr/lib/debug/下的:.build-id/nn/nnn...nnn.debug文件,文件名是hash key议惰。

存放在/usr/lib/debug/下的其它*.debug文件慎颗,其文件名,是庫(kù)文件名+.debug后綴言询。

glibc的源代碼

當(dāng)使用gdb調(diào)試時(shí)俯萎,需要在機(jī)器碼與源代碼之間,建立起映射關(guān)系运杭。這就需要三個(gè)信息:

機(jī)器碼:可執(zhí)行文件夫啊、動(dòng)態(tài)鏈接庫(kù),例如:/lib64/libc-2.18.so

源代碼:顯然就是glibc-debuginfo中辆憔,包含的.c和.h等源文件撇眯。

映射關(guān)系:你應(yīng)該猜到了,它們就保存在*.debug文件中虱咧。

三熊榛、debuginfo是如何創(chuàng)建出來的?
當(dāng)我們使用gcc的-g選項(xiàng)編譯程序時(shí)腕巡,機(jī)器碼與源代碼的映射關(guān)系玄坦,會(huì)被默認(rèn)地與可執(zhí)行程序、動(dòng)態(tài)鏈接庫(kù)合并在一起绘沉。例如下面a.out可執(zhí)行程序煎楣,已經(jīng)包含了映射關(guān)系:

[yunkai@fedora t]$ nl main.c
`

1  #include <stdio.h>
 2  int main()
 3  {
 4    printf("hello, world!\n");
 5    return 0;
 6  }

`

[yunkai@fedora t]$ gcc -g main.c

[yunkai@fedora t]$ ls -l

total 16

-rwxrwxr-x 1 yunkai yunkai 9502 Apr 9 14:55 a.out

-rw-rw-r-- 1 yunkai yunkai 76 Apr 9 14:49 main.c

把映射關(guān)系等調(diào)試信息,與可執(zhí)行文件车伞、動(dòng)態(tài)鏈接庫(kù)合并在一起择懂,會(huì)帶來一個(gè)顯著的問題:可執(zhí)行文件或庫(kù)的Size變得很大。這對(duì)于那些不關(guān)心調(diào)試信息的普通用戶另玖,是不必要的困曙。

例如,Linux的內(nèi)核日矫,如果帶上Debuginfo赂弓,會(huì)無謂的增加幾百M(fèi)的大小。如果一個(gè)Linux操作系統(tǒng)的所有庫(kù)都帶上各自的Debuginfo哪轿,那么光是一個(gè)干凈的操作系統(tǒng)盈魁,就需要浪費(fèi)掉幾G甚至十幾G的磁盤空間。如果是通過網(wǎng)絡(luò)安裝窃诉,還將浪費(fèi)所有用戶的帶寬杨耙,并顯著的拖慢安裝的進(jìn)度赤套。正是了為解決這個(gè)問題,在Linux上的各種程序和庫(kù)珊膜,在生成RPM時(shí)容握,就已經(jīng)把Debuginfo單獨(dú)的抽取出來,因此形成了獨(dú)立的debuginfo包车柠。

問題是共虑,如何讓程序生成分離的debuginfo呢己沛?我們可以通過objcopy命令的--only-keep-debug選項(xiàng)來實(shí)現(xiàn)品姓,下面的命令把調(diào)試信息從a.out中讀取出來攀细,寫到a.out.debug文件中:

[yunkai@fedora t]$ objcopy --only-keep-debug ./a.out a.out.debug

[yunkai@fedora t]$ ls -l

total 24

-rwxrwxr-x 1 yunkai yunkai 9502 Apr 9 14:55 a.out

-rwxrwxr-x 1 yunkai yunkai 6022 Apr 9 15:22 a.out.debug

-rw-rw-r-- 1 yunkai yunkai 76 Apr 9 14:49 main.c

既然已經(jīng)把調(diào)試信息,保存到了a.out.debug文件中塑陵,就可以通過objcopy的--strip-debug選項(xiàng)給a.out瘦身了(也可以使用strip --strip-debug ./a.out感憾,效果一樣):

[yunkai@fedora t]$ objcopy --strip-debug ./a.out

[yunkai@fedora t]$ ls -l

total 24

-rwxrwxr-x 1 yunkai yunkai 8388 Apr  9 15:27 a.out

-rwxrwxr-x 1 yunkai yunkai 6022 Apr  9 15:22 a.out.debug

-rw-rw-r-- 1 yunkai yunkai   76 Apr  9 14:49 main.c

當(dāng)把調(diào)試信息從a.out中清除后,使用gdb對(duì)a.out進(jìn)行調(diào)試令花,會(huì)報(bào)no debugging symbols found:

[yunkai@fedora t]$ gdb ./a.out

GNU gdb (GDB) Fedora 7.6.50.20130731-19.fc20

...
Reading symbols from /home/yunkai/t/a.out...(no debugging symbols found)...done.

(gdb) 

顯然阻桅,gdb找不到調(diào)試信息了。因此兼都,我們需要在a.out中埋下一些線索嫂沉,以便gdb借助這些線索,可以正確地查找到它對(duì)應(yīng)的debug文件:a.out.debug俯抖。

在Linux下输瓜,可執(zhí)行文件或庫(kù),通常是ELF(Executable and Linkable Format)格式芬萍。這種格式,含有session headers搔啊。而調(diào)試信息的線索柬祠,正好可以通過一個(gè)約定的session header來保存,它叫.gnu_debuglink负芋÷祝可通過objcopy的--add-gnu-debuglink選項(xiàng),把調(diào)試信息的文件名(a.out.debug)保存到a.out的.gnu_debuglink這個(gè)header中旧蛾。然后gdb就可以正常調(diào)試了:

[yunkai@fedora t]$ objcopy --add-gnu-debuglink=a.out.debug ./a.out

[yunkai@fedora t]$ objdump -s -j .gnu_debuglink ./a.out


./a.out:     file format elf64-x86-64


Contents of section .gnu_debuglink:

 0000 612e6f75 742e6465 62756700 3fe5803b  a.out.debug.?..;

[yunkai@fedora t]$ gdb a.out

...

Reading symbols from /home/yunkai/t/a.out...Reading symbols from /home/yunkai/t/a.out.debug...done.

上面的objcopy命令莽龟,其實(shí)是把a(bǔ).out.debug的文件名以及這個(gè)文件的CRC校驗(yàn)碼,寫到了.gnu_debuglink這個(gè)header的值中锨天,但是并沒有告訴a.out.debug所在的路徑(上面通過objdump命令毯盈,可以打印出.gnu_debuglink這個(gè)header的內(nèi)容)。

那么gdb是按照怎樣的規(guī)則病袄,去查找a.out.debug文件呢搂赋?在解答這個(gè)問題之前赘阀,我們先來看另一個(gè)session header,叫.note.gnu.build-id:

[yunkai@fedora t]$ readelf -t ./a.out | grep build-id

  [ 3] .note.gnu.build-id

[yunkai@fedora t]$ readelf -n ./a.out

...

Notes at offset 0x00000274 with length 0x00000024:

  Owner                 Data size       Description

  GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)

    Build ID: 888010ffb999590e7158422ea813169be34085a1


[yunkai@fedora t]$ readelf -n ./a.out.debug 

...

Notes at offset 0x00000274 with length 0x00000024:

  Owner                 Data size       Description

  GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)

    Build ID: 888010ffb999590e7158422ea813169be34085a1

這個(gè)session header是a.out原生就存在的脑奠,因此也被拷貝到了a.out.debug中基公。這個(gè)header,保存了一個(gè)Build ID宋欺,這個(gè)ID是根據(jù)a.out文件自動(dòng)計(jì)算出來的轰豆,每個(gè)執(zhí)行文件或庫(kù),都有它唯一的Build ID齿诞。

在第2節(jié)中酸休,我們注意到這種文件:.build-id/nn/nnnn...nnnn.debug,前兩個(gè)“nn”就是它的Build ID前兩位掌挚,后面的nnnn...nnnn則是Build ID的剩余部分雨席。而這個(gè)nnnn...nnnn.debug文件,只是改了個(gè)名字而已吠式。

而gdb陡厘,則是通過下面的順序查找a.out.debug文件:

<global debug directory>/.build-id/nn/nnnn...nnnn.a.out.debug

<the path of a.out>/a.out.debug

<the path of a.out>/.debug/a.out.debug

<global debug directory>/<the patch of a.out>/a.out.debug

而<global debug directory>默認(rèn)為/usr/lib/debug/√卣迹可以在gdb中糙置,通過set/show debug-file-directory命令來設(shè)置或查看這個(gè)值:

[yunkai@fedora t]$ gdb ./a.out

...

(gdb) show debug-file-directory

The directory where separate debug symbols are searched for is "/usr/lib/debug".

既然a.out的Build ID為:888010ffb999590e7158422ea813169be34085a1,可以把a(bǔ).out.debug文件是目,移動(dòng)到/usr/lib/debug/.build-id/88/8010ffb999590e7158422ea813169be34085a1.debug:

[yunkai@fedora t]$ sudo cp a.out.debug \

/usr/lib/debug/.build-id/88/8010ffb999590e7158422ea813169be34085a1.debug

[yunkai@fedora t]$ gdb ./a.out

...

Reading symbols from /home/yunkai/t/a.out...Reading symbols from /usr/lib/debug/.build-id/88/8010ffb999590e7158422ea813169be34085a1.debug...done.

done.

由上可見谤饭,gdb就會(huì)優(yōu)先從/usr/lib/debug/.build-id/查找到對(duì)應(yīng)的debug信息。

四懊纳、a.out.debug里有什么內(nèi)容揉抵?
gcc目前會(huì)默認(rèn)會(huì)采用DWARF 4格式來保存調(diào)試信息∴头瑁可以通過readelf -w來查看DWARF的內(nèi)容:

[yunkai@fedora t]$ readelf -w ./a.out.debug

...

Contents of the .debug_info section:


  Compilation Unit @ offset 0x0:

   Length:        0x8d (32-bit)

   Version:       4

   Abbrev Offset: 0x0

   Pointer Size:  8

 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)

    <c>   DW_AT_producer    : (indirect string, offset: 0x6a): GNU C 4.8.2 20131212 (Red Hat 4.8.2-7) -mtune=generic -march=x86-64 -g

    <10>   DW_AT_language    : 1        (ANSI C)

    <11>   DW_AT_name        : (indirect string, offset: 0x2f): main.c

    <15>   DW_AT_comp_dir    : (indirect string, offset: 0x5b): /home/yunkai/t

...

DWARF內(nèi)部通過DIE(Debugging Information Entry)冤今,形成一顆調(diào)用樹,DWARF在設(shè)計(jì)的時(shí)候茂缚,就考慮到了各種語言的支持戏罢,雖然它通常與ELF格式的文件一起工作,但它其實(shí)并不依賴ELF脚囊。

由于DWARF比較自由的設(shè)計(jì)龟糕,使它不僅支持C/C++,也支持Java/Python等等幾乎所有語言的調(diào)試信息的表達(dá)悔耘。

在DWARF里讲岁,通常包含:源代碼與機(jī)器碼的映射關(guān)系的行號(hào)表、宏信息、inline函數(shù)的信息催首、Call Frame信息等扶踊。

但對(duì)于普通用戶,通常不需要了解DWARF的太多細(xì)節(jié)郎任,如果好奇的話秧耗,推薦閱讀文獻(xiàn)5。

五舶治、在代碼中生成Marker探針
通過gcc的-g選項(xiàng)分井,所有函數(shù)名,都會(huì)自動(dòng)的生成相應(yīng)的debuginfo霉猛,供systemtap進(jìn)行探測(cè)尺锚,這種方法在英文上稱為:Debuginfo-based instrumentation,它的局限性在于惜浅,只能收集到函數(shù)調(diào)用的初始時(shí)刻瘫辩、以及函數(shù)返回的結(jié)束時(shí)刻的上下文信息。

為了解決這個(gè)問題坛悉,又提出了一種新方法:Compiled-in instrumentation伐厌,它可以讓程序員,把探針安插到指定的某行代碼中裸影,從而可以收集到那行代碼執(zhí)行時(shí)的上下文信息挣轨,這種探針被稱為Marker探針。

編寫Marker探針轩猩,需要在代碼中包含頭文件:

include <sys/sdt.h>

然后在目標(biāo)行卷扮,插入下面的Marker宏之一:

DTRACE_PROBE(provider, name)

DTRACE_PROBE4(provider, name, arg1, arg2, arg3, arg4)

寫好Marker探針并成功編譯后,可以使用下面的systemtap指令來查看Marker探針是否生效:
stap -L 'process("/path/to/a.out").mark("*")'

更具體的操作方法詳見文獻(xiàn)6均践,值得一提的是晤锹,Marker探針是非常輕量的,它幾乎對(duì)程序的性能沒有影響彤委,因?yàn)樗粫?huì)在代碼中生成nop匯編指令抖甘。它是通過把現(xiàn)場(chǎng)的上下文信息,保存在ELF文件的特定的section header(.stapsdt.base)來實(shí)現(xiàn)的葫慎,只會(huì)增加debuginfo文件的大小。

六薇宠、參考文獻(xiàn)
http://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html

http://sourceware.org/binutils/docs-2.17/binutils/objcopy.html

https://blogs.oracle.com/dbx/entry/gnu_debuglink_or_debugging_system

https://blogs.oracle.com/dbx/entry/creating_separate_debug_info

http://dwarfstd.org/doc/DWARF4.pdf

https://sourceware.org/systemtap/wiki/AddingUserSpaceProbingToApps
————————————————
版權(quán)聲明:本文為CSDN博主「Chinainvent」的原創(chuàng)文章偷办,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明澄港。
原文鏈接:https://blog.csdn.net/chinainvent/article/details/24129311

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末椒涯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子回梧,更是在濱河造成了極大的恐慌废岂,老刑警劉巖祖搓,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異湖苞,居然都是意外死亡拯欧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門财骨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來镐作,“玉大人,你說我怎么就攤上這事隆箩「眉郑” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵捌臊,是天一觀的道長(zhǎng)杨蛋。 經(jīng)常有香客問我,道長(zhǎng)理澎,這世上最難降的妖魔是什么逞力? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮矾端,結(jié)果婚禮上掏击,老公的妹妹穿的比我還像新娘。我一直安慰自己秩铆,他們只是感情好砚亭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著殴玛,像睡著了一般捅膘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上滚粟,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天寻仗,我揣著相機(jī)與錄音,去河邊找鬼凡壤。 笑死署尤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的亚侠。 我是一名探鬼主播曹体,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼硝烂!你這毒婦竟也來了箕别?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎串稀,沒想到半個(gè)月后除抛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡母截,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年到忽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片微酬。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绘趋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出颗管,到底是詐尸還是另有隱情陷遮,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布垦江,位于F島的核電站帽馋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏比吭。R本人自食惡果不足惜绽族,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衩藤。 院中可真熱鬧吧慢,春花似錦、人聲如沸赏表。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓢剿。三九已至逢慌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間间狂,已是汗流浹背攻泼。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鉴象,地道東北人忙菠。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像纺弊,于是被迫代替她去往敵國(guó)和親只搁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344