Linux下的除錯工具

李先靜發表於2020-04-06

Linux下的除錯工具

 

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

 

1.         mtrace

linux下開發應用程式,用C/C++語言的居多。記憶體洩露和記憶體越界等記憶體錯誤,無疑是其中最頭疼的問題之一。glibc為解決記憶體錯誤提供了兩種方案:

 

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

 

另外一種方法更簡單,glibc已經為第一種方案提供了預設的實現,你要做的只是在特定的位置呼叫mtrace/muntrace兩個函式,它們的函式原型如下:

       #include <mcheck.h>

       void mtrace(void);

void muntrace(void);

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

 

另外,還需要設定一個環境變數MALLOC_TRACE,它是一個檔名,要保證當前使用者有許可權建立和寫入該檔案。glibc的記憶體管理器會把記憶體分配的歷史資訊寫入到MALLOC_TRACE指定的檔案中。

 

程式執行完畢後,使用mtrace工具分析這些記憶體分配歷史資訊,可以查出記憶體錯誤的位置(mtraceglibc-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

對於真正意義的偵錯程式來說,gdblinux下是獨一無二的。它有多種包裝,有字元介面的,也有圖形介面的,有單獨執行的,也有整合到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/ 下載最新版本。

 

 

相關文章