Linux gdb調試器用法全面解析__Linux

來源:互聯網
上載者:User

GDB是GNU開源組織發布的一個強大的UNIX下的程式調試工具,GDB主要可協助工程師完成下面4個方面的功能: 啟動程式,可以按照工程師自訂的要求隨心所欲的運行程式。 讓被調試的程式在工程師指定的斷點處停住,斷點可以是條件運算式。 當程式被停住時,可以檢查此時程式中所發生的事,並追索上文。 動態地改變程式的執行環境。 不管是調試Linux核心空間的驅動還是調試使用者空間的應用程式,掌握gdb的用法都是必須。而且,調試核心和調試應用程式時使用的gdb命令是完全相同的,下面以代碼清單22.2的應用程式為例示範gdb調試器的用法。

 
1  int add(int a, int b)2  {3    return a + b;4  }5  6  main()7  {8    int sum[10] = 9    {10     0, 0, 0, 0, 0, 0, 0, 0, 0, 0     11   }  ;12   int i;13   14   int array1[10] =15   {16     48, 56, 77, 33, 33, 11, 226, 544, 78, 9017   };18   int array2[10] =19   {20     85, 99, 66, 0x199, 393, 11, 1, 2, 3, 421   };22 23   for (i = 0; i < 10; i++)24   {25     sum[i] = add(array1[i], array2[i]);26   }27 }

使用命令gcc –g gdb_example.c –o gdb_example編譯上述程式,得到包含調試資訊的二進位檔案example,執行gdb gdb_example命令進入調試狀態:

[root@localhost driver_study]# gdb gdb_exampleGNU gdb Red Hat Linux (5.3post-0.20021129.18rh)Copyright 2003 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB.  Type "show warranty" for details.This GDB was configured as "i386-redhat-linux-gnu"...(gdb)

1、list命令

在gdb中運行list命令(縮寫l)可以列出代碼,list的具體形式包括: list <linenum> ,顯示程式第linenum行周圍的來源程式,如:

(gdb) list 1510        11        int array1[10] =12        {13          48, 56, 77, 33, 33, 11, 226, 544, 78, 9014        };15        int array2[10] =16        {17          85, 99, 66, 0x199, 393, 11, 1, 2, 3, 418        };19
list <function> ,顯示函數名為function的函數的來源程式,如:
(gdb) list main2       {3         return a + b;4       }56       main()7       {8         int sum[10];9         int i;10        11        int array1[10] =
list,顯示當前行後面的來源程式。 list - ,顯示當前行前面的來源程式。

下面示範了使用gdb中的run(縮寫r)、break(縮寫b)、next(縮寫n)命令控製程序的運行,並使用print(縮寫p)命令列印程式中的變數sum的過程:

(gdb) break addBreakpoint 1 at 0x80482f7: file gdb_example.c, line 3.(gdb) run  Starting program: /driver_study/gdb_example Breakpoint 1, add (a=48, b=85) at gdb_example.c:3warning: Source file is more recent than executable.3         return a + b;(gdb) next4       }(gdb) nextmain () at gdb_example.c:2323        for (i = 0; i < 10; i++)(gdb) next25          sum[i] = add(array1[i], array2[i]);(gdb) print sum$1 = {133, 0, 0, 0, 0, 0, 0, 0, 0, 0}
2、run命令

在gdb中,運行程式使用run命令。在程式運行前,我們可以設定如下4方面的工作環境: 程式運行參數 set args 可指定運行時參數,如: set args 10 20 30 40 50;show args 命令可以查看設定好的運行參數。 運行環境

path <dir> 可設定程式的運行路徑;how paths可查看程式的運行路徑;set environment varname [=value]用於設定環境變數,如set env USER=baohua;

show environment [varname]則用於查看環境變數。 工作目錄 cd <dir> 相當於shell的cd命令;pwd 顯示當前所在的目錄。 程式的輸入輸出

info terminal 用於顯示程式用到的終端的模式;gdb中也可以使用重新導向控製程序輸出,如run > outfile;

tty命令可以指定輸入輸出的終端裝置,如:tty /dev/ttyS1。 3、break命令

在gdb中用break命令來設定斷點,設定斷點的方法包括: break <function> 在進入指定函數時停住,C++中可以使用class::function或function(type, type)格式來指定函數名。 break <linenum> 在指定行號停住。 break +offset / break -offset 在當前行號的前面或後面的offset行停住,offiset為自然數。 break filename:linenum 在源檔案filename的linenum行處停住。 break filename:function 在源檔案filename的function函數的入口處停住。 break *address 在程式啟動並執行記憶體位址處停住。 break break命令沒有參數時,表示在下一條指令處停住。 break ... if <condition>

