前言:
linux環境下幾乎隻有gdb可用,windows環境下windbg也是以gdb作為底部承載,是以gdb的重要作用可見一斑,但gdb作為基礎套件,其不具備界面展示,是以使用起來不是很友善,這裡對其做簡要的使用說明。
前提條件:
使用gdb進行調試必須滿足以下至少一個條件:
1)在編譯時使用了-g選項,那麼得到的二進制檔案便是包含調試資訊的,那麼此二進制檔案是可調試的;
2) 編譯時沒使用-g選項增加調試資訊,但有與此二進制檔案配套的debuginfo包,那麼可以将此debuginfo包與gdb一起運作,此時相當于把調試資訊打入二進制檔案中。
注:debuginfo也是在編譯時生成的,通常是通過-g選項生成帶有調試資訊的二進制檔案,然後再使用objcopy将調試資訊從二進制檔案中分離出來,後面會介紹objcopy的使用
DebugInfo的來龍去脈:
- debuginfo是指gdb所使用的debug資訊,比如使用gcc -g選項,編譯的輸出檔案中就會包含debuginfo
- 一般情況下,都是把debuginfo和可執行檔案分開,因為放在一起,會導緻可執行檔案過大
- 如何獲得可執行檔案和debuginfo呢?使用如下指令可以
objcopy --only-keep-debug ./a.out a.out.debug
#這裡拷貝出來,此時可執行檔案中還是有debuginfo的,多出來一個.debug檔案,原來的a.out大小不變
- 如果把a.out瘦身呢?使用如下指令可以
objcopy --strip-debug ./a.out(也可以使用strip --strip-debug ./a.out)
#此時,在使用gdb來調試a.out的時候,已經不行了,報錯no debugging symbols found
- 該怎麼使用debuginfo呢?使用如下指令即可
objcopy --add-gnu-debuglink=a.out.debug ./a.out
#此時,在使用gdb來調試a.out,發現可以了
- 為什麼加了有debuginfo ,調試的時候有些地方仍然沒有debug資訊
-g 選項預設不會包括一些宏定義的資訊。可以在編譯時使用-g3
GDB使用細節:
#1 - 指令補全,和shell一樣,用TAB補全,同樣有輕按兩下TAB羅列可選項的功能
#2 - 基本指令
run 啟動程式
start 主函數處設定斷點,啟動程式,每次過來執行都會停住,相當于一個長期斷點
starti 主函數處設定斷點,啟動程式,僅在第一個過來執行時會挺住,相當于一次性斷點
set args 設定運作參數,進入互動界面後,set args arg1 arg2,依次設定參數1——arg1 和 參數2——arg2
show args 檢視已設定的參數
continue 一直執行,知道下一個斷點,若無斷點,則一直執行到程式結束
bt 輸出目前調用棧
n step over
s step into
break 設定斷點
break lineno 目前檔案的指定行處
break filename:lineno 特定檔案的指定行處
break functionname 目前檔案的指定函數入口
break filename:functionname 特定檔案的指定函數入口
break *address 某個虛拟位址
break 下一條指令處
break ... if xxx 滿足xxx條件,停在...處(...為上面提到的lineno、functionname等等)
比如:break printf if i>100
info 檢視相關資訊
info break 檢視斷點資訊
info threads 檢視線程資訊
info r 檢視寄存器資訊
condition 為斷點加條件(類似于break ... if xxx,更細化)
假設已經設定了斷點3
condition 3 a>10 當a>10的時候,在3号斷點停住
condition 3 清除加在3号斷點上的條件(比如上面的a>10)
ignore 3 100 進入斷點3的前100次将被忽略,從101次開始斷點才有效
#3 - 多程序調試
預設情況下,gdb跟蹤父程序
可以在任何時候設定跟蹤子程序:set follow-fork-mode childe
那麼exec系列函數是否能夠使用gdb跟蹤呢?
#4 - 多線程調試
info threads 檢視所有線程(其中帶*号的為目前線程)
thread 2 切換到2号線程
where 輸出線程調用棧(對應bt)
break ... thread 2 為2号線程打上斷點(...為之前提到的lineno、functionname等等)
進階用法:
#1 輸出字元串
p (char *)字元串初始位址
#2 輸出完整字元串
gdb的字元串輸出有長度限制,最多隻能輸出多少個,可使用show print elements檢視
當字元串商都超過這個限制時,可以通過set print elements N,來把這個限制擴充到N個字元
#3 gdb value <optimized out>的解決方式
調整優化深度,預設優化深度為-O2,調整為-O0即可
===================================================GDB 進階=====================================================
#1 http://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/gdb.html
#2 列印表達式
print 表達式:簡記為 p ,其中“表達式”可以是任何目前正在被測試程式的有效表達式,比如目前正在調試C語言的程式,
那麼“表達式”可以是任何C語言的有效表達式,包括數字,變量甚至是函數調用。
print a:将顯示整數 a 的值
print ++a:将把 a 中的值加1,并顯示出來
print name:将顯示字元串 name 的值
print gdb_test(22):将以整數22作為參數調用 gdb_test() 函數
print gdb_test(a):将以變量 a 作為參數調用 gdb_test() 函數
display 表達式:在單步運作時将非常有用,使用display指令設定一個表達式後,它将在每次單步進行指令後,緊接着輸
出被設定的表達式及值。如: display a
watch 表達式:設定一個監視點,一旦被監視的“表達式”的值改變,gdb将強行終止正在被調試的程式。如: watch a
whatis :查詢變量或函數
info function: 查詢函數
擴充info locals: 顯示目前堆棧頁的所有變量
#3 查詢運作資訊
where/bt :目前運作的堆棧清單;
bt backtrace 顯示目前調用堆棧
up/down 改變堆棧顯示的深度
set args 參數:指定運作時的參數
show args:檢視設定好的參數
info program: 來檢視程式的是否在運作,程序号,被暫停的原因。
#4 分割視窗
layout:用于分割視窗,可以一邊檢視代碼,一邊測試:
layout src:顯示源代碼視窗
layout asm:顯示反彙編視窗
layout regs:顯示源代碼/反彙編和CPU寄存器視窗
layout split:顯示源代碼和反彙編視窗
Ctrl + L:重新整理視窗
#5 更強大的工具 -- cgdb
cgdb可以看作gdb的界面增強版,用來替代gdb的 gdb -tui。cgdb主要功能是在調試時進行代碼的同步顯示,這無疑增加了調試的友善性,
提高了調試效率。界面類似vi,符合unix/linux下開發人員習慣;如果熟悉gdb和vi,幾乎可以立即使用cgdb。