GDB是GNU開源組織發佈的一個強大的UNIX下的程序調試工具。或許,各位比較喜歡那種圖形界面方式的,像VC、BCB等IDE的調試,但若是你是在UNIX平臺下作軟件,你會發現GDB這個調試工具備比VC、BCB的圖形化調試器更強大的功能。所謂「寸有所長,尺有所短」就是這個道理。 java
通常來講,GDB主要幫忙你完成下面四個方面的功能:linux
A.啓動你的程序,能夠按照你的自定義的要求爲所欲爲的運行程序。
B.可以讓被調試的程序在你所指定的調置的斷點處停住。(斷點能夠是條件表達式)
C.當程序被停住時,能夠檢查此時你的程序中所發生的事。
D.動態的改變你程序的執行環境。c++
從上面看來,GDB和通常的調試工具沒有什麼兩樣,基本上也是完成這些功能,不過在細節上,你會發現GDB這個調試工具的強大,你們可能比較習慣了圖形化的調試工具,但有時候,命令行的調試工具卻有着圖形化工具所不能完成的功能。讓咱們一一看來。正則表達式
源程序:tst.cshell
1 #include <stdio.h> 2 3 int func(int n) 4 { 5 int sum=0,i; 6 for(i=0; i<n; i++) 7 { 8 sum+=i; 9 } 10 return sum; 11 } 12 13 main() 14 { 15 int i; 16 long result = 0; 17 for(i=1; i<=100; i++) 18 { 19 result += i; 20 } 21 22 printf("result[1-100] = %d /n", result ); 23 printf("result[1-250] = %d /n", func(250) ); 24 }
編譯生成執行文件:(Linux下)express
1 hchen/test> cc -g tst.c -o tst
使用GDB調試:編程
1 hchen/test> gdb tst <---------- 啓動GDB 2 GNU gdb 5.1.1 3 Copyright 2002 Free Software Foundation, Inc. 4 GDB is free software, covered by the GNU General Public License, and you are 5 welcome to change it and/or distribute copies of it under certain conditions. 6 Type "show copying" to see the conditions. 7 There is absolutely no warranty for GDB. Type "show warranty" for details. 8 This GDB was configured as "i386-suse-linux"... 9 (gdb) l <-------------------- l命令至關於list,從第一行開始例出原碼。 10 1 #include <stdio.h> 11 2 12 3 int func(int n) 13 4 { 14 5 int sum=0,i; 15 6 for(i=0; i<n; i++) 16 7 { 17 8 sum+=i; 18 9 } 19 10 return sum; 20 (gdb) <-------------------- 直接回車表示,重複上一次命令 21 11 } 22 12 23 13 24 14 main() 25 15 { 26 16 int i; 27 17 long result = 0; 28 18 for(i=1; i<=100; i++) 29 19 { 30 20 result += i; 31 (gdb) break 16 <-------------------- 設置斷點,在源程序第16行處。 32 Breakpoint 1 at 0x8048496: file tst.c, line 16. 33 (gdb) break func <-------------------- 設置斷點,在函數func()入口處。 34 Breakpoint 2 at 0x8048456: file tst.c, line 5. 35 (gdb) info break <-------------------- 查看斷點信息。 36 Num Type Disp Enb Address What 37 1 breakpoint keep y 0x08048496 in main at tst.c:16 38 2 breakpoint keep y 0x08048456 in func at tst.c:5 39 (gdb) r <--------------------- 運行程序,run命令簡寫 40 Starting program: /home/hchen/test/tst 41 42 Breakpoint 1, main () at tst.c:17 <---------- 在斷點處停住。 43 17 long result = 0; 44 (gdb) n <--------------------- 單條語句執行,next命令簡寫。 45 18 for(i=1; i<=100; i++) 46 (gdb) n 47 20 result += i; 48 (gdb) n 49 18 for(i=1; i<=100; i++) 50 (gdb) n 51 20 result += i; 52 (gdb) c <--------------------- 繼續運行程序,continue命令簡寫。 53 Continuing. 54 result[1-100] = 5050 <----------程序輸出。 55 56 Breakpoint 2, func (n=250) at tst.c:5 57 5 int sum=0,i; 58 (gdb) n 59 6 for(i=1; i<=n; i++) 60 (gdb) p i <--------------------- 打印變量i的值,print命令簡寫。 61 $1 = 134513808 62 (gdb) n 63 8 sum+=i; 64 (gdb) n 65 6 for(i=1; i<=n; i++) 66 (gdb) p sum 67 $2 = 1 68 (gdb) n 69 8 sum+=i; 70 (gdb) p i 71 $3 = 2 72 (gdb) n 73 6 for(i=1; i<=n; i++) 74 (gdb) p sum 75 $4 = 3 76 (gdb) bt <--------------------- 查看函數堆棧。 77 #0 func (n=250) at tst.c:5 78 #1 0x080484e4 in main () at tst.c:24 79 #2 0x400409ed in __libc_start_main () from /lib/libc.so.6 80 (gdb) finish <--------------------- 退出函數。 81 Run till exit from #0 func (n=250) at tst.c:5 82 0x080484e4 in main () at tst.c:24 83 24 printf("result[1-250] = %d /n", func(250) ); 84 Value returned is $6 = 31375 85 (gdb) c <--------------------- 繼續運行。 86 Continuing. 87 result[1-250] = 31375 <----------程序輸出。 88 89 Program exited with code 027. <--------程序退出,調試結束。 90 (gdb) q <--------------------- 退出gdb。 91 hchen/test>
好了,有了以上的感性認識,仍是讓咱們來系統地認識一下gdb吧。數組
通常來講GDB主要調試的是C/C++的程序。要調試C/C++的程序,首先在編譯時,咱們必需要把調試信息加到可執行文件中。使用編譯器(cc/gcc/g++)的 -g 參數能夠作到這一點。如:sass
1 > cc -g hello.c -o hello 2 > g++ -g hello.cpp -o hello
若是沒有-g,你將看不見程序的函數名、變量名,所代替的全是運行時的內存地址。當你用-g把調試信息加入以後,併成功編譯目標代碼之後,讓咱們來看看如何用gdb來調試他。數據結構
啓動GDB的方法有如下幾種:
A.gdb <program>
program也就是你的執行文件,通常在固然目錄下。
B.gdb <program> core
用gdb同時調試一個運行程序和core文件,core是程序非法執行後core dump後產生的文件。
C.gdb <program> <PID>
若是你的程序是一個服務程序,那麼你能夠指定這個服務程序運行時的進程ID。gdb會自動attach上去,並調試他。program應該在PATH環境變量中搜索獲得。
GDB啓動時,能夠加上一些GDB的啓動開關,詳細的開關能夠用gdb -help查看。我在下面只例舉一些比較經常使用的參數:
1 -symbols <file> 2 -s <file>
從指定文件中讀取符號表。
1 -se file
從指定文件中讀取符號表信息,並把他用在可執行文件中。
1 -core <file> 2 -c <file>
調試時core dump的core文件。
1 -directory <directory> 2 -d <directory>
加入一個源文件的搜索路徑。默認搜索路徑是環境變量中PATH所定義的路徑。
啓動gdb後,就你被帶入gdb的調試環境中,就可使用gdb的命令開始調試程序了,gdb的命令可使用help命令來查看,以下所示:
1 /home/hchen> gdb 2 GNU gdb 5.1.1 3 Copyright 2002 Free Software Foundation, Inc. 4 GDB is free software, covered by the GNU General Public License, and you are 5 welcome to change it and/or distribute copies of it under certain conditions. 6 Type "show copying" to see the conditions. 7 There is absolutely no warranty for GDB. Type "show warranty" for details. 8 This GDB was configured as "i386-suse-linux". 9 (gdb) help 10 List of classes of commands: 11 12 aliases -- Aliases of other commands 13 breakpoints -- Making program stop at certain points 14 data -- Examining data 15 files -- Specifying and examining files 16 internals -- Maintenance commands 17 obscure -- Obscure features 18 running -- Running the program 19 stack -- Examining the stack 20 status -- Status inquiries 21 support -- Support facilities 22 tracepoints -- Tracing of program execution without stopping the program 23 user-defined -- User-defined commands 24 25 Type "help" followed by a class name for a list of commands in that class. 26 Type "help" followed by command name for full documentation. 27 Command name abbreviations are allowed if unambiguous. 28 (gdb)
gdb的命令不少,gdb把它分紅許多個種類。help命令只是例出gdb的命令種類,若是要看種類中的命令,可使用help <class> 命令,如:help breakpoints,查看設置斷點的全部命令。也能夠直接help <command>來查看命令的幫助。
gdb中,輸入命令時,能夠不用打全命令,只用打命令的前幾個字符就能夠了,固然,命令的前幾個字符應該要標誌着一個惟一的命令,在Linux下,你能夠敲擊兩次TAB鍵來補齊命令的全稱,若是有重複的,那麼gdb會把其例出來。
示例一:在進入函數func時,設置一個斷點。能夠敲入break func,或是直接就是b func
1 (gdb) b func 2 Breakpoint 1 at 0x8048458: file hello.c, line 10.
示例二:敲入b按兩次TAB鍵,你會看到全部b打頭的命令:
1 (gdb) b 2 backtrace break bt 3 (gdb)
示例三:只記得函數的前綴,能夠這樣:
1 (gdb) b make_ <按TAB鍵>
(再按下一次TAB鍵,你會看到:)
1 make_a_section_from_file make_environ 2 make_abs_section make_function_type 3 make_blockvector make_pointer_type 4 make_cleanup make_reference_type 5 make_command make_symbol_completion_list 6 (gdb) b make_
GDB把全部make開頭的函數所有例出來給你查看。
示例四:調試C++的程序時,有能夠函數名同樣。如:
1 (gdb) b 'bubble( M-? 2 bubble(double,double) bubble(int,int) 3 (gdb) b 'bubble(
你能夠查看到C++中的全部的重載函數及參數。(注:M-?和「按兩次TAB鍵」是一個意思)
要退出gdb時,只用發quit或命令簡稱q就好了。
在gdb環境中,你能夠執行UNIX的shell的命令,使用gdb的shell命令來完成:
1 shell <command string>
調用UNIX的shell來執行<command string>,環境變量SHELL中定義的UNIX的shell將會被用來執行<command string>,若是SHELL沒有定義,那就使用UNIX的標準shell:/bin/sh。(在Windows中使用Command.com或cmd.exe)
還有一個gdb命令是make:
1 make <make-args>
當以gdb <program>方式啓動gdb後,gdb會在PATH路徑和當前目錄中搜索<program>的源文件。如要確認gdb是否讀到源文件,可以使用l或list命令,看看gdb是否能列出源代碼。
在gdb中,運行程序使用r或是run命令。程序的運行,你有可能須要設置下面四方面的事。
set args 可指定運行時參數。(如:set args 10 20 30 40 50)
show args 命令能夠查看設置好的運行參數。
path <dir> 可設定程序的運行路徑。
show paths 查看程序的運行路徑。
set environment varname [=value] 設置環境變量。如:set env USER=hchen
show environment [varname] 查看環境變量
cd <dir> 至關於shell的cd命令。
pwd 顯示當前的所在目錄。
info terminal 顯示你程序用到的終端的模式。
使用重定向控制程序輸出。如:run > outfile
tty命令能夠指寫輸入輸出的終端設備。如:tty /dev/ttyb
兩種方法:
A.在UNIX下用ps查看正在運行的程序的PID(進程ID),而後用gdb <program> PID格式掛接正在運行的程序。
B.先用gdb <program>關聯上源代碼,並進行gdb,在gdb中用attach命令來掛接進程的PID。並用detach來取消掛接的進程。
調試程序中,暫停程序運行是必須的,GDB能夠方便地暫停程序的運行。你能夠設置程序的在哪行停住,在什麼條件下停住,在收到什麼信號時停往等等。以便於你查看運行時的變量,以及運行時的流程。
當進程被gdb停住時,你可使用info program 來查看程序的是否在運行,進程號,被暫停的緣由。
在gdb中,咱們能夠有如下幾種暫停方式:斷點(BreakPoint)、觀察點(WatchPoint)、捕捉點(CatchPoint)、信號(Signals)、線程中止(Thread Stops)。若是要恢復程序運行,可使用c或是continue命令。
咱們用break命令來設置斷點。正面有幾點設置斷點的方法:
1 break <function>
在進入指定函數時停住。C++中可使用class::function或function(type,type)格式來指定函數名。
1 break <linenum>
在指定行號停住。
1 break +offset
2 break -offset
在當前行號的前面或後面的offset行停住。offiset爲天然數。
1 break filename:linenum
在源文件filename的linenum行處停住。
1 break filename:function
在源文件filename的function函數的入口處停住。
1 break *address
在程序運行的內存地址處停住。
1 break
break命令沒有參數時,表示在下一條指令處停住。
1 break ... if <condition>
...能夠是上述的參數,condition表示條件,在條件成立時停住。好比在循環境體中,能夠設置break if i=100,表示當i爲100時停住程序。
查看斷點時,可以使用info命令,以下所示:(注:n表示斷點號)
1 info breakpoints [n]
2 info break [n]
你可設置捕捉點來補捉程序運行時的一些事件。如:載入共享庫(動態連接庫)或是C++的異常。設置捕捉點的格式爲:
1 catch <event>
當event發生時,停住程序。event能夠是下面的內容:
A.throw 一個C++拋出的異常。(throw爲關鍵字)
B.catch 一個C++捕捉到的異常。(catch爲關鍵字)
C.exec 調用系統調用exec時。(exec爲關鍵字,目前此功能只在HP-UX下有用)
D.fork 調用系統調用fork時。(fork爲關鍵字,目前此功能只在HP-UX下有用)
E.vfork 調用系統調用vfork時。(vfork爲關鍵字,目前此功能只在HP-UX下有用)
F.load 或 load <libname> 載入共享庫(動態連接庫)時。(load爲關鍵字,目前此功能只在HP-UX下有用)
G.unload 或 unload <libname> 卸載共享庫(動態連接庫)時。(unload爲關鍵字,目前此功能只在HP-UX下有用)
1 tcatch <event>
只設置一次捕捉點,當程序停住之後,應點被自動刪除。
上面說了如何設置程序的中止點,GDB中的中止點也就是上述的三類。在GDB中,若是你以爲已定義好的中止點沒有用了,你可使用delete、clear、disable、enable這幾個命令來進行維護。
1 clear
清除全部的已定義的中止點。
1 clear <function> 2 clear <filename:function>
清除全部設置在函數上的中止點。
1 clear <linenum> 2 clear <filename:linenum>
清除全部設置在指定行上的中止點。
1 delete [breakpoints] [range...]
刪除指定的斷點,breakpoints爲斷點號。若是不指定斷點號,則表示刪除全部的斷點。range表示斷點號的範圍(如:3-7)。其簡寫命令爲d。
比刪除更好的一種方法是disable中止點,disable了的中止點,GDB不會刪除,當你還須要時,enable便可,就好像回收站同樣。
1 disable [breakpoints] [range...]
disable所指定的中止點,breakpoints爲中止點號。若是什麼都不指定,表示disable全部的中止點。簡寫命令是dis.
1 enable [breakpoints] [range...]
enable所指定的中止點,breakpoints爲中止點號。
1 enable [breakpoints] once range...
enable所指定的中止點一次,當程序中止後,該中止點立刻被GDB自動disable。
1 enable [breakpoints] delete range...
enable所指定的中止點一次,當程序中止後,該中止點立刻被GDB自動刪除。
前面在說到設置斷點時,咱們提到過能夠設置一個條件,當條件成立時,程序自動中止,這是一個很是強大的功能,這裏,我想專門說說這個條件的相關維護命令。通常來講,爲斷點設置一個條件,咱們使用if關鍵詞,後面跟其斷點條件。而且,條件設置好後,咱們能夠用condition命令來修改斷點的條件。(只有break和watch命令支持if,catch目前暫不支持if)
1 condition <bnum> <expression>
修改斷點號爲bnum的中止條件爲expression。
1 condition <bnum>
清除斷點號爲bnum的中止條件。
還有一個比較特殊的維護命令ignore,你能夠指定程序運行時,忽略中止條件幾回。
1 ignore <bnum> <count>
表示忽略斷點號爲bnum的中止條件count次。
咱們可使用GDB提供的command命令來設置中止點的運行命令。也就是說,當運行的程序在被中止住時,咱們可讓其自動運行一些別的命令,這頗有利行自動化調試。對基於GDB的自動化調試是一個強大的支持。
1 commands [bnum]
2 ... command-list ... 3 end
爲斷點號bnum指寫一個命令列表。當程序被該斷點停住時,gdb會依次運行命令列表中的命令。
例如:
1 break foo if x>0
2 commands
3 printf "x is %d/n",x 4 continue 5 end
斷點設置在函數foo中,斷點條件是x>0,若是程序被斷住後,也就是,一旦x的值在foo函數中大於0,GDB會自動打印出x的值,並繼續運行程序。
若是你要清除斷點上的命令序列,那麼只要簡單的執行一下commands命令,並直接在打個end就好了。
在C++中,可能會重複出現同一個名字的函數若干次(函數重載),在這種狀況下,break <function>不能告訴GDB要停在哪一個函數的入口。固然,你可使用break <function(type)>也就是把函數的參數類型告訴GDB,以指定一個函數。不然的話,GDB會給你列出一個斷點菜單供你選擇你所須要的斷點。你只要輸入你菜單列表中的編號就能夠了。如:
1 (gdb) b String::after
2 [0] cancel 3 [1] all 4 [2] file:String.cc; line number:867 5 [3] file:String.cc; line number:860 6 [4] file:String.cc; line number:875 7 [5] file:String.cc; line number:853 8 [6] file:String.cc; line number:846 9 [7] file:String.cc; line number:735 10 > 2 4 6 11 Breakpoint 1 at 0xb26c: file String.cc, line 867. 12 Breakpoint 2 at 0xb344: file String.cc, line 875. 13 Breakpoint 3 at 0xafcc: file String.cc, line 846. 14 Multiple breakpoints were set. 15 Use the "delete" command to delete unwanted 16 breakpoints. 17 (gdb)
可見,GDB列出了全部after的重載函數,你能夠選一下列表編號就好了。0表示放棄設置斷點,1表示全部函數都設置斷點。
當程序被停住了,你能夠用continue命令恢復程序的運行直到程序結束,或下一個斷點到來。也可使用step或next命令單步跟蹤程序。
1 continue [ignore-count]
2 c [ignore-count] 3 fg [ignore-count]
恢復程序運行,直到程序結束,或是下一個斷點到來。ignore-count表示忽略其後的斷點次數。continue,c,fg三個命令都是同樣的意思。
1 step <count>
單步跟蹤,若是有函數調用,他會進入該函數。進入函數的前提是,此函數被編譯有debug信息。很像VC等工具中的step in。後面能夠加count也能夠不加,不加表示一條條地執行,加表示執行後面的count條指令,而後再停住。
1 next <count>
一樣單步跟蹤,若是有函數調用,他不會進入該函數。很像VC等工具中的step over。後面能夠加count也能夠不加,不加表示一條條地執行,加表示執行後面的count條指令,而後再停住。
1 set step-mode
2 set step-mode on
打開step-mode模式,因而,在進行單步跟蹤時,程序不會由於沒有debug信息而不停住。這個參數有很利於查看機器碼。
1 set step-mod off
關閉step-mode模式。
1 finish
運行程序,直到當前函數完成返回。並打印函數返回時的堆棧地址和返回值及參數值等信息。
1 until 或 u
當你厭倦了在一個循環體內單步跟蹤時,這個命令能夠運行程序直到退出循環體。
1 stepi 或 si
2 nexti 或 ni
單步跟蹤一條機器指令!一條程序代碼有可能由數條機器指令完成,stepi和nexti能夠單步執行機器指令。與之同樣有相同功能的命令是「display/i $pc」 ,當運行完這個命令後,單步跟蹤會在打出程序代碼的同時打出機器指令(也就是彙編代碼)
信號是一種軟中斷,是一種處理異步事件的方法。通常來講,操做系統都支持許多信號。尤爲是UNIX,比較重要應用程序通常都會處理信號。UNIX定義了許多信號,好比SIGINT表示中斷字符信號,也就是Ctrl+C的信號,SIGBUS表示硬件故障的信號;SIGCHLD表示子進程狀態改變信號;SIGKILL表示終止程序運行的信號,等等。信號量編程是UNIX下很是重要的一種技術。
GDB有能力在你調試程序的時候處理任何一種信號,你能夠告訴GDB須要處理哪種信號。你能夠要求GDB收到你所指定的信號時,立刻停住正在運行的程序,以供你進行調試。你能夠用GDB的handle命令來完成這一功能。
1 handle <signal> <keywords...>
在GDB中定義一個信號處理。信號<signal>能夠以SIG開頭或不以SIG開頭,能夠用定義一個要處理信號的範圍(如:SIGIO-SIGKILL,表示處理從SIGIO信號到SIGKILL的信號,其中包括SIGIO,SIGIOT,SIGKILL三個信號),也可使用關鍵字all來標明要處理全部的信號。一旦被調試的程序接收到信號,運行程序立刻會被GDB停住,以供調試。其<keywords>能夠是如下幾種關鍵字的一個或多個。
1 nostop
當被調試的程序收到信號時,GDB不會停住程序的運行,但會打出消息告訴你收到這種信號。
1 stop
當被調試的程序收到信號時,GDB會停住你的程序。
1 print
當被調試的程序收到信號時,GDB會顯示出一條信息。
1 noprint
當被調試的程序收到信號時,GDB不會告訴你收到信號的信息。
1 pass 2 noignore
當被調試的程序收到信號時,GDB不處理信號。這表示,GDB會把這個信號交給被調試程序會處理。
1 nopass 2 ignore
當被調試的程序收到信號時,GDB不會讓被調試程序來處理這個信號。
1 info signals 2 info handle
查看有哪些信號在被GDB檢測中。
若是你程序是多線程的話,你能夠定義你的斷點是否在全部的線程上,或是在某個特定的線程。GDB很容易幫你完成這一工做。
1 break <linespec> thread <threadno> 2 break <linespec> thread <threadno> if ...
linespec指定了斷點設置在的源程序的行號。threadno指定了線程的ID,注意,這個ID是GDB分配的,你能夠經過「info threads」命令來查看正在運行程序中的線程信息。若是你不指定thread <threadno>則表示你的斷點設在全部線程上面。你還能夠爲某線程指定斷點條件。如:
1 (gdb) break frik.c:13 thread 28 if bartab > lim
當你的程序被GDB停住時,全部的運行線程都會被停住。這方便你你查看運行程序的整體狀況。而在你恢復程序運行時,全部的線程也會被恢復運行。那怕是主進程在被單步調試時。
當程序被停住了,你須要作的第一件事就是查看程序是在哪裏停住的。當你的程序調用了一個函數,函數的地址,函數參數,函數內的局部變量都會被壓入「棧」(Stack)中。你能夠用GDB命令來查看當前的棧中的信息。
下面是一些查看函數調用棧信息的GDB命令:
1 backtrace 2 bt
打印當前的函數調用棧的全部信息。如:
1 (gdb) bt 2 #0 func (n=250) at tst.c:6 3 #1 0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30 4 #2 0x400409ed in __libc_start_main () from /lib/libc.so.6
從上能夠看出函數的調用棧信息:__libc_start_main --> main() --> func()
1 backtrace <n> 2 bt <n>
n是一個正整數,表示只打印棧頂上n層的棧信息。
1 backtrace <-n> 2 bt <-n>
-n表一個負整數,表示只打印棧底下n層的棧信息。
若是你要查看某一層的信息,你須要在切換當前的棧,通常來講,程序中止時,最頂層的棧就是當前棧,若是你要查看棧下面層的詳細信息,首先要作的是切換當前棧。
1 frame <n> 2 f <n>
n是一個從0開始的整數,是棧中的層編號。好比:frame 0,表示棧頂,frame 1,表示棧的第二層。
1 up <n>
表示向棧的上面移動n層,能夠不打n,表示向上移動一層。
1 down <n>
表示向棧的下面移動n層,能夠不打n,表示向下移動一層。
上面的命令,都會打印出移動到的棧層的信息。若是你不想讓其打出信息。你可使用這三個命令:
1 select-frame <n> 對應於 frame 命令。 2 up-silently <n> 對應於 up 命令。 3 down-silently <n> 對應於 down 命令。
查看當前棧層的信息,你能夠用如下GDB命令:
1 frame 或 f
會打印出這些信息:棧的層編號,當前的函數名,函數參數值,函數所在文件及行號,函數執行到的語句。
1 info frame 2 info f
這個命令會打印出更爲詳細的當前棧層的信息,只不過,大多數都是運行時的內內地址。好比:函數地址,調用函數的地址,被調用函數的地址,目前的函數是由什麼樣的程序語言寫成的、函數參數地址及值、局部變量的地址等等。如:
1 (gdb) info f 2 Stack level 0, frame at 0xbffff5d4: 3 eip = 0x804845d in func (tst.c:6); saved eip 0x8048524 4 called by frame at 0xbffff60c 5 source language c. 6 Arglist at 0xbffff5d4, args: n=250 7 Locals at 0xbffff5d4, Previous frame's sp is 0x0 8 Saved registers: 9 ebp at 0xbffff5d4, eip at 0xbffff5d8
info args
打印出當前函數的參數名及其值。
info locals
打印出當前函數中全部局部變量及其值。
info catch
打印出當前的函數中的異常處理信息。
GDB 能夠打印出所調試程序的源代碼,固然,在程序編譯時必定要加上-g的參數,把源程序信息編譯到執行文件中。否則就看不到源程序了。當程序停下來之後,GDB會報告程序停在了那個文件的第幾行上。你能夠用list命令來打印程序的源代碼。仍是來看一看查看源代碼的GDB命令吧。
1 list <linenum>
顯示程序第linenum行的周圍的源程序。
1 list <function>
顯示函數名爲function的函數的源程序。
1 list
顯示當前行後面的源程序。
1 list -
顯示當前行前面的源程序。
通常是打印當前行的上5行和下5行,若是顯示函數是是上2行下8行,默認是10行,固然,你也能夠定製顯示的範圍,使用下面命令能夠設置一次顯示源程序的行數。
1 set listsize <count>
設置一次顯示源代碼的行數。
1 show listsize
查看當前listsize的設置。
list命令還有下面的用法:
1 list <first>, <last>
顯示從first行到last行之間的源代碼。
1 list , <last>
顯示從當前行到last行之間的源代碼。
1 list +
日後顯示源代碼。
通常來講在list後面能夠跟如下這們的參數:
1 <linenum> 行號。 2 <+offset> 當前行號的正偏移量。 3 <-offset> 當前行號的負偏移量。 4 <filename:linenum> 哪一個文件的哪一行。 5 <function> 函數名。 6 <filename:function> 哪一個文件中的哪一個函數。 7 <*address> 程序運行時的語句在內存中的地址。
不只如此,GDB還提供了源代碼搜索的命令:
1 forward-search <regexp> 2 search <regexp>
向前面搜索。
1 reverse-search <regexp>
所有搜索。
其中,<regexp>就是正則表達式,也主一個字符串的匹配模式,關於正則表達式,我就不在這裏講了,還請各位查看相關資料。
某些時候,用-g編譯事後的執行程序中只是包括了源文件的名字,沒有路徑名。GDB提供了可讓你指定源文件的路徑的命令,以便GDB進行搜索。
1 directory <dirname ... > 2 dir <dirname ... >
加一個源文件路徑到當前路徑的前面。若是你要指定多個路徑,UNIX下你可使用「:」,Windows下你可使用「;」。
1 directory
清除全部的自定義的源文件搜索路徑信息。
1 show directories
顯示定義了的源文件搜索路徑。
你可使用info line命令來查看源代碼在內存中的地址。info line後面能夠跟「行號」,「函數名」,「文件名:行號」,「文件名:函數名」,這個命令會打印出所指定的源碼在運行時的內存地址,如:
1 (gdb) info line tst.c:func 2 Line 5 of "tst.c" starts at address 0x8048456 <func+6> and ends at 0x804845d <func+13>.
還有一個命令(disassemble)你能夠查看源程序的當前執行時的機器碼,這個命令會把目前內存中的指令dump出來。以下面的示例表示查看函數func的彙編代碼。
1 (gdb) disassemble func 2 Dump of assembler code for function func: 3 0x8048450 <func>: push %ebp 4 0x8048451 <func+1>: mov %esp,%ebp 5 0x8048453 <func+3>: sub $0x18,%esp 6 0x8048456 <func+6>: movl $0x0,0xfffffffc(%ebp) 7 0x804845d <func+13>: movl $0x1,0xfffffff8(%ebp) 8 0x8048464 <func+20>: mov 0xfffffff8(%ebp),%eax 9 0x8048467 <func+23>: cmp 0x8(%ebp),%eax 10 0x804846a <func+26>: jle 0x8048470 <func+32> 11 0x804846c <func+28>: jmp 0x8048480 <func+48> 12 0x804846e <func+30>: mov %esi,%esi 13 0x8048470 <func+32>: mov 0xfffffff8(%ebp),%eax 14 0x8048473 <func+35>: add %eax,0xfffffffc(%ebp) 15 0x8048476 <func+38>: incl 0xfffffff8(%ebp) 16 0x8048479 <func+41>: jmp 0x8048464 <func+20> 17 0x804847b <func+43>: nop 18 0x804847c <func+44>: lea 0x0(%esi,1),%esi 19 0x8048480 <func+48>: mov 0xfffffffc(%ebp),%edx 20 0x8048483 <func+51>: mov %edx,%eax 21 0x8048485 <func+53>: jmp 0x8048487 <func+55> 22 0x8048487 <func+55>: mov %ebp,%esp 23 0x8048489 <func+57>: pop %ebp 24 0x804848a <func+58>: ret 25 End of assembler dump.
在你調試程序時,當程序被停住時,你可使用print命令(簡寫命令爲p),或是同義命令inspect來查看當前程序的運行數據。print命令的格式是:
1 print <expr> 2 print /<f> <expr>
<expr>是表達式,是你所調試的程序的語言的表達式(GDB能夠調試多種編程語言),<f>是輸出的格式,好比,若是要把表達式按16進制的格式輸出,那麼就是/x。
print和許多GDB的命令同樣,能夠接受一個表達式,GDB會根據當前的程序運行的數據來計算這個表達式,既然是表達式,那麼就能夠是當前程序運行中的const常量、變量、函數等內容。惋惜的是GDB不能使用你在程序中所定義的宏。
表達式的語法應該是當前所調試的語言的語法,因爲C/C++是一種大衆型的語言,因此,本文中的例子都是關於C/C++的。(而關於用GDB調試其它語言的章節,我將在後面介紹)
在表達式中,有幾種GDB所支持的操做符,它們能夠用在任何一種語言中
@
是一個和數組有關的操做符,在後面會有更詳細的說明。
::
指定一個在文件或是一個函數中的變量。
1 {<type>} <addr>
表示一個指向內存地址<addr>的類型爲type的一個對象。
在GDB中,你能夠隨時查看如下三種變量的值
A.全局變量(全部文件可見的)
B.靜態全局變量(當前文件可見的)
C.局部變量(當前Scope可見的)
若是你的局部變量和全局變量發生衝突(也就是重名),通常狀況下是局部變量會隱藏全局變量,也就是說,若是一個全局變量和一個函數中的局部變量同名時,如
果當前中止點在函數中,用print顯示出的變量的值會是函數中的局部變量的值。若是此時你想查看全局變量的值時,你可使用「::」操做符:
1 file::variable 2 function::variable
能夠經過這種形式指定你所想查看的變量,是哪一個文件中的或是哪一個函數中的。例如,查看文件f2.c中的全局變量x的值:
1 gdb) p 'f2.c'::x
固然,「::」操做符會和C++中的發生衝突,GDB能自動識別「::」 是否C++的操做符,因此你沒必要擔憂在調試C++程序時會出現異常。
另外,須要注意的是,若是你的程序編譯時開啓了優化選項,那麼在用GDB調試被優化過的程序時,可能會發生某些變量不能訪問,或是取值錯誤碼的狀況。這個是很正常的,由於優化程序會刪改你的程序,整理你程序的語句順序,剔除一些無心義的變量等,因此在GDB調試這種程序時,運行時的指令和你所編寫指令就有不同,也就會出現你所想象不到的結果。對付這種狀況時,須要在編譯程序時關閉編譯優化。通常來講,幾乎全部的編譯器都支持編譯優化的開關,例如,GNU的C/C++編譯器GCC,你可使用「-gstabs」選項來解決這個問題。關於編譯器的參數,還請查看編譯器的使用說明文檔。
有時候,你須要查看一段連續的內存空間的值。好比數組的一段,或是動態分配的數據的大小。你可使用GDB的「@」操做符,「@」的左邊是第一個內存的地址的值 ,「@」的右邊則你你想查看內存的長度。例如,你的程序中有這樣的語句:
1 int *array = (int *) malloc (len * sizeof (int));
因而,在GDB調試過程當中,你能夠以以下命令顯示出這個動態數組的取值:
1 p *array@len
@的左邊是數組的首地址的值,也就是變量array所指向的內容,右邊則是數據的長度,其保存在變量len中,其輸出結果,大約是下面這個樣子的:
1 (gdb) p *array@len
1 $1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}
若是是靜態數組的話,能夠直接用print數組名,就能夠顯示數組中全部數據的內容了。
通常來講,GDB會根據變量的類型輸出變量的值。但你也能夠自定義GDB的輸出的格式。例如,你想輸出一個整數的十六進制,或是二進制來查看這個整型變量的中的位的狀況。要作到這樣,你可使用GDB的數據顯示格式:
x 按十六進制格式顯示變量。
d 按十進制格式顯示變量。
u 按十六進制格式顯示無符號整型。
o 按八進制格式顯示變量。
t 按二進制格式顯示變量。
a 按十六進制格式顯示變量。
c 按字符格式顯示變量。
f 按浮點數格式顯示變量。
1 (gdb) p i 2 $21 = 101 3 4 (gdb) p/a i 5 $22 = 0x65 6 7 (gdb) p/c i 8 $23 = 101 'e' 9 10 (gdb) p/f i 11 $24 = 1.41531145e-43 12 13 (gdb) p/x i 14 $25 = 0x65 15 16 (gdb) p/t i 17 $26 = 1100101
你可使用examine命令(簡寫是x)來查看內存地址中的值。x命令的語法以下所示:
1 x/<n/f/u> <addr>
n、f、u是可選的參數。 n 是一個正整數,表示顯示內存的長度,也就是說從當前地址向後顯示幾個地址的內容。
f 表示顯示的格式,參見上面。若是地址所指的是字符串,那麼格式能夠是s,若是地址是指令地址,那麼格式能夠是i。
u 表示從當前地址日後請求的字節數,若是不指定的話,GDB默認是4個bytes。u參數能夠用下面的字符來代替,b表示單字節,h表示雙字節,w表示四字節,g表示八字節。當咱們指定了字節長度後,GDB會從指內存定的內存地址開始,讀寫指定字節,並把其看成一個值取出來。
<addr>表示一個內存地址。 n/f/u三個參數能夠一塊兒使用。例如:
命令:x/3uh 0x54320 表示,從內存地址0x54320讀取內容,h表示以雙字節爲一個單位,3表示三個單位,u表示按十六進制顯示。
你能夠設置一些自動顯示的變量,當程序停住時,或是在你單步跟蹤時,這些變量會自動顯示。相關的GDB命令是display。
1 display <expr> 2 display/<fmt> <expr> 3 display/<fmt> <addr>
expr是一個表達式,fmt表示顯示的格式,addr表示內存地址,當你用display設定好了一個或多個表達式後,只要你的程序被停下來,GDB會自動顯示你所設置的這些表達式的值。
格式i和s一樣被display支持,一個很是有用的命令是:
1 display/i $pc
$pc是GDB的環境變量,表示着指令的地址,/i則表示輸出格式爲機器指令碼,也就是彙編。因而當程序停下後,就會出現源代碼和機器指令碼相對應的情形,這是一個頗有意思的功能。下面是一些和display相關的GDB命令:
1 undisplay <dnums...> 2 delete display <dnums...>
刪除自動顯示,dnums意爲所設置好了的自動顯式的編號。若是要同時刪除幾個,編號能夠用空格分隔,若是要刪除一個範圍內的編號,能夠用減號表示(如:2-5)
1 disable display <dnums...> 2 enable display <dnums...>
disable和enalbe不刪除自動顯示的設置,而只是讓其失效和恢復。
1 info display
查看display設置的自動顯示的信息。GDB會打出一張表格,向你報告固然調試中設置了多少個自動顯示設置,其中包括,設置的編號,表達式,是否enable。
GDB中關於顯示的選項比較多,這裏我只例舉大多數經常使用的選項。
1 set print address 2 set print address on
打開地址輸出,當程序顯示函數信息時,GDB會顯出函數的參數地址。系統默認爲打開的,如:
1 (gdb) f 2 #0 set_quotes (lq=0x34c78 "<<", rq=0x34c88 ">>") 3 at input.c:530 4 530 if (lquote != def_lquote)
1 set print address off
關閉函數的參數地址顯示,如:
1 (gdb) set print addr off 2 (gdb) f 3 #0 set_quotes (lq="<<", rq=">>") at input.c:530 4 530 if (lquote != def_lquote)
1 show print address
查看當前地址顯示選項是否打開。
1 set print array 2 set print array on
打開數組顯示,打開後當數組顯示時,每一個元素佔一行,若是不打開的話,每一個元素則以逗號分隔。這個選項默認是關閉的。與之相關的兩個命令以下,我就再也不多說了。
1 set print array off 2 show print array
1 set print elements <number-of-elements>
這個選項主要是設置數組的,若是你的數組太大了,那麼就能夠指定一個<number-of-elements>來指定數據顯示的最大長度,當到達這個長度時,GDB就再也不往下顯示了。若是設置爲0,則表示不限制。
1 show print elements
查看print elements的選項信息。
1 set print null-stop <on/off>
若是打開了這個選項,那麼當顯示字符串時,遇到結束符則中止顯示。這個選項默認爲off。
1 set print pretty on
若是打開printf pretty這個選項,那麼當GDB顯示結構體時會比較漂亮。如:
1 $1 = { 2 next = 0x0, 3 flags = { 4 sweet = 1, 5 sour = 1 6 }, 7 meat = 0x54 "Pork" 8 }
1 set print pretty off
關閉printf pretty這個選項,GDB顯示結構體時會以下顯示:
1 $1 = {next = 0x0, flags = {sweet = 1, sour = 1}, meat = 0x54 "Pork"}
1 show print pretty
查看GDB是如何顯示結構體的。
1 set print sevenbit-strings <on/off>
設置字符顯示,是否按「/nnn」的格式顯示,若是打開,則字符串或字符數據按/nnn顯示,如「/065」。
1 show print sevenbit-strings
查看字符顯示開關是否打開。
1 set print union <on/off>
設置顯示結構體時,是否顯式其內的聯合體數據。例若有如下數據結構:
1 typedef enum {Tree, Bug} Species; 2 typedef enum {Big_tree, Acorn, Seedling} Tree_forms; 3 typedef enum {Caterpillar, Cocoon, Butterfly} 4 Bug_forms; 5 6 struct thing { 7 Species it; 8 union { 9 Tree_forms tree; 10 Bug_forms bug; 11 } form; 12 }; 13 14 struct thing foo = {Tree, {Acorn}};
當打開這個開關時,執行 p foo 命令後,會以下顯示:
1 $1 = {it = Tree, form = {tree = Acorn, bug = Cocoon}}
當關閉這個開關時,執行 p foo 命令後,會以下顯示:
1 $1 = {it = Tree, form = {...}}
1 show print union
查看聯合體數據的顯示方式
1 set print object <on/off>
在C++中,若是一個對象指針指向其派生類,若是打開這個選項,GDB會自動按照虛方法調用的規則顯示輸出,若是關閉這個選項的話,GDB就無論虛函數表了。這個選項默認是off。
1 show print object
查看對象選項的設置。
1 set print static-members <on/off>
這個選項表示,當顯示一個C++對象中的內容是,是否顯示其中的靜態數據成員。默認是on。
1 show print static-members
查看靜態數據成員選項設置。
1 set print vtbl <on/off>
當此選項打開時,GDB將用比較規整的格式來顯示虛函數表時。其默認是關閉的。
1 show print vtbl
查看虛函數顯示格式的選項。
當你用GDB的print查看程序運行時的數據時,你每個print都會被GDB記錄下來。GDB會以$1, $2, $3 .....這樣的方式爲你每個print命令編上號。因而,你可使用這個編號訪問之前的表達式,如$1。這個功能所帶來的好處是,若是你先前輸入了一個比較長的表達式,若是你還想查看這個表達式的值,你可使用歷史記錄來訪問,省去了重複輸入。
你能夠在GDB的調試環境中定義本身的變量,用來保存一些調試程序中的運行數據。要定義一個GDB的變量很簡單隻需。使用GDB的set命令。GDB的環境變量和UNIX同樣,也是以$起頭。如:
1 set $foo = *object_ptr
使用環境變量時,GDB會在你第一次使用時建立這個變量,而在之後的使用中,則直接對其賦值。環境變量沒有類型,你能夠給環境變量定義任一的類型。包括結構體和數組。
1 show convenience
該命令查看當前所設置的全部的環境變量。
這是一個比較強大的功能,環境變量和程序變量的交互使用,將使得程序調試更爲靈活便捷。例如:
1 set $i = 0
2 print bar[$i++]->contents
因而,當你就沒必要,print bar[0]->contents, print bar[1]->contents地輸入命令了。輸入這樣的命令後,只用敲回車,重複執行上一條語句,環境變量會自動累加,從而完成逐個輸出的功能。
要查看寄存器的值,很簡單,可使用以下命令:
1 info registers
查看寄存器的狀況。(除了浮點寄存器)
1 info all-registers
查看全部寄存器的狀況。(包括浮點寄存器)
1 info registers <regname ...>
查看所指定的寄存器的狀況。
寄存器中放置了程序運行時的數據,好比程序當前運行的指令地址(ip),程序的當前堆棧地址(sp)等等。你一樣可使用print命令來訪問寄存器的狀況,只須要在寄存器名字前加一個$符號就能夠了。如:p $eip。
一旦使用GDB掛上被調試程序,當程序運行起來後,你能夠根據本身的調試思路來動態地在GDB中更改當前被調試程序的運行線路或是其變量的值,這個強大的功能可以讓你更好的調試你的程序,好比,你能夠在程序的一次運行中走遍程序的全部分支。
修改被調試程序運行時的變量值,在GDB中很容易實現,使用GDB的print命令便可完成。如:
1 (gdb) print x=4
x=4這個表達式是C/C++的語法,意爲把變量x的值修改成4,若是你當前調試的語言是Pascal,那麼你可使用Pascal的語法:x:=4。
在某些時候,頗有可能你的變量和GDB中的參數衝突,如:
1 (gdb) whatis width 2 type = double 3 (gdb) p width 4 $4 = 13 5 (gdb) set width=47 6 Invalid syntax in expression.
由於,set width是GDB的命令,因此,出現了「Invalid syntax in expression」的設置錯誤,此時,你可使用set var命令來告訴GDB,width不是你GDB的參數,而是程序的變量名,如:
1 (gdb) set var width=47
另外,還可能有些狀況,GDB並不報告這種錯誤,因此保險起見,在你改變程序變量取值時,最好都使用set var格式的GDB命令。
通常來講,被調試程序會按照程序代碼的運行順序依次執行。GDB提供了亂序執行的功能,也就是說,GDB能夠修改程序的執行順序,可讓程序執行隨意跳躍。這個功能能夠由GDB的jump命令來完:
1 jump <linespec>
指定下一條語句的運行點。<linespce>能夠是文件的行號,能夠是file:line格式,能夠是+num這種偏移量格式。表式着下一條運行語句從哪裏開始。
1 jump <address>
這裏的<address>是代碼行的內存地址。
注意,jump命令不會改變當前的程序棧中的內容,因此,當你從一個函數跳到另外一個函數時,當函數運行完返回時進行彈棧操做時必然會發生錯誤,可能結果仍是很是奇怪的,甚至於產生程序Core Dump。因此最好是同一個函數中進行跳轉。
熟悉彙編的人都知道,程序運行時,有一個寄存器用於保存當前代碼所在的內存地址。因此,jump命令也就是改變了這個寄存器中的值。因而,你可使用「set $pc」來更改跳轉執行的地址。如:
1 set $pc = 0x485
使用singal命令,能夠產生一個信號量給被調試的程序。如:中斷信號Ctrl+C。這很是方便於程序的調試,能夠在程序運行的任意位置設置斷點,並在該斷點用GDB產生一個信號量,這種精確地在某處產生信號很是有利程序的調試。
語法是:signal <singal>,UNIX的系統信號量一般從1到15。因此<singal>取值也在這個範圍。
single命令和shell的kill命令不一樣,系統的kill命令發信號給被調試程序時,是由GDB截獲的,而single命令所發出一信號則是直接發給被調試程序的。
若是你的調試斷點在某個函數中,並還有語句沒有執行完。你可使用return命令強制函數忽略尚未執行的語句並返回。
1 return 2 return <expression>
使用return命令取消當前函數的執行,並當即返回,若是指定了<expression>,那麼該表達式的值會被認做函數的返回值。
1 call <expr>
表達式中能夠一是函數,以此達到強制調用函數的目的。並顯示函數的返回值,若是函數返回值是void,那麼就不顯示。
另外一個類似的命令也能夠完成這一功能——print,print後面能夠跟表達式,因此也能夠用他來調用函數,print和call的不一樣是,若是函數返回void,call則不顯示,print則顯示函數返回值,並把該值存入歷史數據中。
GDB支持下列語言:C, C++, Fortran, PASCAL, Java, Chill, assembly, 和 Modula-2。通常說來,GDB會根據你所調試的程序來肯定固然的調試語言,好比:發現文件名後綴爲「.c」的,GDB會認爲是C程序。文件名後綴爲「.C, .cc, .cp, .cpp, .cxx, .c++」的,GDB會認爲是C++程序。然後綴是「.f, .F」的,GDB會認爲是Fortran程序,還有,後綴爲若是是「.s, .S」的會認爲是彙編語言。
也就是說,GDB會根據你所調試的程序的語言,來設置本身的語言環境,並讓GDB的命令跟着語言環境的改變而改變。好比一些GDB命令須要用到表達式或變量時,這些表達式或變量的語法,徹底是根據當前的語言環境而改變的。例如C/C++中對指針的語法是*p,而在Modula-2中則是p^。而且,若是你當前的程序是由幾種不一樣語言一同編譯成的,那到在調試過程當中,GDB也能根據不一樣的語言自動地切換語言環境。這種跟着語言環境而改變的功能,真是體貼開發人員的一種設計。
下面是幾個相關於GDB語言環境的命令:
1 show language
查看當前的語言環境。若是GDB不能識爲你所調試的編程語言,那麼,C語言被認爲是默認的環境。
1 info frame
查看當前函數的程序語言。
1 info source
查看當前文件的程序語言。
若是GDB沒有檢測出當前的程序語言,那麼你也能夠手動設置當前的程序語言。使用set language命令便可作到。
當set language命令後什麼也不跟的話,你能夠查看GDB所支持的語言種類:
1 (gdb) set language 2 The currently understood settings are: 3 4 local or auto Automatic setting based on source file 5 c Use the C language 6 c++ Use the C++ language 7 asm Use the Asm language 8 chill Use the Chill language 9 fortran Use the Fortran language 10 java Use the Java language 11 modula-2 Use the Modula-2 language 12 pascal Use the Pascal language 13 scheme Use the Scheme language
因而你能夠在set language後跟上被列出來的程序語言名,來設置當前的語言環境。