zoukankan      html  css  js  c++  java
  • valgrind使用

    参数配置

    gcc

    • -g: 增加调试信息,供valgrind精确定位。
    • -O0:关闭gcc优化;优化产生的代码可能会造成valgrind误判。

    valgrind

    • --leak-check=full

      no: 不进行内存泄露检测; summary: 显示内存泄露情况; full:不仅显示内存泄露,还显示出错代码。

    • --show-reachable=yes

      详细显示still reachable 和 indirectly lost两种类型的内存泄露,默认不显示;如case1和case4。

    内存泄露

    内存泄露: 由于疏忽或错误造成程序未能释放已经不能再使用的内存。 —— 维基百科

    指针

    start-pointer: 指向内存起始位置 
    interior-pointer: 指向内存中间位置



    泄露类型

    possibly lost: 指针指向内存的内部位置。



    still reachable: 程序运行结束后,虽然没有被释放,但仍然可以访问。



    definitely lost:内存无法被访问。



    indirectly lost:虽然有地址指向该空间,但已经无法被访问了。



    泄露举例

         Pointer chain            AAA Leak Case   BBB Leak Case
    ------------- ------------- -------------
    (1) RRR ------------> BBB DR
    (2) RRR ---> AAA ---> BBB DR IR
    (3) RRR BBB DL
    (4) RRR AAA ---> BBB DL IL
    (5) RRR ------?-----> BBB (y)DR, (n)DL
    (6) RRR ---> AAA -?-> BBB DR (y)IR, (n)DL
    (7) RRR -?-> AAA ---> BBB (y)DR, (n)DL (y)IR, (n)IL
    (8) RRR -?-> AAA -?-> BBB (y)DR, (n)DL (y,y)IR, (n,y)IL, (_,n)DL
    (9) RRR AAA -?-> BBB DL (y)IL, (n)DL

    Pointer chain legend:
    - RRR: a root set node or DR block
    - AAA, BBB: heap blocks
    - --->: a start-pointer
    - -?->: an interior-pointer

    Leak Case legend:
    - DR: Directly reachable
    - IR: Indirectly reachable
    - DL: Directly lost
    - IL: Indirectly lost
    - (y)XY: it's XY if the interior-pointer is a real pointer
    - (n)XY: it's XY if the interior-pointer is not a real pointer
    - (_)XY: it's XY in either case

    case1: RRR ---> BBB

    void *RRR;
    int main()
    {
    RRR = malloc(8);
    return 0;
    }

    ==1244== LEAK SUMMARY: 
    ==1244==  still reachable: 8 bytes in 1 blocks

    case2: RRR ---> AAA ---> BBB

    void **RRR;
    int main()
    {
    RRR = (void**)malloc(8);
    *RRR = malloc(8);
    return 0;
    }

    ==1345== LEAK SUMMARY: 
    ==1345==  still reachable: 16 bytes in 2 blocks

    case3:RRR    BBB

    int main()
    {
    void *RRR = malloc(8);
    return 0;
    }

    ==1400== LEAK SUMMARY: 
    ==1400==  definitely lost: 8 bytes in 1 blocks

    case4:RRR   AAA ---> BBB

    int main()
    {
    void **RRR = (void**)malloc(8);
    *RRR = malloc(8);
    return 0;
    }

    ==1461== LEAK SUMMARY: 
    ==1461==  definitely lost: 8 bytes in 1 blocks 
    ==1461==  indirectly lost: 8 bytes in 1 blocks

    case5:RRR -?-> BBB

    void *RRR;
    int main()
    {
    RRR = malloc(8);
    RRR = (char*)RRR + 2;
    return 0;
    }

    ==1530== LEAK SUMMARY: 
    ==1530==  possibly lost: 8 bytes in 1 blocks

    case6:RRR ---> AAA -?-> BBB

    void **RRR;
    int main()
    {
    RRR = (void**)malloc(8);
    *RRR = malloc(8);
    *RRR = (char*)(*RRR) + 2;

    return 0;
    }

    ==1587== LEAK SUMMARY: 
    ==1587==  possibly lost: 8 bytes in 1 blocks 
    ==1587==  still reachable: 8 bytes in 1 blocks

    case7:RRR -?-> AAA ---> BBB

    void **RRR;
    int main()
    {
    RRR = (void**)malloc(8);
    *RRR = malloc(8);
    RRR = (void**)((char*)RRR + 1);
    return 0;
    }

    ==1642== LEAK SUMMARY: 
    ==1642==  possibly lost: 16 bytes in 2 blocks

    case8:RRR -?-> AAA -?-> BBB

    void **RRR;
    int main()
    {
    RRR = (void**)malloc(8);
    *RRR = malloc(8);

    *RRR = ((char*)(*RRR) + 1);
    RRR = (void**)((char*)RRR + 1);
    return 0;
    }

    ==1776== LEAK SUMMARY: 
    ==1776==  possibly lost: 16 bytes in 2 blocks

    case9:RRR    AAA -?-> BBB

    int main()
    {
    void **RRR = (void**)malloc(8);
    *RRR = malloc(8);

    *RRR = ((char*)(*RRR) + 1);
    return 0;
    }

    ==3856== LEAK SUMMARY: 
    ==3856==  definitely lost: 8 bytes in 1 blocks 
    ==3856==  indirectly lost: 8 bytes in 1 blocks 

    内存错误

    读写越界

     8 int *arr = new int[2];
    9 arr[2] = 2;
    10 arr[0] = arr[2];
    11
    12 delete [] arr;

    ==2371== Invalid write of size 4 
    ==2371==  at 0x80485FD: main (test.cpp:9) 
    ==2371== 
    ==2371== Invalid read of size 4 
    ==2371==  at 0x8048607: main (test.cpp:10)

    NOTE: 如果在栈空间上申请数组arr, valgrind没有检测出读写越界。详见原理简述

    地址重叠

     8 char *str = new char[10];
    9 char *src = str;
    10 char *dst = str + 2;
    11 memcpy(dst, src, 4);
    12
    13 delete [] str;

    ==2413== Source and destination overlap in memcpy(0x433902a, 0x4339028, 4) 
    ==2413==  at 0x402EE13: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
    ==2413==  by 0x8048654: main (test.cpp:11)

    多次释放

     8  char *str = new char[10];
    9 char *rep = str;
    10
    11 delete [] str;
    12 delete [] rep;

    ==2440== Invalid free() / delete / delete[] / realloc() 
    ==2440==  at 0x402BD38: operator delete 
    ==2440==  by 0x8048623: main (test.cpp:12) 
    ==2440== Address 0x4339028 is 0 bytes inside a block of size 10 free’d 
    ==2440==  at 0x402BD38: operator delete 
    ==2440==  by 0x8048610: main (test.cpp:11) 
    ==2440== HEAP SUMMARY: 
    ==2440==  in use at exit: 0 bytes in 0 blocks 
    ==2440==  total heap usage: 1 allocs, 2 frees, 10 bytes allocated

    分配尺寸错误

     9  int *arr = new int[-1];
    10 delete [] arr;

    ==2496== Argument ‘size’ of function __builtin_vec_new has a fishy (possibly negative) value: -1 
    ==2496==  at 0x402ADFC: operator new (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
    ==2496==  by 0x80485F1: main (test.cpp:9)

    释放内部指针

    int main()
    {
    void *RRR = malloc(10);
    RRR = (char*)RRR + 1;

    free(RRR);
    }

    ==3953== HEAP SUMMARY: 
    ==3953== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1 
    ==3953==  at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
    ==3953==  by 0x8048461: main (inter.cpp:5) 
    ==3953== 
    ==3953== LEAK SUMMARY: 
    ==3953==  definitely lost: 10 bytes in 1 blocks

    分配与释放函数不一致

     9 int *arr = new int[10];
    10
    11 free(arr);

    ==2519== Mismatched free() / delete / delete [] 
    ==2519==  at 0x402B3D8: free 
    ==2519==  by 0x8048601: main (test.cpp:11) 
    ==2519== Address 0x4339028 is 0 bytes inside a block of size 40 alloc’d 
    ==2519==  at 0x402ADFC: operator new 
    ==2519==  by 0x80485F1: main (test.cpp:9)

    原理简述

    valgrind memcheck 在内部模拟一个CPU环境,所有的数据处理流程,都在内部CPU进行模拟。

    valid-value (V) bits

    valgrind为程序数据流中的每个bit都关联了一个valid-value bit(简称V),用来检测该bit是否有效。比如int val = 4,当CPU从内存中加载val时,会同时从valid-value位图中读取32个bit;赋值操作发生时,该32个valid-value bit被设置为有效;当变量回写内存时,valid-value也会同时被写入内存。

    NOTE: 数据中每个bit都会有一个V bit 与之关联,而不是对每个字节进行监控。X = X | (1 << 3) 只对某个bit赋值。

    V 设置时机 
    变量被赋值时。

    V 检测时机

    • 变量值用于生产地址
    • 变量值参影响程序运行流
    • 变量值用于系统调用

    case1

    int main()
    {
    int *p;
    int result = *p;
    }

    ==4413== Use of uninitialised value of size 4 
    ==4413==  at 0x80483F6: main (address.cpp:6)

    case2

    int main()
    {
    int result;
    if (result == 6)
    {
    cout << "got" << endl;
    }
    }

    ==4436== Conditional jump or move depends on uninitialised value(s) 
    ==4436==  at 0x80486BB: main (address.cpp:8) 

    如果未初始化变量不进行分支决策,则不会报错。

    int main()
    {
    int arr[2];
    int result;

    for (size_t i = 0; i < 2; i++)
    {
    result += arr[i];
    }

    return 0;
    }

    ==1169== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

    case3

    int main()
    {
    int result;
    exit(result);
    }

    ==4450== Syscall param exit_group(status) contains uninitialised byte(s) 
    ==4450==  at 0x41DBBD4: _Exit (_exit.S:33)

    疑问: 为什么不是每次使用变量的时候,都进行有效检测? 
    解释: 正如官方文档所说,C语言中涉及内存地址对齐

    struct S { int x; char c};

    struct S s1, s2;
    s1.x = 0;
    s1.c = ' ';

    s2 = s1;

    结构体S涉及内存对齐,大小为8个字节。s1中,末尾的3个字节V始终是无效,如果在赋值的时候也进行检测,则会报错,实际上却没有必要。

    valid-address (A) bits

    valgrind为程序中的每个地址都关联了一个valid-address(简称A),用于检测地址是否有效。每个字节对应一个V bit。

    A bit 设置时机

    • 程序启动时,全局数据区域设置为可访问。
    • 调用new/malloc时,将分配空间设置为可访问;delete/free时,设置为不可访问。
    • 函数调用栈,栈指针SPBP之间的位置,被设置为可访问。栈回收时,设置为不可访问。
    • 某些系统调用。如mmap,进行内存映射。

    函数栈

    int main()
    {
    int arr[2];
    arr[2] = 2;
    }

    ==1125== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) 
    NOTE: 栈上的读写越界检测不出来

    A bit 检测时机 
    当读写内存时,查询A bit,检测读写地址是否有效。

    V & A

    V: 数据是否有效 
    A: 地址是否有效

    NOTE: calloc 会同时设置A & V bit,calloc返回后,将区间内容设置为0。

  • 相关阅读:
    Linux下查找软件,rpm命令 dpkg命令 apt命令
    python3 requests的content和text方法
    python3爬虫超简单实例
    python3 爬取深圳主板公司名称,公司网址
    python3 类的学习
    ubuntu16.04同时使用 pyhton2.7和3.5,并随意切换
    (4)puppet常用的资源及其常用属性
    (3)puppet清单定义资源的语法
    (2)puppet单机测试命令apply
    lvs+keepalived高可用负载均衡
  • 原文地址:https://www.cnblogs.com/thammer/p/8583373.html
Copyright © 2011-2022 走看看