簡單的Memory leak跟蹤(三) 方案2:Dbghelp

dbghelp這個方案比較複雜,速度也比較慢,不過用在Trace的場合也不算太糟糕。

原理是,dbghelp.lib、dbghelp.h提供了一大堆獲取當前調用棧信息(ESP、EBP),並通過這些調用棧,配合上相應模塊的pdb文件,得出當前的調用模塊(dll)、調用函數、調用行和指令。

具體的原理就不再廢話了,網上dbghelp的使用方面的文檔也很多,貼幾個參考:

HOWTO: Dump Call Stack

一個調試器的實現(五)調試符號

使用DbgHelp獲取函數調用堆棧之inline assembly(內聯彙編)法


談一下實現的原理:

還是截獲new,但不需要提供new的特殊版本了,劫持全局new即可。

每次new之後,使用dbghelp功能來獲取當前Call Stack,並剔除掉從new到Tracer的這幾級Stack(就把最上面幾個Stack扔掉就行,具體扔掉幾個,根據實現不同而不同,我的實現從new開始Call了三層,所以扔掉3層即可,具體的可以大家自己來)。

建立一個hash,key仍然是分配的內存地址,value這裏,有個優化的方案。因爲CallStack比較大,如果要每次都存當前的CallStack,Tracer最後佔的內存就太多了。但注意一點,new雖然在一個程序中能調用成百萬上千萬次,但new所在的地方,所可能出現的Call Stack的數量卻是有限的,可能撐死也就千、萬這個數量級。所以,Call Stack一旦獲取後,我們可以先將Call Stack,緩存到一個Vector裏,並算出CRC放到另一個map裏。然後,hash的value只要保存當前Call Stack ID即可。

下次獲取Callstack後,先算CRC到map中查一查,如果已經有相應的Call Stack了,就用相應的Call Stack即可,否則再生成新的,這樣空間就得到優化了。

其他的跟之前那個Tracer一樣,delete時,從Tracer表中去掉相應地址的記錄。程序結束後,看hash是否清空,沒清空就dump即可。

dump時還需要使用dbghelp功能。


因爲這種實現需要劫持C++原始的void* operator new(size_t InSize),所以,需要像上一章那樣說的,所有的hash、vector都需要用我們自己提供的、取消了Tracer功能的allocator。traceMalloc的實現的過程中如果有其他對operator new和new的調用,也需要一併消滅掉。


dbghelp這部分本身理解不是特別透徹,就不亂發表意見了,稍後發出代碼,請輕噴~。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章