Valgrind是一款用于内存调试、内存泄漏检测以及性能分析、检测线程错误的软件开发工具。
Valgrind 是运行在Linux 上的多用途代码剖析和内存调试软件。主要包括Memcheck、Callgrind、Cachegrind 等工具,每个工具都能完成一项任务调试、检测或分析。可以检测内存泄露、线程违例和Cache 的使用等。Valgrind 基于仿真方式对程序进行调试,它先于应用程序获取实际处理器的控制权,并在实际处理器的基础上仿真一个虚拟处理器,并使应用程序运行于这个虚拟处理器之上,从而对应用程序的运行进行监视。应用程序并不知道该处理器是虚拟的还是实际的,已经编译成二进制代码的应用程序并不用重新进行编译,Valgrind 直接解释二进制代码使得应用程序基于它运行,从而能够检查内存操作时可能出现的错误。所以在Valgrind下运行的程序运行速度要慢得多,而且使用的内存要多得多 - 例如,Memcheck工具下的程序是正常情况的两倍多。因此,最好在性能好的机器上使用Valgrind。
wget https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2
wyj@wyj-virtual-machine:~/Download$ tar -xjvf valgrind-3.15.0.tar.bz2
wyj@wyj-virtual-machine:~/Download$ cd valgrind-3.15.0/
因为valgrind支持多个平台,根据当前主机配置valgrind
wyj@wyj-virtual-machine:~/Download/valgrind-3.15.0$ ./configure
./configure之后就有makefile出现,接着就是make编译,安装
wyj@wyj-virtual-machine:~/Download/valgrind-3.15.0$ make
wyj@wyj-virtual-machine:~/Download/valgrind-3.15.0$ sudo make install
查看一下版本看看是否安装好
wyj@wyj-virtual-machine:~/Download/valgrind-3.15.0$ valgrind --version
valgrind-3.15.0
三、valgrind使用
因为我也是第一次用,所以就先试试官方的快速入门试试,慢慢探索它的功能
#include <stdio.h>
#include <stdlib.h>
void fun(void)
{
int *x = malloc(10*sizeof(int));
x [10] = 0; //问题1:堆块溢出
} //问题2:内存泄漏 - x未释放
int main(int argc, char **argv)
{
fun() ;
return 0 ;
}
编译程序-g以包含调试信息,以便Memcheck的错误消息包含确切的行号。
wyj@wyj-virtual-machine:~/c_code$ gcc -g valgrind_test.c -o valgrind_test
正常运行
wyj@wyj-virtual-machine:~/c_code$ ./valgrind_test
Memcheck是默认工具。--leak-check 选项打开详细的内存泄漏检测器。程序运行速度会比正常情况慢很多(例如20到30倍),并且会占用更多内存。Memcheck将发出有关内存错误和检测到的泄漏的消息。
wyj@wyj-virtual-machine:~/c_code$ valgrind --leak-check=yes ./valgrind_test
一开始是valgrind信息“==62414==”表示进程号
==62414== Memcheck, a memory error detector
==62414== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==62414== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==62414== Command: ./valgrind_test
程序访问非法地址的内存,无效写入
==62414== Invalid write of size 4
==62414== at 0x40054B: fun (valgrind_test.c:21)
==62414== by 0x400566: main (valgrind_test.c:28)
==62414== Address 0x5201068 is 0 bytes after a block of size 40 alloc'd
==62414== at 0x4C2AEC3: malloc (vg_replace_malloc.c:309)
==62414== by 0x40053E: fun (valgrind_test.c:20)
==62414== by 0x400566: main (valgrind_test.c:28)
==62414==
堆区情况:
==62414== HEAP SUMMARY:
==62414== in use at exit: 40 bytes in 1 blocks
==62414== total heap usage: 1 allocs, 0 frees, 40 bytes allocated
内存泄漏消息如下所示:
==62414== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==62414== at 0x4C2AEC3: malloc (vg_replace_malloc.c:309)
==62414== by 0x40053E: fun (valgrind_test.c:20)
==62414== by 0x400566: main (valgrind_test.c:28)
有几种泄漏; 两个最重要的类别是,肯定泄露(definitely lost),可能已经泄露(possibly lost)
==62414== LEAK SUMMARY:
==62414== definitely lost: 40 bytes in 1 blocks
==62414== indirectly lost: 0 bytes in 0 blocks
==62414== possibly lost: 0 bytes in 0 blocks
==62414== still reachable: 0 bytes in 0 blocks
==62414== suppressed: 0 bytes in 0 blocks
==62414==
==62414== For lists of detected and suppressed errors, rerun with: -s
==62414== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
关于Memcheck的错误消息说明:
http://valgrind.org/docs/manual/mc-manual.html#mc-manual.errormsgs
Valgrind直接与现有可执行文件一起使用。无需重新编译,重新链接或以其他方式修改要检查的程序。
使用方法:
wyj@wyj-virtual-machine:~/c_code$ valgrind --help
usage: valgrind [options] prog-and-args
valgrind [valgrind-options] your-prog [your-prog-options]
最重要的选项是--tool决定运行哪个Valgrind工具。例如,如果要ls -l使用内存检查工具Memcheck 运行该命令 ,请发出以下命令
wyj@wyj-virtual-machine:~$ valgrind --tool=memcheck ls -l
==62521== Memcheck, a memory error detector
==62521== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==62521== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==62521== Command: ls -l
==62521==
总用量 148
drwxrwxr-x 9 wyj wyj 4096 12月 26 2018 apue
drwxrwxr-x 5 wyj wyj 4096 8月 11 10:40 c_code
drwxrwxr-x 2 wyj wyj 4096 7月 31 16:56 crc
drwxrwxr-x 13 wyj wyj 4096 8月 11 10:12 Download
......
==62521==
==62521== HEAP SUMMARY:
==62521== in use at exit: 20,073 bytes in 29 blocks
==62521== total heap usage: 266 allocs, 237 frees, 94,000 bytes allocated
==62521==
==62521== LEAK SUMMARY:
==62521== definitely lost: 0 bytes in 0 blocks
==62521== indirectly lost: 0 bytes in 0 blocks
==62521== possibly lost: 0 bytes in 0 blocks
==62521== still reachable: 20,073 bytes in 29 blocks
==62521== suppressed: 0 bytes in 0 blocks
==62521== Rerun with --leak-check=full to see details of leaked memory
==62521==
==62521== For lists of detected and suppressed errors, rerun with: -s
==62521== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Memcheck是默认设置,可以不用加tool直接使用
wyj@wyj-virtual-machine:~$ valgrind ls -l
最常用的工具,用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc()/free()/new/delete的调用都会被捕获。所以,它能检测以下问题:
1.对未初始化内存的使用;
2.读/写释放后的内存块;
3.读/写超出malloc分配的内存块;
4.读/写不适当的栈中内存块;
5.内存泄漏,指向一块内存的指针永远丢失;
6.不正确的malloc/free或new/delete匹配;
7,memcpy()相关函数中的dst和src指针重叠。
这些问题往往是C/C++程序员最头疼的问题,Memcheck在这里帮上了大忙。
和gprof类似的分析工具,但它对程序的运行观察更是入微,能给我们提供更多的信息。和gprof不同,它不需要在编译源代码时附加特殊选项,但加上调试选项是推荐的。Callgrind收集程序运行时的一些数据,建立函数调用关系图,还可以有选择地进行cache模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate可以把这个文件的内容转化成可读的形式。
Cache分析器,它模拟CPU中的一级缓存I1,Dl和二级缓存,能够精确地指出程序中cache的丢失和命中。如果需要,它还能够为我们提供cache丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。
它主要用来检查多线程程序中出现的竞争问题。Helgrind寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。Helgrind实现了名为“Eraser”的竞争检测算法,并做了进一步改进,减少了报告错误的次数。不过,Helgrind仍然处于实验阶段。
堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。Massif能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。
此外,lackey和nulgrind也会提供。Lackey是小型工具,很少用到;Nulgrind只是为开发者展示如何创建一个工具。我们就不做介绍了。
valgrind --tool=helgrind prog-and-args使用多线程调试工具
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
void * thread_worker1(void *args) ;
typedef struct worker_ctx_s
{
int shared_var ;
pthread_mutex_t lock ;
} worker_ctx_t ;
void *thread_worker1(void *args)
{
worker_ctx_t *ctx = (worker_ctx_t *)args ;
if(!args)
{
printf("%s() get invalid arguments\n", __FUNCTION__) ; // __FUNCTION__ get function name
pthread_exit(NULL) ;
}
printf("Thread worker1 [%ld] start running...\n", pthread_self()) ;
/* 两次上锁导致死锁 */
pthread_mutex_lock(&(ctx->lock)) ;
pthread_mutex_lock(&(ctx->lock)) ;
pthread_mutex_unlock( &ctx->lock ) ;
sleep(1) ;
printf("Thread worker1 exit...\n") ;
pthread_exit(NULL) ;
return NULL ;
}
int main(int argc, char **argv)
{
worker_ctx_t worker_ctx ;
pthread_t tid ;
pthread_attr_t thread_attr ;
worker_ctx.shared_var = 1000 ;
pthread_mutex_init(&worker_ctx.lock, NULL) ;
if ( pthread_attr_init(&thread_attr) != 0 )
{
printf("pthread_attr_init() failed: %s\n", strerror(errno)) ;
return -1 ;
}
if ( pthread_attr_setstacksize( &thread_attr, 1024*120) != 0 )
{
printf("pthread_setstacksize() failure: %s\n", strerror(errno) ) ;
return -2 ;
}
if ( pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) != 0 )
{
printf("pthread_attr_setdetachstate() error: %s\n", strerror(errno)) ;
return -3 ;
}
/* Create thread */
pthread_create(&tid, &thread_attr, thread_worker1, &worker_ctx);
if ( pthread_attr_destroy(&thread_attr) != 0 )
{
printf("pthread_attr_destroy() failed :%s\n", strerror(errno)) ;
return -4 ;
}
sleep(5) ;
return 0 ;
}
wyj@wyj-virtual-machine:~/c_code$ gcc -g valgrind_test_helgrand.c -o valgrind_test_helgrand -lpthread
wyj@wyj-virtual-machine:~/c_code$ valgrind --tool=helgrind ./valgrind_test_helgrand
==63432== Helgrind, a thread error detector
==63432== Copyright (C) 2007-2017, and GNU GPL'd, by OpenWorks LLP et al.
==63432== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==63432== Command: ./valgrind_test_helgrand
程序打印的消息
Thread worker1 [67376896] start running...
==63432== ---Thread-Announcement------------------------------------------
两个线程被创建
==63432== Thread #2 was created
==63432== at 0x5158FFE: clone (clone.S:74)
==63432== by 0x4E44199: do_clone.constprop.3 (createthread.c:75)
==63432== by 0x4E458BA: create_thread (createthread.c:245)
==63432== by 0x4E458BA: pthread_create@@GLIBC_2.2.5 (pthread_create.c:611)
==63432== by 0x4C31CBA: pthread_create_WRK (hg_intercepts.c:427)
==63432== by 0x4C32D98: pthread_create@* (hg_intercepts.c:460)
==63432== by 0x400CA2: main (valgrind_test_helgrand.c:84)
==63432==
==63432== ----------------------------------------------------------------
尝试重新锁定本身已持有的非递归锁
==63432== Thread #2: Attempt to re-lock a non-recursive lock I already hold
==63432== at 0x4C2F259: mutex_lock_WRK (hg_intercepts.c:899)
==63432== by 0x4C3317D: pthread_mutex_lock (hg_intercepts.c:925)
==63432== by 0x400B5C: thread_worker1 (valgrind_test_helgrand.c:44)
==63432== by 0x4C31EAE: mythread_wrapper (hg_intercepts.c:389)
==63432== by 0x4E45183: start_thread (pthread_create.c:312)
==63432== by 0x515903C: clone (clone.S:111)
锁之前已经获取过
==63432== Lock was previously acquired
==63432== at 0x4C2F320: mutex_lock_WRK (hg_intercepts.c:909)
==63432== by 0x4C3317D: pthread_mutex_lock (hg_intercepts.c:925)
==63432== by 0x400B4C: thread_worker1 (valgrind_test_helgrand.c:43)
==63432== by 0x4C31EAE: mythread_wrapper (hg_intercepts.c:389)
==63432== by 0x4E45183: start_thread (pthread_create.c:312)
==63432== by 0x515903C: clone (clone.S:111)
==63432==
==63432== ----------------------------------------------------------------
退出线程时任保持一个锁
==63432== Thread #2: Exiting thread still holds 1 lock
==63432== at 0x4E4BF1C: __lll_lock_wait (lowlevellock.S:135)
==63432== by 0x4E47648: _L_lock_909 (pthread_mutex_lock.c:151)
==63432== by 0x4E4746E: pthread_mutex_lock (pthread_mutex_lock.c:79)
==63432== by 0x4C2F2BB: mutex_lock_WRK (hg_intercepts.c:902)
==63432== by 0x4C3317D: pthread_mutex_lock (hg_intercepts.c:925)
==63432== by 0x400B5C: thread_worker1 (valgrind_test_helgrand.c:44)
==63432== by 0x4C31EAE: mythread_wrapper (hg_intercepts.c:389)
==63432== by 0x4E45183: start_thread (pthread_create.c:312)
==63432== by 0x515903C: clone (clone.S:111)
==63432==
==63432==
==63432== Use --history-level=approx or =none to gain increased speed, at
==63432== the cost of reduced accuracy of conflicting-access information
==63432== For lists of detected and suppressed errors, rerun with: -s
==63432== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 26 from 25)
好好分析valgrind调试信息就能知道有没有错误,错误在哪,错误原因是什么,也是刚了解valgrind这个工具,之后再慢慢探索它的功能。