一. 簡述valgrind是什么,為何能進(jìn)行內(nèi)存泄露
valgrind是一個(gè)程序調(diào)試及性能分析的工具集唱矛,涵蓋memcheck, cachegrind,helgrind,callgrind署驻,啟動valgrind時(shí)通過--tool來指定具體要調(diào)用的工具严卖。不論使用哪個(gè)工具全庸,通過valgrind來啟動程序時(shí)都會取得對程序的控制權(quán)靶端,從關(guān)聯(lián)庫中讀取調(diào)試信息。然后在valgrind核心提供的虛擬CPU上運(yùn)行程序藤韵,根據(jù)選擇的工具處理代碼虐沥,該工具會向代碼中植入檢測代碼,并把這些代碼作為最終代碼返回給valgrind核心,最后valgrind核心運(yùn)行這些代碼欲险。
我們最常用valgrind進(jìn)行內(nèi)存泄露檢查镐依,通過--tool = memcheck --leak-check=yes指定。需要注意的是天试,memcheck會加入代碼檢查每一片內(nèi)存的訪問和進(jìn)行值運(yùn)算槐壳,導(dǎo)致整體代碼大小至少增加12倍,運(yùn)行速度比平時(shí)慢25-50倍喜每,所以使用valgrind時(shí)务唐,保證機(jī)器環(huán)境有足夠多的內(nèi)存,如果進(jìn)程本身啟動內(nèi)存有十幾G带兜,那用valgrind啟動程序時(shí)枫笛,一般啟動特別慢,可能1h才能啟動程序刚照⌒糖桑或者,valgrind根本就拉不起來程序无畔,此時(shí)啊楚,需要修改程序的相關(guān)配置參數(shù),想辦法將程序啟動的進(jìn)程內(nèi)存減小浑彰。
二.編譯程序時(shí)的注意
1. 編譯時(shí)恭理,打開調(diào)試模式(gcc編譯器的-g選項(xiàng))。
2. 編譯時(shí)闸昨,關(guān)閉編譯優(yōu)化選項(xiàng)蚯斯。一些編譯優(yōu)化選項(xiàng)(比如-O2或者更高的優(yōu)化選項(xiàng)),可能會使得memcheck提交錯(cuò)誤的未初始化報(bào)告饵较,因此拍嵌,為了使得valgrind的報(bào)告更精確,在編譯的時(shí)候最好不要使用優(yōu)化選項(xiàng)循诉。
3. 如果程序有對tcmalloc編譯依賴的話横辆,需要將tcmalloc從編譯依賴中去掉,否則valgrind掃描可能會遺漏一些報(bào)警信息茄猫。
4. 當(dāng)檢查的是C++程序的時(shí)候狈蚤,還應(yīng)該考慮另一個(gè)選項(xiàng) -fno-inline。它使得函數(shù)調(diào)用鏈很清晰划纽,這樣可以減少你在瀏覽大型C++程序時(shí)的混亂脆侮。比如在使用這個(gè)選項(xiàng)的時(shí)候,用memcheck檢查openoffice就很容易勇劣。當(dāng)然靖避,你可能不會做這項(xiàng)工作潭枣,但是使用這一選項(xiàng)使得valgrind生成更精確的錯(cuò)誤報(bào)告和減少混亂。
三. valgrind下載 & 安裝
---> 下載:wget?http://www.valgrind.org/downloads/valgrind-3.8.1.tar.bz2
目前最新的版本是:Release 3.12.0 20 October 2016
---> 解壓:bzip2 -d valgrind-3.8.1.tar.bz2; tar -xvf valgrind-3.8.1.tar
---> 安裝: cd valgrind-3.8.1; ./configure --prefix=/home/work/tools/valgrind-3.8.1.install;make;make install
安裝好后在valgrind-3.8.1.install目錄里有: bin, lib, include, share 4個(gè)子目錄
---> 確認(rèn)是否安裝正確:valgrind --version
---> 編輯/etc/profile幻捏,將valgrind工具加入PATH盆犁,再source /etc/profile,這樣每次使用時(shí)都是新安裝的valgrind
四篡九、valgrind memcheck選項(xiàng)
參見手冊http://www.valgrind.org/docs/manual/mc-manual.html
valgrind的選項(xiàng)非常的多谐岁,可以參考翻譯手冊中的說明進(jìn)行選用,下面列出幾個(gè)比較常用的:
Option :--leak-check=no|summary|full|yes? [default summary]
Purpose : 當(dāng)這個(gè)選項(xiàng)打開時(shí)榛臼,會當(dāng)客戶程序結(jié)束時(shí)查找內(nèi)存泄露伊佃。如果設(shè)置為summary,Valgrind會報(bào)告有多少內(nèi)存泄露發(fā)生了讽坏。如果設(shè)置為full或yes锭魔,Valgrind給出每一個(gè)獨(dú)立的泄露的詳細(xì)信息例证。
Option :--leak-resolution=high|med|low ?[default low]
Purpose :在做內(nèi)存泄漏檢查時(shí)路呜,確定memcheck將怎么樣考慮不同的棧是相同的情況。當(dāng)設(shè)置為low時(shí)织咧,只需要前兩層棧匹配就認(rèn)為是相同的情況胀葱;當(dāng)設(shè)置為med,必須要四層棧匹配笙蒙,當(dāng)設(shè)置為high時(shí)抵屿,所有層次的棧都必須匹配。
注意--leak-resolution= 設(shè)置并不影響memcheck查找內(nèi)存泄漏的能力捅位。它只是改變了結(jié)果如何輸出轧葛。
Option: --show-reachable=yes|no ?[default no]
Purpose : 當(dāng)這個(gè)選項(xiàng)關(guān)閉時(shí),內(nèi)存泄露檢測器只顯示沒有指針指向的內(nèi)存塊艇搀,或者只能找到指向塊中間的指針尿扯。當(dāng)這個(gè)選項(xiàng)打開時(shí),內(nèi)存泄露檢測器還報(bào)告有指針指向的內(nèi)存塊焰雕。這些塊是最有可能出現(xiàn)內(nèi)存泄露的地方衷笋。你的程序可能,至少在原則上矩屁,應(yīng)該在退出前釋放這些內(nèi)存塊辟宗。這些有指針指向的內(nèi)存塊和沒有指針指向的內(nèi)存塊,或者只有內(nèi)部指針指向的塊吝秕,都可能產(chǎn)生內(nèi)存泄露泊脐,因?yàn)閷?shí)際上沒有一個(gè)指向塊起始的指針可以拿來釋放,即使你想去釋放它烁峭。
Option: -v
Purpose : 顯示詳細(xì)信息容客。在各個(gè)方面顯示你的程序的額外信息,例如:共享對象加載,使用的重置耘柱,執(zhí)行引擎和工具的進(jìn)程如捅,異常行為的警告信息。重復(fù)這個(gè)標(biāo)記可以增加詳細(xì)的級別调煎。
Option : -fno-inline
Purpose : 當(dāng)檢查的是C++程序的時(shí)候镜遣,可考慮這個(gè)選項(xiàng)-fno-inline。它使得函數(shù)調(diào)用鏈很清晰
Option : --max-stackframe=[default: 2000000]
Purpose : 棧的最大值士袄,默認(rèn)2000000悲关。如果棧指針的偏移超過這個(gè)數(shù)量,valgrind則會認(rèn)為程序是切換到了另外一個(gè)棧執(zhí)行娄柳。
五. 內(nèi)存泄露掃描過程
---> 程序啟動(如果程序本身內(nèi)存十幾個(gè)G寓辱,啟動非常慢,可能1h多才啟動):
?nohup valgrind--tool=memcheck --leak-check=full --show-reachable=yes
--max-stackframe=8000000 --log-file=./valgrind.log [程序啟動命令] &
---> 判斷程序是否啟動成功:
搜索程序端口是否被監(jiān)聽來判斷程序是否被valgrind拉起
netstat -anlp|grep 9600|grep LISTEN ? ##如果進(jìn)程名是valgrind赤拒,則表示程序已經(jīng)在valgrind環(huán)境中啟動成功秫筏。
---> 給程序放一些請求,請求盡可能覆蓋到程序的各個(gè)分支
---> 程序優(yōu)雅退出挎挖,不要讓服務(wù)在valgrind環(huán)境里core掉了这敬。盡量服務(wù)有捕獲kill信號能力,一般kill -9 pid會導(dǎo)致服務(wù)core掉蕉朵。
---> 退出等待個(gè)幾分鐘崔涂,讓valgrind收集退出信息輸出到valgrind.log。最后分析valgrind.log
六. valgrind.log 報(bào)告分析
見手冊http://www.valgrind.org/docs/manual/mc-manual.html 始衅,關(guān)注關(guān)注definitely lost和possible lost處可能的內(nèi)存泄露冷蚂。
1)首先搜關(guān)鍵字『LEAK SUMMARY』,看『definitely lost』對應(yīng)是否有值汛闸,如果不為0蝙茶,肯定有內(nèi)存泄露。
2)如果有內(nèi)存泄露蛉拙,往上搜報(bào)告中『definitely lost in loss』的報(bào)警處尸闸,對照程序代碼一一排查。
3)常見內(nèi)存泄露警告
---> a. Illegal read / Illegal write errors
已釋放后的內(nèi)存進(jìn)行讀/寫或數(shù)組的越界訪問等孕锄,valgrind報(bào)告程序在嘗試讀寫非法內(nèi)存吮廉,造成的后果是程序易發(fā)生segmentation fault(吐core)風(fēng)險(xiǎn)。
Invalid write of size X畸肆,訪問超出了范圍的內(nèi)存宦芦,試圖從該內(nèi)存讀取數(shù)據(jù)
---> b. Use of uninitialised values
檢測使用未初始化變量,可以檢測在條件判斷語句中使用未初始化變量轴脐,因此應(yīng)該養(yǎng)成在聲明變量時(shí)就進(jìn)行初始化的習(xí)慣
Conditional jump or move depends on uninitialised value(s)
---> c. When a heap block is freed with an inappropriate deallocation function
使用不正確的方法釋放內(nèi)存调卑,比如new/delete , malloc/free使用混淆了抡砂,new分配的地方用free釋放了
Mismatched free() / delete / delete []
---> d. Illegal frees
重復(fù)內(nèi)存釋放,比如2次使用free釋放同一塊內(nèi)存
Invalid free() / delete / delete[]
---> e. Overlapping source and destination blocks
針對C語言常見的memcpy, strcpy, strncpy, strcat, strncat拷貝類函數(shù)恬涧,出現(xiàn)源串和目標(biāo)串地址重疊
Source and destination overlap in memcpy(0xbffff294, 0xbffff280, 21)
---> f. Fishy argument values
所有內(nèi)存分配類函數(shù)在分配內(nèi)存時(shí)注益,都會傳入一個(gè)內(nèi)存分配大小的數(shù),如果你傳入的數(shù)大于機(jī)器所能分配的最大數(shù)時(shí)溯捆,如64位機(jī)器上丑搔,你傳入的size大于了2**63
Argument 'size' of function malloc has a fishy (possibly negative) value: -3
---> g. Leak detecion
HEAP SUMMARY: ?
如果指定--show-reachable=yes,在程序退出時(shí)memcheck會收集reachable and indirectly lost blocks
LEAK SUMMARY:
memcheck會記錄由malloc/new等函數(shù)創(chuàng)建的所有的heap blocks提揍,因此在程序退出時(shí)啤月,memcheck能夠知道哪些block沒有free。如下:
definitely lost: 4 bytes in 1 blocks
indirectly lost: 0 bytes in 0 blocks
possibly lost: 0 bytes in 0 blocks
still reachable: 95 bytes in 6 blocks
definitely lost:如果一個(gè)block在程序在退出后劳跃,memcheck找不到指向它的pointer谎仲,一般是由于在代碼中對該block沒有free造成的,要重點(diǎn)關(guān)注刨仑。
possibly lost:在程序退出時(shí)郑诺,memcheck發(fā)現(xiàn)仍有 interior pointer(如果一個(gè)pointer指向一個(gè)block的中間某個(gè)位置)指向一個(gè)block,那么該block被認(rèn)為是possibly lost贸人。
still reachable:如果memcheck發(fā)現(xiàn)仍有start pointer指向一個(gè)block间景,那么該block就是still reachable。
4)valgrind不能查出哪些錯(cuò)誤?
valgrind不對靜態(tài)數(shù)組(分配在棧上)進(jìn)行邊界檢查艺智。如果在程序中聲明了一個(gè)數(shù)組:
int main()
{
char x[10];
x[11] = ′a′;
}
valgrind則不會警告你,出于測試目的圾亏,你可以把數(shù)組改為動態(tài)在堆上分配的數(shù)組十拣,這樣就可能進(jìn)行邊界檢查了。這個(gè)方法好像有點(diǎn)得不償失的感覺志鹃。
七. 安裝或使用中踩得各種坑
1)valgrind: failed to start tool 'memcheck' for platform 'amd64-linux'
解決辦法:
該錯(cuò)誤是valgrind啟動時(shí)夭问,無法找到相關(guān)工具文件,有可能是安裝好后相關(guān)lib的path未設(shè)置曹铃,也有可能是一開始安裝就有問題缰趋。解決辦法如下:
如果已經(jīng)安裝成功,試一下導(dǎo)出VALGRIND_LIB路徑陕见,用法如下(假設(shè)valgrind已經(jīng)被安裝到/home/work/tools/valgrind目錄):##親測有效##
export VALGRIND_LIB=/home/work/tools/valgrind3.8.1/lib/valgrind
2)valgrind --version報(bào)錯(cuò)
valgrind: mmap((nil), 134512640) failed during startup.
valgrind: is there a hard virtual memory limit set?
解決辦法:
見下秘血,版本太老,里邊有bug评甜,重新安裝一下較新版本應(yīng)該能解決灰粮。https://twiki.cern.ch/twiki/bin/view/LHCb/CodeAnalysisTools
3)vg_alloc_ThreadState: no free slots available
Increase VG_N_THREADS, rebuild and try again.
valgrind: the 'impossible' happened:
VG_N_THREADS is too low
解決辦法:
a) 修改VG_N_THREADS和NTHREADS的值,安裝用的是3.8.1版本忍坷,默認(rèn)VG_N_THREADS是500粘舟,現(xiàn)在改為5000熔脂,同時(shí)NTHREADS由498改成4998。
include/pub_tool_threadstate.h: #define VG_N_THREADS 5000
./memcheck/tests/err_disable4.c: #define NTHREADS 4998 ?// VG_N_THREADS - 2
b) 按照1進(jìn)行重新安裝柑肴。
4) Valgrind: FATAL: VG_N_SEGMENTS is too low.
解決辦法:
VG_N_SEGMENTS默認(rèn)是5000霞揉。修改coregrind/m_aspacemgr/aspacemgr-linux.c: VG_N_SEGMENTS從5000改到50000,VG_N_NAME從1000改到5000晰骑。
按照1進(jìn)行重新安裝零聚。
5)valgrind.log報(bào)告中,在definitely lost的代碼處全是些侍?隶症??岗宣?蚂会?全寫花了,看不出具體泄露的是在哪一行代碼
解決辦法:
加上-g耗式,打開調(diào)試信息重新編譯程序胁住;
valgrind掃描時(shí)加上--show-reachable=yes。
參考:
1. http://www.xlgps.com/article/216977.html
2. 如何使用Valgrind memcheck工具進(jìn)行C/C++的內(nèi)存泄漏檢測
3.?用Valgrind查找內(nèi)存泄漏和無效內(nèi)存訪問
4.?valgrind介紹