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)
使用 memcpy
, strcpy
, strncpy
, strcat
, strncat
时会出现的 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