Solaris上,常常可以用LD_PRELOAD輔助mdb做一些調試、測試工作,可以發(fā)現(xiàn)一些其它手段難以發(fā)現(xiàn)的問題;最近就遇到一個。
事情源于替換了程序中的某個基礎部分之后,程序運行起來占用的物理內存有了較為顯著的增加,卻難以一下子拿出來個讓人信服的原因。于是自然想到了去看一下程序真正運行的時候,某一部分內存是誰分配的。之前用 pmap -xalsF pid發(fā)現(xiàn)【heap】部分有顯著增加,又不是在新加入的那個動態(tài)庫里邊。
Solaris上有強大的mdb,輔助不同的模塊可以得出很多有意思的結論,其中l(wèi)ibumem.so即可以查看內存的分配的情況,并可以檢測是否有內存泄漏。
啟動的方法很簡便:
export UMEM_DEBUG=default
export UMEM_LOGGING=transaction
LD_PRELOAD=/lib/libumem.so
export LD_PRELOAD
然后在此shell中啟動程序,新打開一個終端,同樣設置好LD_PRELOAD(否則會提示錯誤),
查找正運行的程序的進程號(調試的程序),生成一個core文件:
ps -ef | grep <appname>
gcore <pid>
ls core.<pid>
用mdb打開新生成的core文件,第一行應該提示加載了libumem.so.
接下來,用libumem.so提供的walker和dcmds就可以查詢程序運行以來到產(chǎn)生core文件的那一時間點豐富的內存信息了.
mdb core.pid
>::findleaks
>::umalog
>::umem_log
更多可用的命令,可以用::dmods -l查看。
整個過程非常繁雜,因為應用程序比較大,分配內存的log實在是太多了,但是突然發(fā)現(xiàn)運行目錄下邊多了不少core文件,一下子奇怪了,之前可是花費了很多時間在提高代碼質量上,按道理不應該會有core產(chǎn)生了。打開這些core,用pstack,居然發(fā)現(xiàn)某個模塊啟動的子進程在調用free的地方abort了,按圖索驥查看代碼,在某個旮旯里邊,幾年沒人動的小角落里,發(fā)現(xiàn)分配內存的地方:
char* path1 = getenv("MYENV");
char path2[] = "bin/logDir/log.xxx"
char* path = malloc(sizeof(path1) + sizeof(path2));
strcpy(path, path1);
strcat(path, path2);
.
free(path);
exit(0);
.
原來最初寫這塊純C代碼的人打了馬虎眼,分配的內存有問題,導致free的時候出問題,但正常情況下,這里的exit之后,進程也就退出了,居然沒有core文件出來,導致這個Bug居然被隱藏了數(shù)年。
libumem和LD_PRELOAD居然把它挖了出來,馬上修改之。
所謂“禍患常積于忽微”,最不起眼的地方,往往會衍生一些麻煩,不時咬你一口。
討厭的"legacy code without evolution/refactoring/test......",每個負責任的職業(yè)程序員都應該去深思
【注】Linux上似乎也有l(wèi)ibumem.so,但是卻沒有pstack/mdb這些好用的工具,只有valgrind/gdb了;solaris上不但有mdb/dtrace,還有dbx,雖然gdb也是可用的