天天看點

記憶體洩漏檢查工具valgrind的安裝與使用

一、 安裝

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運作得更慢,但有可能追查到未初始化值的根源;