linux下使用valgrind排查内存泄漏

安装

yum install valgrind -y

说明

1)概念

Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。

Valgrind包括memcheck、Callgrind、Cachegrind、Helgrind、Massif等工具。

memcheck用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc()/free()/new/delete 的调用都会被捕获。

2)命令行

valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --undef-value-errors=no --log-file=log ./可执行程序名
  • –tool=memcheck:使用 memcheck 工具检测内存错误,包括使用未初始化的变量、读写越界等;
  • –leak-check=full:全面检测内存泄漏,不仅仅检测未释放的内存,还会检测处理时出现的一些问题;
  • –show-leak-kinds=all:显示所有的内存泄漏信息;
  • –undef-value-errors=no:不检查未定义的值错误;
  • –log-file=log:将日志信息输出到 log 文件中;

几种内存问题

申请内存多次释放

示例

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;

void MyTest()
{
	char* p = (char*)malloc(sizeof(char)*10);
	
	delete[] p;

	cout << "MyTest finished" << endl;

	delete[] p;
}

int main()
{
	MyTest();

	return 0;
}

测试

执行valgrind --leak-check=yes ./testValgrind查看结果。

[root@localhost debug]# valgrind --leak-check=yes ./testValgrind
==5532== Memcheck, a memory error detector
==5532== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5532== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==5532== Command: ./testValgrind
==5532== 
==5532== Mismatched free() / delete / delete []
==5532==    at 0x4C2BB8F: operator delete[](void*) (vg_replace_malloc.c:651)
==5532==    by 0x400A1A: MyTest() (testValgrind.cpp:10)
==5532==    by 0x400A64: main (testValgrind.cpp:19)
==5532==  Address 0x6b529d0 is 0 bytes inside a block of size 10 alloc'd
==5532==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==5532==    by 0x400A03: MyTest() (testValgrind.cpp:8)
==5532==    by 0x400A64: main (testValgrind.cpp:19)
==5532== 
MyTest finished
==5532== Invalid free() / delete / delete[] / realloc()
==5532==    at 0x4C2BB8F: operator delete[](void*) (vg_replace_malloc.c:651)
==5532==    by 0x400A58: MyTest() (testValgrind.cpp:14)
==5532==    by 0x400A64: main (testValgrind.cpp:19)
==5532==  Address 0x6b529d0 is 0 bytes inside a block of size 10 free'd
==5532==    at 0x4C2BB8F: operator delete[](void*) (vg_replace_malloc.c:651)
==5532==    by 0x400A1A: MyTest() (testValgrind.cpp:10)
==5532==    by 0x400A64: main (testValgrind.cpp:19)
==5532==  Block was alloc'd at
==5532==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==5532==    by 0x400A03: MyTest() (testValgrind.cpp:8)
==5532==    by 0x400A64: main (testValgrind.cpp:19)
==5532== 
==5532== 
==5532== HEAP SUMMARY:
==5532==     in use at exit: 32 bytes in 1 blocks
==5532==   total heap usage: 22 allocs, 22 frees, 136,124 bytes allocated
==5532== 
==5532== LEAK SUMMARY:
==5532==    definitely lost: 0 bytes in 0 blocks
==5532==    indirectly lost: 0 bytes in 0 blocks
==5532==      possibly lost: 0 bytes in 0 blocks
==5532==    still reachable: 32 bytes in 1 blocks
==5532==         suppressed: 0 bytes in 0 blocks
==5532== Reachable blocks (those to which a pointer was found) are not shown.
==5532== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==5532== 
==5532== For lists of detected and suppressed errors, rerun with: -s
==5532== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
[root@localhost debug]# 

结果分析:

