gdb中經常使用的命令和用例講解

總述

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

1.1 幾種方法

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

1.2 參數

-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

1.3 shell command

若是要執行shell comands, 能夠用 shell 開頭就行

shell command-string
!command-string

若是是make, 則能夠直接輸入make make-args

1.4 log

若是你想把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

二. 使用幫助

主要有兩個命令

2.1 help

若是知道命令名, 直接輸入help <command_name>

2.2 apropos

若是不記得命令名,能夠搜索關鍵字, 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: 設置書籤

2.1 start

後面能夠跟參數, 如start 1 2 3

2.2 ignore

忽略某函數或文件
在調試程序中可能會不關心某個函數或文件,這時能夠用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的函數的構造和解構函數

2.3 checkpoint

gdb能夠在程序執行的過程當中保留快照(狀態)信息,稱之爲checkpoint,能夠在進來返回到該處再次查看當時的信息,好比內存、寄存器以及部分系統狀態。經過設置checkpoint,萬一調試的過程當中錯誤發生了可是已經跳過了錯誤發生的地方,就能夠快速返回checkpoint再開始調試,而不用重啓程序從新來過。

2.3.1 checkpoint

對當前執行狀態保存一個快照,gdb會自動產生一個對應的編號供後續標識使用。從提示信息看來,其實每創建一個checkpoint,都會fork出一個子進程,因此每一個checkpoint都有一個惟一的進程ID所對應着。

2.3.2 info checkpoints

查看全部的checkpoint,包括進程ID、代碼地址和當時的行號或者符號。

2.3.3 restart checkpoint-id

恢復程序狀態到checkpoint-id指明的checkpoint,全部的程序變量、寄存器、棧都會被還原到那個狀態,同時gdb還會將時鐘信息回退到那個點。恢復過程當中程序的狀態和系統的部分狀態是支持的,好比文件指針等信息,可是寫入文件的數據、傳輸到外部設備的數據都沒法被回退。

2.3.4 delete checkpoint checkpoint-id

刪除指定的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]

4.1 Function name和Address

