zoukankan      html  css  js  c++  java
  • 内存问题的排查工具和方法– Clang的AddressSanitizer

    1 概述

    Valgrind可以有效地监测处大多数内存问题,你肯定忍不住会想,既然c/c++的内存问题这么常见,为什么不在编译器中加入内存问题检测的功能呢? 很可惜,GCC中还目前还不支持内存检测,可喜的是,clang支持。这里我们看看如何用clang发现内存问题

    2 clang

    clang 是一个C、C++、Objective-C编程语言的编译器前端。它采用了底层虚拟机作为其后端。它的目标是提供一个GNU编译器套装(GCC)的替代品, 作者是克里斯·拉特纳,在苹果公司的赞助下进行开发。

    3 内存泄漏监测

    AddressSanitizer是clang中的一个内存错误检测器,它可以检测到以下问题:

    • Out-of-bounds accesses to heap, stack and globals
    • Use-after-free
    • Use-after-return (to some extent)
    • Double-free, invalid free
    • Memory leaks (experimental)

    使用clang编译代码时用-fsanitize=address就能打开AddressSanitizer工具,为了在检测到内存错误时打印出您的程序调用栈,需要在编译时加上选项 -fno-omit-frame-pointer选项,同时为了得出更清晰的调用栈信息,请用-O1选项编译程序。

    4 示例代码

    下面我用clang3.4做一个示例

     1:  int main()
     2:  {
     3:      char *p = malloc(sizeof(char) * 10);
     4:      if (p == NULL) {
     5:          return 0;
     6:      }
     7:  
     8:      struct elem *e = malloc(sizeof(struct elem));
     9:      if (e == NULL) {
    10:          free(p);
    11:          return 0;
    12:      }
    13:  
    14:      e->a = 10;
    15:      e->b = 10.10;
    16:      e->c = p;
    17:  
    18:      double *xx = &e->b;
    19:  
    20:      printf("%f
    ", *xx);
    21:  
    22:      free(e);
    23:  
    24:      printf("%f
    ", *xx);
    25:  
    26:      return 0;
    27:  }
    

    上面的代码中有两处问题,一是p未被释放,导致了内存泄漏;二是xx指向了一块被释放了的内存。我们看看怎么用clang检测这两个问题

    4.1 编译它

    1:  clang -O1 -g -fsanitize=address -fno-omit-frame-pointer -o core core.c
    

    4.2 用AddressSanitizer监测进程的内存泄漏

    直接运行core文件,它就会自动打印出检测到的内存错误

     1:  [cobbliu@kftest25 test]$ ./core
     2:  10.100000
     3:  =================================================================
     4:  ==11254==ERROR: AddressSanitizer: heap-use-after-free on address 0x60300000efe8 at pc 0x48211a bp 0x7fff2c776450 sp 0x7fff2c776448
     5:  READ of size 8 at 0x60300000efe8 thread T0
     6:      #0 0x482119 in main /home/cobbliu/test/core.c:35
     7:      #1 0x36a101ecdc in __libc_start_main (/lib64/libc.so.6+0x36a101ecdc)
     8:      #2 0x481f3c in _start (/home/cobbliu/test/core+0x481f3c)
     9:  
    10:  0x60300000efe8 is located 8 bytes inside of 24-byte region [0x60300000efe0,0x60300000eff8)
    11:  freed by thread T0 here:
    12:      #0 0x46bca9 in __interceptor_free /home/ads/build23_6u0_x64/workspace/t-coresystem-clang/label/build23_6u0_x64/t-coresystem-clang/llvm-3.4/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:64
    13:      #1 0x4820c0 in main /home/cobbliu/test/core.c:32
    14:      #2 0x36a101ecdc in __libc_start_main (/lib64/libc.so.6+0x36a101ecdc)
    15:  
    16:  previously allocated by thread T0 here:
    17:      #0 0x46be29 in malloc /home/ads/build23_6u0_x64/workspace/t-coresystem-clang/label/build23_6u0_x64/t-coresystem-clang/llvm-3.4/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:74
    18:      #1 0x48202a in main /home/cobbliu/test/core.c:18
    19:      #2 0x36a101ecdc in __libc_start_main (/lib64/libc.so.6+0x36a101ecdc)
    20:  
    21:  SUMMARY: AddressSanitizer: heap-use-after-free /home/cobbliu/test/core.c:35 main
    22:  Shadow bytes around the buggy address:
    23:    0x0c067fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    24:    0x0c067fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    25:    0x0c067fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    26:    0x0c067fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    27:    0x0c067fff9de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    28:  =>0x0c067fff9df0: fa fa fa fa fa fa fa fa fa fa fa fa fd[fd]fd fa
    29:    0x0c067fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    30:    0x0c067fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    31:    0x0c067fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    32:    0x0c067fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    33:    0x0c067fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    34:  Shadow byte legend (one shadow byte represents 8 application bytes):
    35:    Addressable:           00
    36:    Partially addressable: 01 02 03 04 05 06 07
    37:    Heap left redzone:     fa
    38:    Heap right redzone:    fb
    39:    Freed heap region:     fd
    40:    Stack left redzone:    f1
    41:    Stack mid redzone:     f2
    42:    Stack right redzone:   f3
    43:    Stack partial redzone: f4
    44:    Stack after return:    f5
    45:    Stack use after scope: f8
    46:    Global redzone:        f9
    47:    Global init order:     f6
    48:    Poisoned by user:      f7
    49:    ASan internal:         fe
    50:  ==11254==ABORTING
    

    可以看到,程序在提示core.c的第35行有个heap-use-after-free的错误,而且在最后还有个summary,把出错的代码位置和相应的栈信息打了出来。

    5 示例代码2

    上面的代码做一些小修改,我们看看它对double-free问题的检测

    1:  /...
    2:      struct elem *e2 = e;
    3:      free(e);
    4:      free(e2);
    5:  /...
    6:  }
    

    按照上面相同的方法编译并运行后,提示信息如下:

     1:  [cobbliu@kftest25 test]$ ./core
     2:  10.100000
     3:  =================================================================
     4:  ==11952==ERROR: AddressSanitizer: attempting double-free on 0x60300000efe0 in thread T0:
     5:      #0 0x46bca9 in __interceptor_free /home/ads/build23_6u0_x64/workspace/t-coresystem-clang/label/build23_6u0_x64/t-coresystem-clang/llvm-3.4/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:64
     6:      #1 0x4820bd in main /home/cobbliu/test/core.c:34
     7:      #2 0x36a101ecdc in __libc_start_main (/lib64/libc.so.6+0x36a101ecdc)
     8:      #3 0x481f3c in _start (/home/cobbliu/test/core+0x481f3c)
     9:  
    10:  0x60300000efe0 is located 0 bytes inside of 24-byte region [0x60300000efe0,0x60300000eff8)
    11:  freed by thread T0 here:
    12:      #0 0x46bca9 in __interceptor_free /home/ads/build23_6u0_x64/workspace/t-coresystem-clang/label/build23_6u0_x64/t-coresystem-clang/llvm-3.4/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:64
    13:      #1 0x4820b0 in main /home/cobbliu/test/core.c:33
    14:      #2 0x36a101ecdc in __libc_start_main (/lib64/libc.so.6+0x36a101ecdc)
    15:  
    16:  previously allocated by thread T0 here:
    17:      #0 0x46be29 in malloc /home/ads/build23_6u0_x64/workspace/t-coresystem-clang/label/build23_6u0_x64/t-coresystem-clang/llvm-3.4/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:74
    18:      #1 0x482026 in main /home/cobbliu/test/core.c:18
    19:      #2 0x36a101ecdc in __libc_start_main (/lib64/libc.so.6+0x36a101ecdc)
    20:  
    21:  SUMMARY: AddressSanitizer: double-free /home/ads/build23_6u0_x64/workspace/t-coresystem-clang/label/build23_6u0_x64/t-coresystem-clang/llvm-3.4/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:64 __interceptor_free
    22:  ==11952==ABORTING
    

    可以看到,AddressSanitizer报错,说core.c的34行有一个double-free的错误

    6 示例代码3

    上面的代码做一些小修改,把释放e的代码注释掉,看看它对内存泄漏的检测

    1:  /...
    2:      //free(e);
    3:  /...
    4:  }
    

    按照上面相同的方法编译并运行后,提示信息如下:

    1:  [cobbliu@kftest25 test]$ ./core
    2:  10.100000
    

    可以看到,对内存泄漏,AddressSanitizer无法检测出来 clang中有一个工具叫LeakSanitizer,它的设计目标是用来检测内存泄漏。直到3.7版,LeakSanitizer也是在实验阶段。

    7 AddressSanitizer的缺陷

    • AddressSanitizer工具编译的程序的堆栈和栈占用比原生程序的大。
    • AddressSanitizer不支持静态编译

    更新:gcc4.8版本之后,有了对AddressSanitizer的支持!

    Date: 2015-04-16T21:24+0800

    Author: Cobbliu

    Org version 7.9.3f with Emacs version 24

    Validate XHTML 1.0
  • 相关阅读:
    Leetcode:42. Trapping Rain Water
    Leetcode: 41. First Missing Positive
    Leetcode: 40. Combination Sum II
    多项式全家桶
    BZOJ 3878 [AHOI&JSOI2014]奇怪的计算器 (线段树)
    BZOJ 2959 长跑 (LCT+并查集)
    BZOJ 3028 食物 (生成函数+数学题)
    luogu P5504 [JSOI2011]柠檬
    hdu 6399 City Development
    luogu P3826 [NOI2017]蔬菜
  • 原文地址:https://www.cnblogs.com/cobbliu/p/4433341.html
Copyright © 2011-2022 走看看