Valgrind ---内存调试,内存泄漏检测以及性能分析的软件开发工具
【一】用valgrind對代碼進行內(nèi)存檢測的時候,如果提示“Conditional jump or move depends on uninitialised value(s)”,有可能是某些變量未初始化造成的。
例如我遇到的兩處這樣的提示,一處是由于?struct tm 結(jié)構(gòu)體未初始化,另一處是由于 char tmp[512]未初始化造成的。要初始化,只需memset即可,這樣做之后,valgrind不再會提示有問題。
請在90%以上的時間里相信valgrind,而不是堅持自己的代碼不需要做任何改動。
【2】
- 1. 概述
- 2.?Valgrind
- 3. 內(nèi)存泄漏監(jiān)測
- 3.1. 示例代碼
- 3.2. 編譯它
- 3.3. 用Valgrind監(jiān)測進程的內(nèi)存泄漏
- 4. 懸掛指針
- 4.1. 示例代碼
- 4.2.?Valgrind運行結(jié)果
- 5. 多次釋放同一個指針
- 5.1. 示例代碼
- 5.2.?Valgrind?監(jiān)測
- 6.?Valgrind的優(yōu)缺點
- 6.1. Advantages
- 6.2. Disadvantages
- 7.?Valgrind的其他工具
- 7.1. Cachegrind
- 7.2. Callgrind
- 7.3. Helgrind
- 7.4. DRD
- 7.5. Massif
- 7.6. DHAT
- 8. 參考
1?概述
在用C/C++編程的時候,經(jīng)常會出現(xiàn)下面三種內(nèi)存問題:
- 內(nèi)存泄漏
- 懸掛指針
- 多次釋放同一塊內(nèi)存
本系列文章簡要介紹排查這三個問題的工具和方法,先看看Valgrind
2?Valgrind
Valgrind是一款可以監(jiān)測內(nèi)存使用情況、監(jiān)測內(nèi)存泄漏的工具。對于一些規(guī)模不是很大的應(yīng)用程序,Valgrind是一把利器。
3?內(nèi)存泄漏監(jiān)測
3.1?示例代碼
1: int main() 2: { 3: char *p = malloc(sizeof(char) * 10); 4: if (p == NULL) { 5: return 0; 6: } 7: 8: *p++ = 'a'; 9: *p++ = 'b'; 10: 11: printf("%s\n", *p); 12: 13: return 0; 14: }3.2?編譯它
1: gcc -g -o core1 core1.c3.3?用Valgrind監(jiān)測進程的內(nèi)存泄漏
1: valgrind --leak-check=yes --show-reachable=yes ./coreValgrind的輸出為為:
1: ==25500== Memcheck, a memory error detector 2: ==25500== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. 3: ==25500== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info 4: ==25500== Command: ./core1 5: ==25500== 6: ==25500== Conditional jump or move depends on uninitialised value(s) 7: ==25500== at 0x36A104546A: vfprintf (in /lib64/libc-2.12.so) 8: ==25500== by 0x36A104EAC9: printf (in /lib64/libc-2.12.so) 9: ==25500== by 0x40055D: main (core1.c:13) 10: ==25500== 11: (null) 12: ==25500== 13: ==25500== HEAP SUMMARY: 14: ==25500== in use at exit: 10 bytes in 1 blocks 15: ==25500== total heap usage: 1 allocs, 0 frees, 10 bytes allocated 16: ==25500== 17: ==25500== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1 18: ==25500== at 0x4A0515D: malloc (vg_replace_malloc.c:195) 19: ==25500== by 0x400515: main (core1.c:5) 20: ==25500== 21: ==25500== LEAK SUMMARY: 22: ==25500== definitely lost: 10 bytes in 1 blocks 23: ==25500== indirectly lost: 0 bytes in 0 blocks 24: ==25500== possibly lost: 0 bytes in 0 blocks 25: ==25500== still reachable: 0 bytes in 0 blocks 26: ==25500== suppressed: 0 bytes in 0 blocks 27: ==25500== 28: ==25500== For counts of detected and suppressed errors, rerun with: -v 29: ==25500== Use --track-origins=yes to see where uninitialised values come from 30: ==25500== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 6 from 6)可以看到,Valgrind提示在第五行分配的內(nèi)存未被釋放
4?懸掛指針
4.1?示例代碼
1: struct elem { 2: int a; 3: double b; 4: }; 5: 6: int main() 7: { 8: struct elem *e = malloc(sizeof(struct elem)); 9: if (e == NULL) { 10: return 0; 11: } 12: 13: e->a = 10; 14: e->b = 10.10; 15: 16: double *xx = &e->b; 17: 18: printf("%f\n", *xx); 19: 20: free(e); 21: 22: printf("%f\n", *xx); 23: 24: return 0; 25: }4.2?Valgrind運行結(jié)果
同樣用-g編譯后valgrind運行的結(jié)果:
1: [cobbliu@MacBook]$ valgrind --leak-check=yes --show-reachable=yes ./core2 2: ==26148== Memcheck, a memory error detector 3: ==26148== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. 4: ==26148== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info 5: ==26148== Command: ./core2 6: ==26148== 7: 10.100000 8: ==26148== Invalid read of size 8 9: ==26148== at 0x4005CA: main (core2.c:26) 10: ==26148== Address 0x502a048 is 8 bytes inside a block of size 16 free'd 11: ==26148== at 0x4A04D72: free (vg_replace_malloc.c:325) 12: ==26148== by 0x4005C5: main (core2.c:24) 13: ==26148== 14: 10.100000 15: ==26148== 16: ==26148== HEAP SUMMARY: 17: ==26148== in use at exit: 0 bytes in 0 blocks 18: ==26148== total heap usage: 1 allocs, 1 frees, 16 bytes allocated 19: ==26148== 20: ==26148== All heap blocks were freed -- no leaks are possible 21: ==26148== 22: ==26148== For counts of detected and suppressed errors, rerun with: -v 23: ==26148== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 6)可以看到在free(e)后,指針xx成為了懸掛指針,此后對xx的讀,如果xx指向的內(nèi)存還未被glibc回收,進程不會core掉。valgrind提示在26行做了對xx的 Invalid read.
5?多次釋放同一個指針?
5.1?示例代碼
1: int main() 2: { 3: char *p = malloc(sizeof(char) * 10); 4: if (p == NULL) { 5: return 0; 6: } 7: 8: char *q = p; 9: 10: *p++ = 'a'; 11: *p++ = 'b'; 12: 13: printf("%s\n", *p); 14: 15: free(p); 16: free(q); 17: return 0; 18: }5.2?Valgrind?監(jiān)測
1: [cobbliu@MacBook]$ valgrind --leak-check=yes --show-reachable=yes ./core1 2: ==26874== Memcheck, a memory error detector 3: ==26874== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. 4: ==26874== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info 5: ==26874== Command: ./core1 6: ==26874== 7: ==26874== Conditional jump or move depends on uninitialised value(s) 8: ==26874== at 0x36A104546A: vfprintf (in /lib64/libc-2.12.so) 9: ==26874== by 0x36A104EAC9: printf (in /lib64/libc-2.12.so) 10: ==26874== by 0x4005B5: main (core1.c:15) 11: ==26874== 12: (null) 13: ==26874== Invalid free() / delete / delete[] 14: ==26874== at 0x4A04D72: free (vg_replace_malloc.c:325) 15: ==26874== by 0x4005C1: main (core1.c:17) 16: ==26874== Address 0x502a042 is 2 bytes inside a block of size 10 alloc'd 17: ==26874== at 0x4A0515D: malloc (vg_replace_malloc.c:195) 18: ==26874== by 0x400565: main (core1.c:5) 19: ==26874== 20: ==26874== 21: ==26874== HEAP SUMMARY: 22: ==26874== in use at exit: 0 bytes in 0 blocks 23: ==26874== total heap usage: 1 allocs, 2 frees, 10 bytes allocated 24: ==26874== 25: ==26874== All heap blocks were freed -- no leaks are possible 26: ==26874== 27: ==26874== For counts of detected and suppressed errors, rerun with: -v 28: ==26874== Use --track-origins=yes to see where uninitialised values come from 29: ==26874== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 6 from 6)可以看到,valgrind提示在17行有一個Invalid Free()
6?Valgrind的優(yōu)缺點
valgrind默認使用memcheck工具做內(nèi)存監(jiān)測。
6.1?Advantages
用valgrind監(jiān)測內(nèi)存泄漏,不用重新編譯應(yīng)用程序,不用重新鏈接應(yīng)用程序,不用對應(yīng)用進程做任何修改。如果想查看詳細的出錯信息,只需要在編譯時加上-g選項。
6.2?Disadvantages
不管valgrind在使用memcheck工具監(jiān)測內(nèi)存時,它會接管應(yīng)用程序,并且讀取應(yīng)用程序可執(zhí)行文件和庫文件中的debug信息來顯示詳細的出錯位置。當valgrind啟動后,應(yīng)用 進程實際上在valgrind的虛擬環(huán)境中執(zhí)行,valgrind會將每行代碼傳遞給memcheck工具,memcheck工具再加入自己的調(diào)試信息,之后再將合成的代碼真正運行。memcheck工具在 應(yīng)用進程每個防存操作和每個變量賦值操作時加入額外的統(tǒng)計代碼,通常情況下,使用memcheck工具后應(yīng)用程序的運行時間會比原生代碼慢大約10-50倍。
其次,對于一些不停機運行的服務(wù)器程序的內(nèi)存問題,valgrind無能為力。不僅僅是因為valgrind無法使之停止,還有可能是因為服務(wù)器進程本身就被設(shè)計為申請一些生命周期 與進程生命周期一樣長的內(nèi)存,永遠不釋放,這些內(nèi)存會被valgrind報泄漏錯誤。
再次,valgrind對多線程程序支持得不夠好。在多線程程序執(zhí)行時,valgrind在同一時刻只讓其中一個線程執(zhí)行,它不會充分利用多核的環(huán)境。在用valgrind運行您的多線程程序 時,您的寶貴程序的運行情況可能跟不使用valgrind的運行情況千差萬別。
7?Valgrind的其他工具
除了memcheck工具外,valgrind工具包還有一些別的好用的工具
7.1?Cachegrind
這個工具模擬 CPU中的一級緩存I1,D1和L2二級緩存,能夠精確地指出程序中 cache的丟失和命中。如果需要,它還能夠為我們提供cache丟失次數(shù),內(nèi)存引用次數(shù),以及每行 代碼,每個函數(shù),每個模塊,整個程序產(chǎn)生的指令數(shù)。這對優(yōu)化程序有很大的幫助。 詳情見這里
7.2?Callgrind
Callgrind收集程序運行時的一些數(shù)據(jù),函數(shù)調(diào)用關(guān)系等信息,還可以有選擇地進行cache 模擬。在運行結(jié)束時,它會把分析數(shù)據(jù)寫入一個文件。callgrindannotate可以把?這個文件的內(nèi)容轉(zhuǎn)化成可讀的形式。 詳情見這里
7.3?Helgrind
它主要用來檢查C/C++多線程程序(使用POSIX線程)中出現(xiàn)的同步問題。Helgrind 尋找內(nèi)存中被多個線程訪問,而又沒有一貫加鎖的區(qū)域,這些區(qū)域往往是線程之間失去同 步的地方,而且會導致難以發(fā)掘的錯誤。Helgrind實現(xiàn)了名為"Eraser" 的競爭檢測算法,并做了進一步改進,減少了報告錯誤的次數(shù)。 詳情見這里
7.4?DRD
這也使一款多線程程序監(jiān)測工具,它提供的監(jiān)測信息比Helgrind更豐富。 詳情見這里
7.5?Massif
堆棧分析器,它能測量程序在堆棧中使用了多少內(nèi)存。告訴我們堆塊,堆管理塊和棧的大小。對于那些被應(yīng)用進程釋放但是還沒有交還給操作系統(tǒng)的內(nèi)存,memcheck是監(jiān)測 不出來的,而Massif能有效第監(jiān)測到這類內(nèi)存。 詳情見這里
7.6?DHAT
這個工具能詳細地顯示應(yīng)用進程如何使用堆棧,以使用戶更好地評估程序的設(shè)計。 詳情見這里
8?參考
- http://www.valgrind.org/
總結(jié)
以上是生活随笔為你收集整理的Valgrind ---内存调试,内存泄漏检测以及性能分析的软件开发工具的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VLC视频播放器原理详细分析含TS流格式
- 下一篇: ngx_timeofday,定时器管理