用mtrace定位內(nèi)存泄漏
一. 緣起
有的公眾號(hào)讀者,看完我上次寫給大學(xué)生的查bug方法后,希望我多分享一些查bug的實(shí)踐經(jīng)驗(yàn)和具體步驟,比如如何查內(nèi)存泄漏和core dump問題。所以,就打算寫這篇文章。

二. 內(nèi)存泄漏簡介
內(nèi)存泄漏,是一個(gè)談虎色變的問題。我個(gè)人的基礎(chǔ)非常差,大學(xué)畢業(yè)后,才第一次聽說內(nèi)存泄漏。當(dāng)時(shí),我有點(diǎn)懵圈,心想內(nèi)存泄漏了,是要重新去買新的內(nèi)存設(shè)備嗎?很傻很天真!
后來,我又聽說了很多次內(nèi)存泄漏,查資料后才知道,原來這是一個(gè)軟件層面的東西。比如,使用了malloc, 但沒有使用free, 或者使用了new, 但沒有使用delete, 都會(huì)造成內(nèi)存泄漏。
為什么說內(nèi)存泄漏會(huì)談虎色變呢?因?yàn)椋?br>
內(nèi)存泄漏危害性大
內(nèi)存泄漏潛伏時(shí)間長
內(nèi)存泄漏不容易定位
那么,如果內(nèi)存泄漏了,我們該如何去定位呢?在C/C++相關(guān)的面試中,不問這個(gè)問題的面試官,是不合格的;不會(huì)回答這個(gè)問題的面試者,也是不合格的。當(dāng)然,這種說法,似乎有一點(diǎn)絕對。
有的面試者回答,只有小心配對使用malloc/free和new/delete, 就能避免內(nèi)存泄漏。顯然,這種面試者缺乏基本的工程實(shí)踐認(rèn)知,缺乏對敵人的敬畏。
有的面試者回答,使用智能指針,就能避免內(nèi)存泄漏。顯然,這只是一種預(yù)防機(jī)制。在實(shí)際項(xiàng)目中,各種復(fù)雜因素導(dǎo)致的后果是內(nèi)存已經(jīng)泄漏,需要定位。
還有的面試者,更是想出了出人意料的答案,那就是檢查代碼!這又是對敵人缺乏了解啊。大型工程的代碼動(dòng)輒幾十萬行,誰敢走讀代碼來查內(nèi)存泄漏呢?
對我而言,查殺bug是我的相對強(qiáng)項(xiàng)(實(shí)話說,架構(gòu)能力需要加強(qiáng))。mtrace和valgrind是典型的內(nèi)存泄漏分析工具。今天,我們聊mtrace定位內(nèi)存泄漏。
三. mtrace簡介
我們來看下mtrace的用途:
ubuntu@VM-0-15-ubuntu:~$ man mtraceMTRACE(1) Linux user manual MTRACE(1)NAMEmtrace - interpret the malloc trace logSYNOPSISmtrace [option]... [binary] mtracedataDESCRIPTIONmtrace is a Perl script used to interpret and provide human readable output of the trace log contained inthe file mtracedata, whose contents were produced by mtrace(3). If binary is provided, the output ofmtrace also contains the source file name with line number information for problem locations (assumingthat binary was compiled with debugging information).???????For?more?information?about?the?mtrace(3)?function?and?mtrace?script?usage,?see?mtrace(3).
顯然,mtrace命令是用來分析malloc函數(shù)的trace log.?
那么,這個(gè)trace log是怎么生成的呢??且看上面的see mtrace(3). 有的朋友看到這里,不知道怎么敲命令了,以為是:
ubuntu@VM-0-15-ubuntu:~$ man mtrace(3)-bash: syntax error near unexpected token `('ubuntu@VM-0-15-ubuntu:~$
其實(shí),正確的姿勢如下:
ubuntu@VM-0-15-ubuntu:~$ man 3 mtraceMTRACE(3) Linux Programmer's Manual MTRACE(3)NAMEmtrace, muntrace - malloc tracingSYNOPSIS#includevoid mtrace(void);void muntrace(void);DESCRIPTIONThe mtrace() function installs hook functions for the memory-allocation functions (malloc(3), realloc(3)memalign(3), free(3)). These hook functions record tracing information about memory allocation and deal[mlocation. The tracing information can be used to discover memory leaks and attempts to free nonallocatedmemory in a program.The muntrace() function disables the hook functions installed by mtrace(), so that tracing information isno longer recorded for the memory-allocation functions. If no hook functions were successfully installedby mtrace(), muntrace() does nothing.When mtrace() is called, it checks the value of the environment variable MALLOC_TRACE, which should con[mtain the pathname of a file in which the tracing information is to be recorded. If the pathname is suc[mcessfully opened, it is truncated to zero length.If MALLOC_TRACE is not set, or the pathname it specifies is invalid or not writable, then no hook func[mtions are installed, and mtrace() has no effect. In set-user-ID and set-group-ID programs, MALLOC_TRACEis ignored, and mtrace() has no effect.
顯然,mtrace函數(shù)是用來記錄malloc的trace log的。
所以,對于mtrace, 我們有如下的基本認(rèn)知:
mtrace函數(shù)記錄malloc的trace log
mtrace命令分析上述記錄的trace log
這也就是用mtrace來定位內(nèi)存泄漏的原理。那么,具體如何來查內(nèi)存泄漏呢?不要著急,繼續(xù)往下看。
四. 用mtrace定位內(nèi)存泄漏
首先,我們來寫一段有內(nèi)存泄漏的程序:
int main(){????setenv("MALLOC_TRACE",?"test.log",?"1");mtrace();int *p = (int *)malloc(2 * sizeof(int));return 0;}
我們來分析一下這段程序:
setenv是設(shè)置相關(guān)環(huán)境變量。
mtrace函數(shù)記錄malloc的trace log.
malloc函數(shù)用于分配堆內(nèi)存
我們來編譯并運(yùn)行一下(注意在編譯時(shí)帶-g參數(shù)):
ubuntu@VM-0-15-ubuntu:~$ gcc -g test.cubuntu@VM-0-15-ubuntu:~$ubuntu@VM-0-15-ubuntu:~$ubuntu@VM-0-15-ubuntu:~$ ./a.outubuntu@VM-0-15-ubuntu:~$ ls test.logtest.logubuntu@VM-0-15-ubuntu:~$ cat test.log= Start@ ./a.out:[0x4005eb] + 0x1649570 0x8@ /lib/x86_64-linux-gnu/libc.so.6:(clearenv+0x5d)[0x7f1bc48f7e9d] - 0x1649010@ /lib/x86_64-linux-gnu/libc.so.6:(tdestroy+0x4cf)[0x7f1bc49c291f] - 0x16490e0@ /lib/x86_64-linux-gnu/libc.so.6:[0x7f1bc4a3223c] - 0x1649100
顯然,編譯運(yùn)行后,生成了trace log, 即test.log文件。用cat命令查看,貌似也發(fā)現(xiàn)不了什么東西,這是因?yàn)椋藙蒎e(cuò)了。
我們不僅僅要用test.log, 還要結(jié)合二進(jìn)制文件a.out呢,如下:
ubuntu@VM-0-15-ubuntu:~$ mtrace a.out test.log- 0x00000000018ab010 Free 3 was never alloc'd 0x7fb41725fe9d- 0x00000000018ab0e0 Free 4 was never alloc'd 0x7fb41732a91f- 0x00000000018ab100 Free 5 was never alloc'd 0x7fb41739a23cMemory not freed:-----------------Address Size Caller0x00000000018ab570??????0x8??at?/home/ubuntu/test.c:8ubuntu@VM-0-15-ubuntu:~$
Oh, nice啊!終于查出是第8行,存在內(nèi)存泄漏。接下來,我們打算修復(fù)代碼,并再次驗(yàn)證。
五. 修復(fù)后再驗(yàn)證
修復(fù)內(nèi)存泄漏后,代碼為:
int main(){????setenv("MALLOC_TRACE",?"test.log",?"1");mtrace();int *p = (int *)malloc(2 * sizeof(int));free(p);return 0;}
編譯運(yùn)行,并查看是否有內(nèi)存泄漏:
ubuntu@VM-0-15-ubuntu:~$ gcc -g test.cubuntu@VM-0-15-ubuntu:~$ubuntu@VM-0-15-ubuntu:~$ubuntu@VM-0-15-ubuntu:~$ ./a.outubuntu@VM-0-15-ubuntu:~$ mtrace a.out test.log- 0x00000000006ad010 Free 4 was never alloc'd 0x7faa9b044e9d- 0x00000000006ad0e0 Free 5 was never alloc'd 0x7faa9b10f91f- 0x00000000006ad100 Free 6 was never alloc'd 0x7faa9b17f23cNo memory leaks.ubuntu@VM-0-15-ubuntu:~$
看到No memory leaks后,心情就好了,沒有內(nèi)存泄漏了。
六. 最后的話
無論是筆試面試,還是平時(shí)工作,對于內(nèi)存泄漏問題,都要有自己的一套處理辦法。
