Valgrind 检测内存泄露教程

系统环境

  • Ubuntu 22.10 kinetic

简单介绍

Valgrind是一套 Linux 下的 Debug 工具,常用于检测程序内存泄漏,非法读写等问题。

维基百科 - Valgrind - Wikipedia

本文主要介绍 Memcheck,可以用于检查程序的内存泄漏情况。

安装 Valgrind

官方下载地址 - Valgrind Download

sudo apt update && apt full-upgrade -y
sudo apt install valgrind -y
valgrind --version

使用方法

valgrind ./<filename>
valgrind --leak-check=<no|summary|yes|full> [default: summary]

常见错误

1.非法读写错误

#include <stdio.h>
int main(){
	int *ptr = 0;
	*ptr = 0;
	return 0;
}
gcc -g -O0 test.c -o test  
./test
[1]    7453 segmentation fault (core dumped)  ./test

valgrind ./test
==7484== Memcheck, a memory error detector
==7484== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7484== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==7484== Command: ./test
==7484== 
==7484== Invalid write of size 4
==7484==    at 0x10913D: main (test.c:4)
==7484==  Address 0x0 is not stack'd, malloc'd or (recently) free'd

2.访问未初始化值

#include <stdio.h>
int main(){
	  int x;
	  printf ("x = %d\n", x);
}
gcc -g -O0 test.c -o test  
./test
x = 32695 # garbage value

valgrind ./test 
==8056== Memcheck, a memory error detector
==8056== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8056== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==8056== Command: ./test
==8056== 
==8056== Conditional jump or move depends on uninitialised value(s)
==8056==    at 0x48C80D3: __vfprintf_internal (vfprintf-process-arg.c:58)
==8056==    by 0x48BC7AE: printf (printf.c:33)
==8056==    by 0x10916D: main (test.c:5)
...

3 在系统调用中使用未初始化或不可寻址的值

#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 -O0 test.c -o test  
./test

valgrind ./test
==8321== Memcheck, a memory error detector
==8321== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8321== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==8321== Command: ./test
==8321== 
==8321== Syscall param write(buf) points to uninitialised byte(s)
==8321==    at 0x4973D94: write (write.c:26)
==8321==    by 0x1091C6: main (test.c:7)
==8321==  Address 0x4a6f040 is 0 bytes inside a block of size 10 alloc'd
==8321==    at 0x4844899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==8321==    by 0x10919E: main (test.c:5)
==8321== 
==8321== Syscall param exit_group(status) contains uninitialised byte(s)
==8321==    at 0x4949DF1: _Exit (_exit.c:30)
==8321==    by 0x48A5561: __run_exit_handlers (exit.c:136)
==8321==    by 0x48A561F: exit (exit.c:143)
==8321==    by 0x1091D3: main (test.c:8)

4.非法释放内存

#include <stdlib.h>
#include <unistd.h>
int main()
{
    int *ptr = malloc(sizeof(int));
    free(ptr);  // This is a legal free
    free(ptr);  // This is an illegal free
    return 0;
}
gcc -g -O0 test.c -o test  
./test
free(): double free detected in tcache 2
[1]    8614 IOT instruction (core dumped)  ./test

valgrind ./test                                        
==8670== Memcheck, a memory error detector
==8670== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8670== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==8670== Command: ./test
==8670== 
==8670== Invalid free() / delete / delete[] / realloc()
==8670==    at 0x484727F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==8670==    by 0x10919A: main (test.c:7)
==8670==  Address 0x4a6f040 is 0 bytes inside a block of size 4 free'd
==8670==    at 0x484727F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==8670==    by 0x10918E: main (test.c:6)
==8670==  Block was alloc'd at
==8670==    at 0x4844899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==8670==    by 0x10917E: main (test.c:5)

5.操作符不匹配

如果用 malloc/calloc/realloc/valloc/memalign 分配,则必须使用 free 释放。

如果用 new 分配,则必须用 delete 释放。

如果用 new[] 分配,则必须用 delete[] 释放。

需要注意的是在 Linux 中运行这样的程序是不会有任何问题, 但可能会在其他的平台上崩溃。

#include<stdlib.h>

using namespace std;

int main(){
        int *ptr = (int*)malloc(10 * sizeof(int));
        delete(ptr);
        return 0;
}
g++ -g -O0 test.cpp -o test  
./test

