1、valgrind安装及使用

一、valgrind介绍

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

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

  Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。Valgrind的体系结构如下图所示:

  

 

 

 

 

二、valgrind功能介绍

  • Valgrind包括如下一些工具:
  • Memcheck。检查程序中的内存问题,如泄漏、越界、非法指针等
  • Callgrind。它主要用来检查程序中函数调用过程中出现的问题。
  • Cachegrind。分析CPU的cache命中率、丢失率,用于进行代码优化。
  • Helgrind。它主要用来检查多线程程序中出现的竞争问题。
  • Massif。它主要用来检查程序中堆栈使用中出现的问题。
  • Extension。可以利用core提供的功能,自己编写特定的内存调试工具

三、valgrind安装

  下载地址:https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2

tar -jxvf valgrind-3.12.0.tar.bz2
cd valgrind-3.12.0
./configure
make
sudo make install

 

四、valgrind使用

(1)memcheck用法:

  用法:

    valgrind --tool=mencheck ./程序名
可以检测的问题:
1、使用未初始化的内存。
2、读/写释放后的内存块;
3、内存读写越界(数组访问越界/访问已经释放的内存),读/写超出malloc分配的内存块;
4、读/写不适当的栈中内存块;
5、内存泄漏,指向一块内存的指针永远丢失;
6、不正确的malloc/free或new/delete匹配(重复释放/使用不匹配的分配和释放函数);
7、内存覆盖,memcpy()相关函数中的dst和src指针重叠

 

使用valgrind来对内存泄漏进行检测,
valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --undef-value-errors=no --log-file=log ./可执行文件名
在log文件最后会有个summary,其中对内存泄露进行了分类,总共有五类:
(1)“definitely lost” 意味着你的程序一定存在内存泄露;
(2)”indirectly lost”意味着你的程序一定存在内存泄露,并且泄露情况和指针结构相关
(3) “possibly lost” 意味着你的程序一定存在内存泄露,除非你是故意进行着不符合常规的操作,例如将指针指向某个已分配内存块的中间位置。
(4) “still reachable” 意味着你的程序可能是没问题的,但确实没有释放掉一些本可以释放的内存。这种情况是很常见的,并且通常基于合理的理由。
(5)”suppressed” 意味着有些泄露信息被压制了。在默认的 suppression 文件中可以看到一些 suppression 相关设置。

其中,如果二叉树的根节点被判定为”definitely lost”,则其所有子节点将被判定为”indirectly lost”,而如果你正确修复了类型为 “definitely lost” 的根节点泄露,那么类型为 “indirectly lost” 的子节点泄露也会随着消失。

对于如上图所示的情况,posslbly lost其实并没有造成内存上的影响,如果想要过滤掉该类报告信息,可以加入--show-possibly-lost=no ,而对于”still reachable” ,同样可以通过--show-reachable=yes来控制是否输出相应的信息。

如果某些需要的库没有找到,用指令

export LD_LIBRARY_PATH=/usr/local/mysql/lib:$LD_LIBRARY_PATH

 使用范例:

#include <stdio.h>
#include <stdlib.h>

void fun(void)

{
        int *x = (int *)malloc(10 * sizeof(int));

        x[10] = 0; //问题1:堆块溢出

} //问题2:内存泄漏 -  x未释放

int main(int argc, char **argv)
{
        fun();

        return 0;
}

 

 

 

 

 (2)Cachegrind用法

用法:valgrind –tool=cachegrind ./程序名

 

 

 

 

 

 

 

(3)helgrind用法


用法:valgrind --tool=helgrind ./程序名

 

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

 

    

 

(4)Callgrind使用

  Callgrind可以生成程序性能分析的图形,首先来说说程序性能分析的工具吧,通常可以使用gnu自带的gprof,它的使用方法是:在编译程序时添加-pg参数,例如:

#include <stdio.h>  
#include <malloc.h>   
#include <unistd.h>
void test()  
{  
    sleep(1);  
}  
void f()  
{  
    int i;  
    for( i = 0; i < 5; i ++)  
        test();  
}  
int main()  
{  
    f();  
    printf("process is over! ");  
    return 0;  
}  

 

首先执行 gcc -pg -o test main.cpp,然后运行该程序./test,程序运行完成后会在当前目录下生成gmon.out文件(这个文件gprof在分析程序时需要),
再执行gprof ./test | gprof2dot.py |dot -Tpng -o report.png,打开report.png结果:

    

 

 显示test被调用了5次,程序中耗时所占百分比最多的是test函数。

 

  再来看 Callgrind 的生成调用图过程吧,执行:valgrind --tool=callgrind ./test,执行完成后在目录下生成"callgrind.out.XXX"的文件这是分析文件,可以直接利用:callgrind_annotate callgrind.out.XXX 打印结果,也可以使用:gprof2dot.py -f callgrind callgrind.out.XXX |dot -Tpng -o report.png 来生成图形化结果:

 

 

 

 

 

  它生成的结果非常详细,甚至连函数入口,及库函数调用都标识出来了。

 

posted @ 2022-08-07 10:01  zwj鹿港小镇  阅读(902)  评论(0)    收藏  举报