valgrind小结

  • Post author:
  • Post category:其他

一、valgrind简介

Valgrind是一款用于内存调试、内存泄漏检测以及性能分析、检测线程错误的软件开发工具。

Valgrind一般包含下列工具:

1.Memcheck

最常用的工具,用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc()/free()/new/delete的调用都会被捕获。所以,它能检测以下问题:

1.对未初始化内存的使用;

2.读/写释放后的内存块;

3.读/写超出malloc分配的内存块;

4.读/写不适当的栈中内存块;

5.内存泄漏,指向一块内存的指针永远丢失;

6.不正确的malloc/free或new/delete匹配;

7,memcpy()相关函数中的dst和src指针重叠。

这些问题往往是C/C++程序员最头疼的问题,Memcheck在这里帮上了大忙。

memcheck 工具的常用选型

1、leak-check

–leak-check=<no|summary|yes|full> [default: summary]

用于控制内存泄漏检测力度。

no,不检测内存泄漏;

summary,仅报告总共泄漏的数量,不报告具体泄漏位置;

yes/full,报告泄漏总数、泄漏的具体位置。

2、show-reachable

–show-reachable=<yes|no> [default: no]

用于控制是否检测控制范围之外的泄漏,比如全局指针、static指针等。

3、undef-value-errors

–undef-value-errors=<yes|no> [default: yes]

用于控制是否检测代码中使用未初始化变量的情况。

4、其他选项

–log-file=filename 将结果输出到文件。

–log-socket=192.168.0.1:12345 输出到网络。

–trace-children=<yes|no> [default: no]

–track-fds=<yes|no> [default: no]

–log-fd=<number> [default: 2, stderr]

–xml=<yes|no> [default: no]

–num-callers=<number> [default: 12]

–show-below-main=<yes|no> [default: no]

内存检查选项:

–leak-check=<no|summary|yes|full> [default: summary]

当这个选项打开时,当客户程序结束时查找内存泄漏。内存泄漏意味着有用malloc分配内存块,但是没有用free释放,而且没有指针指向这块内存。这样的内存块永远不能被程序释放,因为没有指针指向它们。如果设置为summary,Valgrind会报告有多少内存泄漏发生了。如果设置为full或yes,Valgrind给出每一个独立的泄漏的详细信息。

–show-reachable=<yes|no> [default: no]

当这个选项关闭时,内存泄漏检测器只显示没有指针指向的内存块,或者只能找到指向块中间的指针。当这个选项打开时,内存泄漏检测器还报告有指针指向的内存块。这些块是最有可能出现内存泄漏的地方。你的程序可能,至少在原则上,应该在退出前释放这些内存块。这些有指针指向的内存块和没有指针指向的内存块,或者只有内部指针指向的块,都可能产生内存泄漏,因为实际上没有一个指向块起始的指针可以拿来释放,即使你想去释放它。

–leak-resolution=<low|med|high> [default: low]

在做内存泄漏检查时,确定memcheck将怎么样考虑不同的栈是相同的情况。当设置为low时,只需要前两层栈匹配就认为是相同的情况;当设置为med,必须要四层栈匹配,当设置为high时,所有层次的栈都必须匹配。对于hardcore内存泄漏检查,你很可能需要使用–leak-resolution=high和–num-callers=40或者更大的数字。注意这将产生巨量的信息,这就是为什么默认选项是四个调用者匹配和低分辨率的匹配。注意–leak-resolution= 设置并不影响memcheck查找内存泄漏的能力。它只是改变了结果如何输出。

–freelist-vol=<number> [default: 5000000]

当客户程序使用free(C中)或者delete(C++)释放内存时,这些内存并不是马上就可以用来再分配的。这些内存将被标记为不可访问的,并被放到一个已释放内存的队列中。这样做的目的是,使释放的内存再次被利用的点尽可能的晚。这有利于memcheck在内存块释放后这段重要的时间检查对块不合法的访问。这个选项指定了队列所能容纳的内存总容量,以字节为单位。默认的值是5000000字节。增大这个数目会增加memcheck使用的内存,但同时也增加了对已释放内存的非法使用的检测概率。

