unix下sdb命令詳解
Unix系統開發-sdb的啟動 首先來看看在哪些情況下需要對程序進行調試。 第一種情況(這是大多數用戶都會碰到的),程序在運行過程中忽然跳了出來,屏幕上顯示一個xxxx-core dumped消息,然后Shell提示符就又顯示出來了,其中xxxx表示出錯原因。這種情況的出現一般是系統核心認為進程的執行出現了異常,如進程試圖去訪問一塊不允許它訪問的存儲區域(Memory Fault,Segmentation Fault);或者掃描某個無終止符的字符串(Bus Error);或者浮點運算溢出或被0除(Arithmetic Exception),等等。此時操作系統會把進程當時的內存映象寫到當前目錄下的一個名叫core的文件中。這種情況下我們可以使用sdb來檢查此core文件,以決定出錯的地點以及程序執行的狀態,如函數間的調用關系、變量的值,等等。 第二種情況,程序可能并沒有什么異常行為,但就是怎么也得不到正確的輸出結果。這時需要在該進程運行過程中對之進行調試。這種情況下我們可以使用sdb逐條語句地跟蹤程序的執行過程,并在執行過程中檢查有關變量的值的變化情況。 上述兩種情況并不是絕然分開的。實際上它們可以結合在一起使用。例如,當我們利用core文件對某個已終止的進程進行調試時,可以在sdb中重新啟動相應程序的運行,然后對語句的執行進行一些控制。這樣我們就能夠知道在出現異常之前哪個程序到底是如何動作的。 為了使sdb能夠很好地對程序進行調試,在編譯程序時應指示編譯程序和鏈接程序在目標代碼中加入調試用的各種信息,如程序中的變量名、函數名及其在源程序中的行號等。我們知道,使用-g選項可以完成這一點。如我們可以用如下命令編譯前一章給出的有毛病的程序代碼: $ cc -o myprog myprog.c myfunc.c myprog.c: myfunc.c: $ ls -l myprog -rwx-xr-x 1 yxz users 4224 Sep 1 10:17 myprog $ cc -g -o myprog myprog.c myfunc.c myprog.c myfunc.c $ ls -l myprog total 26 -rwxr-xr-x 1 yxz users 5404 Sep 1 10:21 myprog $ 這時我們會發現,新生成的myprog比不帶-g 選項生成的myprog要大的多。故在程序調試完成之后應將可執行程序中的調試用信息去掉。最簡單的方法當然是使用不帶-g 選項的cc命令重新編譯一遍。另外UNIX系統提供了另外一個名為strip的工具,使用此命令也可以將程序中的調試信息去掉。 現在我們可以試著運行一下那個有問題的程序myprog。在shell提示符下輸入: $ myprog 1 111 Arithmetic Exception -core dumped $ 我們看到,程序由于異常而推出了,并且在當前目錄下將生成一個名為core 的文件。這個文件有時非常龐大。在文件系統的維護中,有一條就是要定期找出各目錄下的core 文件并將其刪除掉。 發生此種情況時可以使用sdb來對之進行調試。輸入: $ sdb myprog 即可進入sdb調試程序。 sdb將接受三個參數: 待調試的可執行文件名; 待調試的core文件名,一般缺省是core; 由冒號分隔的一個目錄表,sdb將在這些目錄表中去查找有關的源文件。此目錄表的缺省設置是當前目錄 有時當前目錄下的core文件可能并不是待調試的程序的core 文件,此時用這個core 文件進行調試就是不合適的了。為防止這一點,可在命令行中指定第二個參數為減號(-),如下所示: $ sdb myprog - 這里的'-'告訴sdb忽略當前目錄下的core文件。 第三種情況,我們試用對活動過程(正在運行的進程)進行調試的情況。例如,假定某個程序正在后臺運行,但我們注意到該程序的某些部分執行起來非常慢,這時我們可以在不殺死這個進程的情況下對之進行調試: $ sdb /proc/1111 這里1111為待調試進程的進程號,用戶可以用PS命令得到。系統在/proc目錄下用文件的形式保存了每一個活動進程的信息,而文件名正好就是相應的進程號。 指定的進程將在執行時遇到第一個系統調用或調用sdb后收到某個軟中斷信號時暫停其運行,我們就可以在sdb中檢查變量的值、設置斷點、恢復執行,等等。在退出sdb時,控制又返回程序,執行進程又從其原停止的地方繼續執行。 第四種情況,一般情況下當被調試的活動進程在收到某個軟中斷信號時sdb會停止該進程。為了防止這一點,可以使用-s 選項。例如: $ sdb -s 14 myprog 將告訴sdb不要因為軟中斷信號14(鬧鐘報警信號)而使進程的執行停止。此時該信號被傳給相應進程。在程序接收并處理多個軟中斷信號的情況下,可以使用多個-s選項。 在sdb命令行中還有其他一些選項,對此我們不再一一列舉,讀者可以參考命令幫助。 在使用上述方法之一進入sdb之后,便可以進行在前一節中提到的各種操作,如顯示或設置變量值、函數調用關系、控制語句的執行等。下一節我們將詳細討論完成這些操作的方法。
UNIX系統開發-sdb命令使用:程序執行控制 我們說程序調試的主要目的是觀察變量的變化情況。但如果程序就一直不停地運行直至它終止或出錯時才停下來的話,將沒有機會去觀察任何變量。因此,如何有效地控制程序中各語句的執行,使之在適當的時候暫停下來,待調試人員在顯示或設置了某些存儲單元的值之后再從停下的地方繼續執行下去,是調試程序應具有的一項基本功能。在這一節中我們就來具體介紹如何控制程序的運行。 對程序執行控制的第一個問題是設置斷點。一個斷點實際上就是程序中某行語句。當程序執行到這條語句時控制會重復返回到sdb,由此提供給用戶進行其他操作,如顯示變量值的機會。 1.斷點的設置和刪除 在sdb中設置斷點的方法比較多。但基本是b命令的變種。例如,我們可以使用如下語句在main()函數的第一個可執行行(非變量定義這類語句)設置一個斷點: * main:b 也可以直接用行號來設置斷點。例如,在myprog.c中,第10行是main()函數中的第一個可執行語句,則使用如下命令也能達到同樣的效果: * 10b 注意這是在myprog.c為當前文件的情況下進行的。 如果直接輸入: * b 命令,則可將當前行設成是一個斷點。但是若當前行不是一個可執行語句,那么sdb將把當前行之后的第一個可執行語句處設置一個斷點。 在設置完斷點之后,為了解程序中設置有哪些斷點,可以使用B命令: * B 0x80483f0 myprog.c:10 main+0x8 0x80483f7 myprog.c:11 main+0xf 0x8048407 myprog.c:12 main+0xlf 0x8048440 myprog.c:9 TestInput 0x8048447 myprog.c:10 TestInput+ 0x7 0x804482 myprog.c:13 TestInput+ 0x42 在設置完斷點之后要將其刪除,可以使用d命令。如: * main:d sdb將把在main()函數中設置的第一個可執行行上的斷點刪除。如果直接使用d命令,sdb將逐個列出所有斷點并詢問用戶是否刪除之。回答y斷點將被刪除。使用D命令則可刪除程序中設置的所有斷點。 2.sdb中啟動程序的運行 在設置好所需的斷點之后我們就可以重新啟動程序的運行了。這可以使用r命令。如: * r 111 2 BREAKPOINT process 554 function main() in myprog.c 10: for(i=1;i<argc:i++) * sdb將在main()函數中設置第一個斷點處停下來并顯示該行的語句。r后面給出的是傳給可執行程序myprog的兩個參數。因此上述命令同在shell提示符下輸入: $ myrprog 111 2 是相同的。不同的只是在sdb中程序的執行會在斷點處停下來。 如果只輸入 * r 命令,sdb使用最近一次執行調試程序時給它提供的參數來啟動之。如果想不帶任何參數來重新啟動程序的運行,可使用 * R 命令。 3.控制程序的單步或者連續執行 在用r(R)命令啟動程序的運行之后,sdb將在第一個斷點處暫停程序的運行。此時斷點行成為當前行,但并未被執行。此時我們可以在sdb的星號(*)提示符下輸入前面已介紹過的或后面將要介紹的各種命令。當然最重要的是顯示那些關鍵變量的值,以了解程序的運行情況。在這之后便可以繼續程序的執行。 繼續執行有兩種方式。的一種是單步方式,即逐條語句執行。這可以使用S命令完成: * s STEPPED process 584 function main() in myprog.c 14: printf('The %dth value '%s' tis BAD!n',i,argv[i]); * sdb在執行完當前語句之后,將當前行后移一行并顯示出其代碼。對于用戶自定義的函數調用,S命令并不將其當成是一條語句,此時它將指示sdb進入該函數(使之成為當前函數)。如我們可以接著上面輸入下列命令: * s STEPPED process 584 function main() in myprog.c 11: if(TestInput(argv[i])== TESTOK) * s BREAKPOINT process 594 function main() in myprog.c 11: if(TestInput(argv[i])==TESTOK) * s BREAKPOINT process 584 function TestInput() in myfunc.c 9: {while (* ValueInput)} * s 也能讓sdb將用戶自定義的函數調用,當成是一條普通語句而不進入此函數的定義。此時可以在那些包含有用戶自定義函數調用的行,使用S命令以執行之。 s 和S命令都可以接收一個指明待執行的語句數目的參數。例如: * s 2 使sdb執行當前行及其下那行語句,然后當前行之下的第2行將成為新的當前行。注意當使用s 或S時,如果sdb遇上一個未加-g選項編譯的用戶自定義函數,那么執行將繼續直到一個帶-g 選項編譯的函數為止。 控制程序執行的第二種是使用c或C命令。一個最簡單的c命令將使sdb從當前行把程序執行到下一個斷點處。在c命令中還可以臨時設置“斷點”。例如: * 8c 將使sdb在第8行上設置一個斷點并使程序從當前行執行至該斷點處后停下來,然后將此斷點自動刪除并等待用戶輸入其他命令。 在c命令中還可以指定在繼續執行時后面第幾個斷點處停下來。例如: * c 2 將使sdb從當前行開始執行,直到當前行之后的第2個斷點為止。一般的: * c 命令實際上相當于: * c 1 C命令的作用及用法同c基本上是相同的。只不過此時sdb將進程收到的終止信號傳給該進程進行處理。這對于調試有軟中斷處理的進程是很有用的。但sdb并不是將進程所收到的所有信號都傳送給進程進行處理。要傳遞的信號可在sdb的命令行中用-s選項指定。 我們可以用g命令來告訴sdb在給定的行開始繼續程序的執行: * 6g 此命令使sdb在當前文件的第6行處恢復執行。而: * 6g2 使sdb在當前文件的第6行恢復執行并跳過兩個斷點。但在使用g命令時必須小心。如果此命令跳過了那些必須得初始化語句,或者進程的執行被恢復到某個并不終止進程的函數中,此時都將會是程序的執行出現人為的差錯。一般而言,除非能夠確信所跳過的代碼段是不正確的代碼,不要輕易使用g命令。 4 關聯命令 我們可以告訴sdb在使用到某種情況下暫停下來,完成某些操作之后繼續執行。例如: * 7b t; i/;c 將使得sdb在當前文件的第7行設置一個斷點,然后每當遇到該斷點時都顯示函數調用棧(t)、變量i的值(i/),并繼續程序的執行(c)。 上述命令的一種變化形式是a(Announce命令)。例如: * FunctionName:a 將使得sdb在程序執行過程中每當函數FunctionName被調用時即顯示其函數名和參數值。而命令: * FunctionName:6a 將使得sdb在函數FunctionName中的第6行每次被執行時都將其顯示出來。 5.函數調用 sdb能夠調用程序中用戶自定義的任何函數。這種功能對于下面兩種情況可能比較有用: 在需要用各種不同的參數對函數進行測試。 用用戶自己定義的一個函數來顯示程序中的某些數據。 不論是何種情況,我們均可以用如下命令來進行函數調用: * FunctionName(Arg1,Arg2,....) 或 * FunctionName(Arg1,Arg2,....)/m 在第一種用法下,sdb只是簡單地執行指定的函數。在第二種用法下,sdb將在執行指定的函數之后顯示返回值。此值一般將按十進制方式顯示,除非指定了另外的顯示格式。 提供給函數的參數可以是寄存器名、整數、浮點數、字符、字符串常量或是其他局部當前函數,或者是當前函數可以訪問的變量。 6.變量的監視 sdb提供了一種被成為“觀察點”(Watch)的機制。借此機制,我們可以監視變量值得變化或某些地址的內容的變化。例如: * i $m 可用于設置對某個變量的觀察點。此處變量i為被監視的變量。而用: * 0x00400000:m 則可將地址0x00400000 處設置成觀察點,而0x00400000將成為被監視的地址。 在設置了觀察點之后,sdb會同處理S命令一樣開始程序的單步執行,并在變量的值或指定地址單元的內容被改變時停止程序的執行。由于變量值的改變或地址內容的變化,或者其他什么原因而使程序的執行中止,包括因為遇到斷點而使程序停止的情形,都將使得所設置的觀察點被sdb自動刪掉。 被觀察的變量必須是當前函數的局部變量或者是當前函數所能夠訪問的變量 其他命令 除了上面介紹的五類主要命令之外,sdb還支持其他一些命令。使用這些命令,可以完成其他一些輔助性的工作。分別介紹如下。 . !Shell Command 此命令可以使sdb去執行指定的Shell Command。這里Shell Command是一個合法命令。例如可以輸入: * ! ls -l sdb將執行指定的ls -l 命令,并將其輸出顯示出來。 使用: * ! sh 能夠生成一個新的Shell進程。在此新的Shell中可以完成某些工作,然后用exit命令返回到sdb中。 <FileName 指示sdb從文件FileName中讀取sdb調試命令,并且逐個執行之。 'SomeString 指示sdb顯示出某個字符串。常用此命令給出某些提示信息, k 此命令將使當前對程序的調試終止,之后仍可用r命令重新啟動被調試的程序。如: * k 1111:killed * 這里1111是被調試進程的進程號。 * q 此命令將使sdb停止其自身的運行而返回至原Shell提示符下。在調試完程序之后一般都要使用此命令。
UNIX系統開發-sdb命令使用:源程序的顯示和搜索 sdb命令的使用 同我們前面介紹過的mail,ftp一類工具類似,sdb也是一個命令解釋程序。也就是說,用戶在sdb提示符(一個星號*)下輸入sdb能夠識別的命令,sdb將根據被調試的程序的具體情況給出響應。 例如,在運行myprog出錯,生成core文件之后進入sdb時,sdb將給出如下的響應: $ sdb myprog 12: return ((100/atoi(ValueInput))? TESTOK:! TESTOK); * sdb給出來的實際上是程序出錯所在的函數,在源程序文件中的行號以及出錯那一行的語句。 在sdb的使用中要注意三個“當前”概念: (1)當前文件 即當前將要被執行的語句所在的那個源程序文件 (2)當前函數 即當前將要被執行的語句所在的那個函數 (3)當前行 這個概念只有在編譯時加入-g選項才會有,它指的是將要被執行的那條語句。與當前行相應,有一個行號的概念。它指的是每條語句在程序中位于第幾行。注意行號是從文件頭開始計算的,第一行的行號為1,空白行和注釋也包括在內。 在用core文件進行調試時,當前行和當前函數分別被設成是程序出錯時所執行的那條語句所在地行和函數(如同上面顯示出來的那樣)。但如果在編譯時未加-g選項,顯示出來的將只有函數名和函數的地址了。 在對活動進程進行排錯時,sdb將把當前函數和當前行分別設成是main()函數和main()函數的第一個可執行的語句行。 不論是哪種情況,sdb都將顯示出*提示符。在此提示符之下我們可以輸入各種sdb命令,以控制程序的執行或觀察變量的變化情況,等等。在下面的幾個小節中我們將分別詳細討論這些問題。 源程序的顯示和搜索 程序出錯一般來說不只是出錯的那條語句本身造成的。事實上出現錯誤經常是前面或相關的代碼執行了不正確的操作或少了某些必要的處理。因此調試過程中經常要觀察一下源程序中的語句,或者在程序中搜索某個符號出現在什么地方。其中字符串的搜索功能同vi基本上是相同的,而文件的顯示則同另外一個我們沒有具體討論的編輯器ed類似。下面我們將具體介紹這些命令。 1.源程序的顯示 在用core進入sdb之后,在*提示符后輸入w命令,該命令指示sdb顯示源程序中的當前行為中心的前后10行的內容并保持當前行不變: * w 7:int 8: TestInput(char * ValueInput) 9: {while ( * ValueInput) 10: if (! isdigit( * ValueInput)) return (! TESTOK); 11: else ValueInput++; 12: return ((100/atoi(ValueInput))? TESTOK:! TESTOK); 13: } * 我們看到,在進入sdb時,當前行是第12行,以該行為中心的10行內容正好就是上面所顯示出來的。其他可以顯示源程序語句的sdb命令如下: P 顯示當前行 l 顯示對應于當前指令的那條語句 Z 顯示當前行開始的下面10條語句 Ctrl+D 顯示當前行之后(不包括當前行)的第10條語句 n 顯示第n條語句,這里n是一個數 注意這些命令顯示出的是源程序語句還是匯編語句(后面我們將要介紹)取決于最近一次顯示出的是什么。 2.改變當前行 在用戶顯示語句時,當前行也會相應地發生變化。例如,Z命令將使當前行向程序尾移動9行,而Ctrl+D則使當前行向后移動10行。 在使用數字來顯示某行語句時將使該行語句成為當前行。而在*提示符之后按一下回車,當前行將下移一行。例如,接著上面的例子,輸入: * 8p 8: TEstInput(char * ValueInput) * 回車 9: { while ( * ValueInput)} * 這里8p實際上是兩條命令的組合。它使當前行移至源文件的第八行,然后再顯示出新的當前行。按回車鍵將使當前行后移一行。 3.改變當前源文件 在vi中我們可以用e命令對另外某個文件進行編輯。sdb也提供了e命令,可以用此命令來改變當前文件,如: * e myprog.c current file is now myprog.c * 8p 8: main(int argc,char * argv[]) * 我們看到,當前文件改變之后,sdb將第一行設為是當前行。如果此文件的第一行是個函數,那么該函數便成為當前函數。否則將臨時出現沒有當前函數的情況。 在上一節中,我們介紹過在命令行中可以指定源文件搜索目錄名列表(缺省情況為當前目錄)。如果某個文件不在此搜索目錄中,則可以用e命令將其加入: * e Another SourceDir 這里Another SourceDir是一個目錄名。如果要顯示該目錄下的某個文件,只需要輸入: * e FileName.c 當然直接使用: * e Another SourceDir/FileName.c 也能達到同樣的效果。 使用: * e FunctionName 將使包含函數FunctionName的文件名成為當前文件,而當前函數不言而喻將成為FunctionName。當前行則理所當然的是該函數的第一行。同一程序中函數名在各模塊中的唯一性保證了這一點是能夠成功的,但如果包含指定函數的文件不在當前搜索目錄列表中,則必須用e命令將其加入。 4.字符串的搜索 在vi中,我們可以在命令方式下使用“/“或者“?”命令,從當前位置向后或者向前搜索某個字符串,在sdb中也同樣可以完成這一點。使用這兩個命令我們可以查找源程序中某個或某類符號的出現。之所以說某類,是因為我們可以用正規表達式來指定待搜索的串(也即在搜索串中可以使用*,?,[,],-,^這類特殊字符)。 例如,為了查找myprog.c中argv出現在那些行上,可輸入: * /argv/ 8: main(ini argc,char * argv[]) sdb將從當前行開始向文件尾搜索,到達文件尾之后又從文件頭開始直至搜索到某個匹配的串或到達當前行為止。 與/相反,?命令將從當前行向文件頭方向搜索,因此如果我們將上述/argv/換成: * ? argv? 14: printf('The %dth value' %s'tis BAD! n',i,argv[i]); * 所得的結果一般是不同的。 /或?命令之后的/或?并不是必須的。另外如果要在同一方向上繼續搜索上次搜索過的串,只需要直接輸入/或者?即可。 下一節:函數調用層次顯示........
