1. 程式人生 > >【linux】Valgrind工具集詳解(七):Memcheck(記憶體錯誤檢測器)

【linux】Valgrind工具集詳解(七):Memcheck(記憶體錯誤檢測器)

一、概述

Memcheck是一個記憶體錯誤檢測器。它可以檢測C和C ++程式中常見的以下問題:
1、非法記憶體:如越界、釋放後繼續訪問;
2、使用未初始化的值;
3、釋放記憶體錯誤:如double-free(同一記憶體上執行了兩次free)、或者 malloc、new、new[] 與 free、delete、delete[]錯配使用
4、memcpy函式(或其它相關函式)中src和dst指標重疊;
5、分配函式時,傳遞的size引數非法,如果是一個負數;
6、記憶體洩漏。

像這樣的問題很難通過其他方式找到,經常長時間未被發現,然後造成偶然的,難以診斷的崩潰。

二、Memcheck中錯誤訊息的含義詳解

1、Invalid read of size 4

含義:非法讀取或寫入錯誤。
例子,main.c原始碼如下

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

int main()
{
	int *x = (int *)malloc(sizeof(int)*10);
	int i;
	for(i=0; i<=10; ++i)
	{
		x[i] = i;//當i=10時,越界,非法訪問
	}
}

編譯:gcc -g main.c
記憶體錯誤檢查:valgrind --tool=memcheck ./a.out
錯誤資訊如下

==20979== Memcheck, a memory error detector
==20979== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==20979== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==20979== Command: ./a.out
==20979== Parent PID: 17485
==20979== 
==20979== Invalid write of size 4
==20979==    at 0x400563: main (main.c:10)
==20979==  Address 0x5200068 is 0 bytes after a block of size 40 alloc'd
==20979==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20979==    by 0x40053E: main (main.c:6)
==20979== 
==20979== 
==20979== HEAP SUMMARY:
==20979==     in use at exit: 40 bytes in 1 blocks
==20979==   total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==20979== 
==20979== LEAK SUMMARY:
==20979==    definitely lost: 40 bytes in 1 blocks
==20979==    indirectly lost: 0 bytes in 0 blocks
==20979==      possibly lost: 0 bytes in 0 blocks
==20979==    still reachable: 0 bytes in 0 blocks
==20979==         suppressed: 0 bytes in 0 blocks
==20979== Rerun with --leak-check=full to see details of leaked memory
==20979== 
==20979== For counts of detected and suppressed errors, rerun with: -v
==20979== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
2、Conditional jump or move depends on uninitialised value(s)

含義:使用未初始化的值。
經常會遇到這個錯誤。
例子
main.c原始碼如下

#include <stdio.h>

int main()
{
	int x;
	printf("x = %d\n",x); //此處訪問未初始化的值,錯誤
}

編譯:gcc -g main.c
記憶體錯誤檢查:valgrind --tool=memcheck ./a.out
錯誤資訊如下

==21182== Memcheck, a memory error detector
==21182== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==21182== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==21182== Command: ./a.out
==21182== Parent PID: 17485
==21182== 
==21182== Conditional jump or move depends on uninitialised value(s)
==21182==    at 0x4E814CE: vfprintf (vfprintf.c:1660)
==21182==    by 0x4E8B3D8: printf (printf.c:33)
==21182==    by 0x400548: main (main.c:6)
==21182== 
==21182== Use of uninitialised value of size 8
==21182==    at 0x4E8099B: _itoa_word (_itoa.c:179)
==21182==    by 0x4E84636: vfprintf (vfprintf.c:1660)
==21182==    by 0x4E8B3D8: printf (printf.c:33)
==21182==    by 0x400548: main (main.c:6)
==21182== 
==21182== Conditional jump or move depends on uninitialised value(s)
==21182==    at 0x4E809A5: _itoa_word (_itoa.c:179)
==21182==    by 0x4E84636: vfprintf (vfprintf.c:1660)
==21182==    by 0x4E8B3D8: printf (printf.c:33)
==21182==    by 0x400548: main (main.c:6)
==21182== 
==21182== Conditional jump or move depends on uninitialised value(s)
==21182==    at 0x4E84682: vfprintf (vfprintf.c:1660)
==21182==    by 0x4E8B3D8: printf (printf.c:33)
==21182==    by 0x400548: main (main.c:6)
==21182== 
==21182== Conditional jump or move depends on uninitialised value(s)
==21182==    at 0x4E81599: vfprintf (vfprintf.c:1660)
==21182==    by 0x4E8B3D8: printf (printf.c:33)
==21182==    by 0x400548: main (main.c:6)
==21182== 
==21182== Conditional jump or move depends on uninitialised value(s)
==21182==    at 0x4E8161C: vfprintf (vfprintf.c:1660)
==21182==    by 0x4E8B3D8: printf (printf.c:33)
==21182==    by 0x400548: main (main.c:6)
==21182== 
==21182== 
==21182== HEAP SUMMARY:
==21182==     in use at exit: 0 bytes in 0 blocks
==21182==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==21182== 
==21182== All heap blocks were freed -- no leaks are possible
==21182== 
==21182== For counts of detected and suppressed errors, rerun with: -v
==21182== Use --track-origins=yes to see where uninitialised values come from
==21182== ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 0 from 0)

