// ============================================================== // This file is part of Glest Shared Library (www.glest.org) // // Copyright (C) 2001-2008 MartiƱo Figueroa // // You can redistribute this code and/or modify it under // the terms of the GNU General Public License as published // by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version // ============================================================== #ifndef _LEAKDUMPER_H_ #define _LEAKDUMPER_H_ //#define SL_LEAK_DUMP //SL_LEAK_DUMP controls if leak dumping is enabled or not #ifdef SL_LEAK_DUMP #include // START - Special includes because the use a special new operator that we cannot override #include #include "rapidxml.hpp" #include // END - Special includes because the use a special new operator that we cannot override #include #include #include #include "thread.h" using Shared::Platform::Mutex; using Shared::Platform::MutexSafeWrapper; // START - For gcc backtrace //#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__FreeBSD__) && !defined(BSD) #if defined(HAS_GCC_BACKTRACE) #include #include #include #endif // END - For gcc backtrace using namespace std; //including this header in any file of a project will cause all //leaks to be dumped into leak_dump.txt, but only allocations that //occurred in a file where this header is included will have //file and line number static bool want_full_leak_stacktrace = false; static bool want_full_leak_stacktrace_line_numbers = false; struct AllocInfo { private: static bool application_binary_initialized; public: inline static string &get_application_binary() { static string application_binary = ""; return application_binary; } int line; const char *file; size_t bytes; void *ptr; bool freetouse; bool array; bool inuse; string stack; inline AllocInfo() : ptr(0), file(""), line(-1), bytes(0), array(false), freetouse(false), inuse(false), stack("") { } inline AllocInfo(void* ptr, const char* file, int line, string stacktrace, size_t bytes, bool array) : ptr(ptr), file(file), line(line), bytes(bytes), array(array), freetouse(false), inuse(true), stack(stacktrace) { } //#if defined(__GNUC__) && !defined(__FreeBSD__) && !defined(BSD) #if defined(HAS_GCC_BACKTRACE) inline static int getFileAndLine(char *function, void *address, char *file, size_t flen) { int line=-1; if(want_full_leak_stacktrace_line_numbers == true && AllocInfo::get_application_binary() != "") { const int maxbufSize = 8094; char buf[maxbufSize+1]=""; //char *p=NULL; // prepare command to be executed // our program need to be passed after the -e parameter //sprintf (buf, "/usr/bin/addr2line -C -e ./a.out -f -i %lx", addr); snprintf(buf, 8096,"addr2line -C -e %s -f -i %p",AllocInfo::get_application_binary().c_str(),address); FILE* f = popen (buf, "r"); if (f == NULL) { perror (buf); return 0; } if(function != NULL && function[0] != '\0') { line = 0; for(;function != NULL && function[0] != '\0';) { // get function name char *ret = fgets (buf, maxbufSize, f); if(ret == NULL) { pclose(f); return line; } //printf("Looking for [%s] Found [%s]\n",function,ret); if(strstr(ret,function) != NULL) { break; } // get file and line ret = fgets (buf, maxbufSize, f); if(ret == NULL) { pclose(f); return line; } if(strlen(buf) > 0 && buf[0] != '?') { //int l; char *p = buf; // file name is until ':' while(*p != 0 && *p != ':') { p++; } *p++ = 0; // after file name follows line number strcpy (file , buf); sscanf (p,"%d", &line); } else { strcpy (file,"unknown"); line = 0; } } } // get file and line char *ret = fgets (buf, maxbufSize, f); if(ret == NULL) { pclose(f); return line; } if(strlen(buf) > 0 && buf[0] != '?') { //int l; char *p = buf; // file name is until ':' while(*p != 0 && *p != ':') { p++; } *p++ = 0; // after file name follows line number strcpy (file , buf); sscanf (p,"%d", &line); } else { strcpy (file,"unknown"); line = 0; } pclose(f); } return line; } #endif inline static string getStackTrace() { //#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__FreeBSD__) && !defined(BSD) #if defined(HAS_GCC_BACKTRACE) if(want_full_leak_stacktrace == true) { string errMsg = "\nStack Trace:\n"; //errMsg += "To find line #'s use:\n"; //errMsg += "readelf --debug-dump=decodedline %s | egrep 0xaddress-of-stack\n"; const size_t max_depth = 6; void *stack_addrs[max_depth]; size_t stack_depth = backtrace(stack_addrs, max_depth); char **stack_strings = backtrace_symbols(stack_addrs, stack_depth); //for (size_t i = 1; i < stack_depth; i++) { // errMsg += string(stack_strings[i]) + "\n"; //} //if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); if(stack_depth > 0) { char szBuf[8096]=""; for(size_t i = 1; i < stack_depth; i++) { void *lineAddress = stack_addrs[i]; //getStackAddress(stackIndex); size_t sz = 8096; // just a guess, template names will go much wider char *function = static_cast(malloc(sz)); function[0] = '\0'; char *begin = 0; char *end = 0; // find the parentheses and address offset surrounding the mangled name for (char *j = stack_strings[i]; *j; ++j) { if (*j == '(') { begin = j; } else if (*j == '+') { end = j; } } if (begin && end) { *begin++ = '\0'; *end = '\0'; // found our mangled name, now in [begin, end) int status; char *ret = abi::__cxa_demangle(begin, function, &sz, &status); if (ret) { // return value may be a realloc() of the input function = ret; } else { // demangling failed, just pretend it's a C function with no args strncpy(function, begin, sz); strncat(function, "()", sz); function[sz-1] = '\0'; } //fprintf(out, " %s:%s\n", stack.strings[i], function); snprintf(szBuf,8096,"%s:%s address [%p]",stack_strings[i],function,lineAddress); } else { // didn't find the mangled name, just print the whole line //fprintf(out, " %s\n", stack.strings[i]); snprintf(szBuf,8096,"%s address [%p]",stack_strings[i],lineAddress); } errMsg += string(szBuf); if(want_full_leak_stacktrace_line_numbers == true && AllocInfo::get_application_binary() != "") { char file[8096]=""; int line = getFileAndLine(function, lineAddress, file, 8096); if(line >= 0) { char lineBuf[1024]=""; snprintf(lineBuf,1024,"%d",line); errMsg += " line: " + string(lineBuf); } } errMsg += "\n"; free(function); } } free(stack_strings); // malloc()ed by backtrace_symbols //printf("%s",errMsg.c_str()); return errMsg; } else { static string empty = ""; return empty; } #else static string empty = ""; return empty; #endif } }; // ===================================================== // class AllocRegistry // ===================================================== class AllocRegistry { private: static const size_t maxAllocs= 100000; Mutex *mutex; private: inline AllocRegistry() { // manual memory allocation void *mymutex_mem = malloc(sizeof(Mutex)); // in place new operator mutex = new(mymutex_mem) Mutex; string value = AllocInfo::get_application_binary(); reset(); } private: AllocInfo allocs[maxAllocs]; //array to store allocation info int allocCount; //allocations size_t allocBytes; //bytes allocated int nonMonitoredCount; size_t nonMonitoredBytes; int nextFreeIndex; public: ~AllocRegistry(); inline static AllocRegistry &getInstance() { static AllocRegistry allocRegistry; return allocRegistry; } inline void reset() { allocCount= 0; allocBytes= 0; nonMonitoredCount= 0; nonMonitoredBytes= 0; nextFreeIndex = 0; for(int i = 0; i < maxAllocs; ++i) { allocs[i].freetouse = true; allocs[i].inuse = false; } } inline void allocate(AllocInfo info) { //if(info.line == 0) return; MutexSafeWrapper safeMutexMasterList(mutex); //printf("ALLOCATE.\tfile: %s, line: %d, bytes: " MG_SIZE_T_SPECIFIER ", array: %d inuse: %d\n", info.file, info.line, info.bytes, info.array, info.inuse); if(info.line > 0) { ++allocCount; allocBytes += info.bytes; } for(int i = (nextFreeIndex >= 0 ? nextFreeIndex : 0); i < maxAllocs; ++i) { if(allocs[i].freetouse == true && allocs[i].inuse == false) { allocs[i]= info; nextFreeIndex=-1; return; } } printf("ALLOCATE NOT MONITORED.\tfile: %s, line: %d, ptr [%p], bytes: " MG_SIZE_T_SPECIFIER ", array: %d inuse: %d, \n%s\n", info.file, info.line, info.ptr, info.bytes, info.array, info.inuse, info.stack.c_str()); ++nonMonitoredCount; nonMonitoredBytes+= info.bytes; } inline void deallocate(void* ptr, bool array,const char* file, int line) { //if(line == 0) return; MutexSafeWrapper safeMutexMasterList(mutex); for(int i=0; i < maxAllocs; ++i) { if(allocs[i].freetouse == false && allocs[i].inuse == true && allocs[i].ptr == ptr && allocs[i].array == array) { allocs[i].freetouse= true; allocs[i].inuse = false; nextFreeIndex=i; return; } } if(line > 0) printf("WARNING, possible error on pointer delete for ptr [%p] array = %d, file [%s] line: %d\n%s\n",ptr,array,file, line,AllocInfo::getStackTrace().c_str()); } void dump(const char *path); }; //if an allocation ocurrs in a file where "leaks_dumper.h" is not included //this operator new is called and file and line will be unknown inline void * operator new (size_t bytes) { void *ptr= malloc(bytes); AllocRegistry::getInstance().allocate(AllocInfo(ptr, "unknown", 0, "", bytes, false)); return ptr; } inline void operator delete(void *ptr) { AllocRegistry::getInstance().deallocate(ptr, false, "unknown", 0); free(ptr); } inline void * operator new[](size_t bytes) { void *ptr= malloc(bytes); AllocRegistry::getInstance().allocate(AllocInfo(ptr, "unknown", 0, "", bytes, true)); return ptr; } inline void operator delete [](void *ptr) { AllocRegistry::getInstance().deallocate(ptr, true, "unknown", 0); free(ptr); } //if an allocation ocurrs in a file where "leaks_dumper.h" is included //this operator new is called and file and line will be known inline void * operator new (size_t bytes, const char* file, int line, string stack) { void *ptr= malloc(bytes); AllocRegistry::getInstance().allocate(AllocInfo(ptr, file, line, stack, bytes, false)); return ptr; } inline void operator delete(void *ptr, const char* file, int line) { AllocRegistry::getInstance().deallocate(ptr, false, file, line); free(ptr); } inline void * operator new[](size_t bytes, const char* file, int line,string stack) { void *ptr= malloc(bytes); AllocRegistry::getInstance().allocate(AllocInfo(ptr, file, line, stack, bytes, true)); return ptr; } inline void operator delete [](void *ptr, const char* file, int line) { AllocRegistry::getInstance().deallocate(ptr, true, file, line); free(ptr); } #define new new(__FILE__, __LINE__,AllocInfo::getStackTrace()) //#define new new(__FILE__, __LINE__,"") #endif #endif