前言:
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使用細節:
啓動並調試: gdb program
帶core文件啓動並調試: gdb program core (執行後,停在哪,系統就down在哪)
調試一個運行中的進程: gdb program 1234 或 gdb - 1234 (1234是進程號)
過濾gdb產品信息運行gdb: gdb --silent
在gdb環境中運行shell命令: shell ls
在gdb環境中運行make命令: make all
轉移gdb輸出到文件中:set logging on set logging file filename (filename是文件名)
#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。