“...”可以是上述的break <linenum>、break +offset / break –offset中的參數,condition表示條件,在條件成立時停住。比如在迴圈體中,可以設定break if i=100,表示當i為100時停住程式。

查看斷點時,可使用info命令,如info breakpoints [n]、info break [n](n表示斷點號)。 4、單步命令

在調試過程中,next命令用於逐步執行,類似VC++中的step over。next的單步不會進入函數的內部,與next對應的step(縮寫s)命令則在逐步執行一個函數時,會進入其內部,類似VC++中的step into。下面示範了step命令的執行情況,在23行的add()函數調用處執行step會進入其內部的“return a+b;”語句:

(gdb) break 25Breakpoint 1 at 0x8048362: file gdb_example.c, line 25.(gdb) runStarting program: /driver_study/gdb_example Breakpoint 1, main () at gdb_example.c:2525          sum[i] = add(array1[i], array2[i]);(gdb) stepadd (a=48, b=85) at gdb_example.c:33         return a + b;
逐步執行的更複雜用法包括:

step <count>

單步跟蹤,如果有函數調用,則進入該函數(進入函數的前提是,此函數被編譯有debug資訊)。step後面不加count表示一條條地執行,加表示執行後面的count條指令,然後再停住。 next <count> 單步跟蹤,如果有函數調用,它不會進入該函數。同樣地,next後面不加count表示一條條地執行,加表示執行後面的count條指令,然後再停住。 set step-mode set step-mode on用於開啟step-mode模式,這樣,在進行單步跟蹤時,程式不會因為沒有debug資訊而不停住,這個參數的設定可便於查看機器碼。set step-mod off用於關閉step-mode模式。 finish 運行程式,直到當前函數完成返回,並列印函數返回時的堆棧地址和傳回值及參數值等資訊。 until (縮寫u) 一直在迴圈體內執行單步,退不出來是一件令人煩惱的事情,until命令可以運行程式直到退出迴圈體。 stepi(縮寫si)和nexti(縮寫ni) stepi和nexti用於單步跟蹤一條機器指令,一條程式碼有可能由數條機器指令完成,stepi和nexti可以逐步執行機器指令。 另外,運行“display/i $pc”命令後,單步跟蹤會在打出程式碼的同時打出機器指令,即彙編代碼。 5、continue命令

當程式被停住後,可以使用continue命令(縮寫c,fg命令同continue命令)恢複程式的運行直到程式結束,或到達下一個斷點,命令格式為:

continue [ignore-count]c [ignore-count]fg [ignore-count]
ignore-count表示忽略其後多少次斷點。 假設我們設定了函數斷點 add(),並 watch i,則在continue過程中,每次遇到 add()函數或i發生變化,程式就會停住,如:

(gdb) continueContinuing.Hardware watchpoint 3: iOld value = 2New value = 30x0804838d in main () at gdb_example.c:2323        for (i = 0; i < 10; i++)(gdb) continueContinuing.Breakpoint 1, main () at gdb_example.c:2525          sum[i] = add(array1[i], array2[i]);(gdb) continueContinuing.Hardware watchpoint 3: iOld value = 3New value = 40x0804838d in main () at gdb_example.c:2323        for (i = 0; i < 10; i++)

6、print命令

在偵錯工具時,當程式被停住時,可以使用print命令(縮寫為p),或是同義命令inspect來查看當前程式的運行資料。print命令的格式是:

 print <expr> print /<f> <expr>

<expr>是運算式,是被調試的程式中的運算式, <f>是輸出的格式,比如,如果要把運算式按16進位的格式輸出,那麼就是 /x。在運算式中,有幾種GDB所支援的操作符,它們可以用在任何一種語言中, “@”是一個和數組有關的操作符, “::”指定一個在檔案或是函數中的變數, “{<type>} <addr>”表示一個指向記憶體位址 <addr>的類型為type的一個對象。

下面示範了查看sum[]數組的值的過程:

(gdb) print sum$2 = {133, 155, 0, 0, 0, 0, 0, 0, 0, 0}(gdb) nextBreakpoint 1, main () at gdb_example.c:2525          sum[i] = add(array1[i], array2[i]);(gdb) next23        for (i = 0; i < 10; i++)(gdb) print sum$3 = {133, 155, 143, 0, 0, 0, 0, 0, 0, 0}
當需要查看一段連續記憶體空間的值的時間,可以使用GDB的 “@”操作符, “@”的左邊是第一個記憶體位址, “@”的右邊則是想查看記憶體的長度。例如如下動態申請的記憶體:

