使用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