歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Unix知識 >> 關於Unix

UNIX開發系統程序調試例舉


來看看在哪些情況下需要對程序進行調試。
第一種情況(這是大多數用戶都會碰到的),程序在運行過程中忽然跳了出來,屏幕上顯示一個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之後,便可以進行在前一節中提到的各種操作,如顯示或設置變量值、函數調用關系、控制語句的
執行等。下一節我們將詳細討論完成這些操作的方法。

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);
*
所得的結果一般是不同的。
/或?命令之後的/或?並不是必須的。另外如果要在同一方向上繼續搜索上次搜索過的串,只需要直接輸入/或者?即可。
Copyright © Linux教程網 All Rights Reserved