天天看點

gdb調試手冊

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來作為你程式的标準輸入輸出

繼續閱讀