# 不匹配的free,(testValgrind.cpp:10)进行了
==5532== Mismatched free() / delete / delete []
==5532==    at 0x4C2BB8F: operator delete[](void*) (vg_replace_malloc.c:651)
==5532==    by 0x400A1A: MyTest() (testValgrind.cpp:10)
==5532==    by 0x400A64: main (testValgrind.cpp:19)
==5532==  Address 0x6b529d0 is 0 bytes inside a block of size 10 alloc'd
==5532==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==5532==    by 0x400A03: MyTest() (testValgrind.cpp:8)
==5532==    by 0x400A64: main (testValgrind.cpp:19)
==5532== 

内存泄漏

示例

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;

void MyTest()
{
	char* p = (char*)malloc(sizeof(char)*10);

	cout << &p << endl;
}

int main()
{
	MyTest();

	return 0;
}

测试

执行valgrind --leak-check=yes ./testValgrind查看结果。

[root@localhost debug]# valgrind --leak-check=yes ./testValgrind
==12941== Memcheck, a memory error detector
==12941== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12941== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==12941== Command: ./testValgrind
==12941== 
0x1ffefff8a8
==12941== 
==12941== HEAP SUMMARY:
==12941==     in use at exit: 42 bytes in 2 blocks
==12941==   total heap usage: 22 allocs, 20 frees, 136,124 bytes allocated
==12941== 
==12941== 10 bytes in 1 blocks are definitely lost in loss record 1 of 2
==12941==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==12941==    by 0x400993: MyTest() (testValgrind.cpp:8)
==12941==    by 0x4009CE: main (testValgrind.cpp:15)
==12941== 
==12941== LEAK SUMMARY:
==12941==    definitely lost: 10 bytes in 1 blocks
==12941==    indirectly lost: 0 bytes in 0 blocks
==12941==      possibly lost: 0 bytes in 0 blocks
==12941==    still reachable: 32 bytes in 1 blocks
==12941==         suppressed: 0 bytes in 0 blocks
==12941== Reachable blocks (those to which a pointer was found) are not shown.
==12941== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==12941== 
==12941== For lists of detected and suppressed errors, rerun with: -s
==12941== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
[root@localhost debug]# 

结果分析:

# 10个字节确定丢失,发生了内存泄漏
==12941== 10 bytes in 1 blocks are definitely lost in loss record 1 of 2
==12941==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==12941==    by 0x400993: MyTest() (testValgrind.cpp:8)
==12941==    by 0x4009CE: main (testValgrind.cpp:15)
==12941== 

# 总结摘要部分也可确认该问题
==12941== LEAK SUMMARY:
==12941==    definitely lost: 10 bytes in 1 blocks   --- 确定发生了10个字节的内存泄漏
==12941==    indirectly lost: 0 bytes in 0 blocks
==12941==      possibly lost: 0 bytes in 0 blocks
==12941==    still reachable: 32 bytes in 1 blocks
==12941==         suppressed: 0 bytes in 0 blocks
==12941== Reachable blocks (those to which a pointer was found) are not shown.
==12941== To see them, rerun with: --leak-check=full --show-leak-kinds=all

释放空指针

示例

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;

int main()
{
	char* p = nullptr;
	bool bFind = false;

	// ... 

    // 此时bFind仍为false
	if(bFind)
	{
		p = new char[64];
	}

	delete[] p;

	return 0;
}

测试

执行valgrind --leak-check=yes ./testValgrind查看结果。

[root@localhost debug]# valgrind --leak-check=yes ./testValgrind
==23591== Memcheck, a memory error detector
==23591== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==23591== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==23591== Command: ./testValgrind
==23591== 
==23591== 
==23591== HEAP SUMMARY:
==23591==     in use at exit: 32 bytes in 1 blocks
==23591==   total heap usage: 21 allocs, 20 frees, 136,114 bytes allocated
==23591== 
==23591== LEAK SUMMARY:
==23591==    definitely lost: 0 bytes in 0 blocks
==23591==    indirectly lost: 0 bytes in 0 blocks
==23591==      possibly lost: 0 bytes in 0 blocks
==23591==    still reachable: 32 bytes in 1 blocks
==23591==         suppressed: 0 bytes in 0 blocks
==23591== Reachable blocks (those to which a pointer was found) are not shown.
==23591== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==23591== 
==23591== For lists of detected and suppressed errors, rerun with: -s
==23591== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
[root@localhost debug]# 

