1. Valgrind简介
官网Valgrind介绍是一款用于内存调试、内存泄漏检测以及性能分析的软件开发工具。这些工具之中最有名的是Memcheck。它能够识别很多C或者C++程序中内存相关的错误,这些错误会导致程序崩溃或者出现不可预知的行为。
1)memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等(本文重点学习)。
2)callgrind:检测程序代码的运行时间和调用过程,以及分析程序性能。
3)cachegrind:分析CPU的cache命中率、丢失率,用于进行代码优化。
4)helgrind:用于检查多线程程序的竞态条件。
5)massif:堆栈分析器,指示程序中使用了多少堆内存等信息。
这几个工具的使用是通过命令:valgrand --tool=name 程序名来分别调用的,当不指定tool参数时默认是 --tool=memcheck
2. Valgrind安装
# wget http://valgrind.org/downloads/valgrind-3.15.0.tar.bz2
# tar -xjvf valgrind-3.15.0.tar.bz2
# cd valgrind-3.15.0/
# ./autogen.sh
如果报错上图错误:需要安装autoconf和automake两个工具,请先查看“安装autoconf“和”安装automake“两个步骤
解决办法: # yum -y install install autoconf automake libtool
安装成功以后,继续执行 # ./autogen.sh
# ./configure
# make
# make install
# valgrind --version 检查安装是否成功
Valgrind命令的格式如下:
valgrind [valgrind-options] your-prog [your-prog options]
使用如下命令: valgrind --tool=memcheck --leak-check=full --log-file=reportleak ./程序名
参数含意:
--tool=memcheck 使用的工具,默认即为memcheck
--leak-check=full 要求对与内存错误,给出全部信息
--log-file=*** 表示错误输出文件名
3. Valgrind使用
代码:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
int main()
{
int *p = new int[5];
printf("address:%p\n", p); //申请空间,未释放
int *ptr = new int[10];;
ptr[10] = 7; // 内存越界
memcpy(ptr +1, ptr, 5); // 踩内存
delete []ptr;
delete []ptr;// 重复释放
int *p1;
*p1 = 1; // 非法指针
return 0;
}
编译:# g++ -g -o main main.cpp
valgrind被设计成非侵入式的,它直接工作于可执行文件上,可直接检查编译后的程序。
查看内存信息:
基本命令:
# valgrind --tool=memcheck --leak-check=yes ./main
检测结果输入到文件valgrind_report.log中:
# valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --run-libc-freeres=yes --log-file=./valgrind_report.log ./main
结果打印到屏幕上:
# valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --run-libc-freeres=yes ./main
==1301== Memcheck, a memory error detector
==1301== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1301== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==1301== Command: ./main
==1301==
==1301== Invalid write of size 4 // 内存越界
==1301== at 0x400649: main (main.cpp:12)
==1301== Address 0x5a230c8 is 0 bytes after a block of size 40 alloc'd
==1301== at 0x4C2AB28: operator new[](unsigned long) (vg_replace_malloc.c:433)
==1301== by 0x40063C: main (main.cpp:11)
==1301==
==1301== Source and destination overlap in memcpy(0x5a230a4, 0x5a230a0, 5) // 踩内存
==1301== at 0x4C2E70D: memcpy@@GLIBC_2.14 (vg_replace_strmem.c:1035)
==1301== by 0x40066A: main (main.cpp:14)
==1301==
==1301== Invalid free() / delete / delete[] / realloc() // 重复释放
==1301== at 0x4C2BA7F: operator delete[](void*) (vg_replace_malloc.c:651)
==1301== by 0x400690: main (main.cpp:17)
==1301== Address 0x5a230a0 is 0 bytes inside a block of size 40 free'd
==1301== at 0x4C2BA7F: operator delete[](void*) (vg_replace_malloc.c:651)
==1301== by 0x40067D: main (main.cpp:16)
==1301== Block was alloc'd at
==1301== at 0x4C2AB28: operator new[](unsigned long) (vg_replace_malloc.c:433)
==1301== by 0x40063C: main (main.cpp:11)
==1301==
==1301== Use of uninitialised value of size 8 // 非法指针
==1301== at 0x400695: main (main.cpp:20)
==1301==
==1301==
==1301== Process terminating with default action of signal 11 (SIGSEGV) //由于非法指针赋值导致的程序崩溃
==1301== Bad permissions for mapped region at address 0x400530
==1301== at 0x400695: main (main.cpp:20)
==1301==
==1301== HEAP SUMMARY:
==1301== in use at exit: 20 bytes in 1 blocks
==1301== total heap usage: 2 allocs, 2 frees, 60 bytes allocated
==1301==
==1301== 20 bytes in 1 blocks are still reachable in loss record 1 of 1 //指针未释放
==1301== at 0x4C2AB28: operator new[](unsigned long) (vg_replace_malloc.c:433)
==1301== by 0x40062E: main (main.cpp:8)
==1301==
==1301== LEAK SUMMARY:
==1301== definitely lost: 0 bytes in 0 blocks
==1301== indirectly lost: 0 bytes in 0 blocks
==1301== possibly lost: 0 bytes in 0 blocks
==1301== still reachable: 20 bytes in 1 blocks
==1301== suppressed: 0 bytes in 0 blocks
==1301==
==1301== Use --track-origins=yes to see where uninitialised values come from
==1301== For lists of detected and suppressed errors, rerun with: -s
==1301== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 0 from 0)
段错误
修改代码中存在内存检测有问题的地方,重新检测后发现:
检查程序中的内存问题已经解决。
注意:Valgrind不检查静态分配数组的使用情况!所以对静态分配的数组,Valgrind表示无能为力
4. 结论分析
Memcheck 工具可以检查以下的程序错误:
(1)使用未初始化的内存 (Use of uninitialised memory)
(2)使用已经释放了的内存 (Reading/writing memory after it has been free'd)
(3)使用超过malloc分配的内存空间(Reading/writing off the end of malloc'd blocks)
(4)对堆栈的非法访问 (Reading/writing inappropriate areas on the stack)
(5)申请的空间是否有释放 (Memory leaks – where pointers to malloc'd blocks are lost forever)
(6)malloc/free/new/delete申请和释放内存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])
(7)src和dst的重叠(Overlapping src and dst pointers in memcpy() and related functions)
(8)重复free(Invalid free() / delete / delete[] / realloc())
如果条件允许,你也可以结合Coverity静态内存检查和Valgrind内存检测。注意:Coverity静态检测不能检测动态内存问题。
5. 补充
ASan(Linux):gcc4.8以上版本自带的内存检查工具
特点:方便好用、比Valgrind性能影响小
asan编译和链接的程序宕机的内存位置和变量名,文件行数,变量来源,线程信息等等非常全面的信息当做error输出,方便定位问题。
总结一下,网上说asan在检测程序内存方面的功能实在是强大,比valgrind检查内存泄漏好点,因为valgrind对性能的影响实在是太大,完全不能放到真实环境测试,并且asan给出的问题报告相当详细,基本上看一次错误报告就能找到崩溃原因,有时候dump文件、core文件的信息由于优化和其他程序上下文的差异会导致提供的信息基本上没什么作用,这个时候你就需要它们来助你跳坑。