1. 程式人生 > 其它 >使用Valgrind工具進行C/C++記憶體洩漏檢測

使用Valgrind工具進行C/C++記憶體洩漏檢測

Valgrind memcheck是用於構建動態分析工具的探測框架。它包括一個工具集,每個工具執行某種型別的除錯、分析或類似的任務,以幫助完善你的程式。Valgrind的架構是模組化的,所以可以容易地建立新的工具而又不會擾亂現有的結構。

許多有用的工具被作為標準而提供。

  1.Memcheck是一個記憶體錯誤檢測器。它有助於使你的程式,尤其是那些用C和C++寫的程式,更加準確。

  2.Cachegrind是一個快取和分支預測分析器。它有助於使你的程式執行更快。

  3.Callgrind是一個呼叫圖快取生成分析器。它與Cachegrind的功能有重疊,但也收集Cachegrind不收集的一些資訊。

  4.Helgrind是一個執行緒錯誤檢測器。它有助於使你的多執行緒程式更加準確。

  5.DRD也是一個執行緒錯誤檢測器。它和Helgrind相似,但使用不同的分析技術,所以可能找到不同的問題。

  6.Massif是一個堆分析器。它有助於使你的程式使用更少的記憶體。

  7.DHAT是另一種不同的堆分析器。它有助於理解塊的生命期、塊的使用和佈局的低效等問題。

  8.SGcheck是一個實驗工具,用來檢測堆和全域性陣列的溢位。它的功能和Memcheck互補:SGcheck找到Memcheck無法找到的問題,反之亦然。

  9.BBV是個實驗性質的SimPoint基本塊向量生成器。它對於進行計算機架構的研究和開發很有用處。

  也有一些對大多數使用者沒有用的小工具:Lackey是演示儀器基礎的示例工具;Nulgrind是一個最小化的Valgrind工具,不做分析或者操作,僅用於測試目的。

在這篇文章主要介紹“memcheck”工具。Valgrind memcheck工具的簡單使用方式如下:

valgrind --tool=memcheck ./a.out

從上面的命令可以清楚的看到, 主要的命令是valgrind,而我們想使用的工具是通過'-tool'選項來指定的. 上面的‘a.out’指的是我們想使用memcheck執行的可執行檔案.

該工具可以檢測下列與記憶體相關的問題:

  1)未釋放記憶體的使用

  2)對釋放後記憶體的讀/寫

  3)對已分配記憶體塊尾部的讀/寫

  4)記憶體洩露

  5)不匹配的使用malloc/new/new[] 和 free/delete/delete[]

  6)重複釋放記憶體

注意: 上面列出的並不很全面,但卻包含了能被該工具檢測到的很多普遍的問題。

1 未釋放的記憶體使用

Code :

#include <stdio.h>
#include <stdlib.h> 

int main(void)
{
    char *p; 

    char c = *p; 

    printf("\n [%c]\n",c); 

    return 0;
}

在上面的程式碼中,嘗試使用未初始化的指標 ‘p’。

$ valgrind --tool=memcheck ./val
==2862== Memcheck, a memory error detector
==2862== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2862== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==2862== Command: ./val
==2862==
==2862== Use of uninitialised value of size 8
==2862==    at 0x400530: main (valgrind.c:8)
==2862==

