低調做技術__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx
author: kevin lynx
這個內存泄露工具最基本的原理就是利用宏替換掉標準的malloc、free(暫不考慮其他內存分配函數,如realloc、strdup),記錄下每次內存分配和釋放動作。因為宏的處理發生在預處理階段,所以可以很容易地用你自己的malloc函數替換掉標準的malloc。例如:
要使用以上代碼,用戶在使用時就需要包含lib.h,從而可以使用宏將用戶自己寫的malloc替換為my_mallo。當程序退出時,如果count大于0,那么可以肯定的是有內存泄露。當然,如果count為負數,則很可能對同一個指針進行多次free。
但是以上代碼的功能太局限了。一個真正的內存泄露檢測庫(工具),至少需要報告泄露的代碼文件、函數、行數等信息。當然,如果能報告調用堆棧,就更好了。不過這就依賴于具體的平臺,需要使用特定的系統接口才可以獲取出。
要實現以上功能也很簡單,只需要在每次調用malloc的時候,通過編譯器預定義宏__FILE__、__LINE__、__FUNCTION__(__func__)就可以得到文件名、函數、行號等信息。將這些信息保存起來,然后在free的時候移除相應的信息即可。
最簡單的實現方式,就是保存一個表,表里記錄著每次分配內存的信息:
但是,通過單單一個free函數的void*參數,如何獲取出對應的分配記錄呢?難道:
雖然可行,但是很stupid。這里提供一個小技巧:
意思就是說,我多分配了4字節內存(sizeof( size_t ) ),用于保存這次分配記錄在mem_record中被保存的索引。在釋放內存的時候,通過一些地址偏移計算,就可以獲取出真正的系統malloc返回的地址,從而安全釋放(別給我說這里的計算存在平臺和編譯器的限制,沒認真看文章的SB才說)。
另一個問題是,我們如何處理每次分配釋放時,對于分配記錄那個數據結構,也就是mem_record。每一次free的時候,移除的記錄可能位于mem_record的任何位置。一定時間后,mem_record內部將出現很多漏洞(已經沒用的數組位置)。解決這個問題最直接當然還是最stupid的方法,就是每次free移除記錄時重新整理一遍mem_record。如果你這樣做了,那你的malloc/free比微軟的還慢。
我的解決方法是:size_t free_index[MAX_RECORD];用于保存這些出現漏洞的index。每一次free移除記錄時,就把這個記錄對應的inex保存進來,表示這個index指向的mem_record[]可用。每一次malloc的時候,先從這里取index,如果這里沒有,那可以直接從mem_record的末尾取。
具體細節就不闡述了,典型的空間換時間方法。整個庫很簡單,代碼100來行。我也只進行過粗略的測試。我肯定這100來行代碼是有問題的,相信自己的代碼存在問題是對bug的一種覺悟,哈哈哈。
這個東西只檢測C語言的內存泄露,其實要檢測C++的也很簡單,只需要重載new和delete就可以了。
要放春節假了,在公司的最后幾個小時實在無聊,才做了這個東西,前后花了1個多小時,寫起來感覺不錯。
posted on 2009-01-23 17:43 Kevin Lynx 閱讀(4394) 評論(5) 編輯 收藏 引用 所屬分類: c/c++
本人一直博主學習 回復 更多評論
char *str = (char*) malloc( 100 ); char *str2 = (char*) malloc( 111 ); str = str2; free(str); free(str); getchar();Memory leak report by cmlc, a tiny c memory leak checking library.Detected 0 memory leaks.似乎在free上出了點問題 回復 更多評論
static void cmlc_remove_record( size_t index ){ if( fi_tail < MAX_RECORD - 1 ) { free_index[fi_tail++] = index; free(mem_record[index].address); mem_record[index].address = 0; /* to identify this record has been removed */ }}void cmlc_free( void *address ){ struct memory *mem = (struct memory*)( (char*)address - sizeof( size_t ) ); cmlc_remove_record( mem->index );// free( mem ); //注釋的free,移到cmlc_remove_record中} 回復 更多評論
需要考慮多線程,否則太不安全了,沒有實際意義。 回復 更多評論
微軟的編譯器僅需在cpp開始的地方: #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> 當你認為東西都是放完了以后調用 _CrtDumpMemoryLeaks(); 東西就回到output窗口去了。 回復 更多評論
Powered by: C++博客 Copyright © Kevin Lynx