Valgrind 的安装与使用
1、安装
sudo apt install valgrind
- 参数信息
用法: valgrind [options] prog-and-args [options]: 常用选项,适用于所有Valgrind工具
--tool=<name>
最常用的选项。运行 valgrind中名为toolname的工具。默认memcheck。
-h --help
显示所有选项的帮助,包括内核和选定的工具两者。
--version
显示valgrind内核的版本,每个工具都有各自的版本。
-q --quiet
安静地运行,只打印错误信息。
--verbose
更详细的信息。
--trace-children=<yes|no>
跟踪子线程? [default: no]
--track-fds=<yes|no>
跟踪打开的文件描述?[default: no]
--time-stamp=<yes|no>
增加时间戳到LOG信息? [default: no]
--log-fd=<number>
输出LOG到描述符文件 [2=stderr]
--log-file=<file>
将输出的信息写入到filename.PID的文件里,PID是运行程序的进行ID
--log-file-exactly=<file>
输出LOG信息到 file
LOG信息输出
--xml=yes
将信息以xml格式输出,只有memcheck可用
--num-callers=<number>
show <number> callers in stack traces [12]
--error-exitcode=<number>
如果发现错误则返回错误代码 [0=disable]
--db-attach=<yes|no>
当出现错误,valgrind会自动启动调试器gdb。[default: no]
--db-command=<command>
启动调试器的命令行选项[gdb -nw %f %p]
适用于Memcheck工具的相关选项:
--leak-check=<no|summary|full>
要求对leak给出详细信息? Leak是指,存在一块没有被引用的内存空间,或没有被释放的内存空间,如summary,只反馈一些总结信息,告诉你有多少个malloc,多少个free 等;如果是full将输出所有的leaks,也就是定位到某一个malloc/free。 [default: summary]
--show-reachable=<yes|no>
如果为no,只输出没有引用的内存leaks,或指向malloc返回的内存块中部某处的leaks [default: no]
2、使用
2.1 Memcheck使用
-
检测问题
1、对未初始化内存的使用; 2、读/写释放后的内存块; 3、读/写超出malloc分配的内存块; 4、读/写不适当的栈中内存块; 5、内存泄漏,指向一块内存的指针永远丢失; 6、不正确的malloc/free或new/delete匹配; 7、memcpy()相关函数中的dst和src指针重叠。 -
代码
#include <iostream>
#include <string.h>
#include <stdlib.h>
int main()
{
int x;
if(x == 0) // 使用未初始化的内存
std::cout << "-==" << std::endl;
int *ptr = (int*)malloc(sizeof(int)*10);
ptr[10] = 7; //内存越界
memcpy(ptr + 1, ptr, 5); //内存重叠
free(ptr);
free(ptr); //重复释放
int *p1 =NULL;
*p1 = 1; //非法指针
return 0;
}
- 编译程序
g++ -o memcheck_test memcheck_test.cpp -g -std=c++11
- 使用memcheck
valgrind --tool=memcheck --leak-check=full --trace-children=yes --log-file= "leak.log" ./memcheck_test
- 运行结果
==30871== Memcheck, a memory error detector
==30871== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==30871== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==30871== Command: ./memcheck_test
==30871==
==30871== Conditional jump or move depends on uninitialised value(s) // 使用未初始化的内存
==30871== at 0x400942: main (memcheck_test.cpp:10)
==30871==
==
==30871== Invalid write of size 4 //内存越界
==30871== at 0x400979: main (memcheck_test.cpp:15)
==30871== Address 0x5ab70e8 is 0 bytes after a block of size 40 alloc'd
==30871== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==30871== by 0x40096C: main (memcheck_test.cpp:13)
==30871==
==30871== Source and destination overlap in memcpy(0x5ab70c4, 0x5ab70c0, 5)//内存重叠
==30871== at 0x4C32513: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==30871== by 0x40099A: main (memcheck_test.cpp:16)
==30871==
==30871== Invalid free() / delete / delete[] / realloc() //重复释放
==30871== at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==30871== by 0x4009B2: main (memcheck_test.cpp:19)
==30871== Address 0x5ab70c0 is 0 bytes inside a block of size 40 free'd
==30871== at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==30871== by 0x4009A6: main (memcheck_test.cpp:18)
==30871== Block was alloc'd at
==30871== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==30871== by 0x40096C: main (memcheck_test.cpp:13)
==30871==
==30871== Invalid write of size 4 //非法指针
==30871== at 0x4009BF: main (memcheck_test.cpp:22)
==30871== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==30871==
==30871==
==30871== Process terminating with default action of signal 11 (SIGSEGV)
==30871== Access not within mapped region at address 0x0
==30871== at 0x4009BF: main (memcheck_test.cpp:22)
==30871== If you believe this happened as a result of a stack
==30871== overflow in your program's main thread (unlikely but
==30871== possible), you can try to increase the size of the
==30871== main thread stack using the --main-stacksize= flag.
==30871== The main thread stack size used in this run was 8388608.
==30871==
==30871== HEAP SUMMARY:
==30871== in use at exit: 72,704 bytes in 1 blocks
==30871== total heap usage: 3 allocs, 3 frees, 73,768 bytes allocated
==30871==
==30871== LEAK SUMMARY:
==30871== definitely lost: 0 bytes in 0 blocks
==30871== indirectly lost: 0 bytes in 0 blocks
==30871== possibly lost: 0 bytes in 0 blocks
==30871== still reachable: 72,704 bytes in 1 blocks
==30871== suppressed: 0 bytes in 0 blocks
==30871== Reachable blocks (those to which a pointer was found) are not shown.
==30871== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==30871==
==30871== For counts of detected and suppressed errors, rerun with: -v
==30871== Use --track-origins=yes to see where uninitialised values come from
==30871== ERROR SUMMARY: 5 errors from 5 contexts (suppressed: 0 from 0)
段错误 (核心已转储)
2.2、Callgrind使用
-
使用依赖
-
1、安装gprof2dot.py
下载地址 http://jrfonseca.googlecode.com/svn/trunk/gprof2dot/gprof2dot.py 得到 gprof2dot-2021.2.21.tar.gz 解压 tar -zvxf gprof2dot-2021.2.21.tar.gz -
2、配置gprof2dot.py环境变量
-
1、当前终端单次有效 执行命令 export PATH=$PATH:/home/gzrobot/gprof_dir/gprof2dot-2021.2.21
-
2、当前用户永久有效
vi ~/.profile
通过 “:” 分割追加PATH
-
-

