1. 程式人生 > >linux下記憶體洩露檢測工具Valgrind介紹

linux下記憶體洩露檢測工具Valgrind介紹

一、工作中一個記憶體洩漏問題的解決過程:

  1. 問題背景:我司裝置上執行有多個程序,在裝置執行兩天後,程序jsman所佔用的記憶體達到了1200M bytes(通過ps -aux檢視)。

  2. 解決步驟:

    1. 確定裝置上的軟體版本,根據git的commit號資訊回退程式碼。

    2. 分析哪些操作可能導致程序的記憶體洩漏。jsman程序是net-snmp的子代理程序,是週期性被動響應snmp請求的程序。

    3. 首先出現記憶體洩漏問題時,需要逐步縮小問題的範圍。通過snmpwalk命令依次獲取裝置上支援的6個oid,結合ps -aux命令,定位到問題出現在響應oid為56789.1.1的請求。

    4. 仔細review該部分程式碼。該部分程式碼呼叫到了驅動部api,snmp的api,以及自己實現的業務邏輯。分別註釋掉不同部分的程式碼,進行測試,進一步縮小問題的範圍。

    5. 最後定位到是讀取不存在的xml檔案時,出現的問題。

  3. 總結:

    1. 記錄問題的解決步驟,可以避免重複操作,並且對梳理思路也有幫助。

    2. 放鬆心態,逐步進行,逐漸縮小問題的範圍。

二、記憶體洩漏檢查工具memcheck的使用。

目前在linux開發一個分析實時路況的應用程式,在聯合測試中發現程式存在記憶體洩露的情況。

這下著急了,馬上就要上線了,還好發現了一款Valgrind工具,完美的解決了記憶體洩露的問題。

推薦大家可以使用看看。

Valgrind是執行在Linux上一套基於模擬技術的程式除錯和分析工具,它的主要作者是獲得過Google-O'Reilly開源大獎的Julian Seward,它包含一個核心──一個軟體合成的CPU,和一系列的小工具,每個工具都可以完成一項任務──除錯,分析,或測試等。Valgrind可以檢測記憶體洩漏和記憶體違例,還可以分析cache的使用等,靈活輕巧而又強大,能直穿程式錯誤的心臟,真可謂是程式設計師的瑞士軍刀。 

(一). Valgrind概觀  Valgrind一般包含下列工具:  1.Memcheck (後續我們只對這款記憶體檢測工具做介紹) 最常用的工具,用來檢測程式中出現的記憶體問題,所有對記憶體的讀寫都會被檢測到,一切對malloc()/free()/new/delete的呼叫都會被捕獲。所以,它能檢測以下問題:  1.對未初始化記憶體的使用;  2.讀/寫釋放後的記憶體塊;  3.讀/寫超出malloc分配的記憶體塊;  4.讀/寫不適當的棧中記憶體塊;  5.記憶體洩漏,指向一塊記憶體的指標永遠丟失;  6.不正確的malloc/free或new/delete匹配;  7,memcpy()相關函式中的dst和src指標重疊。  這些問題往往是C/C++程式設計師最頭疼的問題,Memcheck在這裡幫上了大忙。  2.Callgrind  和gprof類似的分析工具,但它對程式的執行觀察更是入微,能給我們提供更多的資訊。和gprof不同,它不需要在編譯原始碼時附加特殊選項,但加上除錯選項是推薦的。Callgrind收集程式執行時的一些資料,建立函式呼叫關係圖,還可以有選擇地進行cache模擬。在執行結束時,它會把分析資料寫入一個檔案。callgrind_annotate可以把這個檔案的內容轉化成可讀的形式。  3.Cachegrind  Cache分析器,它模擬CPU中的一級快取I1,Dl和二級快取,能夠精確地指出程式中cache的丟失和命中。如果需要,它還能夠為我們提供cache丟失次數,記憶體引用次數,以及每行程式碼,每個函式,每個模組,整個程式產生的指令數。這對優化程式有很大的幫助。  4.Helgrind  它主要用來檢查多執行緒程式中出現的競爭問題。Helgrind尋找記憶體中被多個執行緒訪問,而又沒有一貫加鎖的區域,這些區域往往是執行緒之間失去同步的地方,而且會導致難以發掘的錯誤。Helgrind實現了名為“Eraser”的競爭檢測演算法,並做了進一步改進,減少了報告錯誤的次數。不過,Helgrind仍然處於實驗階段。  5. Massif  堆疊分析器,它能測量程式在堆疊中使用了多少記憶體,告訴我們堆塊,堆管理塊和棧的大小。Massif能幫助我們減少記憶體的使用,在帶有虛擬記憶體的現代系統中,它還能夠加速我們程式的執行,減少程式停留在交換區中的機率。  此外,lackey和nulgrind也會提供。Lackey是小型工具,很少用到;Nulgrind只是為開發者展示如何建立一個工具。我們就不做介紹了。 