[#]
==2862==
==2862== HEAP SUMMARY:
==2862==     in use at exit: 0 bytes in 0 blocks
==2862==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==2862==
==2862== All heap blocks were freed -- no leaks are possible
==2862==
==2862== For counts of detected and suppressed errors, rerun with: -v
==2862== Use --track-origins=yes to see where uninitialized values come from
==2862== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

從上面的輸出可以看到,Valgrind檢測到了未初始化的變數,然後給出了警告(上面加粗的幾行(譯者注:貌似上面沒有加粗的)).

2 在記憶體被釋放後進行讀/寫

Code :

#include <stdio.h>
#include <stdlib.h> 

int main(void)
{
    char *p = malloc(1);
    *p = 'a'; 

    char c = *p; 

    printf("\n [%c]\n",c); 

    free(p);
    c = *p;
    return 0;
}

上面的程式碼中,我們有一個釋放了記憶體的指標 ‘p’ 然後我們又嘗試利用指標獲取值.

讓我們執行memcheck來看一下Valgrind對這種情況是如何反應的.

$ valgrind --tool=memcheck ./val
==2849== Memcheck, a memory error detector
==2849== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2849== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==2849== Command: ./val
==2849== 

 [a]
==2849== Invalid read of size 1
==2849==    at 0x400603: main (valgrind.c:30)
==2849==  Address 0x51b0040 is 0 bytes inside a block of size 1 free'd
==2849==    at 0x4C270BD: free (vg_replace_malloc.c:366)
==2849==    by 0x4005FE: main (valgrind.c:29)
==2849==
==2849==
==2849== HEAP SUMMARY:
==2849==     in use at exit: 0 bytes in 0 blocks
==2849==   total heap usage: 1 allocs, 1 frees, 1 bytes allocated
==2849==
==2849== All heap blocks were freed -- no leaks are possible
==2849==
==2849== For counts of detected and suppressed errors, rerun with: -v
==2849== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

從上面的輸出內容可以看到,Valgrind檢測到了無效的讀取操作然後輸出了警告‘Invalid read of size 1′。另注,使用gdb來除錯c程式。

3 從已分配記憶體塊的尾部進行讀/寫

Code :

#include <stdio.h>
#include <stdlib.h> 

int main(void)
{
    char *p = malloc(1);
    *p = 'a'; 

    char c = *(p+1); 

    printf("\n [%c]\n",c); 

    free(p);
    return 0;
}

在上面的程式碼中,我們已經為‘p’分配了一個位元組的記憶體,但我們在將值讀取到‘c’中的時候使用的是地址p+1.

現在我們使用Valgrind執行上面的程式碼 :

$ valgrind --tool=memcheck ./val
==2835== Memcheck, a memory error detector
==2835== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2835== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==2835== Command: ./val
==2835==
==2835== Invalid read of size 1
==2835==    at 0x4005D9: main (valgrind.c:25)
==2835==  Address 0x51b0041 is 0 bytes after a block of size 1 alloc'd
==2835==    at 0x4C274A8: malloc (vg_replace_malloc.c:236)
==2835==    by 0x4005C5: main (valgrind.c:22)
==2835== 

 []
==2835==
==2835== HEAP SUMMARY:
==2835==     in use at exit: 0 bytes in 0 blocks
==2835==   total heap usage: 1 allocs, 1 frees, 1 bytes allocated
==2835==
==2835== All heap blocks were freed -- no leaks are possible
==2835==
==2835== For counts of detected and suppressed errors, rerun with: -v
==2835== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

同樣,該工具在這種情況下也檢測到了無效的讀取操作.

4 記憶體洩露

Code:

#include <stdio.h>
#include <stdlib.h> 

int main(void)
{
    char *p = malloc(1);
    *p = 'a'; 

    char c = *p; 

    printf("\n [%c]\n",c); 

    return 0;
}

在這次的程式碼中, 我們申請了一個位元組但是沒有將它釋放.現在讓我們執行Valgrind看看會發生什麼:

$ valgrind --tool=memcheck --leak-check=full ./val
==2888== Memcheck, a memory error detector
==2888== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2888== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==2888== Command: ./val
==2888== 

 [a]
==2888==
==2888== HEAP SUMMARY:
==2888==     in use at exit: 1 bytes in 1 blocks
==2888==   total heap usage: 1 allocs, 0 frees, 1 bytes allocated
==2888==
==2888== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2888==    at 0x4C274A8: malloc (vg_replace_malloc.c:236)
==2888==    by 0x400575: main (valgrind.c:6)
==2888==
==2888== LEAK SUMMARY:
==2888==    definitely lost: 1 bytes in 1 blocks
==2888==    indirectly lost: 0 bytes in 0 blocks
==2888==      possibly lost: 0 bytes in 0 blocks
==2888==    still reachable: 0 bytes in 0 blocks
==2888==         suppressed: 0 bytes in 0 blocks
==2888==
==2888== For counts of detected and suppressed errors, rerun with: -v
==2888== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

輸出行(上面加粗的部分)顯示,該工具能夠檢測到記憶體的洩露.

注意: 在這裡我們增加了一個選項‘–leak-check=full’來得到記憶體洩露的詳細細節.

5 不匹配地使用malloc/new/new[] 和 free/delete/delete[]

Code:

#include <stdio.h>
#include <stdlib.h>
#include<iostream> 

int main(void)
{
    char *p = (char*)malloc(1);
    *p = 'a'; 

    char c = *p; 

    printf("\n [%c]\n",c);
    delete p;
    return 0;
}

上面的程式碼中,我們使用了malloc()來分配記憶體,但是使用了delete操作符來刪除記憶體。

注意 : 使用g++來編譯上面的程式碼,因為delete操作符是在C++中引進的,而要編譯C++需要使用g++。

memcheck執行結果 :

$ valgrind --tool=memcheck --leak-check=full ./val
==2972== Memcheck, a memory error detector
==2972== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2972== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==2972== Command: ./val
==2972== 

 [a]
==2972== Mismatched free() / delete / delete []
==2972==    at 0x4C26DCF: operator delete(void*) (vg_replace_malloc.c:387)
==2972==    by 0x40080B: main (valgrind.c:13)
==2972==  Address 0x595e040 is 0 bytes inside a block of size 1 alloc'd
==2972==    at 0x4C274A8: malloc (vg_replace_malloc.c:236)
==2972==    by 0x4007D5: main (valgrind.c:7)
==2972==
==2972==
==2972== HEAP SUMMARY:
==2972==     in use at exit: 0 bytes in 0 blocks
==2972==   total heap usage: 1 allocs, 1 frees, 1 bytes allocated
==2972==
==2972== All heap blocks were freed -- no leaks are possible
==2972==
==2972== For counts of detected and suppressed errors, rerun with: -v
==2972== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

從上面的輸出可以看到 (加粗的行), Valgrind清楚的說明了‘不匹配的使用了free() / delete / delete []‘

你可以嘗試在測試程式碼中使用'new'和'free'進行組合來看看Valgrind給出的結果是什麼.

6 重複釋放記憶體

Code :

#include <stdio.h>
#include <stdlib.h> 

int main(void)
{
    char *p = (char*)malloc(1);
    *p = 'a'; 

    char c = *p;
    printf("\n [%c]\n",c);
    free(p);
    free(p);
    return 0;
}

在上面的程式碼中, 重複釋放了'p'指向的記憶體。執行memcheck :

$ valgrind --tool=memcheck --leak-check=full ./val
==3167== Memcheck, a memory error detector
==3167== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==3167== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==3167== Command: ./val
==3167== 

 [a]
==3167== Invalid free() / delete / delete[]
==3167==    at 0x4C270BD: free (vg_replace_malloc.c:366)
==3167==    by 0x40060A: main (valgrind.c:12)
==3167==  Address 0x51b0040 is 0 bytes inside a block of size 1 free'd
==3167==    at 0x4C270BD: free (vg_replace_malloc.c:366)
==3167==    by 0x4005FE: main (valgrind.c:11)
==3167==
==3167==
==3167== HEAP SUMMARY:
==3167==     in use at exit: 0 bytes in 0 blocks
==3167==   total heap usage: 1 allocs, 2 frees, 1 bytes allocated
==3167==
==3167== All heap blocks were freed -- no leaks are possible
==3167==
==3167== For counts of detected and suppressed errors, rerun with: -v
==3167== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

從上面的輸出可以看到(加粗的行), 該功能檢測到我們對同一個指標呼叫了兩次釋放記憶體操作。

在本文中,我們把注意力放在了記憶體管理框架Valgrind,然後使用memcheck(Valgrind框架提供的)工具來了解它是如何降低需要經常操作記憶體的程式設計師的負擔的。該工具能夠檢測到很多手動檢測不到的與記憶體相關的問題。

參考:https://www.oschina.net/translate/valgrind-memcheck

本文來自部落格園,作者:Jcpeng_std,轉載請註明原文連結:https://www.cnblogs.com/JCpeng/p/15125638.html