- 3、配置所有用户有效
vi /etc/profile
在最后加入 export PATH="=$PATH:/home/gzrobot/gprof_dir/gprof2dot-2021.2.21"
再使用 source /etc/profile 使配置生效
-
3、安装graphviz
sudo apt install graphviz -
测试代码
#include <iostream> #include <thread> using namespace std; int add(int a, int b) { for(int i = 0; i < 10000; ++i) { ; } return a + b; } int sub(int a, int b) { for(int i = 0; i < 5000; ++i) {;} return a - b; } int call() { cout << add(1, 2) << endl; cout << sub(2, 4) << endl; } int main() { for(int i = 0; i < 10000; ++i) call(); return 0; } -
编译程序
g++ -o test_gprof test_gprof.cpp -g -std=c++11 -
运行程序生成callgrind.out.xxx
valgrind --tool=callgrind ./test_gprof //单线程性能分析 valgrind --tool=callgrind --separate-threads=yes ./test_gprof //多线程性能分析,线程不能detach,否则无法生成子线程的信息 -
查看callgrind.out.xxx
callgrind_annotate callgrind.out.XXX -
生成图
gprof2dot.py -f callgrind callgrind.out.XXX |dot -Tpng -o report.png
2.3、helgrind的使用
-
未加锁
#include <pthread.h> int var = 0; void* child_fn ( void* arg ) { var++; return NULL; } int main ( void ) { pthread_t child; pthread_t child2; pthread_create(&child,NULL, child_fn, NULL); pthread_create(&child2,NULL,child_fn,NULL); pthread_join(child,NULL); pthread_join(child2,NULL); return 0; } g++ -o helgrind_test helgrind_test.cpp -lpthread -g valgrind --tool=helgrind ./helgrind_test ==32238== Helgrind, a thread error detector ==32238== Copyright (C) 2007-2015, and GNU GPL'd, by OpenWorks LLP et al. ==32238== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info ==32238== Command: ./helgrind_test ==32238== Parent PID: 3535 ==32238== ==32238== ---Thread-Announcement------------------------------------------ ==32238== ==32238== Thread #3 was created ==32238== at 0x516449E: clone (clone.S:74) ==32238== by 0x4E46149: create_thread (createthread.c:102) ==32238== by 0x4E47E83: pthread_create@@GLIBC_2.2.5 (pthread_create.c:679) ==32238== by 0x4C34BB7: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-li nux.so) ==32238== by 0x400720: main (helgrind_test.cpp:26) ==32238== ==32238== ---Thread-Announcement------------------------------------------ ==32238== ==32238== Thread #2 was created ==32238== at 0x516449E: clone (clone.S:74) ==32238== by 0x4E46149: create_thread (createthread.c:102) ==32238== by 0x4E47E83: pthread_create@@GLIBC_2.2.5 (pthread_create.c:679) ==32238== by 0x4C34BB7: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-li nux.so) ==32238== by 0x400705: main (helgrind_test.cpp:24) ==32238== ==32238== ---------------------------------------------------------------- ==32238== ==32238== Possible data race during read of size 4 at 0x6010A8 by thread #3 ==32238== Locks held: none ==32238== at 0x4006BE: child_fn(void*) (helgrind_test.cpp:10) ==32238== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-li nux.so) ==32238== by 0x4E476B9: start_thread (pthread_create.c:333) ==32238== ==32238== This conflicts with a previous write of size 4 by thread #2 ==32238== Locks held: none ==32238== at 0x4006C7: child_fn(void*) (helgrind_test.cpp:10) ==32238== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-li nux.so) ==32238== by 0x4E476B9: start_thread (pthread_create.c:333) ==32238== Address 0x6010a8 is 0 bytes inside data symbol "var" ==32238== ==32238== ---------------------------------------------------------------- ==32238== ==32238== Possible data race during write of size 4 at 0x6010A8 by thread #3 ==32238== Locks held: none ==32238== at 0x4006C7: child_fn(void*) (helgrind_test.cpp:10) ==32238== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-li nux.so) ==32238== by 0x4E476B9: start_thread (pthread_create.c:333) ==32238== ==32238== This conflicts with a previous write of size 4 by thread #2 ==32238== Locks held: none ==32238== at 0x4006C7: child_fn(void*) (helgrind_test.cpp:10) ==32238== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-li nux.so) ==32238== by 0x4E476B9: start_thread (pthread_create.c:333) ==32238== Address 0x6010a8 is 0 bytes inside data symbol "var" ==32238== ==32238== ==32238== For counts of detected and suppressed errors, rerun with: -v ==32238== Use --history-level=approx or =none to gain increased speed, at ==32238== the cost of reduced accuracy of conflicting-access information ==32238== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) -
重复加锁
#include <pthread.h> pthread_mutex_t mut_thread; int var = 0; void* child_fn ( void* arg ) { pthread_mutex_lock(&mut_thread); var++; pthread_mutex_lock(&mut_thread); return NULL; } int main ( void ) { pthread_mutex_init(&mut_thread, NULL); pthread_t child; pthread_t child2; pthread_create(&child,NULL, child_fn, NULL); pthread_create(&child2,NULL,child_fn,NULL); pthread_join(child,NULL); pthread_join(child2,NULL); return 0; } g++ -o helgrind_test helgrind_test.cpp -lpthread -g valgrind --tool=helgrind ./helgrind_test ==29589== Helgrind, a thread error detector ==29589== Copyright (C) 2007-2015, and GNU GPL'd, by OpenWorks LLP et al. ==29589== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info ==29589== Command: ./helgrind_test ==29589== Parent PID: 3535 ==29589== ==29589== ---Thread-Announcement------------------------------------------ ==29589== ==29589== Thread #2 was created ==29589== at 0x516449E: clone (clone.S:74) ==29589== by 0x4E46149: create_thread (createthread.c:102) ==29589== by 0x4E47E83: pthread_create@@GLIBC_2.2.5 (pthread_create.c:679) ==29589== by 0x4C34BB7: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==29589== by 0x4007CC: main (helgrind_test.cpp:27) ==29589== ==29589== ---------------------------------------------------------------- ==29589== ==29589== Thread #2: Attempt to re-lock a non-recursive lock I already hold ==29589== at 0x4C320F4: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==29589== by 0x400784: child_fn(void*) (helgrind_test.cpp:13) ==29589== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==29589== by 0x4E476B9: start_thread (pthread_create.c:333) ==29589== Lock was previously acquired ==29589== at 0x4C321BC: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==29589== by 0x40076B: child_fn(void*) (helgrind_test.cpp:9) ==29589== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==29589== by 0x4E476B9: start_thread (pthread_create.c:333) ==29589== ==29589== ==29589== Process terminating with default action of signal 2 (SIGINT) ==29589== at 0x4E4898D: pthread_join (pthread_join.c:90) ==29589== by 0x4C31DE5: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==29589== by 0x4007F8: main (helgrind_test.cpp:31) ==29589== ---------------------------------------------------------------- ==29589== ==29589== Thread #2: Exiting thread still holds 1 lock ==29589== at 0x4E5026D: __lll_lock_wait (lowlevellock.S:135) ==29589== by 0x4E49DBC: pthread_mutex_lock (pthread_mutex_lock.c:80) ==29589== by 0x4C32156: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==29589== by 0x400784: child_fn(void*) (helgrind_test.cpp:13) ==29589== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==29589== by 0x4E476B9: start_thread (pthread_create.c:333) ==29589== ==29589== ==29589== For counts of detected and suppressed errors, rerun with: -v ==29589== Use --history-level=approx or =none to gain increased speed, at ==29589== the cost of reduced accuracy of conflicting-access information ==29589== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) -
加锁顺序导致死锁
#include <pthread.h> pthread_mutex_t mut_thread; pthread_mutex_t mut_thread1; int var = 0; void* child_fn ( void* arg ) { pthread_mutex_lock(&mut_thread); pthread_mutex_lock(&mut_thread1); var++; pthread_mutex_unlock(&mut_thread); pthread_mutex_unlock(&mut_thread1); return NULL; } void* child_fn1 ( void* arg ) { pthread_mutex_lock(&mut_thread1); pthread_mutex_lock(&mut_thread); var++; pthread_mutex_unlock(&mut_thread1); pthread_mutex_unlock(&mut_thread); return NULL; } int main ( void ) { pthread_mutex_init(&mut_thread, NULL); pthread_mutex_init(&mut_thread1, NULL); pthread_t child; pthread_t child2; pthread_create(&child,NULL, child_fn, NULL); pthread_create(&child2,NULL,child_fn1,NULL); pthread_join(child,NULL); pthread_join(child2,NULL); return 0; } g++ -o helgrind_test helgrind_test.cpp -lpthread -g valgrind --tool=helgrind ./helgrind_test ==3904== Helgrind, a thread error detector ==3904== Copyright (C) 2007-2015, and GNU GPL'd, by OpenWorks LLP et al. ==3904== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info ==3904== Command: ./helgrind_test ==3904== Parent PID: 3535 ==3904== ==3904== ---Thread-Announcement------------------------------------------ ==3904== ==3904== Thread #3 was created ==3904== at 0x516449E: clone (clone.S:74) ==3904== by 0x4E46149: create_thread (createthread.c:102) ==3904== by 0x4E47E83: pthread_create@@GLIBC_2.2.5 (pthread_create.c:679) ==3904== by 0x4C34BB7: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==3904== by 0x4008B4: main (helgrind_test.cpp:44) ==3904== ==3904== ---------------------------------------------------------------- ==3904== ==3904== Thread #3: lock order "0x601080 before 0x6010C0" violated ==3904== ==3904== Observed (incorrect) order is: acquisition of lock at 0x6010C0 ==3904== at 0x4C321BC: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==3904== by 0x400815: child_fn1(void*) (helgrind_test.cpp:22) ==3904== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==3904== by 0x4E476B9: start_thread (pthread_create.c:333) ==3904== ==3904== followed by a later acquisition of lock at 0x601080 ==3904== at 0x4C321BC: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==3904== by 0x40081F: child_fn1(void*) (helgrind_test.cpp:23) ==3904== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==3904== by 0x4E476B9: start_thread (pthread_create.c:333) ==3904== ==3904== Required order was established by acquisition of lock at 0x601080 ==3904== at 0x4C321BC: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==3904== by 0x4007CB: child_fn(void*) (helgrind_test.cpp:10) ==3904== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==3904== by 0x4E476B9: start_thread (pthread_create.c:333) ==3904== ==3904== followed by a later acquisition of lock at 0x6010C0 ==3904== at 0x4C321BC: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==3904== by 0x4007D5: child_fn(void*) (helgrind_test.cpp:11) ==3904== by 0x4C34DB6: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so) ==3904== by 0x4E476B9: start_thread (pthread_create.c:333) ==3904== ==3904== Lock at 0x601080 was first observed ==3904== at 0x4C360BA: pthread_mutex_init (in /usr/lib/valgrind/vgpreload_helgrind-amd64- linux.so) ==3904== by 0x40086F: main (helgrind_test.cpp:35) ==3904== Address 0x601080 is 0 bytes inside data symbol "mut_thread" ==3904== ==3904== Lock at 0x6010C0 was first observed ==3904== at 0x4C360BA: pthread_mutex_init (in /usr/lib/valgrind/vgpreload_helgrind-amd64- linux.so) ==3904== by 0x40087E: main (helgrind_test.cpp:36) ==3904== Address 0x6010c0 is 0 bytes inside data symbol "mut_thread1" ==3904== ==3904== ==3904== ==3904== For counts of detected and suppressed errors, rerun with: -v ==3904== Use --history-level=approx or =none to gain increased speed, at ==3904== the cost of reduced accuracy of conflicting-access information ==3904== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 9 from 9)
thread_mutex_init (in /usr/lib/valgrind/vgpreload_helgrind-amd64- linux.so)
3904 by 0x40087E: main (helgrind_test.cpp:36)
3904 Address 0x6010c0 is 0 bytes inside data symbol “mut_thread1”
3904
3904
3904
3904 For counts of detected and suppressed errors, rerun with: -v
3904 Use --history-level=approx or =none to gain increased speed, at
3904 the cost of reduced accuracy of conflicting-access information
3904 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 9 from 9)

本文介绍了Valgrind的安装及三种主要工具的使用方法:Memcheck用于检测内存错误,Callgrind进行性能分析,Helgrind查找线程同步问题。通过示例代码展示了如何定位内存泄漏、性能瓶颈和线程竞争条件。
2033