可以加上選項 “–track-origins=yes”來查詢,未初始化的來源,但會使Memcheck執行的更慢;
如加上選項 “–track-origins=yes”後列印資訊如下

==21210== Memcheck, a memory error detector
==21210== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==21210== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==21210== Command: ./a.out
==21210== Parent PID: 17485
==21210== 
==21210== Conditional jump or move depends on uninitialised value(s)
==21210==    at 0x4E814CE: vfprintf (vfprintf.c:1660)
==21210==    by 0x4E8B3D8: printf (printf.c:33)
==21210==    by 0x400548: main (main.c:6)
==21210==  Uninitialised value was created by a stack allocation
==21210==    at 0x40052D: main (main.c:4)
==21210== 
==21210== Use of uninitialised value of size 8
==21210==    at 0x4E8099B: _itoa_word (_itoa.c:179)
==21210==    by 0x4E84636: vfprintf (vfprintf.c:1660)
==21210==    by 0x4E8B3D8: printf (printf.c:33)
==21210==    by 0x400548: main (main.c:6)
==21210==  Uninitialised value was created by a stack allocation
==21210==    at 0x40052D: main (main.c:4)
==21210== 
==21210== Conditional jump or move depends on uninitialised value(s)
==21210==    at 0x4E809A5: _itoa_word (_itoa.c:179)
==21210==    by 0x4E84636: vfprintf (vfprintf.c:1660)
==21210==    by 0x4E8B3D8: printf (printf.c:33)
==21210==    by 0x400548: main (main.c:6)
==21210==  Uninitialised value was created by a stack allocation
==21210==    at 0x40052D: main (main.c:4)
==21210== 
==21210== Conditional jump or move depends on uninitialised value(s)
==21210==    at 0x4E84682: vfprintf (vfprintf.c:1660)
==21210==    by 0x4E8B3D8: printf (printf.c:33)
==21210==    by 0x400548: main (main.c:6)
==21210==  Uninitialised value was created by a stack allocation
==21210==    at 0x40052D: main (main.c:4)
==21210== 
==21210== Conditional jump or move depends on uninitialised value(s)
==21210==    at 0x4E81599: vfprintf (vfprintf.c:1660)
==21210==    by 0x4E8B3D8: printf (printf.c:33)
==21210==    by 0x400548: main (main.c:6)
==21210==  Uninitialised value was created by a stack allocation
==21210==    at 0x40052D: main (main.c:4)
==21210== 
==21210== Conditional jump or move depends on uninitialised value(s)
==21210==    at 0x4E8161C: vfprintf (vfprintf.c:1660)
==21210==    by 0x4E8B3D8: printf (printf.c:33)
==21210==    by 0x400548: main (main.c:6)
==21210==  Uninitialised value was created by a stack allocation
==21210==    at 0x40052D: main (main.c:4)
==21210== 
==21210== 
==21210== HEAP SUMMARY:
==21210==     in use at exit: 0 bytes in 0 blocks
==21210==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==21210== 
==21210== All heap blocks were freed -- no leaks are possible
==21210== 
==21210== For counts of detected and suppressed errors, rerun with: -v
==21210== ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 0 from 0)

其中,指出未初始化的位置資訊是:
==21210== Uninitialised value was created by a stack allocation
==21210== at 0x40052D: main (main.c:4)

3、Syscall param * uninitialised byte(s)

Syscall param write(buf) points to uninitialised byte(s)
Syscall param write(buf) points to uninitialised byte(s)
含義:在系統呼叫中使用未初始化或不可定址的值。
Memcheck檢查系統呼叫的所有引數:
它會檢查所有直接引數本身,無論它們是否已初始化。
此外,如果系統呼叫需要從程式提供的緩衝區中讀取,則Memcheck會檢查整個緩衝區是否可定址並初始化其內容。
此外,如果系統呼叫需要寫入使用者提供的緩衝區,Memcheck會檢查緩衝區是否可定址。
系統呼叫後,Memcheck會更新其跟蹤資訊,以準確反映系統呼叫導致的記憶體狀態的任何變化。
例子
原始碼main.c如下

  #include <stdlib.h>
  #include <unistd.h>
  int main( void )
  {
    char* arr  = malloc(10);
    int*  arr2 = malloc(sizeof(int));
    write( 1 /* stdout */, arr, 10 );
    exit(arr2[0]);
  }

