内存调试利器 valgrind

本文介绍了如何使用valgrind这一内存调试工具来检测C/C++程序中的内存错误,如未初始化内存、内存泄漏、堆栈使用问题等。通过示例展示了valgrind的Memcheck、Cachegrind等工具的使用方法及其在检测内存问题时的效果。

新入职的员工往往会遇到的第一个项目是解决一个已有项目的bug,C/C++的BUG常常是由C和C++的细节程序员掌握不到位,或者处理不到位造成的,最常见的错误就是内存相关的错误,对于新手而言,去单纯的review代码无异于大海捞针,一脸茫然。

现在给大家介绍一种调试内存BUG的方式——valgrind。


valgrind官网:

http://www.valgrind.org/


安装:

1.原码安装

wget http://www.valgrind.org/downloads/valgrind-3.10.1.tar.bz2

tar jxvf  valgrind-3.10.1.tar.bz2

cd  valgrind-3.10.1

./configure

make

make install


2.ubuntu下直接apt-get安装

sudo apt-get install valgrind


官网介绍简单翻译过来

Valgrind包括如下一些工具:

(1) Memcheck ,这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。这也是本文将重点介绍的部分。

(2) Callgrind ,它主要用来检查程序中函数调用过程中出现的问题。

(3) Cachegrind ,它主要用来检查程序中缓存使用出现的问题。

(4) Helgrind ,它主要用来检查多线程程序中出现的竞争问题。

(5) Massif ,它主要用来检查程序中堆栈使用中出现的问题。

(6) Extension ,可以利用core提供的功能,自己编写特定的内存调试工具。


其中大家用的最多的就是memcheck。

常见的错误类型


下面针对上述情况进行代码级别使用演示,示例代码C/C++效果相同,暂且选用cpp文件,用g++编译器。

valgrind支持的命令参数:valgrind --help

编译命令:g++ demo.cpp -g -o a.out

测试命令:valgrind  --tool=memcheck  --leak-check=full  --undef-value-errors=yes  --track-fds=yes  ./a.out 

1.使用未初始化内存。

<span style="font-size:18px;">#include <iostream>
using namespace std;

int main(void)
{
    int sum, i;
    for (i = 0; i < 100; i++)
        sum += i;

    cout << sum << endl;
    return 0;
}
</span>
输出结果

==4802== Memcheck, a memory error detector
==4802== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==4802== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==4802== Command: ./a.out
==4802== 
==4802== Conditional jump or move depends on uninitialised value(s)

...

==4802== FILE DESCRIPTORS: 3 open at exit.
==4802== Open file descriptor 2: /dev/pts/0
==4802==    <inherited from parent>
==4802== 
==4802== Open file descriptor 1: /dev/pts/0
==4802==    <inherited from parent>
==4802== 
==4802== Open file descriptor 0: /dev/pts/0
==4802==    <inherited from parent>
==4802== 
==4802== 
==4802== HEAP SUMMARY:
==4802==     in use at exit: 0 bytes in 0 blocks
==4802==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==4802== 
==4802== All heap blocks were freed -- no leaks are possible
==4802== 
==4802== For counts of detected and suppressed errors, rerun with: -v
==4802== Use --track-origins=yes to see where uninitialised values come from
==4802== ERROR SUMMARY: 10 errors from 4 contexts (suppressed: 0 from 0)


堆上内存未初始化使用,memcheck可检测出

<span style="font-size:18px;">#include <iostream>
using namespace std;

int main(void)
{
    char *p = new char[20];

    cout << p[1] << endl;
    delete []p;
    return 0;
}<span style="color:#ff0000;">
</span></span>



2.内存读写越界。

经测试,栈和数据段的越界检测不出来,只有等出现段错误的时候通过core文件去检查。

malloc/new堆空间越界操作可以检测出。

<span style="font-size:18px;">#include <iostream>
using namespace std;

int main(void)
{
//  char *p = new char[20];
    char p [20];
    p[21] = 'X';

    cout << p[21] << endl;
//  delete [] p;
    return 0;
}
</span>


3.内存覆盖。

<span style="font-size:18px;">#include <iostream>
#include <cstring>
using namespace std;

int main(void)
{
    /*  
    char str[] = "hello";
    strcpy(str, str+2);
    */

    char *str = new char[20];
    strcpy(str, "hello");
    strcpy(str, str+2);

    cout << str << endl;
    delete [] str;
    return 0;
}
</span>

4.malloc申请delete释放,new申请free释放。

<span style="font-size:18px;">#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;

int main1(void)
{

    char *str = new char[20];
    strcpy(str, "hello");

    cout << str << endl;
    free(str);
 //   delete [] str;
    return 0;
}
int main(void)
{

    char *str = (char *)malloc(20);
    strcpy(str, "hello");

    cout << str << endl;
    delete [] str;
    return 0;
}
</span>


5.多次释放同一动态内存或释放无效内存地址。

<span style="font-size:18px;">#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;

int main(void)
{

    char *str = new char [20];
    strcpy(str, "hello");

    cout << str << endl;
    delete [] str;
    delete [] str;
    return 0;
}
</span>

6.释放动态内存后仍读写。

<span style="font-size:18px;">#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;

int main(void)
{

    char *str = new char [20];
    strcpy(str, "hello");

    cout << str << endl;
    delete [] str;

    str[0] = 'H';

    return 0;
}
</span>

7.文件指针内存泄露(没关闭文件)

