Valgrind 的使用

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
    

    picture

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)



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值