valgrind ./test
==9189== Memcheck, a memory error detector
==9189== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==9189== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==9189== Command: ./test
==9189== 
==9189== Mismatched free() / delete / delete []
==9189==    at 0x4847B6F: operator delete(void*, unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==9189==    by 0x109198: main (test.cpp:7)
==9189==  Address 0x4db5c80 is 0 bytes inside a block of size 40 alloc'd
==9189==    at 0x4844899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==9189==    by 0x10917E: main (test.cpp:6)

6.内存重叠错误

(Memory Overlapping Error)

使用 memcpystrcpystrncpystrcatstrncat 时会出现的 memory overlap 错误

#include <stdio.h>
#include <string.h>

int main() {
    // C memory overlap example with strcpy
    char a[12] = "Hello World";
    char b[10];
    
    strcpy(b, a);
    printf("b: %s\n", b);
    
    // 使用 strncpy 来避免 overlapping
    // strncpy(b, a, sizeof(b));
    // b[sizeof(b)] = '\0';
    return 0;
}
gcc -g -O0 test.c -o test  
./test
b: Hello World
☁  Desktop  valgrind ./test
==12013== Memcheck, a memory error detector
==12013== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12013== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==12013== Command: ./test
==12013== 
==12013== Source and destination overlap in strcpy(0x1fff000012, 0x1fff00001c)
==12013==    at 0x484AF00: strcpy (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==12013==    by 0x1091CB: main (test.c:9)
==12013== 
b: Hello World
==12013== 
==12013== HEAP SUMMARY:
==12013==     in use at exit: 0 bytes in 0 blocks
==12013==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==12013== 
==12013== All heap blocks were freed -- no leaks are possible
==12013== 
==12013== For lists of detected and suppressed errors, rerun with: -s
==12013== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

7.可疑的参数值

(Fishy argument values)

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

int main() {
    char* str = (char*)malloc(9223372036854775808 * sizeof(char));
    return 0;
}
gcc -g -O0 test.c -o test  
./test
test.c: In function ‘main’:
test.c:5:31: warning: integer constant is so large that it is unsigned
    5 |     char* str = (char*)malloc(9223372036854775808 * sizeof(char));
      |                               ^~~~~~~~~~~~~~~~~~~
test.c:5:24: warning: argument 1 value ‘9223372036854775808’ exceeds maximum object size 9223372036854775807 [-Walloc-size-larger-than=]
    5 |     char* str = (char*)malloc(9223372036854775808 * sizeof(char));
      |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from test.c:2:
/usr/include/stdlib.h:553:14: note: in a call to allocation function ‘malloc’ declared here
  553 | extern void *malloc (size_t __size) __THROW __attribute_malloc__
      |              ^~~~~~

valgrind ./test
^[OH==13025== Memcheck, a memory error detector
==13025== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13025== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==13025== Command: ./test
==13025== 
==13025== Argument 'size' of function malloc has a fishy (possibly negative) value: -9223372036854775808
==13025==    at 0x4844899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==13025==    by 0x109166: main (test.c:5)
==13025== 
==13025== 
==13025== HEAP SUMMARY:
==13025==     in use at exit: 0 bytes in 0 blocks
==13025==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==13025== 
==13025== All heap blocks were freed -- no leaks are possible
==13025== 
==13025== For lists of detected and suppressed errors, rerun with: -s
==13025== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0

8.内存泄漏检测

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

int main() {
    char* str = (char*)malloc(9223372036854775808 * sizeof(char));
    return 0;
}
gcc -g -O0 test.c -o test 

valgrind --leak-check=full ./test
==13171== Memcheck, a memory error detector
==13171== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13171== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==13171== Command: ./test
==13171== 
==13171== 
==13171== HEAP SUMMARY:
==13171==     in use at exit: 11 bytes in 2 blocks
==13171==   total heap usage: 2 allocs, 0 frees, 11 bytes allocated
==13171== 
==13171== 1 bytes in 1 blocks are definitely lost in loss record 1 of 2
==13171==    at 0x4844899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==13171==    by 0x10915E: main (test.c:5)
==13171== 
==13171== 10 bytes in 1 blocks are definitely lost in loss record 2 of 2
==13171==    at 0x4844899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==13171==    by 0x10916C: main (test.c:6)
==13171== 
==13171== LEAK SUMMARY:
==13171==    definitely lost: 11 bytes in 2 blocks
==13171==    indirectly lost: 0 bytes in 0 blocks
==13171==      possibly lost: 0 bytes in 0 blocks
==13171==    still reachable: 0 bytes in 0 blocks
==13171==         suppressed: 0 bytes in 0 blocks
==13171== 
==13171== For lists of detected and suppressed errors, rerun with: -s
==13171== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

参考资料

Memcheck: a memory error detector


最后修改于 2023-02-03


感谢您的支持 :D