valgrind使用简介
1、问题
虽然C/C++是一种非常有用且功能强大的语言,但很难调试。 某些时候可能遇到内存错误。 如果知道出错 ,或者程序一直崩溃,我们可以用gdb/DBX去调试。 但是,有时遇到的问题是由于内存错误造成的,但它不出段错误,很多时候,我们不希望海里捞针,在gdb中设置很多断点,逐步盘查。 还有可能会遇到的另一个问题是内存泄漏:在某个地方,调用malloc分配的内存没有调用free来释放。 Valgrind是一个可以帮助解决这两个问题的程序
2、valgrind功能介绍
Valgrind是一个构建动态分析工具的工具框架,其带有一组工具,每个都执行某种调试,分析或类似的任务,可帮助开发者改进程序。 Valgrind的架构是模块化的,因此可以轻松创建新工具,而不会影响现有结构。标准提供了许多有用的工具。
- Memcheck是一个内存错误检测器。它可以帮助您使您的程序更加正确,特别是使用C和C ++编写的程序。
- Cachegrind是一个缓存和分支预测分析器。它可以帮助您使程序运行得更快。
- Callgrind是一个调用图生成缓存分析器。它与Cachegrind有一些重叠,但也收集了一些Cachegrind没有的信息。
- Helgrind是一个线程错误检测器。它可以帮助您使多线程程序更加正确。
- DRD也是一个线程错误检测器。它与Helgrind类似,但使用不同的分析技术,因此可能会发现不同的问题。
- Massif是一个堆分析器。它可以帮助您使您的程序使用更少的内存。
- DHAT是一种不同类型的堆分析器。它可以帮助您了解块寿命,块利用率和布局低效问题。
- SGcheck是一个可以检测堆栈和全局数组溢出的实验工具。它的功能与Memcheck的功能是互补的:SGcheck发现了Memcheck无法解决的问题,反之亦然。
- BBV是一个实验性的SimPoint基本块矢量生成器。对于从事计算机体系结构研究和开发的人员非常有用。
在这里,我们主要讨论内存错误检测器(Memcheck),用于检测C/C ++程序中常见问题:
- 访问不应该使用的内存,例如溢出,堆溢出,溢出栈顶,在访问释放了的内存。
- 使用未定义的值,即尚未初始化的值或从其他未定义值派生的值。
- 错误地释放堆内存,如重复释放堆,或者不匹配的使用malloc/new/new []与free/delete/delete []
- 在memcpy和相关函数中重叠src和dst指针。
- 在调用内存分配函数时,给size参数传递一个负值。
- 内存泄漏。
像这些问题有时候很难通过其他方式发现,常常长时间未被发现,然后导致偶然的,难以诊断的崩溃。Memcheck还使用命令行选项--xtree-memory和monitor命令xtmemory,对执行树提供内存分析。
3、内存泄露
在这个例子中,释放缓存的逻辑已经被注释,所以产生一个内存泄露的问题。
#define SNPRINTF_ATTR(fmt_pos, firstarg_pos) \ __attribute__ ((format(vsnprintf, fmt_pos, firstarg_pos))) #include <stdio.h> #include <stdarg.h> #include <stdlib.h> using namespace std; class query_buffer { private: char *buf; size_t len; size_t cap; public: query_buffer(): len(0), cap(2048) { buf = (char *)calloc(cap, sizeof(char)); }; virtual ~query_buffer() { //if (buf) free(buf); len = 0; cap = 0; }; void resize_if_needed(size_t new_len) { if (new_len >= cap) { while (new_len > cap) cap *= 2; buf = (char *)realloc(buf, cap*sizeof(char)); } else if (4 * new_len < cap) { cap /= 2; buf = (char *)realloc(buf, cap*sizeof(char)); } }; int append(const char *fmt, ...) SNPRINTF_ATTR(3, 4) { //resize_if_needed(len + max_print_len); va_list args; va_start(args, fmt); int n = vsnprintf(buf + len, static_cast<int>(cap - len), fmt, args); if (n>=0) len += n; va_end(args); return n; }; const char *get_buf() { return this->buf; }; }; int main(void) { query_buffer query_buffer; query_buffer.resize_if_needed(256); int n = query_buffer.append("%d: %s -- %s, %s\n", 1, "engineer", "office", "computer?"); n += query_buffer.append("%d: %s -- %s, %s\n", 2, "chef", "kitchen", "oven?"); n += query_buffer.append("%d: %s -- %s, %s\n", 3, "gardener", "yard", "yard?"); n += query_buffer.append("%d: %s -- %s, %s\n", 4, "nurse", "hospital", "patient?"); printf("Total size %d:\n%s", n, query_buffer.get_buf()); }
编译后,运行程序querybuf:
valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes ./querybuf
==74342== Memcheck, a memory error detector ==74342== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==74342== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info ==74342== Command: ./a.out ==74342== Total size 205: 1: engineer -- office, (what wrong with the computer?) 2: chef -- kitchen, (what wrong with the oven?) 3: gardener -- yard, (what wrong with the yard?) 4: nurse -- hospital, (what wrong with the patient?) ==74342== ==74342== FILE DESCRIPTORS: 3 open at exit. ==74342== Open file descriptor 2: /dev/pts/2 ==74342== <inherited from parent> ==74342== ==74342== Open file descriptor 1: /dev/pts/2 ==74342== <inherited from parent> ==74342== ==74342== Open file descriptor 0: /dev/pts/2 ==74342== <inherited from parent> ==74342== ==74342== ==74342== HEAP SUMMARY: ==74342== in use at exit: 1,024 bytes in 1 blocks ==74342== total heap usage: 2 allocs, 1 frees, 3,072 bytes allocated ==74342== ==74342== 1,024 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==74342== at 0x4C2BB4A: realloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==74342== by 0x400B38: query_buffer::resize_if_needed(unsigned long) (tryvarargs.cpp:32) ==74342== by 0x4008B5: main (tryvarargs.cpp:53) ==74342== ==74342== LEAK SUMMARY: ==74342== definitely lost: 1,024 bytes in 1 blocks ==74342== indirectly lost: 0 bytes in 0 blocks ==74342== possibly lost: 0 bytes in 0 blocks ==74342== still reachable: 0 bytes in 0 blocks ==74342== suppressed: 0 bytes in 0 blocks ==74342== ==74342== For counts of detected and suppressed errors, rerun with: -v ==74342== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)
从上述报错信息可知,realloc的内存没有释放造成内存泄露。这指向释放realloc分配内存的地方有问题--〉类的destructor。
4、读写过界
下面例子有两个问题:
- 字符串数组分配了10个元素,但是在符值时有11个
- 用malloc分配的内存却用delete来释放。
int main(void) { char *buf = (char *)calloc(10, sizeof(char)); for (int i = 0; i < 11; ++i) buf[i] = static_cast<char>('a' + i); printf("%s", buf); delete buf; }
我们运行valgrind来查一下内存:valgrind --tool=memcheck ./heapoverflow
==101781== Memcheck, a memory error detector ==101781== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==101781== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info ==101781== Command: ./heapoverflow ==101781== ==101781== Invalid write of size 1 ==101781== at 0x400A3B: main (tryvarargs.cpp:66) ==101781== Address 0x5a1304a is 0 bytes after a block of size 10 alloc'd ==101781== at 0x4C2B974: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==101781== by 0x400A1A: main (tryvarargs.cpp:63) ==101781== ==101781== Invalid read of size 1 ==101781== at 0x569CA94: vfprintf (in /usr/lib64/libc-2.17.so) ==101781== by 0x56A5C18: printf (in /usr/lib64/libc-2.17.so) ==101781== by 0x400A5C: main (tryvarargs.cpp:68) ==101781== Address 0x5a1304a is 0 bytes after a block of size 10 alloc'd ==101781== at 0x4C2B974: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==101781== by 0x400A1A: main (tryvarargs.cpp:63) ==101781== ==101781== Invalid read of size 1 ==101781== at 0x56CD4FE: _IO_default_xsputn (in /usr/lib64/libc-2.17.so) ==101781== by 0x56CB551: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.17.so) ==101781== by 0x569CA4C: vfprintf (in /usr/lib64/libc-2.17.so) ==101781== by 0x56A5C18: printf (in /usr/lib64/libc-2.17.so) ==101781== by 0x400A5C: main (tryvarargs.cpp:68) ==101781== Address 0x5a1304a is 0 bytes after a block of size 10 alloc'd ==101781== at 0x4C2B974: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==101781== by 0x400A1A: main (tryvarargs.cpp:63) ==101781== ==101781== Mismatched free() / delete / delete [] ==101781== at 0x4C2B131: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==101781== by 0x400A68: main (tryvarargs.cpp:69) ==101781== Address 0x5a13040 is 0 bytes inside a block of size 10 alloc'd ==101781== at 0x4C2B974: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==101781== by 0x400A1A: main (tryvarargs.cpp:63) ==101781== abcdefghijk==101781== ==101781== HEAP SUMMARY: ==101781== in use at exit: 0 bytes in 0 blocks ==101781== total heap usage: 1 allocs, 1 frees, 10 bytes allocated ==101781== ==101781== All heap blocks were freed -- no leaks are possible ==101781== ==101781== For counts of detected and suppressed errors, rerun with: -v ==101781== ERROR SUMMARY: 5 errors from 4 contexts (suppressed: 2 from 2)
输出中已经高亮显示的报错指出:
-
Invalid read of size 1
-
Mismatched free() / delete / delete []
5、在memcpy和相关函数中重叠src和dst指针
类似memcpy使用中重叠内存而出错的问题,不光是刚刚接触c/c++的程序员会犯,许多工作多年的高手也会犯同样问题。有时候是对库函数的定义和限制不清楚,有时候就是疏忽了。而这种问题特别难发现。valgrind的报告可以帮助我们找到问题:
int main(void) { char *buf = (char *)calloc(11, sizeof(char)); for (int i = 0; i < 10; ++i) buf[i] = static_cast<char>('a' + i); printf("%s", buf); char *dest = buf+3; memcpy(dest, buf, 6); printf("%s", dest); free(buf); }
$ valgrind --tool=memcheck ./heapoverflow ==103964== Memcheck, a memory error detector ==103964== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==103964== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info ==103964== Command: ./heapoverflow ==103964== ==103964== Source and destination overlap in memcpy(0x5a13043, 0x5a13040, 6) ==103964== at 0x4C2E1DC: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==103964== by 0x400AF0: main (tryvarargs.cpp:72) ==103964== abcdefghijabcdefj==103964== ==103964== HEAP SUMMARY: ==103964== in use at exit: 0 bytes in 0 blocks ==103964== total heap usage: 1 allocs, 1 frees, 11 bytes allocated ==103964== ==103964== All heap blocks were freed -- no leaks are possible ==103964== ==103964== For counts of detected and suppressed errors, rerun with: -v ==103964== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)
参考文献
[1] http://cs.ecs.baylor.edu/~donahoo/tools/valgrind/
[2] http://pages.cs.wisc.edu/~bart/537/valgrind.html
[3] https://www.thegeekstuff.com/2011/11/valgrind-memcheck/
[4] http://valgrind.org/docs/manual/manual.html