不要混淆[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.

4.2. Pending breakpoints

若是試圖將斷點設在位於還未加載的shared library代碼內,那麼就會顯示相似下面warning

Make breakpoint pending on future shared library load? (y or [n])

但它可能永遠不會工做,若是是下列狀況:

  • shared library不包含任何debugging symbols (被用‘strip’命令移除掉)
  • GDB沒法檢測到library被加載 (好比,在android上使用GDB 6.x)
  • 你輸入的是一個錯誤文件名或函數名

因此能夠用下面幾個命令來作調試

  • info sharedlibrary : 目前加載的shared library
  • info sources : 被GDB識別的源文件
  • info breakpoints : 建立的斷點和它們的狀態

4.3. 條件斷點

大約有如下幾種形式

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來修改這個斷點條件

4.4. condition

condition <break_list> (condition)

好比

cond 3 (i==4)

上面命令把breakpoint 3的條件修改爲(i==4)

4.5. ignore

ignore <break_list> count

上面命令表示break_list指定的斷點將被忽略count次
好比如今i=0, 設置i>4爲條件,那麼忽略3次後,它停在i=8, 忽略了i=5, 6, 7

4.6. commands

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就好了

4.7. define

用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

4.8 tbreak

只生效一次臨時斷點

4.9 rbreak

在全部匹配regexp的函數名處都設置斷點。它的regexp和grep相同

4.10 管理

clear localtion
location能夠是function, file:func, linenum, file:linenum
delete [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

5.1 print

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表示重複上面的表達式

  • ::

定義屬於某個文件或函數的變量

  • {type} addr

把addr的變量按{type}類型解釋

5.2 x

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>

5.3 display

若是你發現你常常要打印某個表達式,你能夠把它加入到"automatic display list"

display expr
display/fmt expr
display/fmt addr
undisplay <dnums>
delete display dnums
disable display dnums
enable display dnums

5.4 set

可使用set <var>=<expr>來修改程序中變量的值

5.5 數值歷史(value Hisitory)

經過使用print命令顯示的值會被自動保存在GDB的數值歷史當中,該值會一直被保留直到符號表被從新讀取或者放棄的時候(好比使用file或symbol-file),此時全部的值歷史將會被丟棄。在使用print打印值的時候,會將值編以整形的歷史編號,而後可使用&dollar;num的方式方便的引用,單個的&dollar;表示最近的數值歷史,而&dollar;&dollar;表示第二新的數值歷史,&dollar;&dollar;n表示倒數第n新的數值歷史(因此能夠推斷&dollar;&dollar;0==&dollar;; &dollar;&dollar;1==&dollar;&dollar;;)。
  好比剛剛打印了一個指向結構體的指針,那麼print $就能夠顯示結構體的信息,而命令print \$.nex甚至能夠顯示結構體中的某些字段。

5.6 Convenience變量

GDB容許自由建立便捷變量用於保存值,後續能夠方便的引用該值,該類型的變量由GDB管理而與正在調試的程序沒有直接的關聯。便捷變量也是使用符號$打頭的,可使用任意名字(除了GDB使用的寄存器名)。
在任意時候使用一個便捷變量都會建立他,若是沒有提供初始化值那麼該變量就是void,直到給其進行賦值操做爲止。便捷變量沒有固定的類型,能夠爲普通變量、數組、結構體等,並且其類型能夠在賦值過程當中改變(爲當前值的類型)。
show convenience|conv
顯示道目前全部的便捷變量和便捷函數,以及其值。
init-if-undefined $variable = expr
若是該變量尚未被建立或初始化,則建立這個變量。若是該變量已經被建立了,則不會建立和初始化該變量,而且expr表達式也不會被求值,因此這種狀況下也不會有expr的反作用發生。
便捷變量的一種經典用法,就是以前提到的連續查看變量時候用於計數做用:

set $i = 0
print bar[$i++]->contents

下面的一些便捷變量是GDB自動建立的:

  • &dollar;_exitcode

當調試程序終止的時候,GDB將會根據程序的退出值自動設置該變量,而且將&dollar;_exitsignal變量設置爲void。

  • &dollar;_exitsignal

當調試中的程序由於一個未捕獲信號而終止,此時GDB會自動將變量&dollar;_exitsignal設置爲該信號,同時重置變量&dollar;_exitcode爲void。

5.7 Convenience函數

便捷函數和便捷變量同樣使用&dollar;打頭引用,其能夠像一個普通函數同樣在表達式中使用,便捷函數只被GDB內部使用。`

(gdb) print $_isvoid ($_exitsignal)

&dollar;_isvoid (expr): 若是expr是void則返回1,不然返回0。
GDB還有不少的便捷函數支持,可是須要在編譯GDB的時候提供Python的支持纔可使用。下面的這些函數意義顯而易見,就不在囉嗦了。
&dollar;_memeq(buf1, buf2, length); &dollar;_regex(str, regex); &dollar;_streq(str1, str2); &dollar;_strlen(str)

5.8 watch

監視點是監視特定內存位置、特定表達式的值是否改變而觸發程序暫停執行,而不用去關心該值到底在代碼的哪一個位置被修改的。監視的表達式能夠是:某個變量的引用、強制地址解析(好比(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只支持硬件模式的檢測點

5.9 catch

可讓gdb在某些事件發生的時候暫停執行,好比C++拋出異常、捕獲異常、調用fork()、加載動態連接庫以及某些系統調用的時候,其格式爲catch event,還有一個變體tcatch event設置臨時捕獲點,其中event的參數能夠爲:

throw|rethrow|catch|exec|fork|vfork|syscall|exception|unhandled|assert

在C++異常拋出、從新拋出、捕獲的時候觸發,可選使用regex參數限定特定的異常類型(在gcc-4.8開始支持),內置變量&dollar;_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的信息會被展現出來,能夠像普通斷點同樣管理之。

五. Stack命令

主要包含如下幾個

bt
frame
info frame
where

5.1 bt

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

5.2 frame

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 *$riplist *$rip得到相似當前行的信息

5.3 info frame

語法以下:

info frame
info frame [addr]

相比直接的frame, 這個命令輸出更詳細的stack frame信息,包括

  • frame的地址
  • 下一層frame的地址
  • 上一層frame的地址(caller)
  • 當前frame是用什麼語言所寫
  • frame的變量地址
  • frame的local變量
  • pc值
  • 在當前frame裏保存的registers
(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

6.1 info proc

info proc all能夠打印出全部進程相關信息

info proc mappings顯示正在運行的進程中映射的內存區域的列表。與 /prod/pid/maps 的輸出相同

6.2 info functions

列出程序中的函數
語法以下:

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()
...

6.3 info source

顯示當前源文件名

info sources

顯示加載的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, 
...

6.4 info files

顯示執行文件名稱和加載的段

(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
...

6.5 info sharedlibrary

顯示共享庫及它們的地址
語法以下:

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
...

6.6 set stop-on-solib-events command

當一個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

7.1 list

查看當前運行源代碼
語法以下:

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

7.2 info line

基本語法是

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>.

7.3 disassemble

顯示某個函數或某函數中一段代碼的彙編
語法以下:

disassemble
disassemble [Function]
disassemble [Address]
disassemble [Start],[End]
disassemble [Function],+[Length]
disassemble [Address],+[Length]
disassemble /m [...]
disassemble /r [...]

/m 表示 把源代碼和彙編一一對應顯示出來,在新版本中建議中/s, 它會考慮compiler優化
/r 表示 顯示彙編指令的原生字節碼

set disassemble-next-line

控制斷點暫停或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

set disassemble-flavor

控制彙編和x命令的風格,有AT&T和intel兩種
語法以下:

set disassembly-flavor att
set disassembly-flavor intel
show disassembly-flavor

八. record & replay

GDB提供加州功能,能夠記錄程序的執行過程(execution log),而後在後續時間上進行前進、後退的程序運行。在調試目標運行時,若是接下來的執行指令在log中存在,就會進入replay mode,該模式下不會真正執行指令,而是將事件、寄存器,內存值從日誌中提取出來,若是接下來的執行指令不在log中,那麼GDB就會在record mode下執行,全部指令將會按歸照正常方式執行,GDB會記錄執行。

8.1 Record

默認的方法是使用full recording, 該模式不支持non-stop mode和異步執行模式的多線程調試。只能調試正在執行的進程

record [full| btrace bts|btrace pt]
record stop
record goto [begin|start|end|n]
info record
record delete

8.1.1 record <method>

開啓進程的R&RT模式

8.1.2 record stop

中止進程的R&RT, 並將全部的execution log刪除。若是在record的中間位置執行本命令,那麼會從這個點開始Live調試

8.1.3 record goto

中轉到指定位置, n表示execution log中的instruction的序號。
能夠用info record查看

8.1.4 record save|restore file

保存和恢復execution log

8.1.5 set record full insn-number-max limit|unlimited

默認最多200,000。

8.1.6 set record full stop-at-limit on|off

當保存指令數目超過限制的話,若是on(默認), 那麼GDB會在首次超過限制的地方中止;若是off,則會自動刪除最舊記錄

8.1.7 record delete

在replay模式下執行該命令,會刪除當前指令以後的全部log, 並開始新的record

8.2 Replay

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]

它們的行爲如他們名字同樣,這裏就再也不多解釋

九. 變量查看

9.1 generate-core-file

產生當前運行進程的一份memory image和它的process status(register values etc...)

gcore [file]

9.2 info registers

(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

9.3 info variable

顯示全局或靜態的變量

info variables
info variables [Regex]

能夠用i var <varName>來查看某個全局或靜態變量是在哪一個文件定義的

9.4 info locals

顯示當前幀的本地變量, 配合frame, up和down來使用

9.5 info args

顯示當前幀的函數的參數, 配合frame,up和down來使用

十. 編輯和搜索

10.1 編輯

能夠直接打開編輯器編輯源文件

edit <number>
edit <function>

10.2 搜索

forward-search <regexp>
fo <regexp>
search <regexp>
reverse-search <regex>
rev <regexp>

能夠經過directory dirname ...來添加源文件路徑

10.3 內存查找

使用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的默認行爲

  • scheduler-locking

off:不會鎖定,全部線程能夠自由運行;
on:當程序resume的時候,只有當前線程能夠運行;
step:該模式是爲單步模式優化的模式,當跟蹤的時候阻止其餘線程搶佔當前線程。當step的時候其餘線程沒法運行,而在使用continue、until、finish相似指令的時候其餘線程能夠自由運行。除非其餘線程運行時候觸發了斷點,不然GDB不會切換當前調試的線程。

  • schedule-multiple

該設置是針對多進程狀況下的調試。
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

十三. 針對語言的支持

13.1 對C的特殊支持

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。

相關文章
相關標籤/搜索