在Linux下寫C/C++程式的程式員,時常與Core Dump相見。在記憶體越界通路,收到不能處理的信号,除零等錯誤出現時,我們精心或不精心寫就的程式就直接一命嗚呼了,Core Dump是Linux仁慈地留下的程式的屍體,幫助程式員們解決了一個又一個問題。
有時配置不給力,Linux直接毀屍滅迹,沒有了Core檔案;又有時,剛好磁盤空間不足,Core檔案寫不下了。沒有Core檔案的時候,如何知道程式在什麼地方出錯了呢?addr2line就在這時派上用場。
這是一個示例程式,func函數傳回參數a除以參數b的結果。這裡使用0作為除數,結果就是程式因為除以0導緻錯誤,直接中斷了。
#include <stdio.h>
int func(int a, int b)
{
return a / b;
}
int main()
{
int x = 10;
int y = 0;
printf("%d / %d = %d\n", x, y, func(x, y));
return 0;
}
使用
$ gcc -o test1 -g test1.c
編譯程式,test1.c是程式檔案名。執行程式,結果程式異常中斷。檢視系統dmesg資訊,發現系統日志的錯誤資訊:
[54106.016179] test1[8352] trap divide error ip:400506 sp:7fff2add87e0 error:0 in test1[400000+1000]
這條資訊裡的ip字段後面的數字就是test1程式出錯時所程式執行的位置。使用addr2line就可以将400506轉換成出錯程式的位置:
$ addr2line -e test1 400506
/home/hanfoo/code/test/addr2line/test1.c:5
這裡的test1.c:5指的就是test1.c的第5行
return a / b;
也正是這裡出現的錯誤。addr2line幫助我們解決了問題。
addr2line如何找到的這一行呢。在可執行程式中都包含有調試資訊,其中很重要的一份資料就是程式源程式的行号和編譯後的機器代碼之間的對應關系Line Number Table。DWARF格式的Line Number Table是一種高度壓縮的資料,存儲的是表格前後兩行的內插補點,在解析調試資訊時,需要按照規則在記憶體裡重建Line Number Table才能使用。
Line Number Table存儲在可執行程式的.debug_line域,使用指令
$ readelf -w test1
可以輸出DWARF的調試資訊,其中有兩行
Special opcode 146: advance Address by 10 to 0x4004fe and Line by 1 to 5
Special opcode 160: advance Address by 11 to 0x400509 and Line by 1 to 6
這裡說明機器二進制編碼的0x4004fe位置開始,對應于源碼中的第5行,0x400509開始就對應與源碼的第6行了,是以400506這個位址對應的是源碼第5行位置。
addr2line通過分析調試資訊中的Line Number Table自動就能把源碼中的出錯位置找出來,再也不怕Linux毀屍滅迹了。