(二). Valgrind下載與安裝

1、下載:

valgrind官網:http://valgrind.org下載

如果安裝有svn則可以直接下載最新版本:svn co svn://svn.valgrind.org/valgrind/trunk valgrind

2、安裝

cd valgrind

 ./autogen.sh

./configure --prefix=...

make

make install

(二). 使用Memcheck 工具進行記憶體分析

1、編譯你的程式debug版本./TestMem

2、執行:valgrind --tool=memcheck --leak-check=full --log-file=./log.txt  ./TestMem

3、耐心等待並分析結果(log.txt結果檔案中會包含引起記憶體洩露的程式碼在在原始碼中的位置,即以下舉例說明中標明為紅色的部分)

舉例說明:

下面是一段有問題的C程式程式碼test.c

#include <stdlib.h> void f(void) { int* x = malloc(10 * sizeof(int)); x[10] = 0; //問題1: 陣列下標越界 } //問題2: 記憶體沒有釋放 int main(void) { f(); return 0; } 1、 編譯程式test.c gcc -Wall test.c -g -o test 2、 使用Valgrind檢查程式BUG valgrind --tool=memcheck --leak-check=full ./test 3、 分析輸出的除錯資訊 ==3908== Memcheck, a memory error detector. ==3908== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al. ==3908== Using LibVEX rev 1732, a library for dynamic binary translation. ==3908== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP. ==3908== Using valgrind-3.2.3, a dynamic binary instrumentation framework. ==3908== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al. ==3908== For more details, rerun with: -v ==3908==  --3908-- DWARF2 CFI reader: unhandled CFI instruction 0:50 --3908-- DWARF2 CFI reader: unhandled CFI instruction 0:50 /*陣列越界錯誤*/ ==3908== Invalid write of size 4  ==3908== at 0x8048384: f (test.c:6) ==3908== by 0x80483AC: main (test.c:11) ==3908== Address 0x400C050 is 0 bytes after a block of size 40 alloc'd ==3908== at 0x40046F2: malloc (vg_replace_malloc.c:149) ==3908== by 0x8048377: f (test.c:5) ==3908== by 0x80483AC: main (test.c:11) ==3908==  ==3908== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 14 from 1) ==3908== malloc/free: in use at exit: 40 bytes in 1 blocks.  ==3908== malloc/free: 1 allocs, 0 frees, 40 bytes allocated. ==3908== For counts of detected errors, rerun with: -v ==3908== searching for pointers to 1 not-freed blocks. ==3908== checked 59,124 bytes. ==3908==  ==3908==  /*有記憶體空間沒有釋放*/ ==3908== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==3908== at 0x40046F2: malloc (vg_replace_malloc.c:149) ==3908== by 0x8048377: f (test.c:5) ==3908== by 0x80483AC: main (test.c:11) ==3908==  ==3908== LEAK SUMMARY: ==3908== definitely lost: 40 bytes in 1 blocks. ==3908== possibly lost: 0 bytes in 0 blocks. ==3908== still reachable: 0 bytes in 0 blocks. ==3908== suppressed: 0 bytes in 0 blocks.