您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關如何解決Linux下c++程序內存泄漏檢測代碼,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
Linux下對于程序內存泄漏檢測的方法很多,最常用的的莫過于使用valgrind工具。但是valgrind相當于讓程序在虛擬機中運行,會帶 來較大的系統資源開銷,還會對程序的運行效率產生較大影響,對于那種資源占用大的程序,如果需要長時間運行才能暴露的泄漏問題,它就顯得不太好用。
linux下的c++程序中自己實現一個輕量級的泄漏檢測代碼其實是比較方便的,下面我就給出一個簡單的范例,并作簡單的說明。當然,我們還是應該提倡使用共享指針,用共享指針自動管理內存可以避免內存泄漏這樣的不必要的麻煩。
基本原理:
利用glibc提供的__malloc_hook, __free_hook系列函數對內存分配是否做監控;(詳見glibc的官方文檔)
利用backtrace函數獲取函數調用棧,并記錄;
利用backtrace_symbols對調用棧對應的函數做解析;
進一步處理:
使用abi::__cxa_demangle把函數名解析為源代碼風格;
使用addr2line解析出函數調用棧對應的代碼行;
對于動態庫(.so)中的地址解析,需要先在/proc/<pid>/maps文件中找到動態庫映射的基地址,才能做解析。
注意:
編譯連接參數中使用-g -rdynamic
以上每步具體實現的代碼可能都沒有達到***,甚至可能是笨辦法,如果有更好的實現方案請直接替換,也歡迎賜教。
示例代碼:
leakmom.cpp
/* Prototypes for __malloc_hook, __free_hook */ #include <malloc.h> #include <map> #include <utility> #include <execinfo.h> #include <errno.h> #include <assert.h> #include <cxxabi.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include "leakmon.h" CMutexLock gLock ; std::map <void*, _PtrInfo> gPtrInfo ; std::map <const LmCallStack*, _AllocInfo , __comp> gLeakInfo; const int LmCallStack:: MAX_STACK_LAYERS = 32; /* Prototypes for our hooks. */ static void my_init_hook ( void); static void *my_malloc_hook ( size_t, const void *); static void my_free_hook ( void*, const void *); void *(*__MALLOC_HOOK_VOLATILE old_malloc_hook)( size_t __size , const void *) ; void (*__MALLOC_HOOK_VOLATILE old_free_hook) ( void *__ptr , const void *); /* Override initializing hook from the C library. */ void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook) ( void) = my_init_hook; void my_init_hook (void) { old_malloc_hook = __malloc_hook ; old_free_hook = __free_hook ; __malloc_hook = my_malloc_hook ; __free_hook = my_free_hook ; } static void *my_malloc_hook ( size_t size , const void *caller ) { void *result ; gLock.lock (); /* Restore all old hooks */ __malloc_hook = old_malloc_hook ; __free_hook = old_free_hook ; /* Call recursively */ result = malloc (size); /* Save underlying hooks */ old_malloc_hook = __malloc_hook ; old_free_hook = __free_hook ; /* printf might call malloc, so protect it too. */ //printf ("malloc (%u) returns %p\n", (unsigned int) size, result); RecordPtr( result , size); /* Restore our own hooks */ __malloc_hook = my_malloc_hook ; __free_hook = my_free_hook ; gLock.unlock (); return result ; } static void my_free_hook ( void *ptr , const void *caller ) { gLock.lock (); /* Restore all old hooks */ __malloc_hook = old_malloc_hook ; __free_hook = old_free_hook ; /* Call recursively */ free (ptr ); /* Save underlying hooks */ old_malloc_hook = __malloc_hook ; old_free_hook = __free_hook ; /* printf might call free, so protect it too. */ //printf ("freed pointer %p\n", ptr); RemovePtr( ptr ); /* Restore our own hooks */ __malloc_hook = my_malloc_hook ; __free_hook = my_free_hook ; gLock.unlock (); } void RecordPtr ( void* ptr, size_t size) { // 獲取調用棧 void *array [LmCallStack:: MAX_STACK_LAYERS]; int cstSize = backtrace( array, LmCallStack ::MAX_STACK_LAYERS); // 保存指針 調用棧 LmCallStack* callstack = new LmCallStack(array , cstSize); gLock.lock (); std::map <const LmCallStack*, _AllocInfo , __comp>:: iterator it = gLeakInfo.find (callstack); if (it != gLeakInfo. end()) { it->second .size += size; it->second .alloc++; _PtrInfo pi (it-> first, size ); gPtrInfo[ptr ] = pi; } else { _AllocInfo aif (size, 1, 0); std::pair <std:: map<const LmCallStack*, _AllocInfo, __comp>::iterator , bool> ret = gLeakInfo .insert( std::pair <const LmCallStack*, _AllocInfo >(callstack, aif)); if (ret .second) { _PtrInfo pi (ret. first->first , size); gPtrInfo[ptr ] = pi; } else { // failed } } gLock.unlock (); } void RemovePtr ( void* ptr ) { gLock.lock (); std::map <void*, _PtrInfo>::iterator it = gPtrInfo.find (ptr); if (it != gPtrInfo. end()) { std::map <const LmCallStack*, _AllocInfo , __comp>:: iterator itc = gLeakInfo .find( it->second .csk); if (itc != gLeakInfo. end()) { itc->second .size -= it->second .size; itc->second .free++; if (0 == (itc ->second. alloc - itc ->second. free)) { assert(0 == itc ->second. size); delete itc ->first; gLeakInfo.erase (itc); } } gPtrInfo.erase (it); } gLock.unlock (); } void Report () { char **strings = NULL; gLock.lock (); __malloc_hook = old_malloc_hook ; __free_hook = old_free_hook ; for (std ::map< const LmCallStack *, _AllocInfo, __comp>::iterator it = gLeakInfo .begin(); it != gLeakInfo .end(); it++) { printf("\n" ); printf("====> size: %ld, allocs: %d, frees: %d, a-f: %d\n", it-> second.size , it-> second.alloc , it-> second.free , it->second .alloc- it->second .free ); printf("====> stacks back trace:\n" ); strings = backtrace_symbols ((void**) it->first ->callstack, it->first ->size); if (strings ) { for(int i = 2; i < it ->first-> size; i ++) { //printf(" %s\n", strings[i]); char output [1024] = {0}; memset(output , 0, 1024); char temp [1024] = {0}; memset(temp , 0, 1024); //// //// get real function name //// if (1 == sscanf (strings[ i], "%*[^(]%*[^_]%[^)+]" , temp)) { int status ; char* realname = abi::__cxa_demangle (temp, 0, 0, & status); if (0 == status ) { char* p = strchr( strings[i ], '('); memcpy(output , strings[ i], p-strings [i]); sprintf(output +(p- strings[i ]), "(%s+%p) " , realname, (( void**)it ->first-> callstack)[i ]); //printf(" -%s\n", realname); free(realname ); } else { char* p = strchr( strings[i ], ')'); memcpy(output , strings[ i], p-strings [i]+2); } } else { char* p = strchr( strings[i ], ')'); memcpy(output , strings[ i], p-strings [i]+2); } FILE * fp ; char module [1024] = {0}; memset(module , 0, 1024); char* pm = strchr( strings[i ], '('); memcpy(module , strings[ i], pm -strings[ i]); if (strstr (module, ".so")) { __pid_t pid = getpid(); sprintf(temp , "grep %s /proc/%d/maps", module, pid ); /// /// get library base-map-address /// fp = popen (temp, "r"); if (fp ) { char baseaddr [64]; unsigned long long base; fgets(temp , sizeof( temp)-1, fp ); //printf("memmap: %s\n", temp); sscanf(temp , "%[^-]", baseaddr); base = strtoll (baseaddr, NULL, 16); //printf("baseaddr:%s\n", baseaddr); //printf(" base:0x%llx\n", base); sprintf(temp , "addr2line -e %s %p", module, (void *)((unsigned long long)((void **)it-> first->callstack )[i]- base)); } } else { sprintf(temp , "addr2line -e %s %p", module, ((void **)it-> first->callstack )[i]); } //// //// get source file name and line number //// fp = popen (temp, "r"); //printf("cmdline: %s\n", temp); if (fp ) { fgets(temp , sizeof( temp)-1, fp ); //printf(" -%s\n", temp); strcat(output , temp); printf(" -> %s" , output); pclose(fp ); } else { printf(" -> %s\n" , output); } } free(strings ); strings = NULL ; } } __malloc_hook = my_malloc_hook ; __free_hook = my_free_hook ; gLock.unlock (); } ////////////////////////////////////////////////////////////////////////// CMutexLock::CMutexLock () { pthread_mutexattr_t m_attr ; pthread_mutexattr_init(&m_attr ); pthread_mutexattr_settype(&m_attr , PTHREAD_MUTEX_RECURSIVE); if (0 != pthread_mutex_init (&m_mutex , & m_attr)) { printf("c_lock::c_lock pthread_mutex_init error<%d>.\n" , errno); assert(0); } pthread_mutexattr_destroy(&m_attr ); } CMutexLock::~CMutexLock () { if(0 != pthread_mutex_destroy (&m_mutex)) { printf("c_lock::~c_lock pthread_mutex_destroy error<%d>.\n" , errno); assert(0); } } void CMutexLock::lock () { if(0 != pthread_mutex_lock (&m_mutex)) { assert("c_lock::lock pthread_mutex_lock " && 0); } } void CMutexLock::unlock () { int iRet = 0; if(0 != (iRet = pthread_mutex_unlock(& m_mutex))) { printf("c_lock::unlock pthread_mutex_unlock ret<%d> error<%d>.\n", iRet, errno ); assert(0); } } 示例代碼: leakmom.h //////////////////////////////////////////////////////////////////////// // // The Executable file MUST be linked with parameter '-rdynamic' !!! // //////////////////////////////////////////////////////////////////////// #pragma once #include <string.h> #include <pthread.h> // class LmCallStack { public: char* callstack ; // pointer to buffer recording callstack addresses int size ; // count of call stacks static const int MAX_STACK_LAYERS; public: LmCallStack(void * csk= NULL, int s=0) { if (csk ) { callstack = new char[ s*sizeof (void*)]; memcpy(callstack , csk, s*sizeof (void*)); } else { callstack = (char *)csk; } size = s ; } ~ LmCallStack() { if (callstack ) { delete[] callstack ; } callstack = NULL ; size = 0; } }; class __comp { public: __comp(){}; bool operator () (const LmCallStack* first , const LmCallStack* second) { return ((first ->size < second->size ) || ( first->size == second-> size && memcmp(first ->callstack, second->callstack , sizeof( void*)*first ->size) < 0) ); } }; struct _PtrInfo { _PtrInfo(const LmCallStack* c=NULL , long s=0) { csk = c ; size = s ; } const LmCallStack * csk; long size ; }; struct _AllocInfo { _AllocInfo(long s=0, int a =0, int f=0) { size=s ; alloc=a ; free=f ; } // long size ; int alloc ; int free ; }; class CMutexLock { public: CMutexLock(); ~ CMutexLock(); public: void lock (); void unlock (); private: pthread_mutex_t m_mutex ; }; // void RecordPtr ( void* ptr, size_t size); void RemovePtr (void* ptr); void Report ();
上述就是小編為大家分享的如何解決Linux下c++程序內存泄漏檢測代碼了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。