最近在學(xué)著編寫一個(gè)操作系統(tǒng)的簡(jiǎn)單內(nèi)核,需要debug工具休蟹,我們這里使用gdb來(lái)進(jìn)行調(diào)試者冤,由于虛擬機(jī)運(yùn)行和本機(jī)是兩個(gè)部分,所以使用 gdb 的遠(yuǎn)程調(diào)試技術(shù)徙赢,這里對(duì) gdb 常見調(diào)試以及遠(yuǎn)程調(diào)試方式做一個(gè)總結(jié)字柠。
遠(yuǎn)程調(diào)試
先對(duì)在調(diào)試操作系統(tǒng)內(nèi)核時(shí)用到的命令做一個(gè)說(shuō)明(這里省略了一部分makefile的內(nèi)容):
為了方便調(diào)試,將debug所用到的命令做一個(gè)簡(jiǎn)單說(shuō)明
……
# -s 這個(gè)參數(shù)指的是啟動(dòng)時(shí)開啟1234端口等待gdb連接
# -S 是指是啟動(dòng)時(shí)不自動(dòng)開始運(yùn)行狡赐,等待調(diào)試器的執(zhí)行命令
debug: kernel.elf os-image.bin
@echo ??????debug??????
${QEMU} -S -s -fda os-image.bin -boot a &
i386-elf-gdb -q -ex "target remote localhost:1234" -ex "symbol-file kernel.elf"
……
我們?cè)谑褂?qemu 啟動(dòng)運(yùn)行鏡像文件時(shí)窑业,使用以下參數(shù):
- -s 這個(gè)參數(shù)指的是啟動(dòng)時(shí)開啟1234端口等待gdb連接
- S 是指是啟動(dòng)時(shí)不自動(dòng)開始運(yùn)行,等待調(diào)試器的執(zhí)行命令
這個(gè)時(shí)候才開啟了遠(yuǎn)程端口以便讓我們后續(xù)能夠進(jìn)行連接到該端口進(jìn)行調(diào)試
我們需要在編譯源代碼的時(shí)候枕屉,加上 -g
參數(shù)常柄,以便能夠?qū)⒃创a信息編譯到可執(zhí)行文件中。
隨后我們使用 gdb 開啟調(diào)試
-
target remote localhost:1234
連接到遠(yuǎn)程端口 -
symbol-file kernel.elf
加載具有源代碼信息編譯到可執(zhí)行文件
`symbol-file 可以追蹤到編譯器提供的庫(kù)和操作系統(tǒng)本身的代碼
調(diào)試符號(hào)就是這些代碼內(nèi)的符號(hào)搀擂,調(diào)試符號(hào)數(shù)據(jù)庫(kù)拐纱,
記錄了變量,函數(shù) 這一類符號(hào)和內(nèi)存定位的關(guān)系
從而可以用 地址相關(guān)信息追蹤到變量名哥倔,函數(shù)名
之后我們就可以使用gdb命令來(lái)進(jìn)行調(diào)試秸架。
常見命令
命令 | 解釋 | 示例 |
---|---|---|
file <文件名> | 加載被調(diào)試的可執(zhí)行程序文件。 因?yàn)橐话愣荚诒徽{(diào)試程序所在目錄下執(zhí)行GDB咆蒿,因而文本名不需要帶路徑东抹。 | (gdb) file gdb-sample |
r | Run的簡(jiǎn)寫蚂子,運(yùn)行被調(diào)試的程序。 如果此前沒(méi)有下過(guò)斷點(diǎn)缭黔,則執(zhí)行完整個(gè)程序食茎;如果有斷點(diǎn),則程序暫停在第一個(gè)可用斷點(diǎn)處馏谨。 | (gdb) r |
c | continue的簡(jiǎn)寫别渔,繼續(xù)執(zhí)行被調(diào)試程序,直至下一個(gè)斷點(diǎn)或程序結(jié)束惧互。 | (gdb) c |
b <行號(hào)> b <函數(shù)名稱> b *<函數(shù)名稱> b *<代碼地址> d [編號(hào)] | b: Breakpoint的簡(jiǎn)寫哎媚,設(shè)置斷點(diǎn)。兩可以使用“行號(hào)”“函數(shù)名稱”“執(zhí)行地址”等方式指定斷點(diǎn)位置喊儡。 其中在函數(shù)名稱前面加“*”符號(hào)表示將斷點(diǎn)設(shè)置在“由編譯器生成的prolog代碼處”拨与。如果不了解匯編,可以不予理會(huì)此用法艾猜。 d: Delete breakpoint的簡(jiǎn)寫买喧,刪除指定編號(hào)的某個(gè)斷點(diǎn),或刪除所有斷點(diǎn)匆赃。斷點(diǎn)編號(hào)從1開始遞增淤毛。 | (gdb) b 8 (gdb) b main (gdb) b *main (gdb) b *0x804835c (gdb) d |
s, n | s: 執(zhí)行一行源程序代碼,如果此行代碼中有函數(shù)調(diào)用算柳,則進(jìn)入該函數(shù)低淡; n: 執(zhí)行一行源程序代碼,此行代碼中的函數(shù)調(diào)用也一并執(zhí)行埠居。 s 相當(dāng)于其它調(diào)試器中的“Step Into (單步跟蹤進(jìn)入)”查牌; n 相當(dāng)于其它調(diào)試器中的“Step Over (單步跟蹤)”。 這兩個(gè)命令必須在有源代碼調(diào)試信息的情況下才可以使用(GCC編譯時(shí)使用“-g”參數(shù))滥壕。 | (gdb) s (gdb) n |
si, ni | si命令類似于s命令纸颜,ni命令類似于n命令。所不同的是绎橘,這兩個(gè)命令(si/ni)所針對(duì)的是匯編指令胁孙,而s/n針對(duì)的是源代碼。 | (gdb) si (gdb) ni |
p <變量名稱> | Print的簡(jiǎn)寫称鳞,顯示指定變量(臨時(shí)變量或全局變量)的值涮较。 | (gdb) p i (gdb) p nGlobalVar |
display ... undisplay <編號(hào)> | display,設(shè)置程序中斷后欲顯示的數(shù)據(jù)及其格式冈止。 例如狂票,如果希望每次程序中斷后可以看到即將被執(zhí)行的下一條匯編指令,可以使用命令 “display /i |
(gdb) display /i $pc (gdb) undisplay 1 |
i | info的簡(jiǎn)寫国瓮,用于顯示各類信息灭必,詳情請(qǐng)查閱“help i”。 | (gdb) i r |
q | quit的簡(jiǎn)寫乃摹,退出GDB調(diào)試環(huán)境禁漓。 | (gdb) q |
help [命令名稱] | GDB幫助命令,提供對(duì)GDB名種命令的解釋說(shuō)明峡懈。 如果指定了“命令名稱”參數(shù)璃饱,則顯示該命令的詳細(xì)說(shuō)明与斤;如果沒(méi)有指定參數(shù)肪康,則分類顯示所有GDB命令,供用戶進(jìn)一步瀏覽和查詢撩穿。 | (gdb) help |
執(zhí)行選項(xiàng)
- -cd:設(shè)置工作目錄
- -q:安靜模式磷支,不打印介紹信息和版本信息
- -d:添加文件查找路徑
- -x:從指定文件中執(zhí)行GDB指令
- -s:設(shè)置讀取的符號(hào)表文件