整理一下在linux下C/C++用gdb工具debug一些提高效率的操作。基本的gdb操作就不在這里贅述了。
- 打印各種變量
x 命令
在gdb中可以使用x命令,來(lái)打印內(nèi)存中的值。具體的格式是x/nfu addr。 含義為以f格式打印從addr開(kāi)始的n個(gè)長(zhǎng)度單元為u的內(nèi)存值。- n表示打印的n個(gè)u的長(zhǎng)度
- f 表示打印格式,u,d表示10進(jìn)制的無(wú)符號(hào)和有符號(hào)數(shù); x表示16進(jìn)制;t表示二進(jìn)制
- u表示單元長(zhǎng)度,b表示一個(gè)byte,w表示4個(gè)byte
int main() { int val = 0xa; char buff[1024]; memset(buff, 0, sizeof(1024)); char name[]="hello world"; strcpy(buff, name); int arr[10] = {0}; arr[0] = 0xa; arr[1] = 10; arr[2] = 20; return 0; } (gdb) x/16db &arr 0x7fffffffe010: 10 0 0 0 10 0 0 0 0x7fffffffe018: 20 0 0 0 0 0 0 0 (gdb) x/16sb &buff 0x7fffffffe050: "hello world" 0x7fffffffe05c: "\177" 0x7fffffffe05f: "" 0x7fffffffe060: "<縱?\177" 0x7fffffffe067: "" 0x7fffffffe068: "X\227\177" 0x7fffffffe06f: "" 0x7fffffffe070: "\020暫?\177" 0x7fffffffe077: "" 0x7fffffffe078: "" 0x7fffffffe079: "" 0x7fffffffe07a: "" 0x7fffffffe07b: "" 0x7fffffffe07c: "\001" 0x7fffffffe07e: "" 0x7fffffffe07f: ""
- 日志打印到某個(gè)文件
set pagination off set logging file a.log set logging overwrite set logging on
-
-tui 圖形界面選項(xiàng)
gdb a.out -tui # gdb tui 啟動(dòng)
gdb attach pid -tui # gdb tui attach
- gdb定義函數(shù)
在開(kāi)發(fā)中有時(shí)會(huì)遇到需要看一個(gè)很大的數(shù)組,里面有沒(méi)有被寫壞的元素。如果數(shù)組中還有嵌套其他結(jié)構(gòu)體,每個(gè)依次打印出來(lái)非常麻煩。這里可以使用gdbinit,定義一個(gè)函數(shù),依次遍歷數(shù)組,打印超過(guò)某個(gè)值的元素。比如:const int MAX_LEN = 1024; struct DEBUG_DETAIL_INFO{ int id; int extra_info; }; struct DEBUG_ARR{ int num; DEBUG_DETAIL_INFO info_arr[MAX_LEN]; }; int main() { DEBUG_ARR arr; init_arr(100, arr); // todo return 0; }
gdb調(diào)用函數(shù)有兩種方式:
- gdb啟動(dòng)時(shí),會(huì)在當(dāng)前目錄下查找 .gdbinit 文件
- gdb在運(yùn)行時(shí),在命令行輸入 source xx.gdb文件 來(lái)加載
gdb啟動(dòng)的時(shí)候,會(huì)在當(dāng)前目錄或home目錄自動(dòng)加載gdbinit腳本。在腳本中定義gdb函數(shù)。(或者gdb attach上去。)
define print_if_my_arr
printf "argc %d, arg %d \n", $argc, $arg0
set $num = arr.num
printf "print_func num %d \n", $num
set $idx = 0
while $idx < $num
if arr.info_arr[$idx].id >= $arg0
printf "idx %d id %d \n", $idx, arr.info_arr[$idx].id
end
set $idx = $idx + 1
end
end
document print_if_my_arr
example : print_if_my_arr 10
desc : print DEBUG_ARR ele if val >= 10
end
gdb實(shí)測(cè)一把
gdbinit文件同理也可以插入其他命令,比如各種讓輸出比較直觀的命令等:
set print pretty on
set print object on
gdb定義的常用函數(shù)可以放到一個(gè)文件里面,需要的時(shí)候通過(guò)source命令加載。
define mybt
set logging file output_bt.log
set logging on
bt
set logging off
end
使用的時(shí)候
gdb attach xxxx
source xxx/path/xxx.gdb
mybt
- gdb 條件斷點(diǎn)
# 文件名 + 行號(hào)的條件斷點(diǎn) b xxx.cpp:20 if value == 100 #xx.cpp 行號(hào)20 如果value == 100 觸發(fā)斷點(diǎn) # 包含一些簡(jiǎn)單計(jì)算的斷點(diǎn) (gdb) p 100 $2 = 100 (gdb) b test1.cpp:8 if i + $2 == 130
- ignore 命令
斷點(diǎn)觸發(fā)被忽略xx次
ignore <break_list> count
-
command命令
觸發(fā)斷點(diǎn)后自動(dòng)執(zhí)行command里面定義的命令
command <break_list>

- gdb return
進(jìn)入某個(gè)函數(shù)執(zhí)行,不想執(zhí)行里面的邏輯,直接gdb return 出去。里面的邏輯就不會(huì)執(zhí)行了。

-
gdb gcore
gcore命令可以從當(dāng)前環(huán)境導(dǎo)出成為一個(gè)core文件,供日后分析。
- handle SIGPIPE nostop
為了gdb不被信號(hào)斷開(kāi),可以屏蔽掉各種pipe信號(hào)。
-
gdb -ex 參數(shù)
生產(chǎn)環(huán)境操作的時(shí)候可以使用
跳出死循環(huán)gdb -ex "bt" -batch -p pid
-
display 命令
對(duì)于debug需要關(guān)注的變量名,可以使用display打印出來(lái)。每次gdb操作的時(shí)候,都會(huì)顯示出來(lái)。示意圖如下:
- info命令
info sharedlibrary #顯示共享庫(kù) (gdb) info sharedlibrary From To Syms Read Shared Object Library 0x00007ffff7dd5050 0x00007ffff7df4854 Yes (*) /lib64/ld-linux-x86-64.so.2 0x00007ffff7aceb60 0x00007ffff7b84bb2 Yes (*) /lib64/libstdc++.so.6 0x00007ffff76c9510 0x00007ffff77687ca Yes (*) /lib64/libm.so.6 0x00007ffff74a7dc0 0x00007ffff74b8a25 Yes (*) /lib64/libgcc_s.so.1 0x00007ffff7104900 0x00007ffff724ef0f Yes (*) /lib64/libc.so.6
- gdb調(diào)用函數(shù)
gdb調(diào)用class的成員函數(shù)void func() { xxx; } b func call func() // gdb 直接調(diào)用這個(gè)函數(shù)// todo
- strip命令 (這個(gè)和gdb關(guān)系不大)
strip命令可以極大的降低可執(zhí)行文件的體積,其實(shí)就是去掉符號(hào)表和調(diào)試信息等。當(dāng)gdb的時(shí)候,拷貝strip下來(lái)的.debug文件到需要gdb的機(jī)器上,然后進(jìn)行g(shù)db attach。這里gdb上來(lái)以后,會(huì)自動(dòng)找到對(duì)應(yīng)的.debug文件。cd xxxx_app_bin_path objcopy --only-keep-debug xxxx_app ./.debug/xxxx.debug strip --strip-debug --strip-unneeded xxxx_app objcopy --add-gnu-debuglink=./.debug/xxxx_app.debug xxxx_app
GDB的基本原理
gdb有兩種跟蹤進(jìn)程的方法,第一種是gdb + a.out的方式啟動(dòng)進(jìn)程,第二種的方式是gdb去attach一個(gè)已經(jīng)啟動(dòng)了的進(jìn)程。這種方式都是利用ptrace系統(tǒng)調(diào)用去跟蹤被調(diào)試的進(jìn)程。
ptrace共有四個(gè)參數(shù):
long ptrace(enum __ptrace_request request,pid_t pid,void *addr,void *data);
// 兩種gdb的區(qū)別在于參數(shù)不同
PTRACE_TRACEME 和 PTRACE_ATTACH
ptrace系統(tǒng)函數(shù)是Linux內(nèi)核提供的一個(gè)用于進(jìn)程跟蹤的系統(tǒng)調(diào)用,通過(guò)它,一個(gè)進(jìn)程(gdb)可以讀寫另外一個(gè)進(jìn)程(test)的指令空間、數(shù)據(jù)空間、堆棧和寄存器的值。而且gdb進(jìn)程接管了test進(jìn)程的所有信號(hào),也就是說(shuō)系統(tǒng)向test進(jìn)程發(fā)送的所有信號(hào),都被gdb進(jìn)程接收到,這樣一來(lái),test進(jìn)程的執(zhí)行就被gdb控制了,從而達(dá)到調(diào)試的目的。
也就是說(shuō),如果沒(méi)有g(shù)db調(diào)試,操作系統(tǒng)與目標(biāo)進(jìn)程之間是直接交互的;如果使用gdb來(lái)調(diào)試程序,那么操作系統(tǒng)發(fā)送給目標(biāo)進(jìn)程的信號(hào)就會(huì)被gdb截獲,gdb根據(jù)信號(hào)的屬性來(lái)決定:在繼續(xù)運(yùn)行目標(biāo)程序時(shí)是否把當(dāng)前截獲的信號(hào)轉(zhuǎn)交給目標(biāo)程序,如此一來(lái),目標(biāo)程序就在gdb發(fā)來(lái)的信號(hào)指揮下進(jìn)行相應(yīng)的動(dòng)作。
第一種方式是通過(guò)gdb去fork一個(gè)子進(jìn)程,子進(jìn)程去執(zhí)行被調(diào)試的進(jìn)程。然后建立父子關(guān)系。如下圖所示:
第二種方式是gdb去attach被調(diào)試的子進(jìn)程。如果想對(duì)一個(gè)已經(jīng)執(zhí)行的進(jìn)程B進(jìn)行調(diào)試,那么就要在gdb這個(gè)父進(jìn)程中調(diào)用ptrace(PTRACE_ATTACH,[其他參數(shù)]),此時(shí),gdb進(jìn)程會(huì)attach(綁定)到已經(jīng)執(zhí)行的進(jìn)程B,gdb把進(jìn)程B收養(yǎng)成為自己的子進(jìn)程,而子進(jìn)程B的行為等同于它進(jìn)行了一次 PTRACE_TRACEME操作。此時(shí)gdb進(jìn)程會(huì)發(fā)送SIGSTO信號(hào)給子進(jìn)程B,子進(jìn)程B接收到SIGSTOP信號(hào)后,就會(huì)暫停執(zhí)行進(jìn)入TASK_STOPED狀態(tài),表示自己準(zhǔn)備好被調(diào)試了。







