当前位置: 首页 > 工具软件 > Valgrind > 使用案例 >

valgrind简介与使用

魏俊茂
2023-12-01

一、valgrind简介

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

Valgrind 是运行在Linux 上的多用途代码剖析和内存调试软件。主要包括Memcheck、Callgrind、Cachegrind 等工具,每个工具都能完成一项任务调试、检测或分析。可以检测内存泄露、线程违例和Cache 的使用等。Valgrind 基于仿真方式对程序进行调试,它先于应用程序获取实际处理器的控制权,并在实际处理器的基础上仿真一个虚拟处理器,并使应用程序运行于这个虚拟处理器之上,从而对应用程序的运行进行监视。应用程序并不知道该处理器是虚拟的还是实际的,已经编译成二进制代码的应用程序并不用重新进行编译,Valgrind 直接解释二进制代码使得应用程序基于它运行,从而能够检查内存操作时可能出现的错误。所以在Valgrind下运行的程序运行速度要慢得多,而且使用的内存要多得多 - 例如,Memcheck工具下的程序是正常情况的两倍多。因此,最好在性能好的机器上使用Valgrind。

 

二、安装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

 

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在这里帮上了大忙。

 

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 --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这个工具,之后再慢慢探索它的功能。

 类似资料: