Linux下的調試工具

轉自:http://www.limodev.cn/blog/archives/120

隨着XP的流行,人們越來越注重軟件的前期設計、後期的實現,以及貫穿於其中的測試工作,經過這個過程出來的自然是高質量的軟件。甚至有人聲稱XP 會淘汰調試器!這當然是有一定道理的,然而就目前的現實來看,這還是一種理想。在日常工作中,調試工具還是必不可少的。在Linux下,調試工具並非只有 gdb,還有很多其它調試工具,它們都各有所長,側重方面也有所不同。本文介紹幾種筆者常用的調試工具:

1. mtrace

在linux下開發應用程序,用C/C++語言的居多。內存泄露和內存越界等內存錯誤,無疑是其中最頭疼的問題之一。glibc爲解決內存錯誤提供了兩種方案:

一種是hook內存管理函數。hook內存管理函數後,你可以通過記下內存分配的歷史記錄,在程序終止時查看是否有內存泄露,這樣就可以找出內存泄露的地方了。你也可以通過在所分配內存的首尾寫入特殊的標誌,在釋放內存時檢查該標誌是否被破壞了,這樣就可以達到檢查內存越界問題的目的。

另外一種方法更簡單,glibc已經爲第一種方案提供了默認的實現,你要做的只是在特定的位置調用mtrace/muntrace兩個函數,它們的函數原型如下:

#include

void mtrace(void);

void muntrace(void);

你可能會問,在哪裏調這兩種函數最好?這沒有固定的答案,要視具體情況而定。對於小程序來說,在進入main時調用mtrace,在退出main函數時調用muntrace。對於大型軟件,這樣做可能會記錄過多的信息,分析這些記錄會比較慢,這時可以在你所懷疑代碼的兩端調用。

另外,還需要設置一個環境變量MALLOC_TRACE,它是一個文件名,要保證當前用戶有權限創建和寫入該文件。glibc的內存管理器會把內存分配的歷史信息寫入到MALLOC_TRACE指定的文件中。

程序運行完畢後,使用mtrace工具分析這些內存分配歷史信息,可以查出內存錯誤的位置(mtrace在glibc-utils軟件包裏)。

2. strace

在編程時,檢查函數的返回值是一種好習慣。對於像glibc等標準C的函數,光檢查返回值是不夠的,還需要檢查errno的值。這樣的程序往往顯得冗長,不夠簡潔。同時也可能是出於偷懶的原因,大多數程序裏並沒有做這樣的檢查。

這樣的程序,一旦出現錯誤,用調試器一步一步定位錯誤,然後想法查出錯誤的原因,也是可以的,不過比較麻煩,對調試器來說有些大材小用,不太可取。這時,用strace命令可能會更方便一點。它可以顯示各個系統調用/信號的執行過程和結果。比如文件打開出錯,一眼就看出來了,連錯誤的原因 (errno)都知道。

3. binutil

binutil是一系列的工具,你可能根本不知道它們的存在,但是沒有它們你卻寸步難行。Binutil包括下列工具:

* ld - the GNU linker.
* as - the GNU assembler.
* addr2line - Converts addresses into filenames and line numbers.
* ar - A utility for creating, modifying and extracting from archives.
* c++filt - Filter to demangle encoded C++ symbols.
* gprof - Displays profiling information.
* nlmconv - Converts object code into an NLM.
* nm - Lists symbols from object files.
* objcopy - Copys and translates object files.
* objdump - Displays information from object files.
* ranlib - Generates an index to the contents of an archive.
* readelf - Displays information from any ELF format object file.
* size - Lists the section sizes of an object or archive file.
* strings - Lists printable strings from files.
* strip - Discards symbols.
* windres - A compiler for Windows resource files.

其中部分工具對調試極有幫助,如:

你可以用objdump反彙編,查看目標文件或可執行文件內部信息。

你可以用addr2line把機器地址轉換到代碼對應的位置。

你可以用nm查看目標文件或可執行文件中的各種符號。

你可以用gprof分析各個函數的使用情況,找出性能的瓶頸所在(這需要加編譯選項)。

4. ld-linux

現在加載ELF可執行文件的工作,已經落到ld-linux.so.2頭上了。你可能會問,這與有調試程序有關係嗎?有的。比如,在linux中,共享庫裏所有非static的函數/全局變量都是export的,更糟的是C語言中沒有名字空間這個概念,導致函數名極易衝突。在多個共享庫中,名字衝突引起的BUG是比較難查的。這時,你可以通過設置LD_ DEBUG環境變量,來觀察ld-linux.so加載可執行文件的過程,從中可以得到不少幫助信息。LD_ DEBUG的取值如下:

* libs display library search paths
* reloc display relocation processing
* files display progress for input file
* symbols display symbol table processing
* bindings display information about symbol binding
* versions display version dependencies
* all all previous options combined
* statistics display relocation statistics
* unused determined unused DSOs
* help display this help message and exit

5. gdb

對於真正意義的調試器來說,gdb在linux下是獨一無二的。它有多種包裝,有字符界面的,也有圖形界面的,有單獨運行的,也有集成到IDE中的。gdb功能強大,圖形界面的gdb容易上手一點,但功能無疑受到了一些限制,相信大部分高手還是願意使用字符界面的。Gdb太常用了,這裏不再多說。

6. gcc/boundschecker

相信很多人用過win32下的BoundsChecker(Compuware公司)和Purify(IBM公司)兩個工具吧。它們的功能實在太強大了,絕非能通過重載內存管理函數就可以做到,它們在編譯時插入了自己的調試代碼。

gcc也有個擴展,通過在編譯時插入調試代碼,來實現更強大的檢查功能。當然這要求重新編譯gcc,你可以到 http://sourceforge.net/projects/boundschecking/ 下載gcc的補丁。它的可移植性非常好,筆者曾一個ARM 平臺項目裏使用過,效果不錯。

7. valgrind

最好的東西往往最後才見到。Valgrind是我的最愛,用習慣了,寫的程序不在valgrind下跑一遍,就像沒有寫單元測試程序一樣,有點放心不下。它有BoundsChecker/Purify的功能,而且速度更快。

有點遺憾的是valgrind目前只支持x86平臺,當然,這對大多數情況已經足夠了。

你可以到http://valgrind.org/ 下載最新版本。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章