使用Valgrind Memcheck檢查內存

使用Valgrind Memcheck

memcheck工具的使用方式如下:

valgrind --tool=memcheck ./a.out

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

該工具可以檢測下列與內存相關的問題 :

·       未釋放內存的使用

·       對釋放後內存的讀/寫

·       對已分配內存塊尾部的讀/寫

·       內存泄露

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

·       重複釋放內存

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

讓我們一個一個地對上面的場景進行討論:

注意: 下面討論的所有測試代碼都應該使用gcc並且加上-g選項(用來在memcheck的輸出中生成行號)進行編譯. 就想我們之前討論過的 C程序被編譯成可執行文件, 它需要經歷四個不同的階段.

1. 使用未初始化的內存

Code :

#include<stdio.h>
#include<stdlib.h>
 
intmain(void)
{
    char *p; 
 
    char c = *p; 
 
    printf("\n [%c]\n",c); 
 
    return0;
}

在上面的代碼中,我們嘗試使用未初始化的指針 ‘p’.

讓我們運行Memcheck來看下結果.

$ 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>
 
intmain(void)
{
    char *p = malloc(1);
    *p = 'a'; 
 
    char c = *p; 
 
    printf("\n [%c]\n",c); 
 
    free(p);
    c = *p;
    return0;
}

上面的代碼中,我們有一個釋放了內存的指針 ‘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 1free'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>
 
intmain(void)
{
    char *p = malloc(1);
    *p = 'a'; 
 
    char c = *(p+1); 
 
    printf("\n [%c]\n",c); 
 
    free(p);
    return0;
}

在上面的代碼中,我們已經爲‘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>
 
intmain(void)
{
    char *p = malloc(1);
    *p = 'a'; 
 
    char c = *p; 
 
    printf("\n [%c]\n",c); 
 
    return0;
}

在這次的代碼中, 我們申請了一個字節但是沒有將它釋放.現在讓我們運行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>
 
intmain(void)
{
    char *p = (char*)malloc(1);
    *p = 'a'; 
 
    char c = *p; 
 
    printf("\n [%c]\n",c);
    delete p;
    return0;
}

上面的代碼中,我們使用了malloc()來分配內存,但是使用了delete操作符來刪除內存.

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

讓我們運行來看一下 :

$ 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: operatordelete(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>
 
intmain(void)
{
    char *p = (char*)malloc(1);
    *p = 'a'; 
 
    char c = *p;
    printf("\n [%c]\n",c);
    free(p);
    free(p);
    return0;
}

在上面的代碼中, 我們兩次釋放了'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 1free'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框架提供的)工具來了解它是如何降低需要經常操作內存的程序員的負擔的. 該工具能夠檢測到很多手動檢測不到的與內存相關的問題

ubuntu下安裝 valgrind

apt-get install valgrind

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