申请与释放的内存不匹配

示例

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;

int main()
{
	char* p = new char[20];

	for(int i = 0; i < 20; i++)
	{
		cout << *(p++) << endl;
	}

	delete[] p;

	return 0;
}

测试

执行valgrind --leak-check=yes ./testValgrind查看结果。

[root@localhost debug]# valgrind --leak-check=yes ./testValgrind
==26240== Memcheck, a memory error detector
==26240== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==26240== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==26240== Command: ./testValgrind
==26240== 
==26240== Conditional jump or move depends on uninitialised value(s)
==26240==    at 0x65C8F6E: _IO_file_overflow@@GLIBC_2.2.5 (in /usr/lib64/libc-2.17.so)
==26240==    by 0x65CA030: _IO_default_xsputn (in /usr/lib64/libc-2.17.so)
==26240==    by 0x65C7A91: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.17.so)
==26240==    by 0x65BC7E1: fwrite (in /usr/lib64/libc-2.17.so)
==26240==    by 0x4ECDBB4: std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) (in /usr/lib64/libstdc++.so.6.0.19)
==26240==    by 0x4ECDDE7: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char) (in /usr/lib64/libstdc++.so.6.0.19)
==26240==    by 0x400A37: main (testValgrind.cpp:12)
==26240== 
==26240== Syscall param write(buf) points to uninitialised byte(s)
==26240==    at 0x663CBA0: __write_nocancel (in /usr/lib64/libc-2.17.so)
==26240==    by 0x65C72F2: _IO_file_write@@GLIBC_2.2.5 (in /usr/lib64/libc-2.17.so)
==26240==    by 0x65C8B0D: _IO_do_write@@GLIBC_2.2.5 (in /usr/lib64/libc-2.17.so)
==26240==    by 0x65C9002: _IO_file_overflow@@GLIBC_2.2.5 (in /usr/lib64/libc-2.17.so)
==26240==    by 0x65C4BD8: putc (in /usr/lib64/libc-2.17.so)
==26240==    by 0x4ECD5C5: std::ostream::put(char) (in /usr/lib64/libstdc++.so.6.0.19)
==26240==    by 0x4ECD811: std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&) (in /usr/lib64/libstdc++.so.6.0.19)
==26240==    by 0x400A4C: main (testValgrind.cpp:12)
==26240==  Address 0x4023000 is in a rw- anonymous segment
==26240== 

==26240== Conditional jump or move depends on uninitialised value(s)
==26240==    at 0x65C7AE5: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.17.so)
==26240==    by 0x65BC7E1: fwrite (in /usr/lib64/libc-2.17.so)
==26240==    by 0x4ECDBB4: std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) (in /usr/lib64/libstdc++.so.6.0.19)
==26240==    by 0x4ECDDE7: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char) (in /usr/lib64/libstdc++.so.6.0.19)
==26240==    by 0x400A37: main (testValgrind.cpp:12)
==26240== 

