MegaGlest/source/shared_lib/include/util/leak_dumper.h

424 lines
12 KiB
C++

// ==============================================================
// 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 <new>
// START - Special includes because the use a special new operator that we cannot override
#include <string>
#include "rapidxml.hpp"
#include <xercesc/dom/DOM.hpp>
// END - Special includes because the use a special new operator that we cannot override
#include <memory>
#include <cstdio>
#include <cstdlib>
#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 <execinfo.h>
#include <cxxabi.h>
#include <signal.h>
#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<char *>(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