編譯:gcc -g main.c
記憶體檢查:valgrind --tool=memcheck ./a.out
錯誤列印資訊如下

==21355== Memcheck, a memory error detector
==21355== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==21355== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==21355== Command: ./a.out
==21355== Parent PID: 17485
==21355== 
==21355== Syscall param write(buf) points to uninitialised byte(s)
==21355==    at 0x4F263C0: __write_nocancel (syscall-template.S:81)
==21355==    by 0x4005F6: main (main.c:7)
==21355==  Address 0x5200040 is 0 bytes inside a block of size 10 alloc'd
==21355==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21355==    by 0x4005CE: main (main.c:5)
==21355==  Uninitialised value was created by a heap allocation
==21355==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21355==    by 0x4005CE: main (main.c:5)
==21355== 
==21355== Syscall param exit_group(status) contains uninitialised byte(s)
==21355==    at 0x4EFC109: _Exit (_exit.c:32)
==21355==    by 0x4E7316A: __run_exit_handlers (exit.c:97)
==21355==    by 0x4E731F4: exit (exit.c:104)
==21355==    by 0x400603: main (main.c:8)
==21355==  Uninitialised value was created by a heap allocation
==21355==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21355==    by 0x4005DC: main (main.c:6)
==21355== 
==21355== 
==21355== HEAP SUMMARY:
==21355==     in use at exit: 14 bytes in 2 blocks
==21355==   total heap usage: 2 allocs, 0 frees, 14 bytes allocated
==21355== 
==21355== LEAK SUMMARY:
==21355==    definitely lost: 0 bytes in 0 blocks
==21355==    indirectly lost: 0 bytes in 0 blocks
==21355==      possibly lost: 0 bytes in 0 blocks
==21355==    still reachable: 14 bytes in 2 blocks
==21355==         suppressed: 0 bytes in 0 blocks
==21355== Rerun with --leak-check=full to see details of leaked memory
==21355== 
==21355== For counts of detected and suppressed errors, rerun with: -v
==21355== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
4、 Invalid free() / delete / delete[] / realloc()

含義:非法釋放。
例子
原始碼main.c如下:

#include <stdlib.h>
#include <unistd.h>
int main( void )
{
	char* arr  = malloc(10);
	free(arr);
	free(arr);
	return 0;
}

編譯:gcc -g main.c
記憶體檢查:valgrind --tool=memcheck ./a.out
錯誤列印資訊如下

==21442== Memcheck, a memory error detector
==21442== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==21442== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==21442== Command: ./a.out
==21442== Parent PID: 17485
==21442== 
==21442== Invalid free() / delete / delete[] / realloc()
==21442==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21442==    by 0x4005AA: main (main.c:7)
==21442==  Address 0x5200040 is 0 bytes inside a block of size 10 free'd
==21442==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21442==    by 0x40059E: main (main.c:6)
==21442== 
==21442== 
==21442== HEAP SUMMARY:
==21442==     in use at exit: 0 bytes in 0 blocks
==21442==   total heap usage: 1 allocs, 2 frees, 10 bytes allocated
==21442== 
==21442== All heap blocks were freed -- no leaks are possible
==21442== 
==21442== For counts of detected and suppressed errors, rerun with: -v
==21442== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
5、Mismatched free() / delete / delete []

含義:分配記憶體和釋放記憶體方法不匹配。
例子:使用malloc分配記憶體,然後使用delete釋放就會報這個錯誤。
原始碼main.c

#include <stdlib.h>
#include <unistd.h>
int main( void )
{
	char* arr  = (char*)malloc(10);
	delete arr;
	return 0;
}

使用G++編譯:g++ -g main.c
記憶體檢查:valgrind --tool=memcheck ./a.out
錯誤列印資訊如下

==21579== Memcheck, a memory error detector
==21579== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==21579== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==21579== Command: ./a.out
==21579== Parent PID: 17485
==21579== 
==21579== Mismatched free() / delete / delete []
==21579==    at 0x4C2C2BC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21579==    by 0x40066E: main (main.c:6)
==21579==  Address 0x5a20040 is 0 bytes inside a block of size 10 alloc'd
==21579==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21579==    by 0x40065E: main (main.c:5)
==21579== 
==21579== 
==21579== HEAP SUMMARY:
==21579==     in use at exit: 0 bytes in 0 blocks
==21579==   total heap usage: 1 allocs, 1 frees, 10 bytes allocated
==21579== 
==21579== All heap blocks were freed -- no leaks are possible
==21579== 
==21579== For counts of detected and suppressed errors, rerun with: -v
==21579== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
6、Source and destination overlap in memcpy