==26240== Invalid free() / delete / delete[] / realloc()
==26240==    at 0x4C2BB8F: operator delete[](void*) (vg_replace_malloc.c:651)
==26240==    by 0x400A65: main (testValgrind.cpp:15)
==26240==  Address 0x6b529e4 is 0 bytes after a block of size 20 alloc'd
==26240==    at 0x4C2AC38: operator new[](unsigned long) (vg_replace_malloc.c:433)
==26240==    by 0x400A03: main (testValgrind.cpp:8)
==26240== 
==26240== 
==26240== HEAP SUMMARY:
==26240==     in use at exit: 52 bytes in 2 blocks
==26240==   total heap usage: 22 allocs, 21 frees, 136,134 bytes allocated
==26240== 
==26240== 20 bytes in 1 blocks are definitely lost in loss record 1 of 2
==26240==    at 0x4C2AC38: operator new[](unsigned long) (vg_replace_malloc.c:433)
==26240==    by 0x400A03: main (testValgrind.cpp:8)
==26240== 
==26240== LEAK SUMMARY:
==26240==    definitely lost: 20 bytes in 1 blocks
==26240==    indirectly lost: 0 bytes in 0 blocks
==26240==      possibly lost: 0 bytes in 0 blocks
==26240==    still reachable: 32 bytes in 1 blocks
==26240==         suppressed: 0 bytes in 0 blocks
==26240== Reachable blocks (those to which a pointer was found) are not shown.
==26240== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==26240== 
==26240== Use --track-origins=yes to see where uninitialised values come from
==26240== For lists of detected and suppressed errors, rerun with: -s
==26240== ERROR SUMMARY: 42 errors from 5 contexts (suppressed: 0 from 0)
[root@localhost debug]# 

结果分析:

# 关于“Conditional jump or move depends on uninitialised value(s)”
某些变量未初始化,检查一下数组/class/struct有没有赋初值,或者说看看初始化的大小是否为正确的大小,下面信息会提示在哪个函数里面。

# 关于“Syscall param write(buf) points to uninitialised byte”
系统调用传递不可访问或未初始化内存。
# 需确认以下几点:
## 1. 参数是否有初始化
## 2. 如果是系统调用读取程序提供的buffer,会产检整个buffer是否可访问和已经初始化
## 3. 如果是系统调用要往用户的buffer写入数据,会检查buffer是否可访问

# 在(testValgrind.cpp:15)处存在非法释放
==26240== Invalid free() / delete / delete[] / realloc()
==26240==    at 0x4C2BB8F: operator delete[](void*) (vg_replace_malloc.c:651)
==26240==    by 0x400A65: main (testValgrind.cpp:15)
==26240==  Address 0x6b529e4 is 0 bytes after a block of size 20 alloc'd
==26240==    at 0x4C2AC38: operator new[](unsigned long) (vg_replace_malloc.c:433)
==26240==    by 0x400A03: main (testValgrind.cpp:8)
==26240== 

# 存在内存泄漏
==26240== LEAK SUMMARY:
==26240==    definitely lost: 20 bytes in 1 blocks  --- 存在20字节内存泄漏
==26240==    indirectly lost: 0 bytes in 0 blocks
==26240==      possibly lost: 0 bytes in 0 blocks
==26240==    still reachable: 32 bytes in 1 blocks
==26240==         suppressed: 0 bytes in 0 blocks

引用已释放的内存块

示例

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;

int main()
{
	char* p = new char[20];

	// ...	

	delete[] p;

	// ... 

	p[0] = 'a';

	return 0;
}

测试

执行valgrind --leak-check=yes ./testValgrind查看结果。

[root@localhost debug]# valgrind --leak-check=yes ./testValgrind
==38628== Memcheck, a memory error detector
==38628== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==38628== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==38628== Command: ./testValgrind
==38628== 
==38628== Invalid write of size 1
==38628==    at 0x4008AF: main (testValgrind.cpp:16)
==38628==  Address 0x6b529d0 is 0 bytes inside a block of size 20 free'd
==38628==    at 0x4C2BB8F: operator delete[](void*) (vg_replace_malloc.c:651)
==38628==    by 0x4008AA: main (testValgrind.cpp:12)
==38628==  Block was alloc'd at
==38628==    at 0x4C2AC38: operator new[](unsigned long) (vg_replace_malloc.c:433)
==38628==    by 0x400893: main (testValgrind.cpp:8)
==38628== 
==38628== 
==38628== HEAP SUMMARY:
==38628==     in use at exit: 32 bytes in 1 blocks
==38628==   total heap usage: 22 allocs, 21 frees, 136,134 bytes allocated
==38628== 
==38628== LEAK SUMMARY:
==38628==    definitely lost: 0 bytes in 0 blocks
==38628==    indirectly lost: 0 bytes in 0 blocks
==38628==      possibly lost: 0 bytes in 0 blocks
==38628==    still reachable: 32 bytes in 1 blocks
==38628==         suppressed: 0 bytes in 0 blocks
==38628== Reachable blocks (those to which a pointer was found) are not shown.
==38628== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==38628== 
==38628== For lists of detected and suppressed errors, rerun with: -s
==38628== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
[root@localhost debug]# 

