debug工具Valgrind使用简介

腾讯teg挂了之后昨天又被其他部门捞起来了,面了20分钟左右,前面基本畅通无阻,但是问到如何检测内存泄漏的时候懵比了…………卒

介绍一个比较实用的工具 Valgrind,英文海星的朋友可以点这个 链接。一个新手教程。

Valgrind它在实质上还是一个debug工具集合,而其中最受欢迎就是 memcheck,用于检测内存泄漏。
其所有检测内容包括(个人总结

  • 内存泄漏
  • 使用未初始化的内存
  • 内存越界
  • 读写已经释放的内存
  • 重复释放

内存泄漏

valgrind默认使用 memcheck参数来检查内存泄漏。所以一般只要 valgrind 程序名 即可。

比如源程序

#include <cstring>

void func()
{
    char* ch = new char[10];
    ch[0]='1';
}

int main()
{
    func();
    return 0;
}

对其进行检查

╰─○ valgrind ./learn
==26826== Memcheck, a memory error detector
==26826== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==26826== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==26826== Command: ./learn
==26826== 
==26826== 
==26826== HEAP SUMMARY:
==26826==     in use at exit: 10 bytes in 1 blocks
==26826==   total heap usage: 2 allocs, 1 frees, 72,714 bytes allocated   <-- 注意看这里
==26826== 
==26826== LEAK SUMMARY:
==26826==    definitely lost: 10 bytes in 1 blocks
==26826==    indirectly lost: 0 bytes in 0 blocks
==26826==      possibly lost: 0 bytes in 0 blocks
==26826==    still reachable: 0 bytes in 0 blocks
==26826==         suppressed: 0 bytes in 0 blocks
==26826== Rerun with --leak-check=full to see details of leaked memory <-- 加参数详细查看
==26826== 
==26826== For counts of detected and suppressed errors, rerun with: -v
==26826== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
╰─○ valgrind --leak-check=full ./learn
==28039== Memcheck, a memory error detector
==28039== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==28039== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==28039== Command: ./learn
==28039== 
==28039== 
==28039== HEAP SUMMARY:
==28039==     in use at exit: 10 bytes in 1 blocks
==28039==   total heap usage: 2 allocs, 1 frees, 72,714 bytes allocated
==28039== 
==28039== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1   <-- 注意看这里
==28039==    at 0x4C2DC6F: operator new[](unsigned long) (vg_replace_malloc.c:423)
==28039==    by 0x1087BB: func() (learn.cpp:17)  <-- 定位
==28039==    by 0x1087D2: main (learn.cpp:23)
==28039== 
==28039== LEAK SUMMARY: <-- 泄漏总览
==28039==    definitely lost: 10 bytes in 1 blocks
==28039==    indirectly lost: 0 bytes in 0 blocks
==28039==      possibly lost: 0 bytes in 0 blocks
==28039==    still reachable: 0 bytes in 0 blocks
==28039==         suppressed: 0 bytes in 0 blocks
==28039== 
==28039== For counts of detected and suppressed errors, rerun with: -v
==28039== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

其他错误

一次一个错误太麻烦了,再写个错误比较多的程序。

#include <cstring>
#include <vector>

void func()
{
    char *str1 = new char[10], *str2 = new char[20];
    strcat(str2,"123");//str2未初始化

    str1[12] = 'a'; //越界

    delete[] str1;

    str1[2] = 'b'; //读写释放内存

    delete[] str1; //重复释放
}

int main()
{
    func();
    return 0;
}

检测如下
–track-origins=yes 参数会将未初始化的错误和其他错误定位

╰─○ valgrind --leak-check=full --track-origins=yes  ./learn
==28876== Memcheck, a memory error detector
==28876== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==28876== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==28876== Command: ./learn
==28876== 
==28876== Conditional jump or move depends on uninitialised value(s)   <-- 未初始化
==28876==    at 0x108824: func() (learn.cpp:18)
==28876==    by 0x108884: main (learn.cpp:31)
==28876==  Uninitialised value was created by a heap allocation
==28876==    at 0x4C2DC6F: operator new[](unsigned long) (vg_replace_malloc.c:423)
==28876==    by 0x108809: func() (learn.cpp:17) <-- 具体定位
==28876==    by 0x108884: main (learn.cpp:31)
==28876== 
==28876== Invalid write of size 1 <-- 无效写
==28876==    at 0x108845: func() (learn.cpp:20)
==28876==    by 0x108884: main (learn.cpp:31)
==28876==  Address 0x5aebc8c is 2 bytes after a block of size 10 alloc'd
==28876==    at 0x4C2DC6F: operator new[](unsigned long) (vg_replace_malloc.c:423)
==28876==    by 0x1087FB: func() (learn.cpp:17)
==28876==    by 0x108884: main (learn.cpp:31)
==28876== 
==28876== Invalid write of size 1 <-- 无效写,因为写了已经释放的内存
==28876==    at 0x108863: func() (learn.cpp:24)
==28876==    by 0x108884: main (learn.cpp:31)
==28876==  Address 0x5aebc82 is 2 bytes inside a block of size 10 free'd
==28876==    at 0x4C2EB0B: operator delete[](void*) (vg_replace_malloc.c:621) <-- 内存释放
==28876==    by 0x10885A: func() (learn.cpp:22)
==28876==    by 0x108884: main (learn.cpp:31)
==28876==  Block was alloc'd at
==28876==    at 0x4C2DC6F: operator new[](unsigned long) (vg_replace_malloc.c:423) <-- 内存申请
==28876==    by 0x1087FB: func() (learn.cpp:17)
==28876==    by 0x108884: main (learn.cpp:31)
==28876== 
==28876== Invalid free() / delete / delete[] / realloc() <-- 无效释放, 重复释放
==28876==    at 0x4C2EB0B: operator delete[](void*) (vg_replace_malloc.c:621)
==28876==    by 0x108878: func() (learn.cpp:26)
==28876==    by 0x108884: main (learn.cpp:31)
==28876==  Address 0x5aebc80 is 0 bytes inside a block of size 10 free'd
==28876==    at 0x4C2EB0B: operator delete[](void*) (vg_replace_malloc.c:621)
==28876==    by 0x10885A: func() (learn.cpp:22)
==28876==    by 0x108884: main (learn.cpp:31)
==28876==  Block was alloc'd at
==28876==    at 0x4C2DC6F: operator new[](unsigned long) (vg_replace_malloc.c:423)
==28876==    by 0x1087FB: func() (learn.cpp:17)
==28876==    by 0x108884: main (learn.cpp:31)
==28876== 
==28876== 
==28876== HEAP SUMMARY:
==28876==     in use at exit: 20 bytes in 1 blocks
==28876==   total heap usage: 3 allocs, 3 frees, 72,734 bytes allocated
==28876== 
==28876== 20 bytes in 1 blocks are definitely lost in loss record 1 of 1
==28876==    at 0x4C2DC6F: operator new[](unsigned long) (vg_replace_malloc.c:423)
==28876==    by 0x108809: func() (learn.cpp:17)
==28876==    by 0x108884: main (learn.cpp:31)
==28876== 
==28876== LEAK SUMMARY:
==28876==    definitely lost: 20 bytes in 1 blocks
==28876==    indirectly lost: 0 bytes in 0 blocks
==28876==      possibly lost: 0 bytes in 0 blocks
==28876==    still reachable: 0 bytes in 0 blocks
==28876==         suppressed: 0 bytes in 0 blocks
==28876== 
==28876== For counts of detected and suppressed errors, rerun with: -v
==28876== ERROR SUMMARY: 5 errors from 5 contexts (suppressed: 0 from 0)

总的来说,使用上还是挺简单的,并且效果不凡。
不过需要注意的地方是,本身检测就需要多运行几次,而多加参数也会增加次数,对于大程序需要注意。