- 論壇徽章:
- 0
|
在LoveUnix上看到的,挺不錯,轉給大家。
PS:文章里提到的工具可以在Google上找。
------------------------------------------------------------------------------------------------------------------
- 轉自:http://bbs.loveunix.net/viewthread.php?tid=25068&extra=&page=2
- 您可以用各種方法來監(jiān)控運行著的用戶空間程序:可以為其運行調(diào)試器并單步調(diào)試該程序,添加打印語句,或者添加工具來分析程序。本文描
- 述了幾種可以用來調(diào)試在 Linux 上運行的程序的方法。我們將回顧四種調(diào)試問題的情況,這些問題包括段錯誤,內(nèi)存溢出和泄漏,還有掛起。
- 本文討論了四種調(diào)試 Linux 程序的情況。在第 1 種情況中,我們使用了兩個有內(nèi)存分配問題的樣本程序,使用 MEMWATCH 和 Yet Another
- Malloc Debugger(YAMD)工具來調(diào)試它們。在第 2 種情況中,我們使用了 Linux 中的 strace 實用程序,它能夠跟蹤系統(tǒng)調(diào)用和信號,從而
- 找出程序發(fā)生錯誤的地方。在第 3 種情況中,我們使用 Linux 內(nèi)核的 Oops 功能來解決程序的段錯誤,并向您展示如何設置內(nèi)核源代碼級調(diào)
- 試器(kernel source level debugger,kgdb),以使用 GNU 調(diào)試器(GNU debugger,gdb)來解決相同的問題;kgdb 程序是使用串行連接的
- Linux 內(nèi)核遠程 gdb。在第 4 種情況中,我們使用 Linux 上提供的魔術鍵控順序(magic key sequence)來顯示引發(fā)掛起問題的組件的信息
- 。
- 常見調(diào)試方法
- 當您的程序中包含錯誤時,很可能在代碼中某處有一個條件,您認為它為真(true),但實際上是假(false)。找出錯誤的過程也就是在找出
- 錯誤后推翻以前一直確信為真的某個條件過程。
- 以下幾個示例是您可能確信成立的條件的一些類型:
- 在源代碼中的某處,某變量有特定的值。
- 在給定的地方,某個結構已被正確設置。
- 對于給定的 if-then-else 語句,if 部分就是被執(zhí)行的路徑。
- 當子例程被調(diào)用時,該例程正確地接收到了它的參數(shù)。
- 找出錯誤也就是要確定上述所有情況是否存在。如果您確信在子例程被調(diào)用時某變量應該有特定的值,那么就檢查一下情況是否如此。如果您
- 相信 if 結構會被執(zhí)行,那么也檢查一下情況是否如此。通常,您的假設都會是正確的,但最終您會找到與假設不符的情況。結果,您就會找
- 出發(fā)生錯誤的地方。
- 調(diào)試是您無法逃避的任務。進行調(diào)試有很多種方法,比如將消息打印到屏幕上、使用調(diào)試器,或只是考慮程序執(zhí)行的情況并仔細地揣摩問題所
- 在。
- 在修正問題之前,您必須找出它的源頭。舉例來說,對于段錯誤,您需要了解段錯誤發(fā)生在代碼的哪一行。一旦您發(fā)現(xiàn)了代碼中出錯的行,請
- 確定該方法中變量的值、方法被調(diào)用的方式以及關于錯誤如何發(fā)生的詳細情況。使用調(diào)試器將使找出所有這些信息變得很簡單。如果沒有調(diào)試
- 器可用,您還可以使用其它的工具。(請注意,產(chǎn)品環(huán)境中可能并不提供調(diào)試器,而且 Linux 內(nèi)核沒有內(nèi)建的調(diào)試器。)
- 本文將討論一類通過人工檢查代碼不容易找到的問題,而且此類問題只在很少見的情況下存在。內(nèi)存錯誤通常在多種情況同時存在時出現(xiàn),而
- 且您有時只能在部署程序之后才能發(fā)現(xiàn)內(nèi)存錯誤。
- 第 1 種情況:內(nèi)存調(diào)試工具
- C 語言作為 Linux 系統(tǒng)上標準的編程語言給予了我們對動態(tài)內(nèi)存分配很大的控制權。然而,這種自由可能會導致嚴重的內(nèi)存管理問題,而這些
- 問題可能導致程序崩潰或隨時間的推移導致性能降級。
- 內(nèi)存泄漏(即 malloc() 內(nèi)存在對應的 free() 調(diào)用執(zhí)行后永不被釋放)和緩沖區(qū)溢出(例如對以前分配到某數(shù)組的內(nèi)存進行寫操作)是一些
- 常見的問題,它們可能很難檢測到。這一部分將討論幾個調(diào)試工具,它們極大地簡化了檢測和找出內(nèi)存問題的過程。
- MEMWATCH
- MEMWATCH 由 Johan Lindh 編寫,是一個開放源代碼 C 語言內(nèi)存錯誤檢測工具,您可以自己下載它(請參閱本文后面部分的參考資料)。只要
- 在代碼中添加一個頭文件并在 gcc 語句中定義了 MEMWATCH 之后,您就可以跟蹤程序中的內(nèi)存泄漏和錯誤了。MEMWATCH 支持 ANSI C,它提供
- 結果日志紀錄,能檢測雙重釋放(double-free)、錯誤釋放(erroneous free)、沒有釋放的內(nèi)存(unfreed memory)、溢出和下溢等等。
- 清單 1. 內(nèi)存樣本(test1.c)
- CODE
- #include <stdlib.h>
- #include <stdio.h>
- #include "memwatch.h"
- int main(void)
- {
- char *ptr1;
- char *ptr2;
- ptr1 = malloc(512);
- ptr2 = malloc(512);
- ptr2 = ptr1;
- free(ptr2);
- free(ptr1);
- }
- 清單 1 中的代碼將分配兩個 512 字節(jié)的內(nèi)存塊,然后指向第一個內(nèi)存塊的指針被設定為指向第二個內(nèi)存塊。結果,第二個內(nèi)存塊的地址丟失
- ,從而產(chǎn)生了內(nèi)存泄漏。
- 現(xiàn)在我們編譯清單 1 的 memwatch.c。下面是一個 makefile 示例:
- test1
- gcc -DMEMWATCH -DMW_STDIO test1.c memwatch
- c -o test1
- 當您運行 test1 程序后,它會生成一個關于泄漏的內(nèi)存的報告。
- 清單 2 展示了示例 memwatch.log 輸出文件。
- 清單 2. test1 memwatch.log 文件
- MEMWATCH 2.67 Copyright © 1992-1999 Johan Lindh
- ...
- double-free: <4> test1.c(15), 0x80517b4 was freed from test1.c(14)
- ...
- unfreed: <2> test1.c(11), 512 bytes at 0x80519e4
- {FE FE FE FE FE FE FE FE FE FE FE FE ..............}
- Memory usage statistics (global):
- N)umber of allocations made: 2
- L)argest memory usage : 1024
- T)otal of all alloc() calls: 1024
- U)nfreed bytes totals : 512
- MEMWATCH 為您顯示真正導致問題的行。如果您釋放一個已經(jīng)釋放過的指針,它會告訴您。對于沒有釋放的內(nèi)存也一樣。日志結尾部分顯示統(tǒng)計
- 信息,包括泄漏了多少內(nèi)存,使用了多少內(nèi)存,以及總共分配了多少內(nèi)存。
- YAMD
- YAMD 軟件包由 Nate Eldredge 編寫,可以查找 C 和 C++ 中動態(tài)的、與內(nèi)存分配有關的問題。在撰寫本文時,YAMD 的最新版本為 0.32。請
- 下載 yamd-0.32.tar.gz(請參閱參考資料)。執(zhí)行 make 命令來構建程序;然后執(zhí)行 make install 命令安裝程序并設置工具。
- 一旦您下載了 YAMD 之后,請在 test1.c 上使用它。請刪除 #include memwatch.h 并對 makefile 進行如下小小的修改:
- 使用 YAMD 的 test1
- gcc -g test1.c -o test1
- 清單 3 展示了來自 test1 上的 YAMD 的輸出。
- 清單 3. 使用 YAMD 的 test1 輸出
- YAMD version 0.32
- Executable: /usr/src/test/yamd-0.32/test1
- ...
- INFO: Normal allocation of this block
- Address 0x40025e00, size 512
- ...
- INFO: Normal allocation of this block
- Address 0x40028e00, size 512
- ...
- INFO: Normal deallocation of this block
- Address 0x40025e00, size 512
- ...
- ERROR: Multiple freeing At
- free of pointer already freed
- Address 0x40025e00, size 512
- ...
- WARNING: Memory leak
- Address 0x40028e00, size 512
- WARNING: Total memory leaks:
- 1 unfreed allocations totaling 512 bytes
- *** Finished at Tue ... 10:07:15 2002
- Allocated a grand total of 1024 bytes 2 allocations
- Average of 512 bytes per allocation
- Max bytes allocated at one time: 1024
- 24 K alloced internally / 12 K mapped now / 8 K max
- Virtual program size is 1416 K
- End.
- YAMD 顯示我們已經(jīng)釋放了內(nèi)存,而且存在內(nèi)存泄漏。讓我們在清單 4 中另一個樣本程序上試試 YAMD。
- 清單 4. 內(nèi)存代碼(test2.c)
- CODE
- #include <stdlib.h>
- #include <stdio.h>
- int main(void)
- {
- char *ptr1;
- char *ptr2;
- char *chptr;
- int i = 1;
- ptr1 = malloc(512);
- ptr2 = malloc(512);
- chptr = (char *)malloc(512);
- for (i; i <= 512; i++) {
- chptr[i] = 'S';
- }
- ptr2 = ptr1;
- free(ptr2);
- free(ptr1);
- free(chptr);
- }
- 您可以使用下面的命令來啟動 YAMD:
- ./run-yamd /usr/src/test/test2/test2
- 清單 5 顯示了在樣本程序 test2 上使用 YAMD 得到的輸出。YAMD 告訴我們在 for 循環(huán)中有“越界(out-of-bounds)”的情況。
- 清單 5. 使用 YAMD 的 test2 輸出
- Running /usr/src/test/test2/test2
- Temp output to /tmp/yamd-out.1243
- *********
- ./run-yamd: line 101: 1248 Segmentation fault (core dumped)
- YAMD version 0.32
- Starting run: /usr/src/test/test2/test2
- Executable: /usr/src/test/test2/test2
- Virtual program size is 1380 K
- ...
- INFO: Normal allocation of this block
- Address 0x40025e00, size 512
- ...
- INFO: Normal allocation of this block
- Address 0x40028e00, size 512
- ...
- INFO: Normal allocation of this block
- Address 0x4002be00, size 512
- ERROR: Crash
- ...
- Tried to write address 0x4002c000
- Seems to be part of this block:
- Address 0x4002be00, size 512
- ...
- Address in question is at offset 512 (out of bounds)
- Will dump core after checking heap.
- Done.
- MEMWATCH 和 YAMD 都是很有用的調(diào)試工具,它們的使用方法有所不同。對于 MEMWATCH,您需要添加包含文件 memwatch.h 并打開兩個編譯時
- 間標記。對于鏈接(link)語句,YAMD 只需要 -g 選項。
- Electric Fence
- 多數(shù) Linux 分發(fā)版包含一個 Electric Fence 包,不過您也可以選擇下載它。Electric Fence 是一個由 Bruce Perens 編寫的 malloc() 調(diào)
- 試庫。它就在您分配內(nèi)存后分配受保護的內(nèi)存。如果存在 fencepost 錯誤(超過數(shù)組末尾運行),程序就會產(chǎn)生保護錯誤,并立即結束。通過
- 結合 Electric Fence 和 gdb,您可以精確地跟蹤到哪一行試圖訪問受保護內(nèi)存。Electric Fence 的另一個功能就是能夠檢測內(nèi)存泄漏。
- 第 2 種情況:使用 strace
- strace 命令是一種強大的工具,它能夠顯示所有由用戶空間程序發(fā)出的系統(tǒng)調(diào)用。strace 顯示這些調(diào)用的參數(shù)并返回符號形式的值。strace
- 從內(nèi)核接收信息,而且不需要以任何特殊的方式來構建內(nèi)核。將跟蹤信息發(fā)送到應用程序及內(nèi)核開發(fā)者都很有用。在清單 6 中,分區(qū)的一種格
- 式有錯誤,清單顯示了 strace 的開頭部分,內(nèi)容是關于調(diào)出創(chuàng)建文件系統(tǒng)操作(mkfs)的。strace 確定哪個調(diào)用導致問題出現(xiàn)。
- 清單 6. mkfs 上 strace 的開頭部分
- CODE
- execve("/sbin/mkfs.jfs", ["mkfs.jfs", "-f", "/dev/test1"], &
- ...
- open("/dev/test1", O_RDWR|O_LARGEFILE) = 4
- stat64("/dev/test1", {st_mode=&, st_rdev=makedev(63, 255), ...}) = 0
- ioctl(4, 0x40041271, 0xbfffe128) = -1 EINVAL (Invalid argument)
- write(2, "mkfs.jfs: warning - cannot setb" ..., 98mkfs.jfs: warning -
- cannot set blocksize on block device /dev/test1: Invalid argument )
- = 98
- stat64("/dev/test1", {st_mode=&, st_rdev=makedev(63, 255), ...}) = 0
- open("/dev/test1", O_RDONLY|O_LARGEFILE) = 5
- ioctl(5, 0x80041272, 0xbfffe124) = -1 EINVAL (Invalid argument)
- write(2, "mkfs.jfs: can\'t determine device"..., ..._exit(1)
- = ?
- 清單 6 顯示 ioctl 調(diào)用導致用來格式化分區(qū)的 mkfs 程序失敗。ioctl BLKGETSIZE64 失敗。(BLKGET-SIZE64 在調(diào)用 ioctl 的源代碼中定
- 義。) BLKGETSIZE64 ioctl 將被添加到 Linux 中所有的設備,而在這里,邏輯卷管理器還不支持它。因此,如果 BLKGETSIZE64 ioctl 調(diào)用
- 失敗,mkfs 代碼將改為調(diào)用較早的 ioctl 調(diào)用;這使得 mkfs 適用于邏輯卷管理器。
復制代碼 |
|