int *array = (int *) malloc (len * sizeof (int));

在GDB調試過程中這樣顯示出這個動態數組的值:

p *array@len

print的輸出格式包括: x 按十六進位格式顯示變數。 d 按十進位格式顯示變數。 u 按十六進位格式顯示無符號整型。 o 按八進位格式顯示變數。 t 按二進位格式顯示變數。 a 按十六進位格式顯示變數。 c 按字元格式設定顯示變數。 f 按浮點數格式顯示變數。 我們可用display命令設定一些自動顯示的變數,當程式停住時,或是單步跟蹤時,這些變數會自動顯示。 如果要修改變數,如x的值,可使用如下命令:

print x=4

當用GDB的print查看程式運行時的資料時,每一個print都會被GDB記錄下來。GDB會以$1,$2,$3 …這樣的方式為每一個print命令編號。我們可以使用這個編號訪問以前的運算式,如$1。 7、watch命令

watch一般來觀察某個運算式(變數也是一種運算式)的值是否有變化了,如果有變化,馬上停住程式。我們有下面的幾種方法來設定觀察點: watch <expr>:為運算式(變數)expr設定一個觀察點。一量運算式值有變化時,馬上停住程式。rwatch <expr>:當運算式(變數)expr被讀時,停住程式。awatch <expr>:當運算式(變數)的值被讀或被寫時,停住程式。info watchpoints:列出當前所設定了的所有觀察點。 下面示範了觀察i並在連續運行next時一旦發現i變化,i值就會顯示出來的過程:

(gdb) watch iHardware watchpoint 3: i(gdb) next23        for (i = 0; i < 10; i++)(gdb) nextHardware watchpoint 3: iOld value = 0New value = 10x0804838d in main () at gdb_example.c:2323        for (i = 0; i < 10; i++)(gdb) nextBreakpoint 1, main () at gdb_example.c:2525          sum[i] = add(array1[i], array2[i]);(gdb) next23        for (i = 0; i < 10; i++)(gdb) nextHardware watchpoint 3: iOld value = 1New value = 20x0804838d in main () at gdb_example.c:2323        for (i = 0; i < 10; i++)

8、examine命令

我們可以使用examine命令(縮寫為x)來查看記憶體位址中的值。examine命令的文法如下所示:

x/<n/f/u> <addr> 

<addr>表示一個記憶體位址。“x/”後的n、f、u都是可選的參數,n 是一個正整數,表示顯示記憶體的長度,也就是說從當前地址向後顯示幾個地址的內容;f 表示顯示的格式,如果地址所指的是字串,那麼格式可以是s,如果地址是指令地址,那麼格式可以是i;u 表示從當前地址往後請求的位元組數,如果不指定的話,GDB預設是4位元組。u參數可以被一些字元代替:b表示單位元組,h表示雙位元組,w表示四位元組,g表示八位元組。當我們指定了位元組長度後,GDB會從指定的記憶體位址開始,讀寫指定位元組,並把其當作一個值取出來。n、f、u這3個參數可以一起使用,例如命令“x/3uh 0x54320”表示從記憶體位址0x54320開始以雙位元組為1個單位(h)、16進位方式(u)顯示3個單位(3)的記憶體。 ==

譬如下面的例子:

main(){        char *c = "hello world";        printf("%s\n", c);}

我們在
char *c = "hello world";
下一行設定斷點後:
(gdb) l1    main()2    {3        char *c = "hello world";4        printf("%s\n", c);5    }(gdb) b 4Breakpoint 1 at 0x100000f17: file main.c, line 4.(gdb) rStarting program: /Users/songbarry/mainReading symbols for shared libraries +. doneBreakpoint 1, main () at main.c:44        printf("%s\n", c);
可以通過多種方式看C指向的字串:

方法1:

(gdb) p c$1 = 0x100000f2e "hello world"
方法2:
(gdb) x/s 0x100000f2e0x100000f2e: "hello world"
方法3:
(gdb) p (char *)0x100000f2e$3 = 0x100000f2e "hello world"
將第一個字元改為大寫:
(gdb) p *(char *)0x100000f2e='H'$4 = 72 'H'
再看看C:
(gdb) p c$5 = 0x100000f2e "Hello world"