<span style="font-size:18px;">#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;

int main(void)
{
    FILE *fp = fopen("file", "w");

    return 0;
}
</span>

输出结果:

==4922== FILE DESCRIPTORS: 4 open at exit.
==4922== Open file descriptor 3: file
==4922==    at 0x5226530: __open_nocancel (syscall-template.S:81)
==4922==    by 0x51B4E17: _IO_file_open (fileops.c:228)
==4922==    by 0x51B4E17: _IO_file_fopen@@GLIBC_2.2.5 (fileops.c:333)
==4922==    by 0x51A92F3: __fopen_internal (iofopen.c:90)
==4922==    by 0x400723: main (8.cpp:9)
==4922== 
==4922== Open file descriptor 2: /dev/pts/0
==4922==    <inherited from parent>
==4922== 
==4922== Open file descriptor 1: /dev/pts/0
==4922==    <inherited from parent>
==4922== 
==4922== Open file descriptor 0: /dev/pts/0
==4922==    <inherited from parent>
==4922== 
==4922== 
==4922== HEAP SUMMARY:
==4922==     in use at exit: 568 bytes in 1 blocks
==4922==   total heap usage: 1 allocs, 0 frees, 568 bytes allocated

==4922== 
==4922== LEAK SUMMARY:
==4922==    definitely lost: 0 bytes in 0 blocks
==4922==    indirectly lost: 0 bytes in 0 blocks
==4922==      possibly lost: 0 bytes in 0 blocks
==4922==    still reachable: 568 bytes in 1 blocks
==4922==         suppressed: 0 bytes in 0 blocks
==4922== Reachable blocks (those to which a pointer was found) are not shown.
==4922== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==4922== 
==4922== For counts of detected and suppressed errors, rerun with: -v
==4922== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)


介绍完memcheck再来简单说说cachegrind。cachegrind的具体介绍,请参见http://www.valgrind.org/docs/manual/cg-manual.html

测试用例:

<span style="font-size:18px;">#include <cstdio>
#include <cstdlib>

#define SIZE 1000

int main(void)
{
    int array[SIZE][SIZE] = {0};
    int i,j;

#if 1
    //情况一
    for (i = 0; i < SIZE; ++i) {
        for (j = 0; j < SIZE; ++j) {
            array[i][j] = i + j;
        }
    }   
#else
    //情况二
    for (j = 0; j < SIZE; ++j) {
        for (i = 0; i < SIZE; ++i) {
            array[i][j] = i + j;
        }
    }
#endif

    return 0;
}</span>


比较D1cache命中缺失率。

情况一输出:

xingwenpeng@ubuntu:~/project/valgrind$ g++ 9.cpp -g
xingwenpeng@ubuntu:~/project/valgrind$ valgrind --tool=cachegrind ./a.out 
==4963== Cachegrind, a cache and branch-prediction profiler
==4963== Copyright (C) 2002-2013, and GNU GPL'd, by Nicholas Nethercote et al.
==4963== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==4963== Command: ./a.out
==4963== 
--4963-- warning: L3 cache found, using its data for the LL simulation.
==4963== 
==4963== I   refs:      13,544,980
==4963== I1  misses:           684
==4963== LLi misses:           679
==4963== I1  miss rate:       0.00%
==4963== LLi miss rate:       0.00%
==4963== 
==4963== D   refs:       7,292,276  (6,028,594 rd   + 1,263,682 wr)
==4963== D1  misses:       126,821  (    1,316 rd   +   125,505 wr)
==4963== LLd misses:       126,644  (    1,169 rd   +   125,475 wr)
==4963== D1  miss rate:        1.7% (      0.0%     +       9.9%  )
==4963== LLd miss rate:        1.7% (      0.0%     +       9.9%  )
==4963== 
==4963== LL refs:          127,505  (    2,000 rd   +   125,505 wr)
==4963== LL misses:        127,323  (    1,848 rd   +   125,475 wr)
==4963== LL miss rate:         0.6% (      0.0%     +       9.9%  )

情况二输出:

xingwenpeng@ubuntu:~/project/valgrind$ valgrind --tool=cachegrind ./a.out 
==4992== Cachegrind, a cache and branch-prediction profiler
==4992== Copyright (C) 2002-2013, and GNU GPL'd, by Nicholas Nethercote et al.
==4992== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==4992== Command: ./a.out
==4992== 
--4992-- warning: L3 cache found, using its data for the LL simulation.
==4992== 
==4992== I   refs:      13,544,980
==4992== I1  misses:           684
==4992== LLi misses:           679
==4992== I1  miss rate:       0.00%
==4992== LLi miss rate:       0.00%
==4992== 
==4992== D   refs:       7,292,276  (6,028,594 rd   + 1,263,682 wr)
==4992== D1  misses:     1,064,337  (    1,316 rd   + 1,063,021 wr)
==4992== LLd misses:        99,148  (    1,169 rd   +    97,979 wr)
==4992== D1  miss rate:       14.5% (      0.0%     +      84.1%  )
==4992== LLd miss rate:        1.3% (      0.0%     +       7.7%  )
==4992== 
==4992== LL refs:        1,065,021  (    2,000 rd   + 1,063,021 wr)
==4992== LL misses:         99,827  (    1,848 rd   +    97,979 wr)
==4992== LL miss rate:         0.4% (      0.0%     +       7.7%  )

防盗链接原文出处:http://blog.youkuaiyun.com/u011673143/article/details/41781217




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值