1. 一個簡單的GDB會話 2. 進入和退出GDB 3. GDB指令 4. 在 GDB 下運作程式 7. 檢視源碼檔案 9. C預處理宏 1. 一個簡單的GDB會話 你可以在空閑的時候閱讀全部的GDB手冊。然而一些指令足以使你開始使用GDB,這一章就描述這些指令。 GNU m4(一個通用的宏處理器)的初始版本之一存在下面的bug:當我們改變單引号的預設表示時,一個用來捕獲一個宏定義的指令停止工作。在下面較短的m4會 話中,我們定義一個宏foo擴充成0000;然後我們使用m4内置的defn指令定義bar做同樣的事情。然而當我們改變左單引号為,右單引号為後,同樣的程式在定義一個新的同義詞baz時卻失敗了。 $ cd gnu/m4 $ ./m4 define(foo,0000) foo 0000 define(bar,defn(`foo')) bar 0000 changequote(,) define(baz,defn(foo)) baz C-d m4: End of input: 0: fatal error: EOF in string 讓我們使用GDB來看看發生了什麼。 $ gdb m4 GDB is free software and you are welcome to 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. GDB 6.5.50.20060706, Copyright 1999 Free Software Foundation, Inc... (gdb) GDB隻讀取很少的符号資料,這些資料足以讓它知道在哪裡能找到剩下的它需要的符号資料,是以,第一個指令提示符顯示的很快。現在我們告訴GDB使用比平時更短的顯示寬度,這樣這些例子可以更好的顯示在這個手冊中。 (gdb) set width 70 我們需要看一看m4内置的changquote是怎麼工作的。看了源碼之後,我們知道相關的子例程是m4_changequote,因為我們使用GDB的break指令在那裡設定一個斷點。 (gdb) break m4_changequote Breakpoint 1 at 0x62f4: file builtin.c, line 879. 通過使用run指令,在GDB的控制下我們啟動m4的運作。隻要控制沒有達到m4_changequote子例程,程式運作的和平時一樣。 (gdb) run Starting program: /work/Editorial/gdb/gnu/m4/m4 define(foo,0000) foo 0000 為了觸發斷點,我們調用changequote。GDB懸挂m4的執行,顯示它停止地方的上下文資訊。 changequote(,) Breakpoint 1, m4_changequote (argc=3, argv=0x33c70) at builtin.c:879 879 if (bad_argc(TOKEN_DATA_TEXT(argv[0]),argc,1,3)) 現在我們使用指令n(next)執行目前函數的下一行。 (gdb) n 882 set_quotes((argc >= 2) ? TOKEN_DATA_TEXT(argv[1])\ : nil, set_quotes看起來像是有問題的子例程,通過使用指令s(step)代替next我們能進入它的内部執行。 step會進入将被執行的子例程的下一行,是以它進入了set_quotes。 (gdb) s set_quotes (lq=0x34c78 "", rq=0x34c88 "") at input.c:530 530 if (lquote != def_lquote) 上面展示了m4(以及它的參數)被懸挂在這個棧裡,這個顯示被稱為棧桢顯示。它顯示了棧的概要。我們可以使用backtrace指令(它也可以寫成bt)來看一看在所有的棧中我們處在哪一個棧裡:backtrace指令顯示每一個活動子例程的棧桢。 (gdb) bt #0 set_quotes (lq=0x34c78 "", rq=0x34c88 "") at input.c:530 #1 0x6344 in m4_changequote (argc=3, argv=0x33c70) at builtin.c:882 #2 0x8174 in expand_macro (sym=0x33320) at macro.c:242 #3 0x7a88 in expand_token (obs=0x0, t=209696, td=0xf7fffa30) at macro.c:71 #4 0x79dc in expand_input () at macro.c:40 #5 0x2930 in main (argc=0, argv=0xf7fffb20) at m4.c:195 我們再前進幾行看看會發生什麼。開始的兩次,我們使用s;後面的兩次我們使用n來避免進入 xstrdup子例程。 (gdb) s 0x3b5c 532 if (rquote != def_rquote) (gdb) s 0x3b80 535 lquote = (lq == nil || *lq == '\0') ? \ def_lquote : xstrdup(lq); (gdb) n 536 rquote = (rq == nil || *rq == '\0') ? def_rquote\ : xstrdup(rq); (gdb) n 538 len_lquote = strlen(rquote); 最後一行顯示看起來有點奇怪。我們可以檢查lquote和rquote變量,看看它們實際上是不是我們指定的新的左單引号和右單引号。我們使用指令p(print)來看它們的值。 (gdb) p lquote $1 = 0x35d40 "" (gdb) p rquote $2 = 0x35d50 "" lquote和rquote的确是新的左右單引号。為了看一些上下文,我們可以使用l(list)指令來顯示圍繞目前行的前後10行源代碼。 (gdb) l 533 xfree(rquote); 534 535 lquote = (lq == nil || *lq == '\0') ? def_lquote\ : xstrdup (lq); 536 rquote = (rq == nil || *rq == '\0') ? def_rquote\ : xstrdup (rq); 537 538 len_lquote = strlen(rquote); 539 len_rquote = strlen(lquote); 540 } 541 542 void 讓我們再向前執行兩行設定len_lquote和len_rquote值的源代碼,然後檢查這些變量的值。 (gdb) n 539 len_rquote = strlen(lquote); (gdb) n 540 } (gdb) p len_lquote $3 = 9 (gdb) p len_rquote $4 = 7 假如len_lquote和len_rquote分别意味着lquote和rquote的長度,這看起來一定有問題。因為p指令能列印任何表達式的值--那些表達式能包括子例程和指派。通過使用它,我們能把它們設定成别的更好的值。 (gdb) p len_lquote=strlen(lquote) $5 = 7 (gdb) p len_rquote=strlen(rquote) $6 = 9 對于修正使用m4内置的defn來設定新的引号引起的問題,這已經足夠了嗎?我們使用c(continue)指令可以讓m4繼續,然後試一試最初因為麻煩的那個例子。 (gdb) c Continuing. define(baz,defn(foo)) baz 0000 成功了!新的引号現在和預設的引号一樣工作正常。問題似乎是那兩個定義了錯誤的長度的行。我們給m4輸入一個EOF讓它退出。 C-d Program exited normally. 這個消息`Program exited normally.'來自于GDB,它表明m4已經完成了執行。我們可以使用 GDB的quit指令來結束我們的GDB會話。 (gdb) quit GDB簡介: ************** 調試器(比如象GDB)能讓你觀察另一個程式在執行時的内部活動,或程式出錯時 發生了什麼。 GDB主要能為你做四件事(包括為了完成這些事而附加的功能),幫助你找出程式 中的錯誤。 * 運作你的程式,設定所有的能影響程式運作的東西。 * 保證你的程式在指定的條件下停止。 * 當你程式停止時,讓你檢查發生了什麼。 * 改變你的程式。那樣你可以試着修正某個bug引起的問題,然後繼續查找另一 個bug. 你可以用GDB來調試C和C++寫的程式。(參考 *C 和C++) 調試Pascal程式時,有一些功能還不能使用。 GDB還可以用來調試FORTRAN程式,盡管現在還不支援表達式的輸入,輸出變量, 或類FORTRAN的詞法。 * GDB是"free software",大家都可以免費拷貝。也可以為GDB增加新的功能,不 過可要遵守GNU的許可協定幺。反正我認為GNU還是比較不錯的:-) 就這句話: Fundamentally, the General Public License is a license which says that you have these freedoms and that you cannot take these freedoms away from anyone else. GDB的作者: Richard Stallman是GDB的始作俑者,另外還有許多别的GNU的成員。許多人 為此作出了貢獻。(都是老外不提也罷,但願他們不要來找我麻煩:-)) 這裡是GDB的一個例子: 原文中是使用一個叫m4的程式。但很遺憾我找不到這個程式的原代碼, 是以沒有辦法來按照原文來說明。不過反正是個例子,我就拿一個作業系統的 程序排程原碼來說明把,原代碼我會附在後面。 首先這個程式叫os.c是一個模拟程序排程的原程式(也許是個老古董了:-))。 先說明一下如何取得包括原代碼符号的可執行代碼。大家有心的話可以去看一下gcc的 man檔案(在shell下打man gcc)。gcc -g -o -g 的意思是生成帶原代碼調試符号的可執行檔案。 -o 的意思是指定可執行檔案名。 (gcc 的指令行參數有一大堆,有興趣可以自己去看看。) 反正在linux下把os.c用以上方法編譯連接配接以後就産生了可供gdb使用的可執行檔案。 我用gcc -g os.c -o os,産生的可執行文檔叫os. 然後打gdb os,就可進入gdb,螢幕提示: GDB is free software and you are welcome to 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. GDB 4.16, Copyright 1995 Free Software Foundation, Inc... (gdb) (gdb)是提示符,在這提示符下可以輸入指令,直到退出。(退出指令是q/Q) 為了盡量和原文檔說明的指令相符,即使在本例子中沒用的指令我也将示範。 首先我們可以設定gdb的螢幕大小。鍵入: (gdb)set width 70 就是把标準螢幕設為70列。 然後讓我們來設定斷點。設定方法很簡單:break或簡單打b後面加行号或函數名 比如我們可以在main 函數上設斷點: (gdb)break main 或(gdb)b main 系統提示:Breakpoint 1 at 0x8049552: file os.c, line 455. 然後我們可以運作這個程式,當程式運作到main函數時程式就會停止傳回到gdb的 提示符下。運作的指令是run或r(gdb中有不少alias,可以看一下help,在gdb下打help) run 後面可以跟參數,就是為程式指定指令行參數。 比如r abcd,則程式就會abcd以作為參數。(這裡要說明的是可以用set args來指定參 數)。打入r或run後,程式就開始運作直到進入main的入口停止,顯示: Starting program: /os Breakpoint 1, main () at os.c:455 455 Initial(); 這裡455 Initial();是将要執行的指令或函數。 gdb提供兩種方式:1.單步進入,step into就是跟蹤到函數内啦。指令是step或s 2.單步,next,就是簡單的單步,不會進入函數。指令是next或n 這兩個指令還有别的用法以後再說。 我們用n指令,鍵入: (gdb)n Success forking process# 1 ,pid is 31474 Success forking process# 2 ,pid is 31475 Success forking process# 3 ,pid is 31476 Success forking process# 5 ,pid is 31478 Success forking process# 6 ,pid is 31479 Dispatching Algorithm : FIFO ******************************************************************************** PCB# PID Priority PC State 1 31474 24 0 WAITING 2 31475 19 0 WAITING 3 31476 16 0 WAITING 4 31477 23 0 WAITING 5 31478 22 0 WAITING 6 31479 20 0 WAITING ****************************************************************************** CPU : NO process running IO : No process Waiting CPU!!! 31474 31475 31476 31477 31478 31479 Waiting IO NONE 456 State=WAITING; 最後的一行就是下一句要執行的指令。我們現在在另一個函數上加斷點。注意我們 可以用l/list指令來顯示原代碼。這裡我們鍵入 (gdb)l 451 main() 452 { 453 int message; 454 455 Initial(); 456 State=WAITING; 457 printf("Use Control-C to halt \n"); 458 signal(SIGALRM,AlarmMessage); 459 signal(SIGINT,InteruptMessage); 460 signal(SIGUSR2,IoMessage); (gdb) l 461 alarm(TimeSlot); 462 for(;;) 463 { 464 message=GetMessage(); 465 switch(message) 466 { 468 break; 469 case CHILD_IO: WaitingIo(); 470 break; 顯示了原代碼,現在在AlarmMessage上加斷點。 (gdb) b AlarmMessage Breakpoint 2 at 0x8048ee3: file os.c, line 259. (gdb) 然後我們繼續運作程式。 (gdb)c c或continue指令讓我們繼續被中斷的程式。 顯示: Continuing. Use Control-C to halt Breakpoint 2, AlarmMessage () at os.c:259 259 ClearSignal(); 注意我們下一句語句就是ClearSignal(); 我們用s/step跟蹤進入這個函數看看它是幹什麼的。 (gdb) s ClearSignal () at os.c:227 227 signal(SIGINT,SIG_IGN); 用l指令列出原代碼: (gdb) l 222 } 223 224 225 void ClearSignal() 226 { 227 signal(SIGINT,SIG_IGN); 228 signal(SIGALRM,SIG_IGN); 229 signal(SIGUSR2,SIG_IGN); 230 } 231 (gdb) 我們可以用s指令繼續跟蹤。現在讓我們來試試bt或backtrace指令。這個指令可以 顯示棧中的内容。 (gdb) bt #0 ClearSignal () at os.c:227 #1 0x8048ee8 in AlarmMessage () at os.c:259 #2 0xbffffaec in ?? () #3 0x80486ae in ___crt_dummy__ () (gdb) 大家一定能看懂顯示的意思。棧頂是AlarmMessage,接下來的函數沒有名字--就是 沒有原代碼符号。這顯示了函數調用的嵌套。 好了,我們跟蹤了半天還沒有檢查過變量的值呢。檢查表達式的值的指令是p或print 格式是p 444444讓我們來找一個變量來看看。:-) (gdb)l 1 還記得l的作用嗎?l或list顯示原代碼符号,l或list加就顯示從開始的 原代碼。好了找到一個讓我們來看看WaitingQueue的内容 (gdb) p WaitingQueue $1 = {1, 2, 3, 4, 5, 6, 0} (gdb) WaitingQueue是一個數組,gdb還支援結構的顯示, (gdb) p Pcb $2 = {{Pid = 0, State = 0, Prior = 0, pc = 0}, {Pid = 31474, State = 2, Prior = 24, pc = 0}, {Pid = 31475, State = 2, Prior = 19, pc = 0}, { Pid = 31476, State = 2, Prior = 16, pc = 0}, {Pid = 31477, State = 2, Prior = 23, pc = 0}, {Pid = 31478, State = 2, Prior = 22, pc = 0}, { Pid = 31479, State = 2, Prior = 20, pc = 0}} (gdb) 這裡可以對照原程式看看。 原文檔裡是一個調試過程,不過我想這裡我已經把gdb的常用功能介紹了一遍,基本上 可以用來調試程式了。:-) 運作GDB(一些詳細的說明): 前面已經提到過如何運作GDB了,現在讓我們來看一些更有趣的東西。你可以在運作 GDB時通過許多指令行參數指定大量的參數和選項,通過這個你可以在一開始就設定好 程式運作的環境。 這裡将要描述的指令行參數覆寫了大多數的情況,事實上在一定環境下有的并沒有 什麼大用處。最通常的指令就是使用一個參數: $gdb 你還可以同時為你的執行檔案指定一個core檔案: $gdb core 你也可以為你要執行的檔案指定一個程序号: $gdb 如:&gdb os 1234将使gdb與程序1234相聯系(attach) 除非你還有一個檔案叫1234的。gdb首先檢查一個core檔案。 如果你是使用一個遠端終端進行遠端調試的話,那如果你的終端不支援的話,你将無法 使用第二個參數甚至沒有core dump。如果你覺得開頭的提示資訊比較礙眼的話,你可以 用gdb -silent。你還可以用指令行參數更加詳細的控制GDB的行為。 打入gdb -help或-h 可以得到這方面的提示。所有的參數都被按照排列的順序傳給gdb 除非你用了-x參數。 當gdb開始運作時,它把任何一個不帶選項字首的參數都當作為一個可執行檔案或core 檔案(或程序号)。就象在前面加了-se或-c選項。gdb把第一個前面沒有選項說明的參數 看作前面加了-se 選項,而第二個(如果有的話)看作是跟着-c選項後面的。 許多選項有縮寫,用gdb -h可以看到。在gdb中你也可以任意的把選項名掐頭去尾,隻 要保證gdb能判斷唯一的一個參數就行。 在這裡我們說明一些最常用的參數選項 -symbols (-s )------從中讀去符号。 -exec (-e )----在合适的時候執行來做用正确的資料與core dump的作比較。 -se ------從中讀取符号并把它作為可執行檔案。 -core (-c )--指定為一個core dump 檔案。 -c ----連接配接到程序号為,與attach指令相似。 -command -x -----執行gdb指令,在指定的檔案中存放着一序列的gdb指令,就 象一個批處理。 -directory(-d) ---指定路徑。把加入到搜尋原檔案的路徑中。 -m -mapped---- 注意這個指令不是在所有的系統上都能用。如果你可以通過mmap系統調用來獲得記憶體 映象檔案,你可以用這個指令來使gdb把你目前檔案裡的符号寫入一個檔案中,這個檔案 将存放在你的目前路徑中。如果你調試的程式叫/temp/fred那麼map檔案就叫 ./fred.syms這樣當你以後再調試這個程式時,gdb會認識到這個檔案的存在,進而從這 個檔案中讀取符号,而不是從可執行檔案中讀取。.syms與主機有關不能共享。 -r -readnow---馬上從符号檔案中讀取整個符号表,而不是使用預設的。預設的符号表是 調入一部分符号,當需要時再讀入一部分。這會使開始進入gdb慢一些,但可以加快以後 的調試速度。 -m和-r一般在一起使用來建立.syms檔案 接下來再談談模式的設定(請聽下回分解 :-)) 附:在gdb文檔裡使用的調試例子我找到了在minix下有這個程式,叫m4有興趣的 可以自己去看看 模式的選擇 -------------- 現在我們來聊聊gdb運作模式的選擇。我們可以用許多模式來運作gdb,例如在“批模式” 或“安靜模式”。這些模式都是在gdb運作時在指令行作為選項指定的。 `-nx' `-n' 不執行任何初始化檔案中的指令。(一般初始化檔案叫做`.gdbinit').一般情況下在 `-quiet' `-q' “安靜模式”。不輸出介紹和版權資訊。這些資訊在“批模式”中也被跳過。 `-batch' “批模式”。在“批模式”下運作。當在指令檔案中的所有指令都被成功的執行後 gdb傳回狀态“0”,如果在執行過程中出錯,gdb傳回一個非零值。 “批模式”在把gdb作為一個過濾器運作時很有用。比如在一台遠端計算機上下載下傳且 執行一個程式。資訊“ Program exited normally”(一般是當運作的程式正常結束 時出現)不會在這種模式中出現。 `-cd DIRECTORY' 把DIRECTORY作為gdb的工作目錄,而非目前目錄(一般gdb預設把目前目錄作為工作目 錄)。 `-fullname' `-f' GNU Emacs 設定這個選項,當我們在Emacs下,把gdb作為它的一個子程序來運作時, Emacs告訴gdb按标準輸出完整的檔案名和行号,一個可視的棧内容。這個格式跟在 檔案名的後面。行号和字元重新按列排,Emacs-to-GDB界面使用\032字元作為一個 顯示一頁原檔案的信号。 `-b BPS' 為遠端調試設定波特率。 `-tty DEVICE' 使用DEVICE來作為你程式的标準輸入輸出 1. 一個簡單的GDB會話 2. 進入和退出GDB 3. GDB指令 4. 在 GDB 下運作程式 7. 檢視源碼檔案 9. C預處理宏 1. 一個簡單的GDB會話 你可以在空閑的時候閱讀全部的GDB手冊。然而一些指令足以使你開始使用GDB,這一章就描述這些指令。 GNU m4(一個通用的宏處理器)的初始版本之一存在下面的bug:當我們改變單引号的預設表示時,一個用來捕獲一個宏定義的指令停止工作。在下面較短的m4會 話中,我們定義一個宏foo擴充成0000;然後我們使用m4内置的defn指令定義bar做同樣的事情。然而當我們改變左單引号為,右單引号為後,同樣的程式在定義一個新的同義詞baz時卻失敗了。 $ cd gnu/m4 $ ./m4 define(foo,0000) foo 0000 define(bar,defn(`foo')) bar 0000 changequote(,) define(baz,defn(foo)) baz C-d m4: End of input: 0: fatal error: EOF in string 讓我們使用GDB來看看發生了什麼。 $ gdb m4 GDB is free software and you are welcome to 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. GDB 6.5.50.20060706, Copyright 1999 Free Software Foundation, Inc... (gdb) GDB隻讀取很少的符号資料,這些資料足以讓它知道在哪裡能找到剩下的它需要的符号資料,是以,第一個指令提示符顯示的很快。現在我們告訴GDB使用比平時更短的顯示寬度,這樣這些例子可以更好的顯示在這個手冊中。 (gdb) set width 70 我們需要看一看m4内置的changquote是怎麼工作的。看了源碼之後,我們知道相關的子例程是m4_changequote,因為我們使用GDB的break指令在那裡設定一個斷點。 (gdb) break m4_changequote Breakpoint 1 at 0x62f4: file builtin.c, line 879. 通過使用run指令,在GDB的控制下我們啟動m4的運作。隻要控制沒有達到m4_changequote子例程,程式運作的和平時一樣。 (gdb) run Starting program: /work/Editorial/gdb/gnu/m4/m4 define(foo,0000) foo 0000 為了觸發斷點,我們調用changequote。GDB懸挂m4的執行,顯示它停止地方的上下文資訊。 changequote(,) Breakpoint 1, m4_changequote (argc=3, argv=0x33c70) at builtin.c:879 879 if (bad_argc(TOKEN_DATA_TEXT(argv[0]),argc,1,3)) 現在我們使用指令n(next)執行目前函數的下一行。 (gdb) n 882 set_quotes((argc >= 2) ? TOKEN_DATA_TEXT(argv[1])\ : nil, set_quotes看起來像是有問題的子例程,通過使用指令s(step)代替next我們能進入它的内部執行。 step會進入将被執行的子例程的下一行,是以它進入了set_quotes。 (gdb) s set_quotes (lq=0x34c78 "", rq=0x34c88 "") at input.c:530 530 if (lquote != def_lquote) 上面展示了m4(以及它的參數)被懸挂在這個棧裡,這個顯示被稱為棧桢顯示。它顯示了棧的概要。我們可以使用backtrace指令(它也可以寫成bt)來看一看在所有的棧中我們處在哪一個棧裡:backtrace指令顯示每一個活動子例程的棧桢。 (gdb) bt #0 set_quotes (lq=0x34c78 "", rq=0x34c88 "") at input.c:530 #1 0x6344 in m4_changequote (argc=3, argv=0x33c70) at builtin.c:882 #2 0x8174 in expand_macro (sym=0x33320) at macro.c:242 #3 0x7a88 in expand_token (obs=0x0, t=209696, td=0xf7fffa30) at macro.c:71 #4 0x79dc in expand_input () at macro.c:40 #5 0x2930 in main (argc=0, argv=0xf7fffb20) at m4.c:195 我們再前進幾行看看會發生什麼。開始的兩次,我們使用s;後面的兩次我們使用n來避免進入 xstrdup子例程。 (gdb) s 0x3b5c 532 if (rquote != def_rquote) (gdb) s 0x3b80 535 lquote = (lq == nil || *lq == '\0') ? \ def_lquote : xstrdup(lq); (gdb) n 536 rquote = (rq == nil || *rq == '\0') ? def_rquote\ : xstrdup(rq); (gdb) n 538 len_lquote = strlen(rquote); 最後一行顯示看起來有點奇怪。我們可以檢查lquote和rquote變量,看看它們實際上是不是我們指定的新的左單引号和右單引号。我們使用指令p(print)來看它們的值。 (gdb) p lquote $1 = 0x35d40 "" (gdb) p rquote $2 = 0x35d50 "" lquote和rquote的确是新的左右單引号。為了看一些上下文,我們可以使用l(list)指令來顯示圍繞目前行的前後10行源代碼。 (gdb) l 533 xfree(rquote); 534 535 lquote = (lq == nil || *lq == '\0') ? def_lquote\ : xstrdup (lq); 536 rquote = (rq == nil || *rq == '\0') ? def_rquote\ : xstrdup (rq); 537 538 len_lquote = strlen(rquote); 539 len_rquote = strlen(lquote); 540 } 541 542 void 讓我們再向前執行兩行設定len_lquote和len_rquote值的源代碼,然後檢查這些變量的值。 (gdb) n 539 len_rquote = strlen(lquote); (gdb) n 540 } (gdb) p len_lquote $3 = 9 (gdb) p len_rquote $4 = 7 假如len_lquote和len_rquote分别意味着lquote和rquote的長度,這看起來一定有問題。因為p指令能列印任何表達式的值--那些表達式能包括子例程和指派。通過使用它,我們能把它們設定成别的更好的值。 (gdb) p len_lquote=strlen(lquote) $5 = 7 (gdb) p len_rquote=strlen(rquote) $6 = 9 對于修正使用m4内置的defn來設定新的引号引起的問題,這已經足夠了嗎?我們使用c(continue)指令可以讓m4繼續,然後試一試最初因為麻煩的那個例子。 (gdb) c Continuing. define(baz,defn(foo)) baz 0000 成功了!新的引号現在和預設的引号一樣工作正常。問題似乎是那兩個定義了錯誤的長度的行。我們給m4輸入一個EOF讓它退出。 C-d Program exited normally. 這個消息`Program exited normally.'來自于GDB,它表明m4已經完成了執行。我們可以使用 GDB的quit指令來結束我們的GDB會話。 (gdb) quit GDB簡介: ************** 調試器(比如象GDB)能讓你觀察另一個程式在執行時的内部活動,或程式出錯時 發生了什麼。 GDB主要能為你做四件事(包括為了完成這些事而附加的功能),幫助你找出程式 中的錯誤。 * 運作你的程式,設定所有的能影響程式運作的東西。 * 保證你的程式在指定的條件下停止。 * 當你程式停止時,讓你檢查發生了什麼。 * 改變你的程式。那樣你可以試着修正某個bug引起的問題,然後繼續查找另一 個bug. 你可以用GDB來調試C和C++寫的程式。(參考 *C 和C++) 調試Pascal程式時,有一些功能還不能使用。 GDB還可以用來調試FORTRAN程式,盡管現在還不支援表達式的輸入,輸出變量, 或類FORTRAN的詞法。 * GDB是"free software",大家都可以免費拷貝。也可以為GDB增加新的功能,不 過可要遵守GNU的許可協定幺。反正我認為GNU還是比較不錯的:-) 就這句話: Fundamentally, the General Public License is a license which says that you have these freedoms and that you cannot take these freedoms away from anyone else. GDB的作者: Richard Stallman是GDB的始作俑者,另外還有許多别的GNU的成員。許多人 為此作出了貢獻。(都是老外不提也罷,但願他們不要來找我麻煩:-)) 這裡是GDB的一個例子: 原文中是使用一個叫m4的程式。但很遺憾我找不到這個程式的原代碼, 是以沒有辦法來按照原文來說明。不過反正是個例子,我就拿一個作業系統的 程序排程原碼來說明把,原代碼我會附在後面。 首先這個程式叫os.c是一個模拟程序排程的原程式(也許是個老古董了:-))。 先說明一下如何取得包括原代碼符号的可執行代碼。大家有心的話可以去看一下gcc的 man檔案(在shell下打man gcc)。gcc -g -o -g 的意思是生成帶原代碼調試符号的可執行檔案。 -o 的意思是指定可執行檔案名。 (gcc 的指令行參數有一大堆,有興趣可以自己去看看。) 反正在linux下把os.c用以上方法編譯連接配接以後就産生了可供gdb使用的可執行檔案。 我用gcc -g os.c -o os,産生的可執行文檔叫os. 然後打gdb os,就可進入gdb,螢幕提示: GDB is free software and you are welcome to 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. GDB 4.16, Copyright 1995 Free Software Foundation, Inc... (gdb) (gdb)是提示符,在這提示符下可以輸入指令,直到退出。(退出指令是q/Q) 為了盡量和原文檔說明的指令相符,即使在本例子中沒用的指令我也将示範。 首先我們可以設定gdb的螢幕大小。鍵入: (gdb)set width 70 就是把标準螢幕設為70列。 然後讓我們來設定斷點。設定方法很簡單:break或簡單打b後面加行号或函數名 比如我們可以在main 函數上設斷點: (gdb)break main 或(gdb)b main 系統提示:Breakpoint 1 at 0x8049552: file os.c, line 455. 然後我們可以運作這個程式,當程式運作到main函數時程式就會停止傳回到gdb的 提示符下。運作的指令是run或r(gdb中有不少alias,可以看一下help,在gdb下打help) run 後面可以跟參數,就是為程式指定指令行參數。 比如r abcd,則程式就會abcd以作為參數。(這裡要說明的是可以用set args來指定參 數)。打入r或run後,程式就開始運作直到進入main的入口停止,顯示: Starting program: /os Breakpoint 1, main () at os.c:455 455 Initial(); 這裡455 Initial();是将要執行的指令或函數。 gdb提供兩種方式:1.單步進入,step into就是跟蹤到函數内啦。指令是step或s 2.單步,next,就是簡單的單步,不會進入函數。指令是next或n 這兩個指令還有别的用法以後再說。 我們用n指令,鍵入: (gdb)n Success forking process# 1 ,pid is 31474 Success forking process# 2 ,pid is 31475 Success forking process# 3 ,pid is 31476 Success forking process# 5 ,pid is 31478 Success forking process# 6 ,pid is 31479 Dispatching Algorithm : FIFO ******************************************************************************** PCB# PID Priority PC State 1 31474 24 0 WAITING 2 31475 19 0 WAITING 3 31476 16 0 WAITING 4 31477 23 0 WAITING 5 31478 22 0 WAITING 6 31479 20 0 WAITING ****************************************************************************** CPU : NO process running IO : No process Waiting CPU!!! 31474 31475 31476 31477 31478 31479 Waiting IO NONE 456 State=WAITING; 最後的一行就是下一句要執行的指令。我們現在在另一個函數上加斷點。注意我們 可以用l/list指令來顯示原代碼。這裡我們鍵入 (gdb)l 451 main() 452 { 453 int message; 454 455 Initial(); 456 State=WAITING; 457 printf("Use Control-C to halt \n"); 458 signal(SIGALRM,AlarmMessage); 459 signal(SIGINT,InteruptMessage); 460 signal(SIGUSR2,IoMessage); (gdb) l 461 alarm(TimeSlot); 462 for(;;) 463 { 464 message=GetMessage(); 465 switch(message) 466 { 468 break; 469 case CHILD_IO: WaitingIo(); 470 break; 顯示了原代碼,現在在AlarmMessage上加斷點。 (gdb) b AlarmMessage Breakpoint 2 at 0x8048ee3: file os.c, line 259. (gdb) 然後我們繼續運作程式。 (gdb)c c或continue指令讓我們繼續被中斷的程式。 顯示: Continuing. Use Control-C to halt Breakpoint 2, AlarmMessage () at os.c:259 259 ClearSignal(); 注意我們下一句語句就是ClearSignal(); 我們用s/step跟蹤進入這個函數看看它是幹什麼的。 (gdb) s ClearSignal () at os.c:227 227 signal(SIGINT,SIG_IGN); 用l指令列出原代碼: (gdb) l 222 } 223 224 225 void ClearSignal() 226 { 227 signal(SIGINT,SIG_IGN); 228 signal(SIGALRM,SIG_IGN); 229 signal(SIGUSR2,SIG_IGN); 230 } 231 (gdb) 我們可以用s指令繼續跟蹤。現在讓我們來試試bt或backtrace指令。這個指令可以 顯示棧中的内容。 (gdb) bt #0 ClearSignal () at os.c:227 #1 0x8048ee8 in AlarmMessage () at os.c:259 #2 0xbffffaec in ?? () #3 0x80486ae in ___crt_dummy__ () (gdb) 大家一定能看懂顯示的意思。棧頂是AlarmMessage,接下來的函數沒有名字--就是 沒有原代碼符号。這顯示了函數調用的嵌套。 好了,我們跟蹤了半天還沒有檢查過變量的值呢。檢查表達式的值的指令是p或print 格式是p 444444讓我們來找一個變量來看看。:-) (gdb)l 1 還記得l的作用嗎?l或list顯示原代碼符号,l或list加就顯示從開始的 原代碼。好了找到一個讓我們來看看WaitingQueue的内容 (gdb) p WaitingQueue $1 = {1, 2, 3, 4, 5, 6, 0} (gdb) WaitingQueue是一個數組,gdb還支援結構的顯示, (gdb) p Pcb $2 = {{Pid = 0, State = 0, Prior = 0, pc = 0}, {Pid = 31474, State = 2, Prior = 24, pc = 0}, {Pid = 31475, State = 2, Prior = 19, pc = 0}, { Pid = 31476, State = 2, Prior = 16, pc = 0}, {Pid = 31477, State = 2, Prior = 23, pc = 0}, {Pid = 31478, State = 2, Prior = 22, pc = 0}, { Pid = 31479, State = 2, Prior = 20, pc = 0}} (gdb) 這裡可以對照原程式看看。 原文檔裡是一個調試過程,不過我想這裡我已經把gdb的常用功能介紹了一遍,基本上 可以用來調試程式了。:-) 運作GDB(一些詳細的說明): 前面已經提到過如何運作GDB了,現在讓我們來看一些更有趣的東西。你可以在運作 GDB時通過許多指令行參數指定大量的參數和選項,通過這個你可以在一開始就設定好 程式運作的環境。 這裡将要描述的指令行參數覆寫了大多數的情況,事實上在一定環境下有的并沒有 什麼大用處。最通常的指令就是使用一個參數: $gdb 你還可以同時為你的執行檔案指定一個core檔案: $gdb core 你也可以為你要執行的檔案指定一個程序号: $gdb 如:&gdb os 1234将使gdb與程序1234相聯系(attach) 除非你還有一個檔案叫1234的。gdb首先檢查一個core檔案。 如果你是使用一個遠端終端進行遠端調試的話,那如果你的終端不支援的話,你将無法 使用第二個參數甚至沒有core dump。如果你覺得開頭的提示資訊比較礙眼的話,你可以 用gdb -silent。你還可以用指令行參數更加詳細的控制GDB的行為。 打入gdb -help或-h 可以得到這方面的提示。所有的參數都被按照排列的順序傳給gdb 除非你用了-x參數。 當gdb開始運作時,它把任何一個不帶選項字首的參數都當作為一個可執行檔案或core 檔案(或程序号)。就象在前面加了-se或-c選項。gdb把第一個前面沒有選項說明的參數 看作前面加了-se 選項,而第二個(如果有的話)看作是跟着-c選項後面的。 許多選項有縮寫,用gdb -h可以看到。在gdb中你也可以任意的把選項名掐頭去尾,隻 要保證gdb能判斷唯一的一個參數就行。 在這裡我們說明一些最常用的參數選項 -symbols (-s )------從中讀去符号。 -exec (-e )----在合适的時候執行來做用正确的資料與core dump的作比較。 -se ------從中讀取符号并把它作為可執行檔案。 -core (-c )--指定為一個core dump 檔案。 -c ----連接配接到程序号為,與attach指令相似。 -command -x -----執行gdb指令,在指定的檔案中存放着一序列的gdb指令,就 象一個批處理。 -directory(-d) ---指定路徑。把加入到搜尋原檔案的路徑中。 -m -mapped---- 注意這個指令不是在所有的系統上都能用。如果你可以通過mmap系統調用來獲得記憶體 映象檔案,你可以用這個指令來使gdb把你目前檔案裡的符号寫入一個檔案中,這個檔案 将存放在你的目前路徑中。如果你調試的程式叫/temp/fred那麼map檔案就叫 ./fred.syms這樣當你以後再調試這個程式時,gdb會認識到這個檔案的存在,進而從這 個檔案中讀取符号,而不是從可執行檔案中讀取。.syms與主機有關不能共享。 -r -readnow---馬上從符号檔案中讀取整個符号表,而不是使用預設的。預設的符号表是 調入一部分符号,當需要時再讀入一部分。這會使開始進入gdb慢一些,但可以加快以後 的調試速度。 -m和-r一般在一起使用來建立.syms檔案 接下來再談談模式的設定(請聽下回分解 :-)) 附:在gdb文檔裡使用的調試例子我找到了在minix下有這個程式,叫m4有興趣的 可以自己去看看 模式的選擇 -------------- 現在我們來聊聊gdb運作模式的選擇。我們可以用許多模式來運作gdb,例如在“批模式” 或“安靜模式”。這些模式都是在gdb運作時在指令行作為選項指定的。 `-nx' `-n' 不執行任何初始化檔案中的指令。(一般初始化檔案叫做`.gdbinit').一般情況下在 `-quiet' `-q' “安靜模式”。不輸出介紹和版權資訊。這些資訊在“批模式”中也被跳過。 `-batch' “批模式”。在“批模式”下運作。當在指令檔案中的所有指令都被成功的執行後 gdb傳回狀态“0”,如果在執行過程中出錯,gdb傳回一個非零值。 “批模式”在把gdb作為一個過濾器運作時很有用。比如在一台遠端計算機上下載下傳且 執行一個程式。資訊“ Program exited normally”(一般是當運作的程式正常結束 時出現)不會在這種模式中出現。 `-cd DIRECTORY' 把DIRECTORY作為gdb的工作目錄,而非目前目錄(一般gdb預設把目前目錄作為工作目 錄)。 `-fullname' `-f' GNU Emacs 設定這個選項,當我們在Emacs下,把gdb作為它的一個子程序來運作時, Emacs告訴gdb按标準輸出完整的檔案名和行号,一個可視的棧内容。這個格式跟在 檔案名的後面。行号和字元重新按列排,Emacs-to-GDB界面使用\032字元作為一個 顯示一頁原檔案的信号。 `-b BPS' 為遠端調試設定波特率。 `-tty DEVICE' 使用DEVICE來作為你程式的标準輸入輸出 |
1. 一個簡單的GDB會話 2. 進入和退出GDB 3. GDB指令 4. 在 GDB 下運作程式 7. 檢視源碼檔案 9. C預處理宏 1. 一個簡單的GDB會話 你可以在空閑的時候閱讀全部的GDB手冊。然而一些指令足以使你開始使用GDB,這一章就描述這些指令。 GNU m4(一個通用的宏處理器)的初始版本之一存在下面的bug:當我們改變單引号的預設表示時,一個用來捕獲一個宏定義的指令停止工作。在下面較短的m4會 話中,我們定義一個宏foo擴充成0000;然後我們使用m4内置的defn指令定義bar做同樣的事情。然而當我們改變左單引号為,右單引号為後,同樣的程式在定義一個新的同義詞baz時卻失敗了。 $ cd gnu/m4 $ ./m4 define(foo,0000) foo 0000 define(bar,defn(`foo')) bar 0000 changequote(,) define(baz,defn(foo)) baz C-d m4: End of input: 0: fatal error: EOF in string 讓我們使用GDB來看看發生了什麼。 $ gdb m4 GDB is free software and you are welcome to 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. GDB 6.5.50.20060706, Copyright 1999 Free Software Foundation, Inc... (gdb) GDB隻讀取很少的符号資料,這些資料足以讓它知道在哪裡能找到剩下的它需要的符号資料,是以,第一個指令提示符顯示的很快。現在我們告訴GDB使用比平時更短的顯示寬度,這樣這些例子可以更好的顯示在這個手冊中。 (gdb) set width 70 我們需要看一看m4内置的changquote是怎麼工作的。看了源碼之後,我們知道相關的子例程是m4_changequote,因為我們使用GDB的break指令在那裡設定一個斷點。 (gdb) break m4_changequote Breakpoint 1 at 0x62f4: file builtin.c, line 879. 通過使用run指令,在GDB的控制下我們啟動m4的運作。隻要控制沒有達到m4_changequote子例程,程式運作的和平時一樣。 (gdb) run Starting program: /work/Editorial/gdb/gnu/m4/m4 define(foo,0000) foo 0000 為了觸發斷點,我們調用changequote。GDB懸挂m4的執行,顯示它停止地方的上下文資訊。 changequote(,) Breakpoint 1, m4_changequote (argc=3, argv=0x33c70) at builtin.c:879 879 if (bad_argc(TOKEN_DATA_TEXT(argv[0]),argc,1,3)) 現在我們使用指令n(next)執行目前函數的下一行。 (gdb) n 882 set_quotes((argc >= 2) ? TOKEN_DATA_TEXT(argv[1])\ : nil, set_quotes看起來像是有問題的子例程,通過使用指令s(step)代替next我們能進入它的内部執行。 step會進入将被執行的子例程的下一行,是以它進入了set_quotes。 (gdb) s set_quotes (lq=0x34c78 "", rq=0x34c88 "") at input.c:530 530 if (lquote != def_lquote) 上面展示了m4(以及它的參數)被懸挂在這個棧裡,這個顯示被稱為棧桢顯示。它顯示了棧的概要。我們可以使用backtrace指令(它也可以寫成bt)來看一看在所有的棧中我們處在哪一個棧裡:backtrace指令顯示每一個活動子例程的棧桢。 (gdb) bt #0 set_quotes (lq=0x34c78 "", rq=0x34c88 "") at input.c:530 #1 0x6344 in m4_changequote (argc=3, argv=0x33c70) at builtin.c:882 #2 0x8174 in expand_macro (sym=0x33320) at macro.c:242 #3 0x7a88 in expand_token (obs=0x0, t=209696, td=0xf7fffa30) at macro.c:71 #4 0x79dc in expand_input () at macro.c:40 #5 0x2930 in main (argc=0, argv=0xf7fffb20) at m4.c:195 我們再前進幾行看看會發生什麼。開始的兩次,我們使用s;後面的兩次我們使用n來避免進入 xstrdup子例程。 (gdb) s 0x3b5c 532 if (rquote != def_rquote) (gdb) s 0x3b80 535 lquote = (lq == nil || *lq == '\0') ? \ def_lquote : xstrdup(lq); (gdb) n 536 rquote = (rq == nil || *rq == '\0') ? def_rquote\ : xstrdup(rq); (gdb) n 538 len_lquote = strlen(rquote); 最後一行顯示看起來有點奇怪。我們可以檢查lquote和rquote變量,看看它們實際上是不是我們指定的新的左單引号和右單引号。我們使用指令p(print)來看它們的值。 (gdb) p lquote $1 = 0x35d40 "" (gdb) p rquote $2 = 0x35d50 "" lquote和rquote的确是新的左右單引号。為了看一些上下文,我們可以使用l(list)指令來顯示圍繞目前行的前後10行源代碼。 (gdb) l 533 xfree(rquote); 534 535 lquote = (lq == nil || *lq == '\0') ? def_lquote\ : xstrdup (lq); 536 rquote = (rq == nil || *rq == '\0') ? def_rquote\ : xstrdup (rq); 537 538 len_lquote = strlen(rquote); 539 len_rquote = strlen(lquote); 540 } 541 542 void 讓我們再向前執行兩行設定len_lquote和len_rquote值的源代碼,然後檢查這些變量的值。 (gdb) n 539 len_rquote = strlen(lquote); (gdb) n 540 } (gdb) p len_lquote $3 = 9 (gdb) p len_rquote $4 = 7 假如len_lquote和len_rquote分别意味着lquote和rquote的長度,這看起來一定有問題。因為p指令能列印任何表達式的值--那些表達式能包括子例程和指派。通過使用它,我們能把它們設定成别的更好的值。 (gdb) p len_lquote=strlen(lquote) $5 = 7 (gdb) p len_rquote=strlen(rquote) $6 = 9 對于修正使用m4内置的defn來設定新的引号引起的問題,這已經足夠了嗎?我們使用c(continue)指令可以讓m4繼續,然後試一試最初因為麻煩的那個例子。 (gdb) c Continuing. define(baz,defn(foo)) baz 0000 成功了!新的引号現在和預設的引号一樣工作正常。問題似乎是那兩個定義了錯誤的長度的行。我們給m4輸入一個EOF讓它退出。 C-d Program exited normally. 這個消息`Program exited normally.'來自于GDB,它表明m4已經完成了執行。我們可以使用 GDB的quit指令來結束我們的GDB會話。 (gdb) quit GDB簡介: ************** 調試器(比如象GDB)能讓你觀察另一個程式在執行時的内部活動,或程式出錯時 發生了什麼。 GDB主要能為你做四件事(包括為了完成這些事而附加的功能),幫助你找出程式 中的錯誤。 * 運作你的程式,設定所有的能影響程式運作的東西。 * 保證你的程式在指定的條件下停止。 * 當你程式停止時,讓你檢查發生了什麼。 * 改變你的程式。那樣你可以試着修正某個bug引起的問題,然後繼續查找另一 個bug. 你可以用GDB來調試C和C++寫的程式。(參考 *C 和C++) 調試Pascal程式時,有一些功能還不能使用。 GDB還可以用來調試FORTRAN程式,盡管現在還不支援表達式的輸入,輸出變量, 或類FORTRAN的詞法。 * GDB是"free software",大家都可以免費拷貝。也可以為GDB增加新的功能,不 過可要遵守GNU的許可協定幺。反正我認為GNU還是比較不錯的:-) 就這句話: Fundamentally, the General Public License is a license which says that you have these freedoms and that you cannot take these freedoms away from anyone else. GDB的作者: Richard Stallman是GDB的始作俑者,另外還有許多别的GNU的成員。許多人 為此作出了貢獻。(都是老外不提也罷,但願他們不要來找我麻煩:-)) 這裡是GDB的一個例子: 原文中是使用一個叫m4的程式。但很遺憾我找不到這個程式的原代碼, 是以沒有辦法來按照原文來說明。不過反正是個例子,我就拿一個作業系統的 程序排程原碼來說明把,原代碼我會附在後面。 首先這個程式叫os.c是一個模拟程序排程的原程式(也許是個老古董了:-))。 先說明一下如何取得包括原代碼符号的可執行代碼。大家有心的話可以去看一下gcc的 man檔案(在shell下打man gcc)。gcc -g -o -g 的意思是生成帶原代碼調試符号的可執行檔案。 -o 的意思是指定可執行檔案名。 (gcc 的指令行參數有一大堆,有興趣可以自己去看看。) 反正在linux下把os.c用以上方法編譯連接配接以後就産生了可供gdb使用的可執行檔案。 我用gcc -g os.c -o os,産生的可執行文檔叫os. 然後打gdb os,就可進入gdb,螢幕提示: GDB is free software and you are welcome to 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. GDB 4.16, Copyright 1995 Free Software Foundation, Inc... (gdb) (gdb)是提示符,在這提示符下可以輸入指令,直到退出。(退出指令是q/Q) 為了盡量和原文檔說明的指令相符,即使在本例子中沒用的指令我也将示範。 首先我們可以設定gdb的螢幕大小。鍵入: (gdb)set width 70 就是把标準螢幕設為70列。 然後讓我們來設定斷點。設定方法很簡單:break或簡單打b後面加行号或函數名 比如我們可以在main 函數上設斷點: (gdb)break main 或(gdb)b main 系統提示:Breakpoint 1 at 0x8049552: file os.c, line 455. 然後我們可以運作這個程式,當程式運作到main函數時程式就會停止傳回到gdb的 提示符下。運作的指令是run或r(gdb中有不少alias,可以看一下help,在gdb下打help) run 後面可以跟參數,就是為程式指定指令行參數。 比如r abcd,則程式就會abcd以作為參數。(這裡要說明的是可以用set args來指定參 數)。打入r或run後,程式就開始運作直到進入main的入口停止,顯示: Starting program: /os Breakpoint 1, main () at os.c:455 455 Initial(); 這裡455 Initial();是将要執行的指令或函數。 gdb提供兩種方式:1.單步進入,step into就是跟蹤到函數内啦。指令是step或s 2.單步,next,就是簡單的單步,不會進入函數。指令是next或n 這兩個指令還有别的用法以後再說。 我們用n指令,鍵入: (gdb)n Success forking process# 1 ,pid is 31474 Success forking process# 2 ,pid is 31475 Success forking process# 3 ,pid is 31476 Success forking process# 5 ,pid is 31478 Success forking process# 6 ,pid is 31479 Dispatching Algorithm : FIFO ******************************************************************************** PCB# PID Priority PC State 1 31474 24 0 WAITING 2 31475 19 0 WAITING 3 31476 16 0 WAITING 4 31477 23 0 WAITING 5 31478 22 0 WAITING 6 31479 20 0 WAITING ****************************************************************************** CPU : NO process running IO : No process Waiting CPU!!! 31474 31475 31476 31477 31478 31479 Waiting IO NONE 456 State=WAITING; 最後的一行就是下一句要執行的指令。我們現在在另一個函數上加斷點。注意我們 可以用l/list指令來顯示原代碼。這裡我們鍵入 (gdb)l 451 main() 452 { 453 int message; 454 455 Initial(); 456 State=WAITING; 457 printf("Use Control-C to halt \n"); 458 signal(SIGALRM,AlarmMessage); 459 signal(SIGINT,InteruptMessage); 460 signal(SIGUSR2,IoMessage); (gdb) l 461 alarm(TimeSlot); 462 for(;;) 463 { 464 message=GetMessage(); 465 switch(message) 466 { 468 break; 469 case CHILD_IO: WaitingIo(); 470 break; 顯示了原代碼,現在在AlarmMessage上加斷點。 (gdb) b AlarmMessage Breakpoint 2 at 0x8048ee3: file os.c, line 259. (gdb) 然後我們繼續運作程式。 (gdb)c c或continue指令讓我們繼續被中斷的程式。 顯示: Continuing. Use Control-C to halt Breakpoint 2, AlarmMessage () at os.c:259 259 ClearSignal(); 注意我們下一句語句就是ClearSignal(); 我們用s/step跟蹤進入這個函數看看它是幹什麼的。 (gdb) s ClearSignal () at os.c:227 227 signal(SIGINT,SIG_IGN); 用l指令列出原代碼: (gdb) l 222 } 223 224 225 void ClearSignal() 226 { 227 signal(SIGINT,SIG_IGN); 228 signal(SIGALRM,SIG_IGN); 229 signal(SIGUSR2,SIG_IGN); 230 } 231 (gdb) 我們可以用s指令繼續跟蹤。現在讓我們來試試bt或backtrace指令。這個指令可以 顯示棧中的内容。 (gdb) bt #0 ClearSignal () at os.c:227 #1 0x8048ee8 in AlarmMessage () at os.c:259 #2 0xbffffaec in ?? () #3 0x80486ae in ___crt_dummy__ () (gdb) 大家一定能看懂顯示的意思。棧頂是AlarmMessage,接下來的函數沒有名字--就是 沒有原代碼符号。這顯示了函數調用的嵌套。 好了,我們跟蹤了半天還沒有檢查過變量的值呢。檢查表達式的值的指令是p或print 格式是p 444444讓我們來找一個變量來看看。:-) (gdb)l 1 還記得l的作用嗎?l或list顯示原代碼符号,l或list加就顯示從開始的 原代碼。好了找到一個讓我們來看看WaitingQueue的内容 (gdb) p WaitingQueue $1 = {1, 2, 3, 4, 5, 6, 0} (gdb) WaitingQueue是一個數組,gdb還支援結構的顯示, (gdb) p Pcb $2 = {{Pid = 0, State = 0, Prior = 0, pc = 0}, {Pid = 31474, State = 2, Prior = 24, pc = 0}, {Pid = 31475, State = 2, Prior = 19, pc = 0}, { Pid = 31476, State = 2, Prior = 16, pc = 0}, {Pid = 31477, State = 2, Prior = 23, pc = 0}, {Pid = 31478, State = 2, Prior = 22, pc = 0}, { Pid = 31479, State = 2, Prior = 20, pc = 0}} (gdb) 這裡可以對照原程式看看。 原文檔裡是一個調試過程,不過我想這裡我已經把gdb的常用功能介紹了一遍,基本上 可以用來調試程式了。:-) 運作GDB(一些詳細的說明): 前面已經提到過如何運作GDB了,現在讓我們來看一些更有趣的東西。你可以在運作 GDB時通過許多指令行參數指定大量的參數和選項,通過這個你可以在一開始就設定好 程式運作的環境。 這裡将要描述的指令行參數覆寫了大多數的情況,事實上在一定環境下有的并沒有 什麼大用處。最通常的指令就是使用一個參數: $gdb 你還可以同時為你的執行檔案指定一個core檔案: $gdb core 你也可以為你要執行的檔案指定一個程序号: $gdb 如:&gdb os 1234将使gdb與程序1234相聯系(attach) 除非你還有一個檔案叫1234的。gdb首先檢查一個core檔案。 如果你是使用一個遠端終端進行遠端調試的話,那如果你的終端不支援的話,你将無法 使用第二個參數甚至沒有core dump。如果你覺得開頭的提示資訊比較礙眼的話,你可以 用gdb -silent。你還可以用指令行參數更加詳細的控制GDB的行為。 打入gdb -help或-h 可以得到這方面的提示。所有的參數都被按照排列的順序傳給gdb 除非你用了-x參數。 當gdb開始運作時,它把任何一個不帶選項字首的參數都當作為一個可執行檔案或core 檔案(或程序号)。就象在前面加了-se或-c選項。gdb把第一個前面沒有選項說明的參數 看作前面加了-se 選項,而第二個(如果有的話)看作是跟着-c選項後面的。 許多選項有縮寫,用gdb -h可以看到。在gdb中你也可以任意的把選項名掐頭去尾,隻 要保證gdb能判斷唯一的一個參數就行。 在這裡我們說明一些最常用的參數選項 -symbols (-s )------從中讀去符号。 -exec (-e )----在合适的時候執行來做用正确的資料與core dump的作比較。 -se ------從中讀取符号并把它作為可執行檔案。 -core (-c )--指定為一個core dump 檔案。 -c ----連接配接到程序号為,與attach指令相似。 -command -x -----執行gdb指令,在指定的檔案中存放着一序列的gdb指令,就 象一個批處理。 -directory(-d) ---指定路徑。把加入到搜尋原檔案的路徑中。 -m -mapped---- 注意這個指令不是在所有的系統上都能用。如果你可以通過mmap系統調用來獲得記憶體 映象檔案,你可以用這個指令來使gdb把你目前檔案裡的符号寫入一個檔案中,這個檔案 将存放在你的目前路徑中。如果你調試的程式叫/temp/fred那麼map檔案就叫 ./fred.syms這樣當你以後再調試這個程式時,gdb會認識到這個檔案的存在,進而從這 個檔案中讀取符号,而不是從可執行檔案中讀取。.syms與主機有關不能共享。 -r -readnow---馬上從符号檔案中讀取整個符号表,而不是使用預設的。預設的符号表是 調入一部分符号,當需要時再讀入一部分。這會使開始進入gdb慢一些,但可以加快以後 的調試速度。 -m和-r一般在一起使用來建立.syms檔案 接下來再談談模式的設定(請聽下回分解 :-)) 附:在gdb文檔裡使用的調試例子我找到了在minix下有這個程式,叫m4有興趣的 可以自己去看看 模式的選擇 -------------- 現在我們來聊聊gdb運作模式的選擇。我們可以用許多模式來運作gdb,例如在“批模式” 或“安靜模式”。這些模式都是在gdb運作時在指令行作為選項指定的。 `-nx' `-n' 不執行任何初始化檔案中的指令。(一般初始化檔案叫做`.gdbinit').一般情況下在 `-quiet' `-q' “安靜模式”。不輸出介紹和版權資訊。這些資訊在“批模式”中也被跳過。 `-batch' “批模式”。在“批模式”下運作。當在指令檔案中的所有指令都被成功的執行後 gdb傳回狀态“0”,如果在執行過程中出錯,gdb傳回一個非零值。 “批模式”在把gdb作為一個過濾器運作時很有用。比如在一台遠端計算機上下載下傳且 執行一個程式。資訊“ Program exited normally”(一般是當運作的程式正常結束 時出現)不會在這種模式中出現。 `-cd DIRECTORY' 把DIRECTORY作為gdb的工作目錄,而非目前目錄(一般gdb預設把目前目錄作為工作目 錄)。 `-fullname' `-f' GNU Emacs 設定這個選項,當我們在Emacs下,把gdb作為它的一個子程序來運作時, Emacs告訴gdb按标準輸出完整的檔案名和行号,一個可視的棧内容。這個格式跟在 檔案名的後面。行号和字元重新按列排,Emacs-to-GDB界面使用\032字元作為一個 顯示一頁原檔案的信号。 `-b BPS' 為遠端調試設定波特率。 `-tty DEVICE' 使用DEVICE來作為你程式的标準輸入輸出 1. 一個簡單的GDB會話 2. 進入和退出GDB 3. GDB指令 4. 在 GDB 下運作程式 7. 檢視源碼檔案 9. C預處理宏 1. 一個簡單的GDB會話 你可以在空閑的時候閱讀全部的GDB手冊。然而一些指令足以使你開始使用GDB,這一章就描述這些指令。 GNU m4(一個通用的宏處理器)的初始版本之一存在下面的bug:當我們改變單引号的預設表示時,一個用來捕獲一個宏定義的指令停止工作。在下面較短的m4會 話中,我們定義一個宏foo擴充成0000;然後我們使用m4内置的defn指令定義bar做同樣的事情。然而當我們改變左單引号為,右單引号為後,同樣的程式在定義一個新的同義詞baz時卻失敗了。 $ cd gnu/m4 $ ./m4 define(foo,0000) foo 0000 define(bar,defn(`foo')) bar 0000 changequote(,) define(baz,defn(foo)) baz C-d m4: End of input: 0: fatal error: EOF in string 讓我們使用GDB來看看發生了什麼。 $ gdb m4 GDB is free software and you are welcome to 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. GDB 6.5.50.20060706, Copyright 1999 Free Software Foundation, Inc... (gdb) GDB隻讀取很少的符号資料,這些資料足以讓它知道在哪裡能找到剩下的它需要的符号資料,是以,第一個指令提示符顯示的很快。現在我們告訴GDB使用比平時更短的顯示寬度,這樣這些例子可以更好的顯示在這個手冊中。 (gdb) set width 70 我們需要看一看m4内置的changquote是怎麼工作的。看了源碼之後,我們知道相關的子例程是m4_changequote,因為我們使用GDB的break指令在那裡設定一個斷點。 (gdb) break m4_changequote Breakpoint 1 at 0x62f4: file builtin.c, line 879. 通過使用run指令,在GDB的控制下我們啟動m4的運作。隻要控制沒有達到m4_changequote子例程,程式運作的和平時一樣。 (gdb) run Starting program: /work/Editorial/gdb/gnu/m4/m4 define(foo,0000) foo 0000 為了觸發斷點,我們調用changequote。GDB懸挂m4的執行,顯示它停止地方的上下文資訊。 changequote(,) Breakpoint 1, m4_changequote (argc=3, argv=0x33c70) at builtin.c:879 879 if (bad_argc(TOKEN_DATA_TEXT(argv[0]),argc,1,3)) 現在我們使用指令n(next)執行目前函數的下一行。 (gdb) n 882 set_quotes((argc >= 2) ? TOKEN_DATA_TEXT(argv[1])\ : nil, set_quotes看起來像是有問題的子例程,通過使用指令s(step)代替next我們能進入它的内部執行。 step會進入将被執行的子例程的下一行,是以它進入了set_quotes。 (gdb) s set_quotes (lq=0x34c78 "", rq=0x34c88 "") at input.c:530 530 if (lquote != def_lquote) 上面展示了m4(以及它的參數)被懸挂在這個棧裡,這個顯示被稱為棧桢顯示。它顯示了棧的概要。我們可以使用backtrace指令(它也可以寫成bt)來看一看在所有的棧中我們處在哪一個棧裡:backtrace指令顯示每一個活動子例程的棧桢。 (gdb) bt #0 set_quotes (lq=0x34c78 "", rq=0x34c88 "") at input.c:530 #1 0x6344 in m4_changequote (argc=3, argv=0x33c70) at builtin.c:882 #2 0x8174 in expand_macro (sym=0x33320) at macro.c:242 #3 0x7a88 in expand_token (obs=0x0, t=209696, td=0xf7fffa30) at macro.c:71 #4 0x79dc in expand_input () at macro.c:40 #5 0x2930 in main (argc=0, argv=0xf7fffb20) at m4.c:195 我們再前進幾行看看會發生什麼。開始的兩次,我們使用s;後面的兩次我們使用n來避免進入 xstrdup子例程。 (gdb) s 0x3b5c 532 if (rquote != def_rquote) (gdb) s 0x3b80 535 lquote = (lq == nil || *lq == '\0') ? \ def_lquote : xstrdup(lq); (gdb) n 536 rquote = (rq == nil || *rq == '\0') ? def_rquote\ : xstrdup(rq); (gdb) n 538 len_lquote = strlen(rquote); 最後一行顯示看起來有點奇怪。我們可以檢查lquote和rquote變量,看看它們實際上是不是我們指定的新的左單引号和右單引号。我們使用指令p(print)來看它們的值。 (gdb) p lquote $1 = 0x35d40 "" (gdb) p rquote $2 = 0x35d50 "" lquote和rquote的确是新的左右單引号。為了看一些上下文,我們可以使用l(list)指令來顯示圍繞目前行的前後10行源代碼。 (gdb) l 533 xfree(rquote); 534 535 lquote = (lq == nil || *lq == '\0') ? def_lquote\ : xstrdup (lq); 536 rquote = (rq == nil || *rq == '\0') ? def_rquote\ : xstrdup (rq); 537 538 len_lquote = strlen(rquote); 539 len_rquote = strlen(lquote); 540 } 541 542 void 讓我們再向前執行兩行設定len_lquote和len_rquote值的源代碼,然後檢查這些變量的值。 (gdb) n 539 len_rquote = strlen(lquote); (gdb) n 540 } (gdb) p len_lquote $3 = 9 (gdb) p len_rquote $4 = 7 假如len_lquote和len_rquote分别意味着lquote和rquote的長度,這看起來一定有問題。因為p指令能列印任何表達式的值--那些表達式能包括子例程和指派。通過使用它,我們能把它們設定成别的更好的值。 (gdb) p len_lquote=strlen(lquote) $5 = 7 (gdb) p len_rquote=strlen(rquote) $6 = 9 對于修正使用m4内置的defn來設定新的引号引起的問題,這已經足夠了嗎?我們使用c(continue)指令可以讓m4繼續,然後試一試最初因為麻煩的那個例子。 (gdb) c Continuing. define(baz,defn(foo)) baz 0000 成功了!新的引号現在和預設的引号一樣工作正常。問題似乎是那兩個定義了錯誤的長度的行。我們給m4輸入一個EOF讓它退出。 C-d Program exited normally. 這個消息`Program exited normally.'來自于GDB,它表明m4已經完成了執行。我們可以使用 GDB的quit指令來結束我們的GDB會話。 (gdb) quit GDB簡介: ************** 調試器(比如象GDB)能讓你觀察另一個程式在執行時的内部活動,或程式出錯時 發生了什麼。 GDB主要能為你做四件事(包括為了完成這些事而附加的功能),幫助你找出程式 中的錯誤。 * 運作你的程式,設定所有的能影響程式運作的東西。 * 保證你的程式在指定的條件下停止。 * 當你程式停止時,讓你檢查發生了什麼。 * 改變你的程式。那樣你可以試着修正某個bug引起的問題,然後繼續查找另一 個bug. 你可以用GDB來調試C和C++寫的程式。(參考 *C 和C++) 調試Pascal程式時,有一些功能還不能使用。 GDB還可以用來調試FORTRAN程式,盡管現在還不支援表達式的輸入,輸出變量, 或類FORTRAN的詞法。 * GDB是"free software",大家都可以免費拷貝。也可以為GDB增加新的功能,不 過可要遵守GNU的許可協定幺。反正我認為GNU還是比較不錯的:-) 就這句話: Fundamentally, the General Public License is a license which says that you have these freedoms and that you cannot take these freedoms away from anyone else. GDB的作者: Richard Stallman是GDB的始作俑者,另外還有許多别的GNU的成員。許多人 為此作出了貢獻。(都是老外不提也罷,但願他們不要來找我麻煩:-)) 這裡是GDB的一個例子: 原文中是使用一個叫m4的程式。但很遺憾我找不到這個程式的原代碼, 是以沒有辦法來按照原文來說明。不過反正是個例子,我就拿一個作業系統的 程序排程原碼來說明把,原代碼我會附在後面。 首先這個程式叫os.c是一個模拟程序排程的原程式(也許是個老古董了:-))。 先說明一下如何取得包括原代碼符号的可執行代碼。大家有心的話可以去看一下gcc的 man檔案(在shell下打man gcc)。gcc -g -o -g 的意思是生成帶原代碼調試符号的可執行檔案。 -o 的意思是指定可執行檔案名。 (gcc 的指令行參數有一大堆,有興趣可以自己去看看。) 反正在linux下把os.c用以上方法編譯連接配接以後就産生了可供gdb使用的可執行檔案。 我用gcc -g os.c -o os,産生的可執行文檔叫os. 然後打gdb os,就可進入gdb,螢幕提示: GDB is free software and you are welcome to 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. GDB 4.16, Copyright 1995 Free Software Foundation, Inc... (gdb) (gdb)是提示符,在這提示符下可以輸入指令,直到退出。(退出指令是q/Q) 為了盡量和原文檔說明的指令相符,即使在本例子中沒用的指令我也将示範。 首先我們可以設定gdb的螢幕大小。鍵入: (gdb)set width 70 就是把标準螢幕設為70列。 然後讓我們來設定斷點。設定方法很簡單:break或簡單打b後面加行号或函數名 比如我們可以在main 函數上設斷點: (gdb)break main 或(gdb)b main 系統提示:Breakpoint 1 at 0x8049552: file os.c, line 455. 然後我們可以運作這個程式,當程式運作到main函數時程式就會停止傳回到gdb的 提示符下。運作的指令是run或r(gdb中有不少alias,可以看一下help,在gdb下打help) run 後面可以跟參數,就是為程式指定指令行參數。 比如r abcd,則程式就會abcd以作為參數。(這裡要說明的是可以用set args來指定參 數)。打入r或run後,程式就開始運作直到進入main的入口停止,顯示: Starting program: /os Breakpoint 1, main () at os.c:455 455 Initial(); 這裡455 Initial();是将要執行的指令或函數。 gdb提供兩種方式:1.單步進入,step into就是跟蹤到函數内啦。指令是step或s 2.單步,next,就是簡單的單步,不會進入函數。指令是next或n 這兩個指令還有别的用法以後再說。 我們用n指令,鍵入: (gdb)n Success forking process# 1 ,pid is 31474 Success forking process# 2 ,pid is 31475 Success forking process# 3 ,pid is 31476 Success forking process# 5 ,pid is 31478 Success forking process# 6 ,pid is 31479 Dispatching Algorithm : FIFO ******************************************************************************** PCB# PID Priority PC State 1 31474 24 0 WAITING 2 31475 19 0 WAITING 3 31476 16 0 WAITING 4 31477 23 0 WAITING 5 31478 22 0 WAITING 6 31479 20 0 WAITING ****************************************************************************** CPU : NO process running IO : No process Waiting CPU!!! 31474 31475 31476 31477 31478 31479 Waiting IO NONE 456 State=WAITING; 最後的一行就是下一句要執行的指令。我們現在在另一個函數上加斷點。注意我們 可以用l/list指令來顯示原代碼。這裡我們鍵入 (gdb)l 451 main() 452 { 453 int message; 454 455 Initial(); 456 State=WAITING; 457 printf("Use Control-C to halt \n"); 458 signal(SIGALRM,AlarmMessage); 459 signal(SIGINT,InteruptMessage); 460 signal(SIGUSR2,IoMessage); (gdb) l 461 alarm(TimeSlot); 462 for(;;) 463 { 464 message=GetMessage(); 465 switch(message) 466 { 468 break; 469 case CHILD_IO: WaitingIo(); 470 break; 顯示了原代碼,現在在AlarmMessage上加斷點。 (gdb) b AlarmMessage Breakpoint 2 at 0x8048ee3: file os.c, line 259. (gdb) 然後我們繼續運作程式。 (gdb)c c或continue指令讓我們繼續被中斷的程式。 顯示: Continuing. Use Control-C to halt Breakpoint 2, AlarmMessage () at os.c:259 259 ClearSignal(); 注意我們下一句語句就是ClearSignal(); 我們用s/step跟蹤進入這個函數看看它是幹什麼的。 (gdb) s ClearSignal () at os.c:227 227 signal(SIGINT,SIG_IGN); 用l指令列出原代碼: (gdb) l 222 } 223 224 225 void ClearSignal() 226 { 227 signal(SIGINT,SIG_IGN); 228 signal(SIGALRM,SIG_IGN); 229 signal(SIGUSR2,SIG_IGN); 230 } 231 (gdb) 我們可以用s指令繼續跟蹤。現在讓我們來試試bt或backtrace指令。這個指令可以 顯示棧中的内容。 (gdb) bt #0 ClearSignal () at os.c:227 #1 0x8048ee8 in AlarmMessage () at os.c:259 #2 0xbffffaec in ?? () #3 0x80486ae in ___crt_dummy__ () (gdb) 大家一定能看懂顯示的意思。棧頂是AlarmMessage,接下來的函數沒有名字--就是 沒有原代碼符号。這顯示了函數調用的嵌套。 好了,我們跟蹤了半天還沒有檢查過變量的值呢。檢查表達式的值的指令是p或print 格式是p 444444讓我們來找一個變量來看看。:-) (gdb)l 1 還記得l的作用嗎?l或list顯示原代碼符号,l或list加就顯示從開始的 原代碼。好了找到一個讓我們來看看WaitingQueue的内容 (gdb) p WaitingQueue $1 = {1, 2, 3, 4, 5, 6, 0} (gdb) WaitingQueue是一個數組,gdb還支援結構的顯示, (gdb) p Pcb $2 = {{Pid = 0, State = 0, Prior = 0, pc = 0}, {Pid = 31474, State = 2, Prior = 24, pc = 0}, {Pid = 31475, State = 2, Prior = 19, pc = 0}, { Pid = 31476, State = 2, Prior = 16, pc = 0}, {Pid = 31477, State = 2, Prior = 23, pc = 0}, {Pid = 31478, State = 2, Prior = 22, pc = 0}, { Pid = 31479, State = 2, Prior = 20, pc = 0}} (gdb) 這裡可以對照原程式看看。 原文檔裡是一個調試過程,不過我想這裡我已經把gdb的常用功能介紹了一遍,基本上 可以用來調試程式了。:-) 運作GDB(一些詳細的說明): 前面已經提到過如何運作GDB了,現在讓我們來看一些更有趣的東西。你可以在運作 GDB時通過許多指令行參數指定大量的參數和選項,通過這個你可以在一開始就設定好 程式運作的環境。 這裡将要描述的指令行參數覆寫了大多數的情況,事實上在一定環境下有的并沒有 什麼大用處。最通常的指令就是使用一個參數: $gdb 你還可以同時為你的執行檔案指定一個core檔案: $gdb core 你也可以為你要執行的檔案指定一個程序号: $gdb 如:&gdb os 1234将使gdb與程序1234相聯系(attach) 除非你還有一個檔案叫1234的。gdb首先檢查一個core檔案。 如果你是使用一個遠端終端進行遠端調試的話,那如果你的終端不支援的話,你将無法 使用第二個參數甚至沒有core dump。如果你覺得開頭的提示資訊比較礙眼的話,你可以 用gdb -silent。你還可以用指令行參數更加詳細的控制GDB的行為。 打入gdb -help或-h 可以得到這方面的提示。所有的參數都被按照排列的順序傳給gdb 除非你用了-x參數。 當gdb開始運作時,它把任何一個不帶選項字首的參數都當作為一個可執行檔案或core 檔案(或程序号)。就象在前面加了-se或-c選項。gdb把第一個前面沒有選項說明的參數 看作前面加了-se 選項,而第二個(如果有的話)看作是跟着-c選項後面的。 許多選項有縮寫,用gdb -h可以看到。在gdb中你也可以任意的把選項名掐頭去尾,隻 要保證gdb能判斷唯一的一個參數就行。 在這裡我們說明一些最常用的參數選項 -symbols (-s )------從中讀去符号。 -exec (-e )----在合适的時候執行來做用正确的資料與core dump的作比較。 -se ------從中讀取符号并把它作為可執行檔案。 -core (-c )--指定為一個core dump 檔案。 -c ----連接配接到程序号為,與attach指令相似。 -command -x -----執行gdb指令,在指定的檔案中存放着一序列的gdb指令,就 象一個批處理。 -directory(-d) ---指定路徑。把加入到搜尋原檔案的路徑中。 -m -mapped---- 注意這個指令不是在所有的系統上都能用。如果你可以通過mmap系統調用來獲得記憶體 映象檔案,你可以用這個指令來使gdb把你目前檔案裡的符号寫入一個檔案中,這個檔案 将存放在你的目前路徑中。如果你調試的程式叫/temp/fred那麼map檔案就叫 ./fred.syms這樣當你以後再調試這個程式時,gdb會認識到這個檔案的存在,進而從這 個檔案中讀取符号,而不是從可執行檔案中讀取。.syms與主機有關不能共享。 -r -readnow---馬上從符号檔案中讀取整個符号表,而不是使用預設的。預設的符号表是 調入一部分符号,當需要時再讀入一部分。這會使開始進入gdb慢一些,但可以加快以後 的調試速度。 -m和-r一般在一起使用來建立.syms檔案 接下來再談談模式的設定(請聽下回分解 :-)) 附:在gdb文檔裡使用的調試例子我找到了在minix下有這個程式,叫m4有興趣的 可以自己去看看 模式的選擇 -------------- 現在我們來聊聊gdb運作模式的選擇。我們可以用許多模式來運作gdb,例如在“批模式” 或“安靜模式”。這些模式都是在gdb運作時在指令行作為選項指定的。 `-nx' `-n' 不執行任何初始化檔案中的指令。(一般初始化檔案叫做`.gdbinit').一般情況下在 `-quiet' `-q' “安靜模式”。不輸出介紹和版權資訊。這些資訊在“批模式”中也被跳過。 `-batch' “批模式”。在“批模式”下運作。當在指令檔案中的所有指令都被成功的執行後 gdb傳回狀态“0”,如果在執行過程中出錯,gdb傳回一個非零值。 “批模式”在把gdb作為一個過濾器運作時很有用。比如在一台遠端計算機上下載下傳且 執行一個程式。資訊“ Program exited normally”(一般是當運作的程式正常結束 時出現)不會在這種模式中出現。 `-cd DIRECTORY' 把DIRECTORY作為gdb的工作目錄,而非目前目錄(一般gdb預設把目前目錄作為工作目 錄)。 `-fullname' `-f' GNU Emacs 設定這個選項,當我們在Emacs下,把gdb作為它的一個子程序來運作時, Emacs告訴gdb按标準輸出完整的檔案名和行号,一個可視的棧内容。這個格式跟在 檔案名的後面。行号和字元重新按列排,Emacs-to-GDB界面使用\032字元作為一個 顯示一頁原檔案的信号。 `-b BPS' 為遠端調試設定波特率。 `-tty DEVICE' 使用DEVICE來作為你程式的标準輸入輸出 |