Initial source commit

This commit is contained in:
Tony Bark 2025-10-03 02:19:59 -04:00
commit f1384c11ee
335 changed files with 52715 additions and 0 deletions

View 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 $@

View 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

View 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:
*/

View 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

View 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/

View 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>

View file

@ -0,0 +1 @@
2.3

View 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);

View 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
}