gdb雖然只是一個調試器,但如何要用好它,必須深入理解linux下程序是如何編譯運行的,比較推薦的書有《CSAPP》(中文版名:深刻理解計算機系統)和《APUE》(中文名:Unix 環境高級編程)。
本文只侷限對gdb的使用上,後面有機會再介紹關於linux下程序編譯運行相關的知識。linux
關於調試目的,你們第一印象確定是發現程序問題。這的確是最主要的目的。另一點是幫助咱們理解他人寫的程序。對我來講這也很是有用。若是發現程序有問題,有時不防試試畫一個流程圖,邊理解程序邊調試問題。android
Stack(棧)ios
當調試的程序暫停時,你首先想知道的是它停在了什麼地方,以及它是怎樣到達那裏的。c++
調試的程序每調用一個函數,就會產生關於這個調用的相關信息。這些信息包括調用前的位置、傳遞的參數、以及被調用函數的局部變量。這些信息保存在一個名爲棧幀(stack frame)的數據塊中。棧幀是在內存中分配的成爲「調用棧」(call stack)的一塊區域。shell
當程序暫停的時候,gdb中一些檢查棧的命令容許你查看這些全部的信息。express
gdb和gdb命令會選擇其中的一個棧幀做爲「當前棧幀」。無論何時用gdb來顯示程序中的變量的值,這個變量老是指當前幀中的變量。有一些特殊的gdb命令可讓你選擇你感興趣的幀。編程
被調試的程序暫停時,gdb自動選擇當前程序在其中運行的幀,並對其做一簡短描述,相似於frame命令。數組
Frames(幀)sass
調用棧是被在內存中分割成一些連續的相鄰的數據塊,它們被稱爲「棧幀」,簡稱「幀」。每一個幀都包含有調用一個函數的相關數據,包括被調用函數的局部變量和被調用函數的地址等等。安全
當程序運行時,棧裏只有一個幀,即main函數的幀,它被稱爲初始化幀或者最層的幀。每調用一個函數就會產生一個新的幀,當函數返回時,相應的幀就會被刪除。若是函數是遞歸調用的,則可能產生不少針對調用這個函數的幀。每次實際調用一次這個函數,產生的幀被成爲「最內層的幀」。這是全部幀裏最新產生的。
在程序中,幀是用地址來標識的,每一個幀都有不少個字節,每一個字節都有本身的地址。每種計算機都有各自不一樣的約定來選擇一個表明幀地址的字節。一般這個地址是記錄在名爲「幀指針」$FP(Frame Pointer Register)的一個寄存器裏。
gdb將每一個存在的棧幀賦予了一個數字,從0開始,編號爲0的幀表明最內層的幀,編號爲1的幀表明調用它的幀,以此類推。這些數字編號實際上並不存在於被調試的程序中,它們只是由gdb賦予的值,以便在gdb命令裏能夠方便地標識幀。
一些編譯器提供了一種使程序不使用棧幀的編譯方法(例如gcc編譯器使用-fomit-frame-pointer選項時就能夠產生不使用幀的函數)。 這只是偶爾用在大量的庫函數調用時的一種方法,以便程序能夠節省創建幀的時間。若是最內層的幀沒有棧幀的話,gdb仍是能夠像往常同樣認爲它有一個單獨的幀並標識爲編號爲0,以便正確地對函數調用鏈進行跟蹤。可是gdb沒有辦法對其它的函數調用幀進行標識。
gdb program gdb program core gdb program gdb --args gcc -O2 -c foo.c gdb --silent
上面的gdb --args gcc -O2 -c foo.c
將調試gcc, 並把-O2 -c foo.c做爲參數傳遞給gcc
-symbol file -s file -exec file -e file -se file #read symbol table from file file and use it as the executable file -core file -c file -pid number -p number -command file -x command -eval-command command -ex command #execute a signal GDB command -init-comamnd file -ix file -init-eval-command comand -iex command -directory directory -d directory # add directory to the path to search for the source and scrip files
若是要執行shell comands, 能夠用 shell 開頭就行
shell command-string !command-string
若是是make, 則能夠直接輸入make make-args
若是你想把GDB命令輸出到一個文件有,有幾種方法控制
set logging on set logging off set logging file <filename> set logging overwrite [on|off] #默認會追加到logfile裏 set logging redirect [on|off] #默認GDB輸出會在terminal和logfile裏顯示,用redirect讓它只在logfile裏顯示 show logging
主要有兩個命令
若是知道命令名, 直接輸入help <command_name>
若是不記得命令名,能夠搜索關鍵字, apropos <regexp>
(gdb) apropos args advance -- Continue the program up to the given location (same form as args for break command) collect -- Specify one or more data items to be collected at a tracepoint handle -- Specify how to handle a signal info args -- Argument variables of current stack frame l -- List specified function or line list -- List specified function or line r -- Start debugged program run -- Start debugged program set args -- Set argument list to give program being debugged when it is started show args -- Show argument list to give program being debugged when it is started u -- Execute until the program reaches a source line greater than the current until -- Execute until the program reaches a source line greater than the current
包含三個命令:
run : 開始運行
start : 運行並停在main函數上
continue :繼續運行
ignore : 忽略某函數或文件
checkpoint: 設置書籤
後面能夠跟參數, 如start 1 2 3
忽略某函數或文件
在調試程序中可能會不關心某個函數或文件,這時能夠用skip
來略過他們。
語法以下:
skip -file <file> skip -gfile <file_glob_pattern> skip -function <linespec> skip -rfunction <regexp> info skip [range]
<linespec>是一個冒號分隔的列表,它能夠包括文件名, 函數名。(filename:linenum, function:label, filename:function, label)
示例以下:
(gdb) skip -rfu ^std::(allocator|basic_string)<.*>::~?\1 *\( (gdb) skip -rfu ^std::([a-zA-z0-9_]+)<.*>::~?\1 *\(
第一個命令忽略std::string的構造和解構函數
第二個命令忽略全部來自std namespace的函數的構造和解構函數
gdb能夠在程序執行的過程當中保留快照(狀態)信息,稱之爲checkpoint,能夠在進來返回到該處再次查看當時的信息,好比內存、寄存器以及部分系統狀態。經過設置checkpoint,萬一調試的過程當中錯誤發生了可是已經跳過了錯誤發生的地方,就能夠快速返回checkpoint再開始調試,而不用重啓程序從新來過。
對當前執行狀態保存一個快照,gdb會自動產生一個對應的編號供後續標識使用。從提示信息看來,其實每創建一個checkpoint,都會fork出一個子進程,因此每一個checkpoint都有一個惟一的進程ID所對應着。
查看全部的checkpoint,包括進程ID、代碼地址和當時的行號或者符號。
恢復程序狀態到checkpoint-id指明的checkpoint,全部的程序變量、寄存器、棧都會被還原到那個狀態,同時gdb還會將時鐘信息回退到那個點。恢復過程當中程序的狀態和系統的部分狀態是支持的,好比文件指針等信息,可是寫入文件的數據、傳輸到外部設備的數據都沒法被回退。
刪除指定的checkpoint。
此外,checkpoint的做用還在於斷點、觀測點不是什麼狀況下均可用的狀況下,由於Linux系統有時候爲了安全考慮,會隨機化新進程的地址空間,這樣重啓調試程序會致使以前使用絕對地址設置的斷點、觀測點不可用。
在一個位置上設置斷點,能夠對應多個位置,gdb要自動在須要的位置插入斷點。在動態庫裏也能夠設置斷點,不過其地址在加載後才能解析。
斷點的設置有幾種方法
break b break [Function Name] break [File Name]:[Line Number] break [Line Number] break *[Address] break [...] if [Condition] break [...] thread [Thread-id] b [...] rbreak [regexp] rbreak [File Name]:[regexp] tbreak [args]
不要混淆[Function name]和[Address], 好比你想在地址0x40138c上設定斷點,若是用
gdb> break 0x4007d9
會失敗, GDB會0x40138c解釋成函數而不是地址。正確的作法是
(gdb) break *0x4007d9
能夠用下面命令獲得相應行的地址:
(gdb) i line gdbprog.cc:14 Line 14 of "gdbprog.cc" starts at address 0x4007d9 <InitArrays(int*)+17> and ends at 0x4007f7 <InitArrays(int*)+47>.
或者
(gdb) disas /m InitArrays Dump of assembler code for function InitArrays(int*): 8 void InitArrays(int* array) 0x00000000004007c8 <+0>: push %rbp 0x00000000004007c9 <+1>: mov %rsp,%rbp 0x00000000004007cc <+4>: mov %rdi,-0x18(%rbp) 9 { 10 11 12 for(int i = 0;i < 10;i++) 0x00000000004007d0 <+8>: movl $0x0,-0x4(%rbp) 0x00000000004007d7 <+15>: jmp 0x40080a <InitArrays(int*)+66> 0x0000000000400807 <+63>: incl -0x4(%rbp) 0x000000000040080a <+66>: cmpl $0x9,-0x4(%rbp) 0x000000000040080e <+70>: jle 0x4007d9 <InitArrays(int*)+17> 13 { 14 ptrArray[i] = array + i; 0x00000000004007d9 <+17>: mov -0x4(%rbp),%ecx 0x00000000004007dc <+20>: mov -0x4(%rbp),%eax 0x00000000004007df <+23>: cltq 0x00000000004007e1 <+25>: shl $0x2,%rax 0x00000000004007e5 <+29>: mov %rax,%rdx 0x00000000004007e8 <+32>: add -0x18(%rbp),%rdx 0x00000000004007ec <+36>: movslq %ecx,%rax 0x00000000004007ef <+39>: mov %rdx,0x6013e0(,%rax,8) 15 iArray[i] = i; 0x00000000004007f7 <+47>: mov -0x4(%rbp),%eax 0x00000000004007fa <+50>: movslq %eax,%rdx 0x00000000004007fd <+53>: mov -0x4(%rbp),%eax 0x0000000000400800 <+56>: mov %eax,0x6013a0(,%rdx,4) 16 } 17 } 0x0000000000400810 <+72>: leaveq 0x0000000000400811 <+73>: retq End of assembler dump.
若是試圖將斷點設在位於還未加載的shared library代碼內,那麼就會顯示相似下面warning
Make breakpoint pending on future shared library load? (y or [n])
但它可能永遠不會工做,若是是下列狀況:
因此能夠用下面幾個命令來作調試
info sharedlibrary
: 目前加載的shared libraryinfo sources
: 被GDB識別的源文件info breakpoints
: 建立的斷點和它們的狀態大約有如下幾種形式
break main if argc > 1 break 180 if (string == NULL && i < 0) break test.c:34 if (x & y) == 1 break myfunc if i % (j+3) != 0 break 44 if strlen(mystring) == 0 b 10 if ((int)$gdb_strcmp(a,"chinaunix") == 0) b 10 if ((int)aa.find("dd",0) == 0)
常常犯的一個錯誤就是 在if和後面的(之間沒放空格
另外注意條件表達式的返回值類型是int
當設置了斷點後, 後面可使用condition和ignore來修改這個斷點條件
condition <break_list> (condition)
好比
cond 3 (i==4)
上面命令把breakpoint 3的條件修改爲(i==4)
ignore <break_list> count
上面命令表示break_list指定的斷點將被忽略count次
好比如今i=0, 設置i>4爲條件,那麼忽略3次後,它停在i=8, 忽略了i=5, 6, 7
commands <break_list>
當斷點被觸發時,運行相應的命令
能夠用info breakpoints
來查看相應斷點上附着的命令
(gdb) i b Num Type Disp Enb Address What 3 breakpoint keep y 0x00000000004007d9 in InitArrays(int*) at gdbprog.cc:14 stop only if (i>4) breakpoint already hit 5 times silent printf "hz: i=%d\n",i c 7 breakpoint keep y 0x0000000000400962 in main() at gdbprog.cc:51 breakpoint already hit 1 time
一個斷點上只容許附着一條命令,再次調用commands 3
後會覆蓋掉前面的命令
所以若是要刪除相應的命令,只需用空的commands 3就好了
用define能夠錄製宏,這些宏就像shell腳本同樣,能夠傳入參數,依次是$arg0, $arg1, ...
能夠把這些宏放在.gdbinit文件中
錄製好宏後就能夠在commands中使用
宏並不支持全部GDB命令,好比silent就不能用於宏中, 它會在運行時報錯: Undefined commands: "silent"
(gdb) define printAndGo Type commands for definition of "printAndGo". End with a line saying just "end". >printf "i=%d\n",$arg0 >cont >end (gdb) commands 3 Type commands for breakpoint(s) 3, one per line. End with a line saying just "end". >silent >printAndGo >end (gdb) i b Num Type Disp Enb Address What 3 breakpoint keep y 0x00000000004007d9 in InitArrays(int*) at gdbprog.cc:14 stop only if (i>4) silent printAndGo i 7 breakpoint keep y 0x0000000000400962 in main() at gdbprog.cc:51
若是要查看這些宏的定義,能夠用show user
來查看
(gdb) show user User command "pbuf": set $i=0 while ($i<strlen(netiring.supply)) print netiring.supply[$i] set $i=$i+1 end User command "pobuf": set $i=0 while ($i<strlen(netoring.consume)) print netoring.consume[$i] set $i=$i+1 end
只生效一次臨時斷點
在全部匹配regexp的函數名處都設置斷點。它的regexp和grep相同
clear localtion
location能夠是function, file:func, linenum, file:linenumdelete [breakpoints] [range...]
[breakpoints]是可選項disable|enable [breakpoints] [range...]
禁用|啓用斷點enable [breakpoints] once range...
啓用斷點一次enable [breakpoints] once range...
僱用斷點cnt次enable [breakpoints] delete range...
臨時啓用斷點,一旦被激活就會把刪除,和tbreak類似
pirnt x display set watch catch tracepoint
print接受表達式和計算它的值。任何該語言支持常值,變量和操做符均可以使用,像條件表達式,函數調用,類型轉換,字符常量。GDB還支持數組常量,語法是{element, element...}, 好比print {1,2,3}.GDB支持還支持下面操做符
二進制操做符, 能夠把momery當成數組。
int *array = (int*) malloc( len * sizeof(int));
可使用下面命令來打印它的值:
(gdb) p *array@len (gdb) p/x (short[2])0x12345678 $1 = {0x1234, 0x5678}
另外還能夠自定義變量
set $i = 0 p dtab[$i++]->fv RET RET
上面的RET表示重複上面的表達式
定義屬於某個文件或函數的變量
把addr的變量按{type}類型解釋
x/nfu addr x addr x
n, the repeat count
f, the display format (x, d, u, o, t, a, c, f, s, i)
u, the unit size (b: bytes, h: halfwords, w: words, g: giant words)
(gdb) x/5i $pc-6 0x804837f <main+11>: mov %esp,%ebp 0x8048381 <main+13>: push %ecx 0x8048382 <main+14>: sub $0x4,%esp => 0x8048385 <main+17>: movl $0x8048460,(%esp) 0x804838c <main+24>: call 0x80482d4 <puts@plt>
若是你發現你常常要打印某個表達式,你能夠把它加入到"automatic display list"
display expr display/fmt expr display/fmt addr undisplay <dnums> delete display dnums disable display dnums enable display dnums
可使用set <var>=<expr>
來修改程序中變量的值
經過使用print命令顯示的值會被自動保存在GDB的數值歷史當中,該值會一直被保留直到符號表被從新讀取或者放棄的時候(好比使用file或symbol-file),此時全部的值歷史將會被丟棄。在使用print打印值的時候,會將值編以整形的歷史編號,而後可使用$num的方式方便的引用,單個的$表示最近的數值歷史,而$$表示第二新的數值歷史,$$n表示倒數第n新的數值歷史(因此能夠推斷$$0==$; $$1==$$;)。
好比剛剛打印了一個指向結構體的指針,那麼print $就能夠顯示結構體的信息,而命令print \$.nex甚至能夠顯示結構體中的某些字段。
GDB容許自由建立便捷變量用於保存值,後續能夠方便的引用該值,該類型的變量由GDB管理而與正在調試的程序沒有直接的關聯。便捷變量也是使用符號$打頭的,可使用任意名字(除了GDB使用的寄存器名)。
在任意時候使用一個便捷變量都會建立他,若是沒有提供初始化值那麼該變量就是void,直到給其進行賦值操做爲止。便捷變量沒有固定的類型,能夠爲普通變量、數組、結構體等,並且其類型能夠在賦值過程當中改變(爲當前值的類型)。show convenience|conv
顯示道目前全部的便捷變量和便捷函數,以及其值。init-if-undefined $variable = expr
若是該變量尚未被建立或初始化,則建立這個變量。若是該變量已經被建立了,則不會建立和初始化該變量,而且expr表達式也不會被求值,因此這種狀況下也不會有expr的反作用發生。
便捷變量的一種經典用法,就是以前提到的連續查看變量時候用於計數做用:
set $i = 0 print bar[$i++]->contents
下面的一些便捷變量是GDB自動建立的:
當調試程序終止的時候,GDB將會根據程序的退出值自動設置該變量,而且將$_exitsignal變量設置爲void。
當調試中的程序由於一個未捕獲信號而終止,此時GDB會自動將變量$_exitsignal設置爲該信號,同時重置變量$_exitcode爲void。
便捷函數和便捷變量同樣使用$打頭引用,其能夠像一個普通函數同樣在表達式中使用,便捷函數只被GDB內部使用。`
(gdb) print $_isvoid ($_exitsignal)
$_isvoid (expr): 若是expr是void則返回1,不然返回0。
GDB還有不少的便捷函數支持,可是須要在編譯GDB的時候提供Python的支持纔可使用。下面的這些函數意義顯而易見,就不在囉嗦了。
$_memeq(buf1, buf2, length); $_regex(str, regex); $_streq(str1, str2); $_strlen(str)
監視點是監視特定內存位置、特定表達式的值是否改變而觸發程序暫停執行,而不用去關心該值到底在代碼的哪一個位置被修改的。監視的表達式能夠是:某個變量的引用、強制地址解析(好比(int )0x12345678,你沒法watch一個地址,由於地址是永遠也不會改變的)、合理的表達式(好比a-b+c/d,gdb會檢測其中引用的各個變量)。
watch [-l|-location] expr [thread thread-id] [mask maskvalue]
thread-id能夠設置特定線程改變expr的時候觸發中斷,默認狀況下針對硬件模式的檢測點全部的線程都會檢測該表達式;-location會讓gdb計算expr的表達式,並將計算的結果做爲地址,並探測該地址上的值(數據類型由expr計算結果決定)。
不少平臺都有實現監視點的專有硬件,若是沒有GDB也會採用虛擬內存技術來實現監視點,而若是前面兩種技術都不可用,則GDB會採用軟件實現監事點自己。軟件模式速度會很是慢,由於若是不支持硬件模式gdb會每次step並計算檢測的表達式,x86構架支持硬件模式,並且監視點的做用也有限,只能當前單個線程能偵測其變化,雖然該值也可能會被其餘線程修改。
監視點在跟蹤那種變量不知道哪裏被修改的情形特別的有效。不過監視點須要變量在當前上下文可訪問的狀況下才可使用,因此在使用中會一般配合斷點先到達事發的附近位置,而後再建立對應的監測點。
watch命令還存在兩個變體:rwatch當expr被程序讀的時候觸發中斷;awatch會在程序讀取或者寫入expr的時候被中斷。rwatch和awatch只支持硬件模式的檢測點
可讓gdb在某些事件發生的時候暫停執行,好比C++拋出異常、捕獲異常、調用fork()、加載動態連接庫以及某些系統調用的時候,其格式爲catch event,還有一個變體tcatch event設置臨時捕獲點,其中event的參數能夠爲:
throw|rethrow|catch|exec|fork|vfork|syscall|exception|unhandled|assert
在C++異常拋出、從新拋出、捕獲的時候觸發,可選使用regex參數限定特定的異常類型(在gcc-4.8開始支持),內置變量$_exception會記錄在catchpoint激活時候的異常。
當異常發生時候,程序一般會停留在libstdc++的異常支持點,此時能夠經過使用up命令切換幀跳轉到用於異常代碼處。
syscall若是不帶參數,則捕獲任意系統調用。
syscall [name | number | group:groupname | g:groupname] …
在進入和/或返回系統調用的時候觸發。name能夠指明catch的系統調用名(定義在/usr/include/asm/unistd.h,且gdb會幫助智能補全),group|g:groupname能夠用來指定一組類別的系統調用,好比g:network,經過智能補全能夠查看支持的group信息。
load|unload [regex]
: 加載和卸載共享庫時候觸發,可選regex進行過濾。
signal [signal… | ‘all’]
能夠在軟件收到信號的時候觸發。gdb自己會佔用SIGTRAP和SIGINT兩個信號,若是不添加額外參數,會catch除了這兩個信號以外的全部信號。
使用info break命令,watchpoint的信息會被展現出來,能夠像普通斷點同樣管理之。
主要包含如下幾個
bt frame info frame where
bt bt [Frame count] bt full
可使用負數, -1是第一層,-2是第二層
(gdb) backtrace #0 level0 () at recursion.cpp:5 #1 0x08048462 in test (level=0) at recursion.cpp:17 #2 0x0804845b in test (level=1) at recursion.cpp:14 #3 0x0804845b in test (level=2) at recursion.cpp:14 #4 0x0804845b in test (level=3) at recursion.cpp:14 #5 0x0804845b in test (level=4) at recursion.cpp:14 #6 0x0804845b in test (level=5) at recursion.cpp:14 #7 0x08048479 in main () at recursion.cpp:22 (gdb) backtrace -2 #6 0x0804845b in test (level=5) at recursion.cpp:14 #7 0x08048479 in main () at recursion.cpp:22
frame是很是有用的命令,它能夠用來顯示當前幀的信息
基本語法是
frame frame [Frame number] f
若是沒有參數,就是當前行的信息
(gdb) frame #0 level0 () at recursion.cpp:5 5 printf("Reached level 0\n"); (gdb) info args No arguments. (gdb) frame 1 #1 0x08048462 in test (level=0) at recursion.cpp:17 17 level0(); (gdb) info args level = 0 (gdb) frame 2 #2 0x0804845b in test (level=1) at recursion.cpp:14 14 test(prevLevel); (gdb) info args level = 1
能夠用info line *$rip
和list *$rip
得到相似當前行的信息
語法以下:
info frame info frame [addr]
相比直接的frame
, 這個命令輸出更詳細的stack frame信息,包括
(gdb) i frame Stack level 0, frame at 0x7fffffffe250: rip = 0x4009e0 in PrintArray (gdbprog.cc:40); saved rip 0x400a3f called by frame at 0x7fffffffe280 source language c++. Arglist at 0x7fffffffe240, args: Locals at 0x7fffffffe240, Previous frame's sp is 0x7fffffffe250 Saved registers: rbp at 0x7fffffffe240, rip at 0x7fffffffe248 (gdb) bt #0 PrintArray () at gdbprog.cc:40 #1 0x0000000000400a3f in main () at gdbprog.cc:57 (gdb) i f 0x0000000000400a3f Stack frame at 0x400a3f: rip = 0x0; saved rip 0x7ffff7ff9720 called by frame at 0x7fffffffe240 Arglist at 0x7fffffffe228, args: Locals at 0x7fffffffe228, Previous frame's sp is 0x7fffffffe238 Saved registers: rip at 0x7fffffffe230
主要包含
info proc info variables info functions info source info sources info sharedlibrary info flies
info proc all
能夠打印出全部進程相關信息
info proc mappings
顯示正在運行的進程中映射的內存區域的列表。與 /prod/pid/maps 的輸出相同
列出程序中的函數
語法以下:
info functions info functions [Regex]
示例以下:
(gdb) info functions All defined functions: File gdbprog.cc: int DoOperation(int**); void InitArrays(int*); void PrintArray(); int main(); static void __static_initialization_and_destruction_0(int, int); static void __tcf_0(void*); static void global constructors keyed to iArray(); Non-debugging symbols: 0x0000000000400660 _init 0x0000000000400688 std::basic_ostream<char, std::char_traits<char> >::operator<<(int) 0x0000000000400688 _ZNSolsEi@plt 0x0000000000400698 std::ios_base::Init::Init() ...
顯示當前源文件名
顯示加載的symbols涉及的源文件
(gdb) info sources Source files for which symbols have been read in: <<C++-namespaces>>, /home/harriszh/cpp/e1/gdbprog.cc, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/bits/locale_facets.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/x86_64-redhat-linux/bits/ctype_base.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/bits/stl_iterator.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/bits/allocator.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/ext/new_allocator.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/bits/stringfwd.h, /usr/include/bits/pthreadtypes.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/x86_64-redhat-linux/bits/gthr-default.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/bits/locale_classes.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/bits/basic_string.tcc, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/bits/basic_string.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/limits, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/x86_64-redhat-linux/bits/atomic_word.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/bits/ios_base.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/iosfwd, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/new, /usr/include/wctype.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/cwctype, /usr/include/stdlib.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/cstdlib, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/cwchar, /usr/include/time.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/ctime, /usr/include/locale.h, /grid/common/pkgsData/gcc-v4.1.2p2/Linux/RHEL3.0-1H2006-x86_64/include/c++/4.1.2/clocale, /usr/include/wchar.h, /usr/include/_G_config.h, /usr/include/bits/types.h, /usr/include/libio.h, /usr/include/stdio.h, ...
顯示執行文件名稱和加載的段
(gdb) i file Symbols from "/home/harriszh/trunk/cpp/exercise/gdbprog". Unix child process: Using the running image of child process 42226. While running this, GDB does not access memory from... Local exec file: `/home/harriszh/trunk/cpp/exercise/gdbprog', file type elf64-x86-64. Entry point: 0x4007f0 0x0000000000400200 - 0x000000000040021c is .interp 0x000000000040021c - 0x000000000040023c is .note.ABI-tag 0x000000000040023c - 0x0000000000400260 is .note.gnu.build-id 0x0000000000400260 - 0x0000000000400294 is .gnu.hash 0x0000000000400298 - 0x00000000004003e8 is .dynsym 0x00000000004003e8 - 0x0000000000400592 is .dynstr 0x0000000000400592 - 0x00000000004005ae is .gnu.version 0x00000000004005b0 - 0x0000000000400600 is .gnu.version_r 0x0000000000400600 - 0x0000000000400630 is .rela.dyn 0x0000000000400630 - 0x0000000000400720 is .rela.plt 0x0000000000400720 - 0x0000000000400738 is .init 0x0000000000400738 - 0x00000000004007e8 is .plt 0x00000000004007f0 - 0x0000000000400ba8 is .text 0x0000000000400ba8 - 0x0000000000400bb6 is .fini 0x0000000000400bb8 - 0x0000000000400bf4 is .rodata 0x0000000000400bf4 - 0x0000000000400c40 is .eh_frame_hdr 0x0000000000400c40 - 0x0000000000400d84 is .eh_frame 0x0000000000601000 - 0x0000000000601018 is .ctors 0x0000000000601018 - 0x0000000000601028 is .dtors 0x0000000000601028 - 0x0000000000601030 is .jcr 0x0000000000601030 - 0x00000000006011f0 is .dynamic 0x00000000006011f0 - 0x00000000006011f8 is .got 0x00000000006011f8 - 0x0000000000601260 is .got.plt 0x0000000000601260 - 0x0000000000601264 is .data 0x0000000000601280 - 0x0000000000601438 is .bss 0x000000347b8001c8 - 0x000000347b8001ec is .note.gnu.build-id in /lib64/ld-linux-x86-64.so.2 0x000000347b8001f0 - 0x000000347b8002a8 is .hash in /lib64/ld-linux-x86-64.so.2 ...
顯示共享庫及它們的地址
語法以下:
info sharedlibrary info shared
示例以下:
(gdb) info sharedlibrary From To Syms Read Shared Object Library 0xb7fde820 0xb7ff6b9f Yes /lib/ld-linux.so.2 0xb7fd83a0 0xb7fd84c8 Yes /home/testuser/libtest/libTest.so 0xb7e30f10 0xb7f655cc Yes /lib/i386-linux-gnu/libc.so.6 ...
當一個shared library加載或卸載時,是否暫停
set stop-on-solib-events 0 set stop-on-solib-events 1 show stop-on-solib-events
默認是不暫停, 若是想暫停設成1
主要命令是
list info line disassemble set disassemble-next-line set disassemble-flavor
查看當前運行源代碼
語法以下:
list <linenum> list <first>, <last> list <function> list list - list *<addr>
示例以下:
(gdb) list *$pc 0x4009e0 is in PrintArray() (gdbprog.cc:40). 35 36 void PrintArray() 37 { 38 int i; 39 40 for(i = 0;i < 10;i++) 41 { 42 if (iArray[i] == 6) 43 cout << "This is an error\n"; 44 else
基本語法是
info line [File]:[Line] info line [Function] info line [File]:[Function] info line *[Address]
好比:
(gdb) info line test.cpp:5 Line 5 of "test.cpp" starts at address 0x80483f0 <main(int, char**)+3> and ends at 0x80483f5 <main(int, char**)+8>. (gdb) info line *0x80483f0 Line 5 of "test.cpp" starts at address 0x80483f0 <main(int, char**)+3> and ends at 0x80483f5 <main(int, char**)+8>. (gdb) info line *0x80483f6 Line 6 of "test.cpp" starts at address 0x80483f5 <main(int, char**)+8> and ends at 0x80483f7. (gdb) info line main Line 4 of "test.cpp" starts at address 0x80483ed <main(int, char**)> and ends at 0x80483f0 <main(int, char**)+3>.
顯示某個函數或某函數中一段代碼的彙編
語法以下:
disassemble disassemble [Function] disassemble [Address] disassemble [Start],[End] disassemble [Function],+[Length] disassemble [Address],+[Length] disassemble /m [...] disassemble /r [...]
/m 表示 把源代碼和彙編一一對應顯示出來,在新版本中建議中/s, 它會考慮compiler優化
/r 表示 顯示彙編指令的原生字節碼
控制斷點暫停或step後是否顯示下一行的彙編,默認是off
語法以下:
set disassemble-next-line on set disassemble-next-line off show disassemble-next-line
示例以下:
(gdb) set disassemble-next-line on (gdb) next 4 } => 0x080483f8 <func+11>: 5d pop %ebp 0x080483f9 <func+12>: c3 ret (gdb) next main () at test.c:9 9 } => 0x08048414 <main+26>: c9 leave 0x08048415 <main+27>: c3 ret
控制彙編和x命令的風格,有AT&T和intel兩種
語法以下:
set disassembly-flavor att set disassembly-flavor intel show disassembly-flavor
GDB提供加州功能,能夠記錄程序的執行過程(execution log),而後在後續時間上進行前進、後退的程序運行。在調試目標運行時,若是接下來的執行指令在log中存在,就會進入replay mode,該模式下不會真正執行指令,而是將事件、寄存器,內存值從日誌中提取出來,若是接下來的執行指令不在log中,那麼GDB就會在record mode下執行,全部指令將會按歸照正常方式執行,GDB會記錄執行。
默認的方法是使用full recording, 該模式不支持non-stop mode和異步執行模式的多線程調試。只能調試正在執行的進程
record [full| btrace bts|btrace pt] record stop record goto [begin|start|end|n] info record record delete
開啓進程的R&RT模式
中止進程的R&RT, 並將全部的execution log刪除。若是在record的中間位置執行本命令,那麼會從這個點開始Live調試
中轉到指定位置, n表示execution log中的instruction的序號。
能夠用info record查看
保存和恢復execution log
默認最多200,000。
當保存指令數目超過限制的話,若是on(默認), 那麼GDB會在首次超過限制的地方中止;若是off,則會自動刪除最舊記錄
在replay模式下執行該命令,會刪除當前指令以後的全部log, 並開始新的record
reverse-continue [ignore-count] rc [ignore-count] reverse-step [count] reverse-stepi [count] reverse-next [count] reverse-nexti [count] reverse-finish set exec-direction [reverse|forward]
它們的行爲如他們名字同樣,這裏就再也不多解釋
產生當前運行進程的一份memory image和它的process status(register values etc...)
gcore [file]
(gdb) i registers rax 0x601280 6296192 rbx 0x0 0 rcx 0x400 1024 rdx 0x3487ae9d18 225614667032 rsi 0x7ffff7ff8000 140737354104832 rdi 0xffffffff 4294967295 rbp 0x7fffffffe240 0x7fffffffe240 rsp 0x7fffffffe230 0x7fffffffe230 r8 0x7ffff7ff8002 140737354104834 r9 0x7ffff7ff9720 140737354110752 r10 0xcccccccccccccccd -3689348814741910323 r11 0x246 582 r12 0x4007f0 4196336 r13 0x7fffffffe350 140737488347984 r14 0x0 0 r15 0x0 0 rip 0x4009e0 0x4009e0 <PrintArray()+88> eflags 0x206 [ PF IF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0
顯示全局或靜態的變量
info variables info variables [Regex]
能夠用i var <varName>
來查看某個全局或靜態變量是在哪一個文件定義的
顯示當前幀的本地變量, 配合frame, up和down來使用
顯示當前幀的函數的參數, 配合frame,up和down來使用
能夠直接打開編輯器編輯源文件
edit <number> edit <function>
forward-search <regexp> fo <regexp> search <regexp> reverse-search <regex> rev <regexp>
能夠經過directory dirname ...
來添加源文件路徑
使用find能夠對內存進行序列字節查找
find [/sn] start_addr, +len, val1 [, val2, …] find [/sn] start_addr, end_addr, val1 [, val2, …]
該命令會在內存中搜索var一、var2…字符序,其範圍從startaddr開始,以結束地址或者長度結束。
參數s表示搜索的大小,能夠是b、h、w、g(跟以前的x命令同樣),若是該參數沒有指定,GDB會考慮當前調試程序的語言環境肯定,好比0x43這種整數就默認爲w(4字節)類型;n表示打印最大搜索命中數量,默認行爲是所有打印。該命令還能夠搜索字符串,字符串使用雙引號括住。
對於查找結果,命中數目保存在變量$numfound中,而最後一個值命令的地址保存在**$**中。
void hello () { static char hello[] = "hello-hello"; static struct { char c; short s; int i; } __attribute__ ((packed)) mixed = { 'c', 0x1234, 0x87654321 }; printf ("%s\n", hello); }
能夠進行以下查找
(gdb) find &hello[0], +sizeof(hello), "hello" 0x804956d <hello.1620+6> 1 pattern found (gdb) find &hello[0], +sizeof(hello), 'h', 'e', 'l', 'l', 'o' 0x8049567 <hello.1620> 0x804956d <hello.1620+6> 2 patterns found. (gdb) find &hello[0], +sizeof(hello), {char[5]}"hello" 0x8049567 <hello.1620> 0x804956d <hello.1620+6> 2 patterns found. (gdb) find /b1 &hello[0], +sizeof(hello), 'h', 0x65, 'l' 0x8049567 <hello.1620> 1 pattern found (gdb) find &mixed, +sizeof(mixed), (char) 'c', (short) 0x1234, (int) 0x87654321 0x8049560 <mixed.1625> 1 pattern found (gdb) print $numfound $1 = 1 (gdb) print $_ $2 = (void *) 0x8049560
GDB對fork產生新進程的調試沒有特殊的支持,當使用fork產生子進程的時候,GDB會繼續調試父進程,子進程不會被阻礙而是繼續執行,而此時若是子進程遇到斷點後,子進程會收到SIGTRAP信號,當在非調試模式下默認會致使子進程停止。若是要調試子進程,可讓子進程啓動後睡眠一段時間(或者監測某個文件是否建立),在這個空隙中使用ps查看子進程的進程號,而後再啓動gdb去attach到這個進程上面去調試。
經過catch命令可讓gdb在fork/vfork/exec調用的時候暫停執行。
影響fork多進程調試還有如下幾個變量:follow-fork-mode
可選值是parent或child,指明當正在被調試的進程fork出子進程的時候,若是該值是parent,那麼父進程被調試子進程正常執行,這也是默認行爲;而當其爲child的時候,則新建立的子進程被調試,父進程正常執行。detach-on-fork
可選值是on或off,知名當被調試的進程fork出子進程後,是否detach某個進程仍是調試他們倆,若是是on,那麼子進程(或父進程,取決於上面follow-fork-mode的值)將會被detach正常執行,這是默認行爲;不然兩個進程都會被gdb控制,其中的一個進程被正常調試,另一個進程被suspend住。follow-exec-mode
gdb對於exec調用的反應,其值能夠爲new或same,當值爲new的時候,以前fork的子進程和exec後的進程都會被保留;若是是same,則exec的進程使用以前fork的進程,exec會用新的執行鏡像替代原先的可執行程序,這是默認行爲。
在多線程程序中,有兩種工做模式: all-stop和non-stop
all-stop
當進程中任意線程由於某種緣由中止執行的時候,全部其餘線程也同時被GDB中止執行。好處是程序中止的時候全部線程的狀態都被保留下來了,能夠切換線程查看整個程序當時的全部狀態(缺點是整個業務所有阻塞暫停了)。
有些操做系統支持OS調試鎖定,能夠修改GDB的默認行爲
off:不會鎖定,全部線程能夠自由運行;
on:當程序resume的時候,只有當前線程能夠運行;
step:該模式是爲單步模式優化的模式,當跟蹤的時候阻止其餘線程搶佔當前線程。當step的時候其餘線程沒法運行,而在使用continue、until、finish相似指令的時候其餘線程能夠自由運行。除非其餘線程運行時候觸發了斷點,不然GDB不會切換當前調試的線程。
該設置是針對多進程狀況下的調試。
off:只有當前線程所在的進程的全部線程容許恢復執行,該off爲默認值。
on:全部進程的全部線程均可以執行。
non-stop
當進程中任意線程中止時,在跟蹤檢查中止線程的時候其餘的線程任然繼續執行,其好處就是對線上系統進行最小化的干擾入侵,整個程序仍是能夠繼續響應外部請求的。
經過下面的設置能夠開啓non-stop模式,通常都是在gdb開始的時候或者鏈接調試目標的時候設置纔會生效,且在調試過程當中不能切換該模式,不是全部平臺都支持non-stop模式,因此即便設置了GDB仍是有可能fallback到all-stop模式。
set pagination off set non-stop on
non-stop模式的驗證也十分的簡單,在一個多線程的網絡程序中先設置斷點,此時發送一個請求那麼處理線程會被中止,接下來刪除該斷點,再次發送一個請求,看該請求是否獲得正常響應。若是non-stop正常開啓生效的話,此時的info threads會顯示其餘線程都是running的狀態。
non-stop模式下的命令默認都是針對當前線程的,若是要針對全部線程,一般須要使用-a參數,好比continue -a
C語言預處理宏
宏自己是個比較麻煩的東西,由於能夠某些點定義、某些點取消定義、某些點從新定義,GDB支持對含有宏的表達式進行展開並顯示展開後結果的功能。若是要讓編譯後的程序具備宏調試功能,須要額外的編譯參數-gdwarf-2和-g3(-g是不夠的)
gdb gcc -gdwarf-2 -g3 sample.c -o sample
上面的gdwarf-2具備gdwarf-三、gdwarf-4等版本,推薦支持的狀況下使用最新版本的調試格式。macro exp|expand expression
顯示expression中全部宏被展開後的結果。由於GDB只會進行宏展開而不會對錶達式取值,因此這裏的expression不須要是合法的表達式。info macro [-a|-all] [–] macro
顯示當前的或者所有的macro宏定義,同時顯示該宏定義所在的源代碼文件及其所在行號位置信息。其中的–表示參數列表結束,由於C中有些宏是可使用-開頭的,這裏用於消除歧義做用。info macros location
顯示全部在location位置有效的宏定義及他們的位置信息。(顯示結果有點多哦)macro define macro replacement-list
macro define macro(arglist) replacement-list
自定義宏及其展開式,經過該命令建立的宏會在GDB求值的每一個表達式中都會生效(只對GDB自己做用,與代碼中使用的宏無關),直到其顯式使用macro undef進行刪除,同時該宏還會覆蓋程序中同名宏的展開(有點詭異)。macro undef macro
只會刪除上面使用macro define定義的宏,而不會刪除程序代碼中定義的宏。macro list
顯示全部經過macro define定義的宏。除了一般在源代碼中執行宏定義,還能夠在編譯的時候在命令行中經過‘-Dname=value’的方式定義,這樣的宏也支持使用info macro命令查看,不過其行號信息顯示爲0。