含義:下面的C庫函式從一個儲存器塊複製一些資料到另一個memcpy、 strcpy、strncpy、strcat、strncat它們src和 dst指標指向的塊不允許重疊。POSIX標準的措辭如下:“如果在重疊的物件之間進行復制,則行為未定義。” 因此,Memcheck會檢查這一點。
例子
原始碼main.c如下:

#include<string.h>

int main( void )
{
	char a[12] = {'h','e','l', 'l','o','\0'};
	memcpy(a+3,a,6);
	return 0;
}

編譯:gcc -g main.c
記憶體檢查:valgrind --tool=memcheck ./a.out
錯誤列印資訊如下

==22017== Memcheck, a memory error detector
==22017== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==22017== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==22017== Command: ./a.out
==22017== 
==22017== Source and destination overlap in memcpy(0xffefffbc3, 0xffefffbc0, 6)
==22017==    at 0x4C2F71C: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==22017==    by 0x400613: main (main.c:6)
==22017== 
==22017== 
==22017== HEAP SUMMARY:
==22017==     in use at exit: 0 bytes in 0 blocks
==22017==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==22017== 
==22017== All heap blocks were freed -- no leaks are possible
==22017== 
==22017== For counts of detected and suppressed errors, rerun with: -v
==22017== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
疑問,當我用memcpy(a, a, 6);時,沒有報錯,哪位大神給解釋下?
7、Argument ‘size’ of function malloc has a fishy (possibly negative) value:

含義:可疑的引數值。
所有記憶體分配函式都使用一個引數來指定應分配的記憶體塊的大小。顯然,請求的大小應該是非負值,並且通常不會過大。例如,在64位計算機上,分配請求的大小超過2 ** 63位元組或者是負值。這樣的值被稱為“可疑的值”。
下列函式中的size引數將被檢查: malloc、calloc、 realloc、memalign、new、 new []、 __builtin_new、 __builtin_vec_new,對於calloc 兩個引數都在檢查中。
例子
原始碼main.c如下

#include <stdlib.h>
#include <unistd.h>
int main( void )
{
	char* arr  = (char *)malloc(-1);
	free(arr);
	return 0;
}

編譯:gcc -g main.c
記憶體檢查:valgrind --tool=memcheck ./a.out
錯誤列印資訊如下

==22177== Memcheck, a memory error detector
==22177== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==22177== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==22177== Command: ./a.out
==22177== 
==22177== Argument 'size' of function malloc has a fishy (possibly negative) value: -1
==22177==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==22177==    by 0x400590: main (main.c:5)
==22177== 
==22177== 
==22177== HEAP SUMMARY:
==22177==     in use at exit: 0 bytes in 0 blocks
==22177==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==22177== 
==22177== All heap blocks were freed -- no leaks are possible
==22177== 
==22177== For counts of detected and suppressed errors, rerun with: -v
==22177== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
8、LEAK SUMMARY:

含義:記憶體洩漏檢查。
Memcheck跟蹤malloc/new函式對應的free/delete等的呼叫,因此,當程式退出時,它知道哪些塊未被釋放。
此功能需要設定引數–leak-check=summary或full。
例子
原始碼main.c如下

#include <stdlib.h>
#include <unistd.h>
int main( void )
{
	char* arr  = (char *)malloc(4);
	//free(arr);//此處沒有釋放
	return 0;
}

編譯:gcc -g main.c
記憶體檢查:valgrind --tool=memcheck --leak-check=full ./a.out
錯誤列印資訊如下

==22289== Memcheck, a memory error detector
==22289== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==22289== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==22289== Command: ./a.out
==22289== 
==22289== 
==22289== HEAP SUMMARY:
==22289==     in use at exit: 4 bytes in 1 blocks
==22289==   total heap usage: 1 allocs, 0 frees, 4 bytes allocated
==22289== 
==22289== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==22289==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==22289==    by 0x40053E: main (main.c:5)
==22289== 
==22289== LEAK SUMMARY:
==22289==    definitely lost: 4 bytes in 1 blocks
==22289==    indirectly lost: 0 bytes in 0 blocks
==22289==      possibly lost: 0 bytes in 0 blocks
==22289==    still reachable: 0 bytes in 0 blocks
==22289==         suppressed: 0 bytes in 0 blocks
==22289== 
==22289== For counts of detected and suppressed errors, rerun with: -v
==22289== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

LEAK SUMMARY:記憶體洩漏總結(分類)
definitely lost: 4 bytes in 1 blocks:絕對丟失,這種情況應該由程式設計師來解決,下面幾種情況,可以當作參考
indirectly lost: 0 bytes in 0 blocks:間接丟失
possibly lost: 0 bytes in 0 blocks:可能丟失
still reachable: 0 bytes in 0 blocks:仍然可以訪問
suppressed: 0 bytes in 0 blocks:抑制錯誤中的丟失