• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            <2008年12月>
            30123456
            78910111213
            14151617181920
            21222324252627
            28293031123
            45678910

            統(tǒng)計

            • 隨筆 - 44
            • 文章 - 0
            • 評論 - 86
            • 引用 - 0

            常用鏈接

            留言簿(6)

            隨筆分類(31)

            隨筆檔案(44)

            Mining

            最新隨筆

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            用BoundsChecker檢測內(nèi)存泄漏 (zz)
              BoundsChecker采用一種被稱為 Code Injection的技術(shù),來截獲對分配內(nèi)存和釋放內(nèi)存的函數(shù)的調(diào)用。簡單地說,當(dāng)你的程序開始運(yùn)行時,BoundsChecker的DLL被自動載入進(jìn)程的地址空間(這可以通過system-level的Hook實(shí)現(xiàn)),然后它會修改進(jìn)程中對內(nèi)存分配和釋放的函數(shù)調(diào)用,讓這些調(diào)用首先轉(zhuǎn)入它的代碼,然后再執(zhí)行原來的代碼。BoundsChecker在做這些動作的時,無須修改被調(diào)試程序的源代碼或工程配置文件,這使得使用它非常的簡便、直接。

              這里我們以malloc函數(shù)為例,截獲其他的函數(shù)方法與此類似。

              需要被截獲的函數(shù)可能在DLL中,也可能在程序的代碼里。比如,如果靜態(tài)連結(jié)C-Runtime Library,那么malloc函數(shù)的代碼會被連結(jié)到程序里。為了截獲住對這類函數(shù)的調(diào)用,BoundsChecker會動態(tài)修改這些函數(shù)的指令。

              以下兩段匯編代碼,一段沒有BoundsChecker介入,另一段則有BoundsChecker的介入:
            126:?_CRTIMP?void?*?__cdecl?malloc?(
            127:?size_t?nSize
            128:?)
            129:?{

            00403C10?push?ebp
            00403C11?mov?ebp,esp
            130:?return?_nh_malloc_dbg(nSize,?_newmode,?_NORMAL_BLOCK,?NULL,?0);
            00403C13?push?
            0
            00403C15?push?
            0
            00403C17?push?
            1
            00403C19?mov?eax,[__newmode?(0042376c)]
            00403C1E?push?eax
            00403C1F?mov?ecx,dword?ptr?[nSize]
            00403C22?push?ecx
            00403C23?call?_nh_malloc_dbg?(00403c80)
            00403C28?add?esp,14h
            131:?}
            以下這一段代碼有BoundsChecker介入:
            126:?_CRTIMP?void?*?__cdecl?malloc?(
            127:?size_t?nSize
            128:?)
            129:?{

            00403C10?jmp?01F41EC8
            00403C15?push?
            0
            00403C17?push?
            1
            00403C19?mov?eax,[__newmode?(0042376c)]
            00403C1E?push?eax
            00403C1F?mov?ecx,dword?ptr?[nSize]
            00403C22?push?ecx
            00403C23?call?_nh_malloc_dbg?(00403c80)
            00403C28?add?esp,14h
            131:?}

              當(dāng)BoundsChecker介入后,函數(shù)malloc的前三條匯編指令被替換成一條jmp指令,原來的三條指令被搬到地址01F41EC8處了。當(dāng)程序進(jìn)入malloc后先jmp到01F41EC8,執(zhí)行原來的三條指令,然后就是BoundsChecker的天下了。大致上它會先記錄函數(shù)的返回地址(函數(shù)的返回地址在stack上,所以很容易修改),然后把返回地址指向?qū)儆贐oundsChecker的代碼,接著跳到malloc函數(shù)原來的指令,也就是在00403c15的地方。當(dāng)malloc函數(shù)結(jié)束的時候,由于返回地址被修改,它會返回到BoundsChecker的代碼中,此時BoundsChecker會記錄由malloc分配的內(nèi)存的指針,然后再跳轉(zhuǎn)到到原來的返回地址去。

              如果內(nèi)存分配/釋放函數(shù)在DLL中,BoundsChecker則采用另一種方法來截獲對這些函數(shù)的調(diào)用。BoundsChecker通過修改程序的DLL Import Table讓table中的函數(shù)地址指向自己的地址,以達(dá)到截獲的目的。

              截獲住這些分配和釋放函數(shù),BoundsChecker就能記錄被分配的內(nèi)存或資源的生命周期。接下來的問題是如何與源代碼相關(guān),也就是說當(dāng)BoundsChecker檢測到內(nèi)存泄漏,它如何報告這塊內(nèi)存塊是哪段代碼分配的。答案是調(diào)試信息(Debug Information)。當(dāng)我們編譯一個Debug版的程序時,編譯器會把源代碼和二進(jìn)制代碼之間的對應(yīng)關(guān)系記錄下來,放到一個單獨(dú)的文件里(.pdb)或者直接連結(jié)進(jìn)目標(biāo)程序,通過直接讀取調(diào)試信息就能得到分配某塊內(nèi)存的源代碼在哪個文件,哪一行上。使用Code Injection和Debug Information,使BoundsChecker不但能記錄呼叫分配函數(shù)的源代碼的位置,而且還能記錄分配時的Call Stack,以及Call Stack上的函數(shù)的源代碼位置。這在使用像MFC這樣的類庫時非常有用,以下我用一個例子來說明:

            void?ShowXItemMenu()
            {
             …
             CMenu?menu;

             menu.CreatePopupMenu();
             
            //add?menu?items.
             menu.TrackPropupMenu();
             …
            }

            void?ShowYItemMenu(?)
            {
             …
             CMenu?menu;
             menu.CreatePopupMenu();
             
            //add?menu?items.
             menu.TrackPropupMenu();
             menu.Detach();
            //this?will?cause?HMENU?leak
             …
            }

            BOOL?CMenu::CreatePopupMenu()
            {
             …
             hMenu?
            =?CreatePopupMenu();
             …
            }
            當(dāng)調(diào)用ShowYItemMenu()時,我們故意造成HMENU的泄漏。但是,對于BoundsChecker來說被泄漏的HMENU是在class CMenu::CreatePopupMenu()中分配的。假設(shè)的你的程序有許多地方使用了CMenu的CreatePopupMenu()函數(shù),如CMenu::CreatePopupMenu()造成的,你依然無法確認(rèn)問題的根結(jié)到底在哪里,在ShowXItemMenu()中還是在ShowYItemMenu()中,或者還有其它的地方也使用了CreatePopupMenu()?有了Call Stack的信息,問題就容易了。BoundsChecker會如下報告泄漏的HMENU的信息:
            Function
            File
            Line

            CMenu::CreatePopupMenu
            E:\
            8168\vc98\mfc\mfc\include\afxwin1.inl
            1009

            ShowYItemMenu
            E:\testmemleak\mytest.cpp
            100
              
            這里省略了其他的函數(shù)調(diào)用

            如此,我們很容易找到發(fā)生問題的函數(shù)是ShowYItemMenu()。當(dāng)使用MFC之類的類庫編程時,大部分的API調(diào)用都被封裝在類庫的class里,有了Call Stack信息,我們就可以非常容易的追蹤到真正發(fā)生泄漏的代碼。

              記錄Call Stack信息會使程序的運(yùn)行變得非常慢,因此默認(rèn)情況下BoundsChecker不會記錄Call Stack信息??梢园凑找韵碌牟襟E打開記錄Call Stack信息的選項(xiàng)開關(guān):

              1. 打開菜單:BoundsChecker|Setting…

              2. 在Error Detection頁中,在Error Detection Scheme的List中選擇Custom

              3. 在Category的Combox中選擇 Pointer and leak error check

              4. 鉤上Report Call Stack復(fù)選框

              5. 點(diǎn)擊Ok

              基于Code Injection,BoundsChecker還提供了API Parameter的校驗(yàn)功能,memory over run等功能。這些功能對于程序的開發(fā)都非常有益。由于這些內(nèi)容不屬于本文的主題,所以不在此詳述了。

              盡管BoundsChecker的功能如此強(qiáng)大,但是面對隱式內(nèi)存泄漏仍然顯得蒼白無力。所以接下來我們看看如何用Performance Monitor檢測內(nèi)存泄漏。

              使用Performance Monitor檢測內(nèi)存泄漏

              NT的內(nèi)核在設(shè)計過程中已經(jīng)加入了系統(tǒng)監(jiān)視功能,比如CPU的使用率,內(nèi)存的使用情況,I/O操作的頻繁度等都作為一個個Counter,應(yīng)用程序可以通過讀取這些Counter了解整個系統(tǒng)的或者某個進(jìn)程的運(yùn)行狀況。Performance Monitor就是這樣一個應(yīng)用程序。

              為了檢測內(nèi)存泄漏,我們一般可以監(jiān)視Process對象的Handle Count,Virutal Bytes 和Working Set三個Counter。Handle Count記錄了進(jìn)程當(dāng)前打開的HANDLE的個數(shù),監(jiān)視這個Counter有助于我們發(fā)現(xiàn)程序是否有Handle泄漏;Virtual Bytes記錄了該進(jìn)程當(dāng)前在虛地址空間上使用的虛擬內(nèi)存的大小,NT的內(nèi)存分配采用了兩步走的方法,首先,在虛地址空間上保留一段空間,這時操作系統(tǒng)并沒有分配物理內(nèi)存,只是保留了一段地址。然后,再提交這段空間,這時操作系統(tǒng)才會分配物理內(nèi)存。所以,Virtual Bytes一般總大于程序的Working Set。監(jiān)視Virutal Bytes可以幫助我們發(fā)現(xiàn)一些系統(tǒng)底層的問題; Working Set記錄了操作系統(tǒng)為進(jìn)程已提交的內(nèi)存的總量,這個值和程序申請的內(nèi)存總量存在密切的關(guān)系,如果程序存在內(nèi)存的泄漏這個值會持續(xù)增加,但是Virtual Bytes卻是跳躍式增加的。

              監(jiān)視這些Counter可以讓我們了解進(jìn)程使用內(nèi)存的情況,如果發(fā)生了泄漏,即使是隱式內(nèi)存泄漏,這些Counter的值也會持續(xù)增加。但是,我們知道有問題卻不知道哪里有問題,所以一般使用Performance Monitor來驗(yàn)證是否有內(nèi)存泄漏,而使用BoundsChecker來找到和解決。

              當(dāng)Performance Monitor顯示有內(nèi)存泄漏,而BoundsChecker卻無法檢測到,這時有兩種可能:第一種,發(fā)生了偶發(fā)性內(nèi)存泄漏。這時你要確保使用Performance Monitor和使用BoundsChecker時,程序的運(yùn)行環(huán)境和操作方法是一致的。第二種,發(fā)生了隱式的內(nèi)存泄漏。這時你要重新審查程序的設(shè)計,然后仔細(xì)研究Performance Monitor記錄的Counter的值的變化圖,分析其中的變化和程序運(yùn)行邏輯的關(guān)系,找到一些可能的原因。這是一個痛苦的過程,充滿了假設(shè)、猜想、驗(yàn)證、失敗,但這也是一個積累經(jīng)驗(yàn)的絕好機(jī)會。

              總結(jié)

              內(nèi)存泄漏是個大而復(fù)雜的問題,即使是Java和.Net這樣有Gabarge Collection機(jī)制的環(huán)境,也存在著泄漏的可能,比如隱式內(nèi)存泄漏。由于篇幅和能力的限制,本文只能對這個主題做一個粗淺的研究。其他的問題,比如多模塊下的泄漏檢測,如何在程序運(yùn)行時對內(nèi)存使用情況進(jìn)行分析等等,都是可以深入研究的題目。如果您有什么想法,建議或發(fā)現(xiàn)了某些錯誤,歡迎和我交流。


            posted on 2006-10-19 13:45 泡泡牛 閱讀(9153) 評論(2)  編輯 收藏 引用 所屬分類: Develop

            評論

            # re: 用BoundsChecker檢測內(nèi)存泄漏 (zz)  2008-10-10 16:02 Pfeng

            大俠,請問您對查找C#的內(nèi)存泄漏是否有好的辦法?TKS!
            MSN: P.feng@hotmail.com
              回復(fù)  更多評論    

            # re: 用BoundsChecker檢測內(nèi)存泄漏 (zz)  2008-12-03 15:53 紫羅

            boundschecker支持什么編譯器呢?
              回復(fù)  更多評論    
            人妻中文久久久久| 久久久久AV综合网成人| 久久久久国产一级毛片高清版| 久久国产精品无码HDAV| 国产精品久久久久久影院| 99久久精品免费看国产| 亚洲中文字幕伊人久久无码| 亚洲狠狠婷婷综合久久久久 | 久久国产色AV免费观看| 久久99久久99小草精品免视看 | 国产精品成人久久久久三级午夜电影| 欧美伊香蕉久久综合类网站| 性做久久久久久久久久久| 久久成人国产精品| 国产香蕉久久精品综合网| 日本精品久久久久中文字幕8 | 狠狠人妻久久久久久综合蜜桃| 日韩久久无码免费毛片软件| 久久久噜噜噜www成人网| 久久久久久噜噜精品免费直播| 亚洲精品白浆高清久久久久久| 久久99精品九九九久久婷婷| 久久婷婷国产综合精品 | 2021久久精品免费观看| 国产免费久久久久久无码| 亚洲av日韩精品久久久久久a| 久久精品亚洲福利| 99久久免费国产特黄| 性做久久久久久久| 99精品国产免费久久久久久下载 | 久久精品aⅴ无码中文字字幕不卡 久久精品aⅴ无码中文字字幕重口 | 久久综合精品国产二区无码| 亚洲国产成人乱码精品女人久久久不卡| 97精品久久天干天天天按摩| 色综合久久久久无码专区| 久久综合亚洲色一区二区三区 | 亚洲第一极品精品无码久久 | 亚洲精品无码久久久影院相关影片| 免费一级欧美大片久久网| 久久天天躁狠狠躁夜夜2020老熟妇| 久久中文字幕一区二区|