结果分析:

动态内存越界

# ==38628==在(testValgrind.cpp:16)处发生动态内存越界
==38628== Invalid write of size 1
==38628==    at 0x4008AF: main (testValgrind.cpp:16)
==38628==  Address 0x6b529d0 is 0 bytes inside a block of size 20 free'd
==38628==    at 0x4C2BB8F: operator delete[](void*) (vg_replace_malloc.c:651)
==38628==    by 0x4008AA: main (testValgrind.cpp:12)
==38628==  Block was alloc'd at
==38628==    at 0x4C2AC38: operator new[](unsigned long) (vg_replace_malloc.c:433)
==38628==    by 0x400893: main (testValgrind.cpp:8)
==38628== 

越界访问

示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;

void MyTest()
{
	char* p = new char[10];
	memset(p, 0, sizeof(char)*10);

	p[10] = 'c';	

	delete[] p;
}

int main()
{
	MyTest();

	return 0;
}

测试

[root@localhost debug]# valgrind --leak-check=yes ./testValgrind
==44390== Memcheck, a memory error detector
==44390== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==44390== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==44390== Command: ./testValgrind
==44390== 
==44390== Invalid write of size 1
==44390==    at 0x400906: MyTest() (testValgrind.cpp:12)
==44390==    by 0x400927: main (testValgrind.cpp:19)
==44390==  Address 0x6b529da is 0 bytes after a block of size 10 alloc'd
==44390==    at 0x4C2AC38: operator new[](unsigned long) (vg_replace_malloc.c:433)
==44390==    by 0x4008E3: MyTest() (testValgrind.cpp:9)
==44390==    by 0x400927: main (testValgrind.cpp:19)
==44390== 
==44390== 
==44390== HEAP SUMMARY:
==44390==     in use at exit: 32 bytes in 1 blocks
==44390==   total heap usage: 22 allocs, 21 frees, 136,124 bytes allocated
==44390== 
==44390== LEAK SUMMARY:
==44390==    definitely lost: 0 bytes in 0 blocks
==44390==    indirectly lost: 0 bytes in 0 blocks
==44390==      possibly lost: 0 bytes in 0 blocks
==44390==    still reachable: 32 bytes in 1 blocks
==44390==         suppressed: 0 bytes in 0 blocks
==44390== Reachable blocks (those to which a pointer was found) are not shown.
==44390== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==44390== 
==44390== For lists of detected and suppressed errors, rerun with: -s
==44390== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
[root@localhost debug]# 

结果分析:

# 在(testValgrind.cpp:12)处程序访问非法地址的内存,无效写入,即堆溢出。
==44390== Invalid write of size 1
==44390==    at 0x400906: MyTest() (testValgrind.cpp:12)
==44390==    by 0x400927: main (testValgrind.cpp:19)
==44390==  Address 0x6b529da is 0 bytes after a block of size 10 alloc'd
==44390==    at 0x4C2AC38: operator new[](unsigned long) (vg_replace_malloc.c:433)
==44390==    by 0x4008E3: MyTest() (testValgrind.cpp:9)
==44390==    by 0x400927: main (testValgrind.cpp:19)
==44390== 
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值