9、set命令

修改寄存器:

(gdb) set $v0 = 0x004000000(gdb) set $epc = 0xbfc00000 

修改記憶體:

(gdb) set {unsigned int}0x8048a51=0x0
譬如對於第8節的例子:

(gdb) set {unsigned int}0x100000f2e=0x0       (gdb) x/10cb 0x100000f2e0x100000f2e:0 '\0'0 '\0'0 '\0'0 '\0'111 'o'32 ' '119 'w'111 'o'0x100000f36:114 'r'108 'l'(gdb) p c$10 = 0x100000f2e ""

10、jump命令

一般來說,被偵錯工具會按照程式碼的運行順序依次執行,但是GDB也提供了亂序執行的功能,也就是說,GDB可以修改程式的執行順序,從而讓程式隨意跳躍。這個功能可以由GDB的jump命令:jump <linespec> 來指定下一條語句的運行點。<linespec>可以是檔案的行號,可以是file:line格式,也可以是+num這種位移量格式,表示下一條運行語句從哪裡開始。jump <address> 這裡的<address>是程式碼的記憶體位址。 注意,jump命令不會改變當前的程式棧中的內容,所以,如果使用jump從一個函數跳轉到另一個函數,當跳轉到的函數運行完返回,進行出棧操作時必然會發生錯誤,這可能導致意想不到的結果,所以最好只用jump在同一個函數中進行跳轉。 11、signal命令

使用singal命令,可以產生一個訊號量給被調試的程式,如中斷訊號“Ctrl+C”。這非常方便於程式的調試,可以在程式啟動並執行任意位置設定斷點,並在該斷點用GDB產生一個訊號量,這種精確地在某處產生訊號的方法非常有利於程式的調試。 signal命令的文法是:signal <signal>,UNIX的系統訊號量通常從1到15,所以<signal>取值也在這個範圍。 12、return命令

如果在函數中設定了調試斷點,在斷點後還有語句沒有執行完,這時候我們可以使用return命令強制函數忽略還沒有執行的語句並返回。

returnreturn <expression>
上述return命令用於取消當前函數的執行,並立即返回,如果指定了 <expression>,那麼該運算式的值會被作為函數的傳回值。

13、call命令

call命令用於強制調用某函數: call <expr> 運算式中可以一是函數,以此達到強制調用函數的目的,它會顯示函數的傳回值(如果函數傳回值不是void)。 其實,前面介紹的print命令也可以完成強制調用函數的功能。 14、info命令

info命令可以在調試時用來查看寄存器、斷點、觀察點和訊號等資訊。要查看寄存器的值,可以使用如下命令: info registers (查看除了浮點寄存器以外的寄存器)info all-registers (查看所有寄存器,包括浮點寄存器)info registers <regname ...> (查看所指定的寄存器) 要查看斷點資訊,可以使用如下命令:info break 列出當前所設定的所有觀察點,使用如下命令:info watchpoints 查看有哪些訊號正在被GDB檢測,使用如下命令:info signals info handle 也可以使用info line命令來查看原始碼在記憶體中的地址。info threads可以看多線程。info line後面可以跟行號、函數名、檔案名稱:行號、檔案名稱:函數名等多種形式,例如下面的命令會列印出所指定的源碼在運行時的記憶體位址:

info line tst.c:func

15、set scheduler-locking off|on|step

off 不鎖定任何線程,也就是所有線程都執行,這是預設值。
on 只有當前被偵錯工具會執行。
step 在單步的時候,除了next過一個函數的情況以外,只有當前線程會執行。

與多線程調試相關的命令還包括:

thread ID
切換當前調試的線程為指定ID的線程。
 
break thread_test.c:123 thread all
在所有線程中相應的行上設定斷點
 
thread apply ID1 ID2 command
讓一個或者多個線程執行GDB命令command。
 
thread apply all command
讓所有被調試線程執行GDB命令command。
16、disassemble

disassemble命令用於反組譯碼,它可被用來查看當前執行時的原始碼的機器碼,其實際上只是把目前記憶體中的指令dump出來。下面的樣本用於查看函數func的彙編代碼:

(gdb) disassemble funcDump of assembler code for function func:0x8048450 <func>:       push   %ebp0x8048451 <func+1>:     mov    %esp,%ebp0x8048453 <func+3>:     sub    $0x18,%esp0x8048456 <func+6>:     movl   $0x0,0xfffffffc(%ebp)...End of assembler dump.
 
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.