1 概述
Valgrind能夠有效地監測處大多數內存問題,你確定忍不住會想,既然c/c++的內存問題這麼常見,爲何不在編譯器中加入內存問題檢測的功能呢? 很惋惜,GCC中還目前還不支持內存檢測,可喜的是,clang支持。這裏咱們看看如何用clang發現內存問題前端
2 clang
clang 是一個C、C++、Objective-C編程語言的編譯器前端。它採用了底層虛擬機做爲其後端。它的目標是提供一個GNU編譯器套裝(GCC)的替代品, 做者是克里斯·拉特納,在蘋果公司的贊助下進行開發。linux
3 內存泄漏監測
AddressSanitizer是clang中的一個內存錯誤檢測器,它能夠檢測到如下問題:c++
- 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\n", *xx); 21: 22: free(e); 23: 24: printf("%f\n", *xx); 25: 26: return 0; 27: }
上面的代碼中有兩處問題,一是p未被釋放,致使了內存泄漏;二是xx指向了一塊被釋放了的內存。咱們看看怎麼用clang檢測這兩個問題app
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,把出錯的代碼位置和相應的棧信息打了出來。ide
5 示例代碼2
上面的代碼作一些小修改,咱們看看它對double-free問題的檢測工具
1: /...
2: struct elem *e2 = e; 3: free(e); 4: free(e2); 5: /... 6: }
按照上面相同的方法編譯並運行後,提示信息以下:post
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的支持!