–workaround-gcc296-bugs=<yes|no> [default: no]

当这个选项打开时,假定读写栈指针以下的一小段距离是gcc 2.96的bug,并且不报告为错误。距离默认为256字节。注意gcc 2.96是一些比较老的Linux发行版(RedHat 7.X)的默认编译器,所以你可能需要使用这个选项。如果不是必要请不要使用这个选项,它可能会使一些真正的错误溜掉。一个更好的解决办法是使用较新的,修正了这个bug的gcc/g++版本。

–partial-loads-ok=<yes|no> [default: no]

控制memcheck如何处理从地址读取时字长度,字对齐,因此哪些字节是可以寻址的,哪些是不可以寻址的。当设置为yes是,这样的读取并不抛出一个寻址错误。而是从非法地址读取的V字节显示为未定义,访问合法地址仍然是像平常一样映射到内存。设置为no时,从部分错误的地址读取与从完全错误的地址读取同样处理:抛出一个非法地址错误,结果的V字节显示为合法数据。注意这种代码行为是违背ISO C/C++标准,应该被认为是有问题的。如果可能,这种代码应该修正。这个选项应该只是做为一个最后考虑的方法。

–undef-value-errors=<yes|no> [default: yes]

控制memcheck是否检查未定义值的危险使用。当设为yes时,Memcheck的行为像Addrcheck, 一个轻量级的内存检查工具,是Valgrind的一个部分,它并不检查未定义值的错误。使用这个选项,如果你不希望看到未定义值错误。

2.Callgrind

和gprof类似的分析工具,但它对程序的运行观察更是入微,能给我们提供更多的信息。和gprof不同,它不需要在编译源代码时附加特殊选项,但加上调试选项是推荐的。Callgrind收集程序运行时的一些数据,建立函数调用关系图,还可以有选择地进行cache模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate可以把这个文件的内容转化成可读的形式。

3.Cachegrind

Cache分析器,它模拟CPU中的一级缓存I1,Dl和二级缓存,能够精确地指出程序中cache的丢失和命中。如果需要,它还能够为我们提供cache丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。

4.Helgrind

它主要用来检查多线程程序中出现的竞争问题。Helgrind寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。Helgrind实现了名为“Eraser”的竞争检测算法,并做了进一步改进,减少了报告错误的次数。不过,Helgrind仍然处于实验阶段。

5. Massif

堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。Massif能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。

此外,lackey和nulgrind也会提供。Lackey是小型工具,很少用到;Nulgrind只是为开发者展示如何创建一个工具。

二、安装valgrind

wget https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2

:~/Download$ tar -xjvf valgrind-3.15.0.tar.bz2

:~/Download$ cd valgrind-3.15.0/

因为valgrind支持多个平台,根据当前主机配置valgrind

:~/Download/valgrind-3.15.0$ ./configure

./configure之后就有makefile出现,接着就是make编译,安装

:~/Download/valgrind-3.15.0$ make

:~/Download/valgrind-3.15.0$ sudo make install

查看一下版本看看是否安装好

:~/Download/valgrind-3.15.0$ valgrind –version

valgrind-3.15.0

三、valgrind使用

1、内存泄漏

#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的错误消息包含确切的行号。

:~/c_code$ gcc -g valgrind_test.c -o valgrind_test

正常运行

:~/c_code$ ./valgrind_test

Memcheck是默认工具。–leak-check 选项打开详细的内存泄漏检测器。程序运行速度会比正常情况慢很多(例如20到30倍),并且会占用更多内存。Memcheck将发出有关内存错误和检测到的泄漏的消息。

:~/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)

2线程锁

#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 ;
}

:~/c_code$ gcc -g valgrind_test_helgrand.c -o valgrind_test_helgrand -lpthread

:~/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)


版权声明:本文为wteruiycbqqvwt原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。