內存泄露檢查工具valgrind memcheck工具的使用

前段時間,客戶現場的一臺服務器上跑的應用佔用內存不停的增加,最後把系統內存全部耗完,被系統kill掉了,查看日誌報out of memory。於是火急火燎的開始分析內存泄露的可能,差不多一個月左右的時間,都在上面耗着,一直找不到內存泄露的地方。


雖然還沒有找到內存泄露的具體原因,但是在網上找到了一個好的內存泄露分析工具,特做記錄。


一、 安裝
1. autoconf
# wget http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz 
# tar -zxvf autoconf-2.69.tar.gz 
# cd autoconf-2.69
# ./configure
# make; make install

2. automake
# wget http://ftp.gnu.org/gnu/automake/automake-1.14.tar.gz
# tar -zxvf automake-1.14.tar.gz 
# cd automake-1.14
# ./bootstrap.sh
# ./configure
# make; make install

3. valgrind
# wget http://valgrind.org/downloads/valgrind-3.9.0.tar.bz2
# tar -jxvf valgrind-3.9.0.tar.bz2
# cd valgrind-3.9.0
# ./autogen.sh
# ./configure
# make; make install

二、快速使用指南
1. 簡介
Valgrind是一款用於內存調試、內存泄漏檢測以及性能分析的軟件工具套裝。
它最流行的工具是Memcheck, 它能檢測C/C++中大部分的內存相關的錯誤。

2. 準備要檢查的程序
程序編譯時使用 “-g”參數,以添加調試信息,這樣Memcheck的錯誤消息可以精確到行;
編譯時使用“-O0”也有必要,只是速度會很慢,“-O1”可能會導致Memecheck的錯誤消息不正確;

3. 在Memcheck下運行程序: 
如果你的程序的運行命令如下:
  myprog arg1 arg2

則使用如下命令行:
  valgrind --leak-check=yes myprog arg1 arg2

Memcheck是valgrind默認的工具,"--leak-check"選項開啓了詳細內存泄漏檢測器;
這時程序會比平時運行得慢很多(如,慢20~30倍),並且會消耗更多的內存;
程序運行結束後,或你用“CTRL+C”中止程序後,Memcheck將會列出檢測到的內存出錯和泄漏的信息;

4. Memcheck輸出信息示例說明
下面是一個很簡單的示例C程序,並帶有一個內存錯誤和一個內存泄漏;
文件名爲:a.c  
1   #include <stdlib.h>

3   void f(void)
4   {
5      int* x = malloc(10 * sizeof(int));
6      x[10] = 0;                          // problem 1: heap block overrun
7   }                                            // problem 2: memory leak -- x not freed
8
9   int main(void)
10 {
11   f();
12   return 0;
13 }

Most error messages look like the following, which describes problem 1, the heap block overrun:
錯誤消息如下,描述了問題1, 內存寫越界
==19182== Invalid write of size 4
==19182== at 0x804838F: f (example.c:6)
==19182== by 0x80483AB: main (example.c:11)
==19182== Address 0x1BA45050 is 0 bytes after a block of size 40 alloc’d
==19182== at 0x1B8FF5CD: malloc (vg_replace_malloc.c:130)
==19182== by 0x8048385: f (example.c:5)
==19182== by 0x80483AB: main (example.c:11)

需要注意的信息有:
. 每個錯誤都會有多行信息說明,需要仔細閱讀.
. 19182是進程ID, 通常不重要;
. 第一行("Invalid write..."),說明了是哪種類型的錯誤;
  在這裏,是程序寫越界了
. 第一行之下的行都是函數調用棧跟蹤,說明了問題的發生的地方;
  函數調用棧可以很大,如果還使用了C++ STL時,更容易使人混亂;從底向上讀有助於理解;
  如果函數調用棧不夠大,可以使用--num-callers選項來擴大;
. 代碼地址(eg. 0x804838F)通常不用關心,有時只是在跟蹤怪異的bug時有用;
. 有些錯誤信息有第二個組成部分,包括內存地址的描述等;
  在這個例子中,這部分的信息描述了寫內存位於第五行的塊分配函數malloc()之後.

按照報告的順序修正錯誤很有必要,因爲後面的錯誤可能是前面的錯誤造成的;
如果不這麼做,會導致Memcheck的使用變得很困難;

內存泄漏的信息通常如下:
==19182== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==19182== at 0x1B8FF5CD: malloc (vg_replace_malloc.c:130)
==19182== by 0x8048385: f (a.c:5)
==19182== by 0x80483AB: main (a.c:11)

函數調用棧說明了泄漏的內存是在哪分配的;
但是,Memcheck並不能說明爲什麼內存泄漏的(忽略"vg_replace_malloc.c",它只是一個實現細節);
有多種類型的內存泄漏,最重要是如下:
. "definitely lost": 你的程序內存泄漏了  -- 需要解決它;
. "probably lost":   你的程序內存泄漏了,可能需要解決;
  除非你對指針做了一些特殊的處理(如將其指向堆的中部)

Memcheck同樣會報告未初始化值的使用,
對於這種情況,通常的消息是"Conditional jump or move depends on uninitialised value(s)"
追蹤這種錯誤的根源可能很難;
可以嘗試使用 “--track-origins=yes”選項來輸出額外的信息;
但這會使Memcheck運行得更慢,但有可能追查到未初始化值的根源;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章