Initial source commit
This commit is contained in:
commit
f1384c11ee
335 changed files with 52715 additions and 0 deletions
22
minorGems/util/development/leakTracer/LeakCheck
Executable file
22
minorGems/util/development/leakTracer/LeakCheck
Executable file
|
@ -0,0 +1,22 @@
|
|||
#!/bin/sh
|
||||
if [ $# -lt 1 ] ; then
|
||||
echo "Usage: $0 <program [program parameters]>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# this looks in the same directory, this
|
||||
# LeakCheck script resides; modify to your
|
||||
# needs:
|
||||
SHLIB=`dirname $0`/LeakTracer.so
|
||||
if [ ! -x $SHLIB ] ; then
|
||||
echo "$SHLIB not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$LEAKTRACE_FILE" ] ; then
|
||||
rm -f leak.out
|
||||
else
|
||||
rm -f "$LEAKTRACE_FILE"
|
||||
fi
|
||||
export LD_PRELOAD=$SHLIB
|
||||
exec $@
|
13
minorGems/util/development/leakTracer/LeakCheckAnalyze
Executable file
13
minorGems/util/development/leakTracer/LeakCheckAnalyze
Executable file
|
@ -0,0 +1,13 @@
|
|||
#!/bin/sh
|
||||
|
||||
CHECKER=`dirname $0`/LeakCheck" $@"
|
||||
ANALYZER=`dirname $0`/leak-analyze" $1 leak.out"
|
||||
|
||||
echo "Checking with: $CHECKER"
|
||||
echo ""
|
||||
$CHECKER
|
||||
|
||||
echo ""
|
||||
echo "Analyzing with: $ANALYZER"
|
||||
echo ""
|
||||
$ANALYZER 2>&1 | more
|
578
minorGems/util/development/leakTracer/LeakTracer.cc
Normal file
578
minorGems/util/development/leakTracer/LeakTracer.cc
Normal file
|
@ -0,0 +1,578 @@
|
|||
/*
|
||||
* Modification History
|
||||
*
|
||||
* 2002-March-31 Jason Rohrer
|
||||
* Added support for strdup.
|
||||
* Fixed bug in strdup.
|
||||
*
|
||||
* 2003-October-3 Jason Rohrer
|
||||
* Added printout of leak contents in report.
|
||||
*
|
||||
* 2004-January-16 Jason Rohrer
|
||||
* Switched to use minorGems platform-independed mutexes.
|
||||
* Changed to use simpler fopen call to open report.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Homepage: <http://www.andreasen.org/LeakTracer/>
|
||||
*
|
||||
* Authors:
|
||||
* Erwin S. Andreasen <erwin@andreasen.org>
|
||||
* Henner Zeller <foobar@to.com>
|
||||
*
|
||||
* This program is Public Domain
|
||||
*/
|
||||
|
||||
#ifdef THREAD_SAVE
|
||||
#define _THREAD_SAVE
|
||||
//#include <pthread.h>
|
||||
#include "minorGems/system/MutexLock.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
/*
|
||||
* underlying allocation, de-allocation used within
|
||||
* this tool
|
||||
*/
|
||||
#define LT_MALLOC malloc
|
||||
#define LT_FREE free
|
||||
#define LT_REALLOC realloc
|
||||
|
||||
/*
|
||||
* prime number for the address lookup hash table.
|
||||
* if you have _really_ many memory allocations, use a
|
||||
* higher value, like 343051 for instance.
|
||||
*/
|
||||
#define SOME_PRIME 35323
|
||||
#define ADDR_HASH(addr) ((unsigned long) addr % SOME_PRIME)
|
||||
|
||||
/**
|
||||
* Filedescriptor to write to. This should not be a low number,
|
||||
* because these often have special meanings (stdin, out, err)
|
||||
* and may be closed by the program (daemons)
|
||||
* So choose an arbitrary higher FileDescriptor .. e.g. 42
|
||||
*/
|
||||
#define FILEDESC 42
|
||||
|
||||
/**
|
||||
* allocate a bit more memory in order to check if there is a memory
|
||||
* overwrite. Either 0 or more than sizeof(unsigned int). Note, you can
|
||||
* only detect memory over_write_, not _reading_ beyond the boundaries. Better
|
||||
* use electric fence for these kind of bugs
|
||||
* <ftp://ftp.perens.com/pub/ElectricFence/>
|
||||
*/
|
||||
typedef unsigned long magic_t;
|
||||
#define MAGIC ((magic_t) 0xAABBCCDDLu)
|
||||
|
||||
/**
|
||||
* this may be more than sizeof(magic_t); if you want more, then
|
||||
* sepecify it like #define SAVESIZE (sizeof(magic_t) + 12)
|
||||
*/
|
||||
#define SAVESIZE (sizeof(magic_t) + 0)
|
||||
|
||||
/**
|
||||
* on 'new', initialize the memory with this value.
|
||||
* if not defined - uninitialized. This is very helpful because
|
||||
* it detects if you initialize your classes correctly .. if not,
|
||||
* this helps you faster to get the segmentation fault you're
|
||||
* implicitly asking for :-).
|
||||
*
|
||||
* Set this to some value which is likely to produce a
|
||||
* segmentation fault on your platform.
|
||||
*/
|
||||
#define SAVEVALUE 0xAA
|
||||
|
||||
/**
|
||||
* on 'delete', clean memory with this value.
|
||||
* if not defined - no memory clean.
|
||||
*
|
||||
* Set this to some value which is likely to produce a
|
||||
* segmentation fault on your platform.
|
||||
*/
|
||||
#define MEMCLEAN 0xEE
|
||||
|
||||
/**
|
||||
* Initial Number of memory allocations in our list.
|
||||
* Doubles for each re-allocation.
|
||||
*/
|
||||
#define INITIALSIZE 32768
|
||||
|
||||
static class LeakTracer {
|
||||
struct Leak {
|
||||
const void *addr;
|
||||
size_t size;
|
||||
const void *allocAddr;
|
||||
bool type;
|
||||
int nextBucket;
|
||||
};
|
||||
|
||||
int newCount; // how many memory blocks do we have
|
||||
int leaksCount; // amount of entries in the leaks array
|
||||
int firstFreeSpot; // Where is the first free spot in the leaks array?
|
||||
int currentAllocated; // currentAllocatedMemory
|
||||
int maxAllocated; // maximum Allocated
|
||||
unsigned long totalAllocations; // total number of allocations. stats.
|
||||
unsigned int abortOn; // resons to abort program (see abortReason_t)
|
||||
|
||||
/**
|
||||
* Have we been initialized yet? We depend on this being
|
||||
* false before constructor has been called!
|
||||
*/
|
||||
bool initialized;
|
||||
bool destroyed; // Has our destructor been called?
|
||||
|
||||
|
||||
FILE *report; // filedescriptor to write to
|
||||
|
||||
/**
|
||||
* pre-allocated array of leak info structs.
|
||||
*/
|
||||
Leak *leaks;
|
||||
|
||||
/**
|
||||
* fast hash to lookup the spot where an allocation is
|
||||
* stored in case of an delete. map<void*,index-in-leaks-array>
|
||||
*/
|
||||
int *leakHash; // fast lookup
|
||||
|
||||
#ifdef THREAD_SAVE
|
||||
MutexLock mutex;
|
||||
#endif
|
||||
|
||||
enum abortReason_t {
|
||||
OVERWRITE_MEMORY = 0x01,
|
||||
DELETE_NONEXISTENT = 0x02,
|
||||
NEW_DELETE_MISMATCH = 0x04
|
||||
};
|
||||
|
||||
public:
|
||||
LeakTracer() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
// Unfortunately we might be called before our constructor has actualy fired
|
||||
if (initialized)
|
||||
return;
|
||||
|
||||
// fprintf(stderr, "LeakTracer::initialize()\n");
|
||||
initialized = true;
|
||||
newCount = 0;
|
||||
leaksCount = 0;
|
||||
firstFreeSpot = 1; // index '0' is special
|
||||
currentAllocated = 0;
|
||||
maxAllocated = 0;
|
||||
totalAllocations = 0;
|
||||
abortOn = OVERWRITE_MEMORY; // only _severe_ reason
|
||||
report = 0;
|
||||
leaks = 0;
|
||||
leakHash = 0;
|
||||
|
||||
char uniqFilename[256];
|
||||
const char *filename = getenv("LEAKTRACE_FILE") ? : "leak.out";
|
||||
struct stat dummy;
|
||||
if (stat(filename, &dummy) == 0) {
|
||||
sprintf(uniqFilename, "%s.%d", filename, getpid());
|
||||
fprintf(stderr,
|
||||
"LeakTracer: file exists; using %s instead\n",
|
||||
uniqFilename);
|
||||
}
|
||||
else {
|
||||
sprintf(uniqFilename, "%s", filename);
|
||||
}
|
||||
|
||||
// not sure why this "open" code is here
|
||||
// (it doesn't open the file properly in MinGW on win32)
|
||||
/*
|
||||
int reportfd = open(uniqFilename,
|
||||
O_WRONLY|O_CREAT|O_TRUNC,S_IREAD|S_IWRITE);
|
||||
if (reportfd < 0) {
|
||||
fprintf(stderr, "LeakTracer: cannot open %s: %m\n",
|
||||
filename);
|
||||
report = stderr;
|
||||
}
|
||||
else {
|
||||
int dupfd = dup2(reportfd, FILEDESC);
|
||||
close(reportfd);
|
||||
report = fdopen(dupfd, "w");
|
||||
if (report == NULL) {
|
||||
report = stderr;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// simpler version using only fopen
|
||||
report = fopen( uniqFilename, "w" );
|
||||
if( report == NULL ) {
|
||||
fprintf( stderr, "LeakTracer: cannot open %s\n",
|
||||
uniqFilename );
|
||||
report = stderr;
|
||||
}
|
||||
|
||||
|
||||
time_t t = time(NULL);
|
||||
fprintf (report, "# starting %s", ctime(&t));
|
||||
|
||||
leakHash = (int*) LT_MALLOC(SOME_PRIME * sizeof(int));
|
||||
memset ((void*) leakHash, 0x00, SOME_PRIME * sizeof(int));
|
||||
|
||||
#ifdef MAGIC
|
||||
fprintf (report, "# memory overrun protection of %d Bytes "
|
||||
"with magic 0x%4lX\n",
|
||||
SAVESIZE, MAGIC);
|
||||
#endif
|
||||
|
||||
#ifdef SAVEVALUE
|
||||
fprintf (report, "# initializing new memory with 0x%2X\n",
|
||||
SAVEVALUE);
|
||||
#endif
|
||||
|
||||
#ifdef MEMCLEAN
|
||||
fprintf (report, "# sweeping deleted memory with 0x%2X\n",
|
||||
MEMCLEAN);
|
||||
#endif
|
||||
if (getenv("LT_ABORTREASON")) {
|
||||
abortOn = atoi(getenv("LT_ABORTREASON"));
|
||||
}
|
||||
|
||||
#define PRINTREASON(x) if (abortOn & x) fprintf(report, "%s ", #x);
|
||||
fprintf (report, "# aborts on ");
|
||||
PRINTREASON( OVERWRITE_MEMORY );
|
||||
PRINTREASON( DELETE_NONEXISTENT );
|
||||
PRINTREASON( NEW_DELETE_MISMATCH );
|
||||
fprintf (report, "\n");
|
||||
#undef PRINTREASON
|
||||
|
||||
#ifdef THREAD_SAVE
|
||||
fprintf (report, "# thread save\n");
|
||||
/*
|
||||
* create default, non-recursive ('fast') mutex
|
||||
* to lock our datastructure where we keep track of
|
||||
* the user's new/deletes
|
||||
*/
|
||||
/*if (pthread_mutex_init(&mutex, NULL) < 0) {
|
||||
fprintf(report, "# couldn't init mutex ..\n");
|
||||
fclose(report);
|
||||
_exit(1);
|
||||
}*/
|
||||
#else
|
||||
fprintf(report, "# not thread save; if you use threads, recompile with -DTHREAD_SAVE\n");
|
||||
#endif
|
||||
fflush(report);
|
||||
}
|
||||
|
||||
/*
|
||||
* the workhorses:
|
||||
*/
|
||||
void *registerAlloc(size_t size, bool type);
|
||||
void registerFree (void *p, bool type);
|
||||
|
||||
/**
|
||||
* write a hexdump of the given area.
|
||||
*/
|
||||
void hexdump(const unsigned char* area, int size);
|
||||
|
||||
/**
|
||||
* Terminate current running progam.
|
||||
*/
|
||||
void progAbort(abortReason_t reason) {
|
||||
if (abortOn & reason) {
|
||||
fprintf(report, "# abort; DUMP of current state\n");
|
||||
writeLeakReport();
|
||||
fclose(report);
|
||||
abort();
|
||||
}
|
||||
else
|
||||
fflush(report);
|
||||
}
|
||||
|
||||
/**
|
||||
* write a Report over leaks, e.g. still pending deletes
|
||||
*/
|
||||
void writeLeakReport();
|
||||
|
||||
~LeakTracer() {
|
||||
// fprintf(stderr, "LeakTracer::destroy()\n");
|
||||
time_t t = time(NULL);
|
||||
fprintf (report, "# finished %s", ctime(&t));
|
||||
writeLeakReport();
|
||||
fclose(report);
|
||||
free(leaks);
|
||||
#ifdef THREAD_SAVE
|
||||
//pthread_mutex_destroy(&mutex);
|
||||
#endif
|
||||
destroyed = true;
|
||||
}
|
||||
} leakTracer;
|
||||
|
||||
void* LeakTracer::registerAlloc (size_t size, bool type) {
|
||||
initialize();
|
||||
|
||||
// fprintf(stderr, "LeakTracer::registerAlloc()\n");
|
||||
|
||||
if (destroyed) {
|
||||
fprintf(stderr, "Oops, registerAlloc called after destruction of LeakTracer (size=%d)\n", size);
|
||||
return LT_MALLOC(size);
|
||||
}
|
||||
|
||||
|
||||
void *p = LT_MALLOC(size + SAVESIZE);
|
||||
// Need to call the new-handler
|
||||
if (!p) {
|
||||
fprintf(report, "LeakTracer malloc %m\n");
|
||||
_exit (1);
|
||||
}
|
||||
|
||||
#ifdef SAVEVALUE
|
||||
/* initialize with some defined pattern */
|
||||
memset(p, SAVEVALUE, size + SAVESIZE);
|
||||
#endif
|
||||
|
||||
#ifdef MAGIC
|
||||
/*
|
||||
* the magic value is a special pattern which does not need
|
||||
* to be uniform.
|
||||
*/
|
||||
if (SAVESIZE >= sizeof(magic_t)) {
|
||||
magic_t *mag;
|
||||
mag = (magic_t*)((char*)p + size);
|
||||
*mag = MAGIC;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef THREAD_SAVE
|
||||
//pthread_mutex_lock(&mutex);
|
||||
mutex.lock();
|
||||
#endif
|
||||
|
||||
++newCount;
|
||||
++totalAllocations;
|
||||
currentAllocated += size;
|
||||
if (currentAllocated > maxAllocated)
|
||||
maxAllocated = currentAllocated;
|
||||
|
||||
for (;;) {
|
||||
for (int i = firstFreeSpot; i < leaksCount; i++)
|
||||
if (leaks[i].addr == NULL) {
|
||||
leaks[i].addr = p;
|
||||
leaks[i].size = size;
|
||||
leaks[i].type = type;
|
||||
leaks[i].allocAddr=__builtin_return_address(1);
|
||||
firstFreeSpot = i+1;
|
||||
// allow to lookup our index fast.
|
||||
int *hashPos = &leakHash[ ADDR_HASH(p) ];
|
||||
leaks[i].nextBucket = *hashPos;
|
||||
*hashPos = i;
|
||||
#ifdef THREAD_SAVE
|
||||
//pthread_mutex_unlock(&mutex);
|
||||
mutex.unlock();
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
||||
// Allocate a bigger array
|
||||
// Note that leaksCount starts out at 0.
|
||||
int new_leaksCount = (leaksCount == 0) ? INITIALSIZE
|
||||
: leaksCount * 2;
|
||||
leaks = (Leak*)LT_REALLOC(leaks,
|
||||
sizeof(Leak) * new_leaksCount);
|
||||
if (!leaks) {
|
||||
fprintf(report, "# LeakTracer realloc failed: %m\n");
|
||||
_exit(1);
|
||||
}
|
||||
else {
|
||||
fprintf(report, "# internal buffer now %d\n",
|
||||
new_leaksCount);
|
||||
fflush(report);
|
||||
}
|
||||
memset(leaks+leaksCount, 0x00,
|
||||
sizeof(Leak) * (new_leaksCount-leaksCount));
|
||||
leaksCount = new_leaksCount;
|
||||
}
|
||||
}
|
||||
|
||||
void LeakTracer::hexdump(const unsigned char* area, int size) {
|
||||
fprintf(report, "# ");
|
||||
for (int j=0; j < size ; ++j) {
|
||||
fprintf (report, "%02x ", *(area+j));
|
||||
if (j % 16 == 15) {
|
||||
fprintf(report, " ");
|
||||
for (int k=-15; k < 0 ; k++) {
|
||||
char c = (char) *(area + j + k);
|
||||
fprintf (report, "%c", isprint(c) ? c : '.');
|
||||
}
|
||||
fprintf(report, "\n# ");
|
||||
}
|
||||
}
|
||||
fprintf(report, "\n");
|
||||
}
|
||||
|
||||
void LeakTracer::registerFree (void *p, bool type) {
|
||||
initialize();
|
||||
|
||||
if (p == NULL)
|
||||
return;
|
||||
|
||||
if (destroyed) {
|
||||
fprintf(stderr, "Oops, allocation destruction of LeakTracer (p=%p)\n", p);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef THREAD_SAVE
|
||||
//pthread_mutex_lock(&mutex);
|
||||
mutex.lock();
|
||||
#endif
|
||||
int *lastPointer = &leakHash[ ADDR_HASH(p) ];
|
||||
int i = *lastPointer;
|
||||
|
||||
while (i != 0 && leaks[i].addr != p) {
|
||||
lastPointer = &leaks[i].nextBucket;
|
||||
i = *lastPointer;
|
||||
}
|
||||
|
||||
if (leaks[i].addr == p) {
|
||||
*lastPointer = leaks[i].nextBucket; // detach.
|
||||
newCount--;
|
||||
leaks[i].addr = NULL;
|
||||
currentAllocated -= leaks[i].size;
|
||||
if (i < firstFreeSpot)
|
||||
firstFreeSpot = i;
|
||||
|
||||
if (leaks[i].type != type) {
|
||||
fprintf(report,
|
||||
"S %10p %10p # new%s but delete%s "
|
||||
"; size %d\n",
|
||||
leaks[i].allocAddr,
|
||||
__builtin_return_address(1),
|
||||
((!type) ? "[]" : " normal"),
|
||||
((type) ? "[]" : " normal"),
|
||||
leaks[i].size);
|
||||
|
||||
progAbort( NEW_DELETE_MISMATCH );
|
||||
}
|
||||
#ifdef MAGIC
|
||||
if ((SAVESIZE >= sizeof(magic_t)) &&
|
||||
*((magic_t*)((char*)p + leaks[i].size)) != MAGIC) {
|
||||
fprintf(report, "O %10p %10p "
|
||||
"# memory overwritten beyond allocated"
|
||||
" %d bytes\n",
|
||||
leaks[i].allocAddr,
|
||||
__builtin_return_address(1),
|
||||
leaks[i].size);
|
||||
fprintf(report, "# %d byte beyond area:\n",
|
||||
SAVESIZE);
|
||||
hexdump((unsigned char*)p+leaks[i].size,
|
||||
SAVESIZE);
|
||||
progAbort( OVERWRITE_MEMORY );
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef THREAD_SAVE
|
||||
# ifdef MEMCLEAN
|
||||
int allocationSize = leaks[i].size;
|
||||
# endif
|
||||
//pthread_mutex_unlock(&mutex);
|
||||
mutex.unlock();
|
||||
#else
|
||||
#define allocationSize leaks[i].size
|
||||
#endif
|
||||
|
||||
#ifdef MEMCLEAN
|
||||
// set it to some garbage value.
|
||||
memset((unsigned char*)p, MEMCLEAN, allocationSize + SAVESIZE);
|
||||
#endif
|
||||
LT_FREE(p);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef THREAD_SAVE
|
||||
//pthread_mutex_unlock(&mutex);
|
||||
mutex.unlock();
|
||||
#endif
|
||||
fprintf(report, "D %10p # delete non alloc or twice pointer %10p\n",
|
||||
__builtin_return_address(1), p);
|
||||
progAbort( DELETE_NONEXISTENT );
|
||||
}
|
||||
|
||||
|
||||
void LeakTracer::writeLeakReport() {
|
||||
initialize();
|
||||
|
||||
if (newCount > 0) {
|
||||
fprintf(report, "# LeakReport\n");
|
||||
fprintf(report, "# %10s | %9s # Pointer Addr\n",
|
||||
"from new @", "size");
|
||||
}
|
||||
for (int i = 0; i < leaksCount; i++)
|
||||
if (leaks[i].addr != NULL) {
|
||||
// This ought to be 64-bit safe?
|
||||
char *memContents = (char *)LT_MALLOC( leaks[i].size + 1 );
|
||||
memcpy( (void *)memContents, (void *)( leaks[i].addr ),
|
||||
leaks[i].size );
|
||||
memContents[ leaks[i].size ] = '\0';
|
||||
fprintf(report, "L %10p %9ld # %p \"%s\"\n",
|
||||
leaks[i].allocAddr,
|
||||
(long) leaks[i].size,
|
||||
leaks[i].addr,
|
||||
memContents );
|
||||
LT_FREE( memContents );
|
||||
}
|
||||
fprintf(report, "# total allocation requests: %6ld ; max. mem used"
|
||||
" %d kBytes\n", totalAllocations, maxAllocated / 1024);
|
||||
fprintf(report, "# leak %6d Bytes\t:-%c\n", currentAllocated,
|
||||
(currentAllocated == 0) ? ')' : '(');
|
||||
if (currentAllocated > 50 * 1024) {
|
||||
fprintf(report, "# .. that is %d kByte!! A lot ..\n",
|
||||
currentAllocated / 1024);
|
||||
}
|
||||
}
|
||||
|
||||
/** -- The actual new/delete operators -- **/
|
||||
|
||||
void* operator new(size_t size) {
|
||||
return leakTracer.registerAlloc(size,false);
|
||||
}
|
||||
|
||||
|
||||
void* operator new[] (size_t size) {
|
||||
return leakTracer.registerAlloc(size,true);
|
||||
}
|
||||
|
||||
|
||||
void operator delete (void *p) {
|
||||
leakTracer.registerFree(p,false);
|
||||
}
|
||||
|
||||
|
||||
void operator delete[] (void *p) {
|
||||
leakTracer.registerFree(p,true);
|
||||
}
|
||||
|
||||
|
||||
// added by Jason Rohrer
|
||||
char *strdup( const char *inString ) {
|
||||
char *outString =
|
||||
(char*)leakTracer.registerAlloc( strlen( inString ) + 1, true );
|
||||
strcpy( outString, inString );
|
||||
return outString;
|
||||
}
|
||||
|
||||
|
||||
/* Emacs:
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
* vi:set tabstop=8 shiftwidth=8 nowrap:
|
||||
*/
|
64
minorGems/util/development/leakTracer/Makefile
Normal file
64
minorGems/util/development/leakTracer/Makefile
Normal file
|
@ -0,0 +1,64 @@
|
|||
#
|
||||
# Modification History
|
||||
#
|
||||
# 2004-January-16 Jason Rohrer
|
||||
# Switched to use minorGems platform-independed mutexes.
|
||||
#
|
||||
|
||||
|
||||
CC = g++
|
||||
|
||||
# Source files
|
||||
SRC := LeakTracer.cc
|
||||
|
||||
ROOT_PATH = ../../../..
|
||||
|
||||
# Switch comment to select a platform
|
||||
# PLATFORM_MUTEX = $(ROOT_PATH)/minorGems/system/win32/MutexLockWin32.cpp
|
||||
PLATFORM_MUTEX = $(ROOT_PATH)/minorGems/system/linux/MutexLockLinux.cpp -pthread
|
||||
|
||||
# Comment both of these out to disable thread safetly
|
||||
C_THREAD=-DTHREAD_SAVE -D_REENTRANT -D_THREAD_SAFE
|
||||
O_THREAD = $(PLATFORM_MUTEX)
|
||||
|
||||
|
||||
# Common flags
|
||||
C_FLAGS = -g -pipe -Wall -W -I$(ROOT_PATH) $(C_THREAD)
|
||||
O_FLAGS = $(C_FLAGS) $(O_THREAD)
|
||||
|
||||
# Object files
|
||||
OBJ_DIR = .
|
||||
OBJ := $(patsubst %.cc,$(OBJ_DIR)/%.o,$(SRC))
|
||||
SHOBJ := $(patsubst %.o,$(OBJ_DIR)/%.so,$(OBJ))
|
||||
|
||||
.PHONY: all clean tidy distrib test
|
||||
|
||||
all: $(OBJ) $(SHOBJ)
|
||||
|
||||
clean: tidy
|
||||
rm -f $(OBJ) leak.out
|
||||
|
||||
tidy:
|
||||
rm -f *~ *orig *bak *rej
|
||||
|
||||
tags: $(SRC) $(INCL)
|
||||
ctags $(SRC) $(INCL)
|
||||
|
||||
distrib: clean all README.html
|
||||
(cd .. && tar cvfz /root/drylock/LeakTracer/LeakTracer.tar.gz --exclude LeakTracer/CVS --exclude LeakTracer/old --exclude LeakTracer/test LeakTracer/)
|
||||
|
||||
$(OBJ_DIR)/%.o: %.cc
|
||||
$(CC) -fPIC -c $(C_FLAGS) $< -o $@
|
||||
|
||||
$(OBJ_DIR)/%.so : $(OBJ_DIR)/%.o
|
||||
$(CC) $(O_FLAGS) -shared -o $@ $<
|
||||
|
||||
README.html: README
|
||||
/root/ed/mcl/util/htmlize.pl README
|
||||
|
||||
test:
|
||||
$(CC) $(C_FLAGS) test.cc -o test
|
||||
./test
|
||||
./LeakCheck ./test
|
||||
./leak-analyze ./test
|
||||
# ./compare-test test.template test.result
|
230
minorGems/util/development/leakTracer/README
Normal file
230
minorGems/util/development/leakTracer/README
Normal file
|
@ -0,0 +1,230 @@
|
|||
Introduction
|
||||
------------
|
||||
|
||||
LeakTracer is a small tool I wrote when checking a C++ program for memory
|
||||
leaks. I couldn't get dmalloc to display what I wanted, and I just saw the
|
||||
__builtin_return_address gcc-extension mentioned.
|
||||
|
||||
To use LeakTracer, run your program using the provided LeakCheck script. It
|
||||
uses the LD_PRELOAD feature to "overlay" some functions on top of your
|
||||
functions (no recompile needed). If your platform does not support LD_PRELOAD,
|
||||
you can add the LeakTracer.o object file to the objects in your Makefile and
|
||||
run your application.
|
||||
|
||||
LeakTracer uses gdb to print out the exact line where the memory was allocated
|
||||
and not freed - this of course means you have to free all dynamically
|
||||
allocated data. LeakTracer also overrides the global operator new and operator
|
||||
delete - this will give problems if you override them as well.
|
||||
|
||||
LeakTracer traces only new/new[] and delete calls - it does not look at
|
||||
malloc/free/realloc.
|
||||
|
||||
Here is some example output:
|
||||
|
||||
Gathered 8 (8 unique) points of data.
|
||||
(gdb)
|
||||
Allocations: 1 / Size: 36
|
||||
0x80608e6 is in NullArcableInstance::NullArcableInstance(void) (Machine.cc:40).
|
||||
39 public:
|
||||
40 NullArcableInstance() : ArcableInstance(new NullArcable) {}
|
||||
|
||||
Allocations: 1 / Size: 8
|
||||
0x8055b02 is in init_types(void) (Type.cc:119).
|
||||
118 void init_types() {
|
||||
119 Type::Integer = new IntegerType;
|
||||
|
||||
Allocations: 1 / Size: 132 (new[])
|
||||
0x805f4ab is in Hashtable<NativeCallable, String, false, true>::Hashtable(unsigned int) (ea/h/Hashtable.h:15).
|
||||
14 Hashtable (uint _size = 32) : size(_size), count(0) {
|
||||
15 table = new List<E, own> [size];
|
||||
|
||||
[...]
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
You need Perl5 and gdb installed to run the leak-analyzer. You need gcc -- I
|
||||
currently use 2.95 but have used it with previous older versions without
|
||||
problems.
|
||||
|
||||
|
||||
You also need to run this on an architecture which supports
|
||||
__builtin_return_address arguments that are greater than 0 - there may be
|
||||
some problems on MIPS there.
|
||||
|
||||
So far this code has been tested under Linux 2.2, x86 system, Solaris and
|
||||
HP-UX.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Just type make. There is no install target; you should put LeakTracer
|
||||
some place you can remember.
|
||||
|
||||
Since version 2.0, it is possible to preload the LeakTracer object on
|
||||
architectures that support LD_PRELOAD (this is at least Linux and probably
|
||||
others -- please report success/failure). This means it is much easier to use
|
||||
the program: you do not need to relink your program with LeakTracer.o.
|
||||
|
||||
In case your platform does not support LD_PRELOAD, you can use LeakTracer in
|
||||
the old pre 2.0 way: add LeakTracer.o to your object files -- at the very end
|
||||
of them (also after -llibrary lines).
|
||||
|
||||
In any case your application must also be compiled with debugging enabled
|
||||
(i.e. -g).
|
||||
|
||||
Running with LeakTracer
|
||||
-----------------------
|
||||
|
||||
If you are using the shared object, run the LeakCheck script. This script
|
||||
should stay in the directory where you install LeakCheck -- it will search for
|
||||
LeakTracer.so file there and load it. E.g.:
|
||||
|
||||
~/src/LeakTracer/LeakCheck yourApplication
|
||||
|
||||
(if you put LeakTracer in ~/src/LeakTracer/)
|
||||
|
||||
Run your application as normal, performing tasks that you want to be traced
|
||||
for memory leaks. While the application runs, LeakTracer will write data about
|
||||
memory allocation to the file "leak.out" in the current directory. You can
|
||||
override the location of that file by setting the LEAKTRACE_FILE environment
|
||||
variable.
|
||||
|
||||
If you cannot use LD_PRELOAD, just run your application as normal after
|
||||
relinking it. It will also produce a "leak.out" file when it finishes.
|
||||
|
||||
|
||||
Detectable errors
|
||||
-----------------
|
||||
|
||||
LeakTracer is capable to detect the following problems with your program
|
||||
|
||||
1) memory which is allocated but not freed
|
||||
2) (limited support for) overwritten memory at the end of the allocated
|
||||
block ( reason = 1 )
|
||||
3) memory which is tried to be deleted but which is not allocated
|
||||
(either because of a garbage pointer or twice deletion)
|
||||
(reason = 2)
|
||||
4) memory which is allocated with new[] but deleted with simple delete
|
||||
and vice versa (reason = 4)
|
||||
|
||||
For the last three problems, LeakTracer can abort() your program if you
|
||||
tell it so; the resulting core-dump allows to debug the problem. By default,
|
||||
only the overwrite memory condition results in an abort of the program
|
||||
because it is inherently critical. The two other conditions are not critical.
|
||||
You can influence what LeakTracer does with the environment variable
|
||||
LT_ABORTREASON
|
||||
which you can set to some numeric value which is the result of the
|
||||
sum of the reasons you find in the parentesis in the enumeration above.
|
||||
To abort on any reason, for example, you would set LT_ABORTREASON to 7.
|
||||
|
||||
|
||||
Analyzing output
|
||||
----------------
|
||||
|
||||
You should then run leak-analyze, since looking at the raw leak.out file will
|
||||
not help you much. To run leak-analyze, you need Perl as well as gdb
|
||||
installed (any version of gdb will do). For example:
|
||||
|
||||
leak-analyze myprog leak.out
|
||||
|
||||
You don't have to specify the leak.out filename if you just use the default
|
||||
one. leak-analyze will run gdb on the file, sending it a number of commands
|
||||
that will show the source lines with the memory leaks.
|
||||
|
||||
leak-analyze should show you something like this:
|
||||
|
||||
Gathered 2 (2 unique) points of data.
|
||||
|
||||
#-- Alloc: Different allocation schemes
|
||||
alloc here :0x80485b7 is in main (test.cc:6).
|
||||
5
|
||||
6 int *wrong = new int[10];
|
||||
..free here :0x80485d9 is in main (test.cc:11).
|
||||
11 delete wrong;
|
||||
|
||||
#-- Leak: Allocations: 1 / Size: 168
|
||||
0x8048593 is in main (test.cc:3).
|
||||
2 int main() {
|
||||
3 int *array = new int [42] ;
|
||||
|
||||
#-- Leak: Allocations: 1 / Size: 4
|
||||
0x80485a5 is in main (test.cc:4).
|
||||
3 int *array = new int [42] ;
|
||||
4 int *foo = new int;
|
||||
|
||||
|
||||
This means that total of two allocations happened, in two different places.
|
||||
|
||||
First a delete error is shown: you allocated some memory using new[] but you
|
||||
freed it using delete. leak-analyze will show where you allocated the memory and where you freed it.
|
||||
|
||||
Afterwards each allocation is shown in turn. There was 1 allocation from this
|
||||
line of code (test.cc:3), and it was 168 bytes in size. Note that of the two
|
||||
lines of code shown, it's the bottom one that created the allocation.
|
||||
|
||||
That's all there is to it - now you should find those memory leaks, fix them
|
||||
and rerun Leak tracer.
|
||||
|
||||
Shared libraries and objects
|
||||
----------------------------
|
||||
|
||||
If you want to analyze the leaks in shared libraries in your file, it may be
|
||||
necessary to make leak-analyze run your program and thus load the shared
|
||||
libraries before searching for addresses.
|
||||
|
||||
To do that, run leak-analyze with the program name, leak name AND another
|
||||
argument which is where to set the breakpoint, e.g.:
|
||||
|
||||
leak-analyze myprog leak.out main
|
||||
|
||||
This will make leak-analyze tell gdb to set a breakpoint on "main" and then
|
||||
run the program. After the analysis is complete, the program will be killed.
|
||||
|
||||
If you want to load some shared libraries, you can set a breakpoint on a
|
||||
different location, e.g. main.cc:42 if you know that once line 42 is reached,
|
||||
all shared objects have been loaded.
|
||||
|
||||
If your program needs some command line arguments, supply them after "main".
|
||||
|
||||
|
||||
Licensing
|
||||
---------
|
||||
|
||||
LeakTracer is public domain (i.e. do with it whatever you feel like).
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
Initial version of LeakTracer was written by Erwin Andreasen. Henner Zeller
|
||||
(foobar@to.com) contributed a rewrite of the code which
|
||||
introduced dynamic loading of LeakTracer and more.
|
||||
|
||||
|
||||
Revision history
|
||||
----------------
|
||||
|
||||
February 21, 1999 v1.0 - only tested internally
|
||||
February 23, 1999 v1.1 - added operator new[] / delete[]
|
||||
February 23, 1999 v1.2 - Oops, forgot to free() the memory..
|
||||
February 26, 1999 v1.3 - allow delete 0
|
||||
March 27, 1999 v1.4 - Allow %p format without leading 0x for non-GNU
|
||||
libc. Option to leak-analyze to run the program.
|
||||
July 21, 1999 v1.5 - Fix for the above suggested by Alan Gonzalez
|
||||
August 21, 2000 v1.6 - use a destructor instead of
|
||||
__attribute__(destructor)
|
||||
November 19, 2000 v2.0 - Rewrite by Henner Zeller introduces LD_PRELOAD
|
||||
and much more
|
||||
February 27, 2001 v2.1 - Further update by Henner: optional thread safety,
|
||||
choose what should make LeakTracer abort(), better
|
||||
tracing of delete on non-new'ed pointers
|
||||
March 2, 2001 v2.2 - Another updated by Henner: hash table to increase
|
||||
performance with many allocations
|
||||
June 13, 2001 v2.3 - Made LT more resistant to being called before init
|
||||
and after destruction
|
||||
|
||||
Authors: Erwin Andreasen <erwin@andreasen.org>
|
||||
Henner Zeller <foobar@to.com>
|
||||
Homepage: http://www.andreasen.org/LeakTracer/
|
||||
|
210
minorGems/util/development/leakTracer/README.html
Normal file
210
minorGems/util/development/leakTracer/README.html
Normal file
|
@ -0,0 +1,210 @@
|
|||
<h3>Table of contents</h3> <UL>
|
||||
<LI><a href="#chap0">Introduction</a>
|
||||
<LI><a href="#chap1">Requirements</a>
|
||||
<LI><a href="#chap2">Installation</a>
|
||||
<LI><a href="#chap3">Running with LeakTracer</a>
|
||||
<LI><a href="#chap4">Detectable errors</a>
|
||||
<LI><a href="#chap5">Analyzing output</a>
|
||||
<LI><a href="#chap6">Shared libraries and objects</a>
|
||||
<LI><a href="#chap7">Licensing</a>
|
||||
<LI><a href="#chap8">Credits</a>
|
||||
<LI><a href="#chap9">Revision history</a>
|
||||
</UL>
|
||||
<PRE>
|
||||
</PRE><h3><a name="chap0">Introduction</a></h3><PRE>
|
||||
LeakTracer is a small tool I wrote when checking a C++ program for memory
|
||||
leaks. I couldn't get dmalloc to display what I wanted, and I just saw the
|
||||
__builtin_return_address gcc-extension mentioned.
|
||||
|
||||
To use LeakTracer, run your program using the provided LeakCheck script. It
|
||||
uses the LD_PRELOAD feature to "overlay" some functions on top of your
|
||||
functions (no recompile needed). If your platform does not support LD_PRELOAD,
|
||||
you can add the LeakTracer.o object file to the objects in your Makefile and
|
||||
run your application.
|
||||
|
||||
LeakTracer uses gdb to print out the exact line where the memory was allocated
|
||||
and not freed - this of course means you have to free all dynamically
|
||||
allocated data. LeakTracer also overrides the global operator new and operator
|
||||
delete - this will give problems if you override them as well.
|
||||
|
||||
LeakTracer traces only new/new[] and delete calls - it does not look at
|
||||
malloc/free/realloc.
|
||||
|
||||
Here is some example output:
|
||||
|
||||
Gathered 8 (8 unique) points of data.
|
||||
(gdb)
|
||||
Allocations: 1 / Size: 36
|
||||
0x80608e6 is in NullArcableInstance::NullArcableInstance(void) (Machine.cc:40).
|
||||
39 public:
|
||||
40 NullArcableInstance() : ArcableInstance(new NullArcable) {}
|
||||
|
||||
Allocations: 1 / Size: 8
|
||||
0x8055b02 is in init_types(void) (Type.cc:119).
|
||||
118 void init_types() {
|
||||
119 Type::Integer = new IntegerType;
|
||||
|
||||
Allocations: 1 / Size: 132 (new[])
|
||||
0x805f4ab is in Hashtable<NativeCallable, String, false, true>::Hashtable(unsigned int) (ea/h/Hashtable.h:15).
|
||||
14 Hashtable (uint _size = 32) : size(_size), count(0) {
|
||||
15 table = new List<E, own> [size];
|
||||
|
||||
[...]
|
||||
|
||||
</PRE><h3><a name="chap1">Requirements</a></h3><PRE>
|
||||
You need Perl5 and gdb installed to run the leak-analyzer. You need gcc -- I
|
||||
currently use 2.95 but have used it with previous older versions without
|
||||
problems.
|
||||
You also need to run this on an architecture which supports
|
||||
__builtin_return_address arguments that are greater than 0 - there may be
|
||||
some problems on MIPS there.
|
||||
|
||||
So far this code has been tested under Linux 2.2, x86 system, Solaris and
|
||||
HP-UX.
|
||||
</PRE><h3><a name="chap2">Installation</a></h3><PRE>
|
||||
Just type make. There is no install target; you should put LeakTracer
|
||||
some place you can remember.
|
||||
|
||||
Since version 2.0, it is possible to preload the LeakTracer object on
|
||||
architectures that support LD_PRELOAD (this is at least Linux and probably
|
||||
others -- please report success/failure). This means it is much easier to use
|
||||
the program: you do not need to relink your program with LeakTracer.o.
|
||||
|
||||
In case your platform does not support LD_PRELOAD, you can use LeakTracer in
|
||||
the old pre 2.0 way: add LeakTracer.o to your object files -- at the very end
|
||||
of them (also after -llibrary lines).
|
||||
|
||||
In any case your application must also be compiled with debugging enabled
|
||||
(i.e. -g).
|
||||
|
||||
</PRE><h3><a name="chap3">Running with LeakTracer</a></h3><PRE>
|
||||
If you are using the shared object, run the LeakCheck script. This script
|
||||
should stay in the directory where you install LeakCheck -- it will search for
|
||||
LeakTracer.so file there and load it. E.g.:
|
||||
|
||||
~/src/LeakTracer/LeakCheck yourApplication
|
||||
|
||||
(if you put LeakTracer in ~/src/LeakTracer/)
|
||||
|
||||
Run your application as normal, performing tasks that you want to be traced
|
||||
for memory leaks. While the application runs, LeakTracer will write data about
|
||||
memory allocation to the file "leak.out" in the current directory. You can
|
||||
override the location of that file by setting the LEAKTRACE_FILE environment
|
||||
variable.
|
||||
|
||||
If you cannot use LD_PRELOAD, just run your application as normal after
|
||||
relinking it. It will also produce a "leak.out" file when it finishes.
|
||||
</PRE><h3><a name="chap4">Detectable errors</a></h3><PRE>
|
||||
LeakTracer is capable to detect the following problems with your program
|
||||
|
||||
1) memory which is allocated but not freed
|
||||
2) (limited support for) overwritten memory at the end of the allocated
|
||||
block ( reason = 1 )
|
||||
3) memory which is tried to be deleted but which is not allocated
|
||||
(either because of a garbage pointer or twice deletion)
|
||||
(reason = 2)
|
||||
4) memory which is allocated with new[] but deleted with simple delete
|
||||
and vice versa (reason = 4)
|
||||
|
||||
For the last three problems, LeakTracer can abort() your program if you
|
||||
tell it so; the resulting core-dump allows to debug the problem. By default,
|
||||
only the overwrite memory condition results in an abort of the program
|
||||
because it is inherently critical. The two other conditions are not critical.
|
||||
You can influence what LeakTracer does with the environment variable
|
||||
LT_ABORTREASON
|
||||
which you can set to some numeric value which is the result of the
|
||||
sum of the reasons you find in the parentesis in the enumeration above.
|
||||
To abort on any reason, for example, you would set LT_ABORTREASON to 7.
|
||||
</PRE><h3><a name="chap5">Analyzing output</a></h3><PRE>
|
||||
You should then run leak-analyze, since looking at the raw leak.out file will
|
||||
not help you much. To run leak-analyze, you need Perl as well as gdb
|
||||
installed (any version of gdb will do). For example:
|
||||
|
||||
leak-analyze myprog leak.out
|
||||
|
||||
You don't have to specify the leak.out filename if you just use the default
|
||||
one. leak-analyze will run gdb on the file, sending it a number of commands
|
||||
that will show the source lines with the memory leaks.
|
||||
|
||||
leak-analyze should show you something like this:
|
||||
|
||||
Gathered 2 (2 unique) points of data.
|
||||
|
||||
#-- Alloc: Different allocation schemes
|
||||
alloc here :0x80485b7 is in main (test.cc:6).
|
||||
5
|
||||
6 int *wrong = new int[10];
|
||||
..free here :0x80485d9 is in main (test.cc:11).
|
||||
11 delete wrong;
|
||||
|
||||
#-- Leak: Allocations: 1 / Size: 168
|
||||
0x8048593 is in main (test.cc:3).
|
||||
2 int main() {
|
||||
3 int *array = new int [42] ;
|
||||
|
||||
#-- Leak: Allocations: 1 / Size: 4
|
||||
0x80485a5 is in main (test.cc:4).
|
||||
3 int *array = new int [42] ;
|
||||
4 int *foo = new int;
|
||||
This means that total of two allocations happened, in two different places.
|
||||
|
||||
First a delete error is shown: you allocated some memory using new[] but you
|
||||
freed it using delete. leak-analyze will show where you allocated the memory and where you freed it.
|
||||
|
||||
Afterwards each allocation is shown in turn. There was 1 allocation from this
|
||||
line of code (test.cc:3), and it was 168 bytes in size. Note that of the two
|
||||
lines of code shown, it's the bottom one that created the allocation.
|
||||
|
||||
That's all there is to it - now you should find those memory leaks, fix them
|
||||
and rerun Leak tracer.
|
||||
|
||||
</PRE><h3><a name="chap6">Shared libraries and objects</a></h3><PRE>
|
||||
If you want to analyze the leaks in shared libraries in your file, it may be
|
||||
necessary to make leak-analyze run your program and thus load the shared
|
||||
libraries before searching for addresses.
|
||||
|
||||
To do that, run leak-analyze with the program name, leak name AND another
|
||||
argument which is where to set the breakpoint, e.g.:
|
||||
|
||||
leak-analyze myprog leak.out main
|
||||
|
||||
This will make leak-analyze tell gdb to set a breakpoint on "main" and then
|
||||
run the program. After the analysis is complete, the program will be killed.
|
||||
|
||||
If you want to load some shared libraries, you can set a breakpoint on a
|
||||
different location, e.g. main.cc:42 if you know that once line 42 is reached,
|
||||
all shared objects have been loaded.
|
||||
|
||||
If your program needs some command line arguments, supply them after "main".
|
||||
</PRE><h3><a name="chap7">Licensing</a></h3><PRE>
|
||||
LeakTracer is public domain (i.e. do with it whatever you feel like).
|
||||
|
||||
</PRE><h3><a name="chap8">Credits</a></h3><PRE>
|
||||
Initial version of LeakTracer was written by Erwin Andreasen. Henner Zeller
|
||||
(foobar@to.com) contributed a rewrite of the code which
|
||||
introduced dynamic loading of LeakTracer and more.
|
||||
</PRE><h3><a name="chap9">Revision history</a></h3><PRE>
|
||||
February 21, 1999 v1.0 - only tested internally
|
||||
February 23, 1999 v1.1 - added operator new[] / delete[]
|
||||
February 23, 1999 v1.2 - Oops, forgot to free() the memory..
|
||||
February 26, 1999 v1.3 - allow delete 0
|
||||
March 27, 1999 v1.4 - Allow %p format without leading 0x for non-GNU
|
||||
libc. Option to leak-analyze to run the program.
|
||||
July 21, 1999 v1.5 - Fix for the above suggested by Alan Gonzalez
|
||||
August 21, 2000 v1.6 - use a destructor instead of
|
||||
__attribute__(destructor)
|
||||
November 19, 2000 v2.0 - Rewrite by Henner Zeller introduces LD_PRELOAD
|
||||
and much more
|
||||
February 27, 2001 v2.1 - Further update by Henner: optional thread safety,
|
||||
choose what should make LeakTracer abort(), better
|
||||
tracing of delete on non-new'ed pointers
|
||||
March 2, 2001 v2.2 - Another updated by Henner: hash table to increase
|
||||
performance with many allocations
|
||||
June 13, 2001 v2.3 - Made LT more resistant to being called before init
|
||||
and after destruction
|
||||
|
||||
Authors: Erwin Andreasen <erwin@andreasen.org>
|
||||
Henner Zeller <foobar@to.com>
|
||||
Homepage: <a href="http://www.andreasen.org/LeakTracer/">http://www.andreasen.org/LeakTracer/</a>
|
||||
|
||||
</PRE>
|
1
minorGems/util/development/leakTracer/VERSION
Normal file
1
minorGems/util/development/leakTracer/VERSION
Normal file
|
@ -0,0 +1 @@
|
|||
2.3
|
116
minorGems/util/development/leakTracer/leak-analyze
Executable file
116
minorGems/util/development/leakTracer/leak-analyze
Executable file
|
@ -0,0 +1,116 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
#
|
||||
# Modification History
|
||||
#
|
||||
# 2004-January-17 Jason Rohrer
|
||||
# Fixed regexps to match both A-F and a-f for hex address strings.
|
||||
#
|
||||
|
||||
|
||||
# Erwin S. Andreasen <erwin@andreasen.org>
|
||||
# Henner Zeller <foobar@to.com>
|
||||
#
|
||||
# Homepage: http://www.andreasen.org/LeakTracer/
|
||||
# This program is Public Domain
|
||||
use IO::Handle;
|
||||
|
||||
die "You must supply at least one argument.\n" unless $#ARGV >= 0;
|
||||
|
||||
$ExeFile = shift @ARGV;
|
||||
$LeaksFile = $#ARGV >= 0 ? shift @ARGV : "leak.out";
|
||||
open (LEAKS, $LeaksFile) or die "Could not open leaks data file $LeaksFile: $!";
|
||||
|
||||
if ($#ARGV >= 0) {
|
||||
$BreakOn = shift @ARGV;
|
||||
# Rest in @ARGV are program arguments
|
||||
}
|
||||
|
||||
$n = $u = 0;
|
||||
while (<LEAKS>) {
|
||||
chop;
|
||||
next if (m/^\s*#/);
|
||||
# 1 2 3 4 5 6 7
|
||||
#if (/^\s*L\s+(0x)?([0-9a-fA-F]+)\s+(0x)?([0-9a-fA-F]+)\s+(0x)?([0-9a-fA-F]+)\s+(\d+)/) {
|
||||
# Allocations, which have not been freed or deallocations which have not
|
||||
# been allocated.
|
||||
# 1 2 3
|
||||
if (/^\s*L\s+(0x)?([0-9a-fA-F]+)\s+(\d+)/) {
|
||||
$addr="$2"; # ",$4,$6";
|
||||
$u++ if not exists $Type{$addr};
|
||||
$Count{$addr}++;
|
||||
$Size{$addr} += $3; # $7;
|
||||
$Type{$addr} = "Leak";
|
||||
$n++;
|
||||
}
|
||||
elsif (/^\s*D\s+(0x)?([0-9a-fA-F]+)/) {
|
||||
$addr="$2"; # ",$4,$6";
|
||||
$u++ if not exists $Type{$addr};
|
||||
$Count{$addr}++;
|
||||
$Type{$addr} = "delete on not allocated memory";
|
||||
$n++;
|
||||
}
|
||||
# allocations/deallocations with other errornous conditions
|
||||
# 1 2 3 4 5
|
||||
elsif (/^\s*([SO])\s+(0x)?([0-9a-fA-F]+)\s+(0x)?([0-9a-fA-F]+)/) {
|
||||
$addrs = "$3,$5,$1";
|
||||
$AllocDealloc{$addrs} = ("$1" =~ m/S/)
|
||||
? "Different allocation schemes"
|
||||
: "This Memory was overwritten";
|
||||
}
|
||||
}
|
||||
|
||||
print STDERR "Gathered $n ($u unique) points of data.\n";
|
||||
|
||||
close (LEAKS);
|
||||
|
||||
|
||||
# Instead of using -batch, we just run things as usual. with -batch,
|
||||
# we quit on the first error, which we don't want.
|
||||
open (PIPE, "|gdb -q $ExeFile") or die "Cannot start gdb";
|
||||
#open (PIPE, "|cat");
|
||||
|
||||
# Change set listsize 2 to something else to show more lines
|
||||
print PIPE "set prompt\nset complaints 1000\nset height 0\n";
|
||||
|
||||
# Optionally, run the program
|
||||
if (defined($BreakOn)) {
|
||||
print PIPE "break $BreakOn\n";
|
||||
print PIPE "run ", join(" ", @ARGV), " \n";
|
||||
}
|
||||
|
||||
|
||||
print PIPE "set listsize 2\n";
|
||||
foreach (sort keys %AllocDealloc) {
|
||||
print PIPE "echo \\n#-- Alloc: $AllocDealloc{$_}\\nalloc here :\n";
|
||||
@addrs = split(/,/,$_);
|
||||
print PIPE "l *0x" . (shift @addrs) . "\necho ..free here :\n";
|
||||
print PIPE "set listsize 1\n";
|
||||
print PIPE "l *0x" . (shift @addrs) . "\n";
|
||||
}
|
||||
|
||||
foreach (sort keys %Type) {
|
||||
print PIPE "echo \\n#-- $Type{$_}: counted $Count{$_}x";
|
||||
if ($Size{$_} > 0) {
|
||||
print PIPE " / total Size: $Size{$_}";
|
||||
}
|
||||
print PIPE "\\n\n";
|
||||
@addrs = split(/,/,$_);
|
||||
print PIPE "set listsize 2\n";
|
||||
print PIPE "l *0x" . (shift @addrs) . "\n";
|
||||
#print PIPE "echo ..called from :\n";
|
||||
#print PIPE "set listsize 1\n";
|
||||
# gdb bails out, if it cannot find an address.
|
||||
#print PIPE "l *0x" . (shift @addrs) . "\necho ..called from :\n";
|
||||
#print PIPE "l *0x" . (shift @addrs) . "\n";
|
||||
}
|
||||
|
||||
if (defined($BreakOn)) {
|
||||
print PIPE "kill\n";
|
||||
}
|
||||
|
||||
print PIPE "quit\n";
|
||||
PIPE->flush();
|
||||
wait();
|
||||
|
||||
close (PIPE);
|
27
minorGems/util/development/leakTracer/test.cc
Normal file
27
minorGems/util/development/leakTracer/test.cc
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Modification History
|
||||
*
|
||||
* 2002-March-31 Jason Rohrer
|
||||
* Added test of strdup support.
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// Small leaky test program
|
||||
|
||||
void foo() {
|
||||
int *x = new int;
|
||||
}
|
||||
|
||||
int main() {
|
||||
char *str = strdup( "Test String" );
|
||||
|
||||
int *z = new int[10];
|
||||
foo();
|
||||
foo();
|
||||
delete z;
|
||||
delete z; // delete value twice
|
||||
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue