Commit c2ef2d5f authored by Duncan White's avatar Duncan White
Browse files

check it all in..

parents
CC = gcc
DEST = ..
LIBDIR = $(DEST)/lib/$(ARCH)
INCDIR = $(DEST)/include
MANDIR = $(DEST)/man/man3
CFLAGS = -g -Wall
all: libmem.a
install: libmem.a
install -m0644 libmem.a $(LIBDIR)
install -m0644 mem.h $(INCDIR)
install -m0644 libmem.3 $(MANDIR)
clean:
rm -f *.o a.out core libmem.a
mem.o: mem.h
libmem.a: mem.o
rm -f libmem.a
ar rc libmem.a mem.o
ranlib libmem.a
From Dr Dobbs Journal, Annual C Issue, August 1990.
- simple memory handling shell for C, that aids the programmer
in tracking down memory leakage. It redefines malloc() etc as macro calls
that keep track of where each block was malloc()ed [it stores the filename and
source lineno].
- Highly recommended!
To install into your TOOLDIR:
- make install
.TH libmem 3 "June 2011"
.SH NAME
libmem \- memory checking library for C
.SH DESCRIPTION
This is a simple memory handling shell for C, that aids the programmer
in tracking down memory leakage.
.PP
It redefines \fBmalloc(), realloc(), strdup(), free()\fP etc as macro calls
that keep track of where each block was malloc()ed [it stores the filename and
source lineno].
.PP
It redefines \fBexit()\fP to list out all blocks that have
been malloc()d and not free(d) - for each block, it shows the size,
the source filename and line no where the block was malloc()d.
.PP
There are also new functions you can call explicitly:
.PP
\fBMem_show()\fP:
display allocated memory chunks,
.PP
\fBMem_used()\fP:
find out how much memory is allocated,
.PP
\fBMem_check()\fP: perform the "display and abort
if any memory chunks are still allocated" check
that the redefined \fBexit()\fP performs.
.PP
Developed from Dr Dobbs Journal, Annual C Issue, August 1990.
.PP
libmem is highly recommended! It is incredibly simple and useful.
.SH USAGE
First install libmem into your TOOLDIR.
Then to use libmem to check for memory leaks in any
body of existing C code:
.PP
Edit all .c files and add '#include <mem.h>'
.PP
Edit the Makefile and put:
.PP
DEST = $(TOOLDIR)
.br
LIBDIR = $(DEST)/lib/$(ARCH)
.br
INCDIR = $(DEST)/include
.br
CFLAGS = -I. -I$(INCDIR) -Wall -g
.br
LDLIBS = -L$(LIBDIR) -lmem
.PP
Now 'make clean all' to do a complete rebuild.
.PP
Just run your program! If it exits and doesn't list
out any malloc()d chunks, your program does not leak
memory, or free invalid pointers.
.SH PORTABILITY
LIBMEM should work fine on any system with a C compiler.
If it doesn't, please let me know!
.SH AUTHOR OF THIS VERSION
Duncan White,
Dept of Computing,
Imperial College London
UK.
.br
Email: d.white@imperial.ac.uk
.br
(Original version: Dr Dobbs Journal, August 1990)
/*
memory checking system derived from Dr Dobbs Journal
*/
extern int Mem_crash; /* should we crash if malloc et al fail? */
/* No if = 0; Yes, with Mem_crash = exit status */
/* By default; Yes, exit status 1 */
extern void * Mem_malloc( size_t size, char * file, int line );
extern void * Mem_xmalloc( size_t size, char * file, int line );
extern void * Mem_xdatadup( size_t size, void * data, char * file, int line );
extern void * Mem_calloc( size_t n, size_t size, char * file, int line );
extern void * Mem_realloc( void * p, size_t size, char * file, int line );
extern char * Mem_strdup( char * p, char * file, int line );
extern char * Mem_xstrdup( char * p, char * file, int line );
extern void Mem_free( void * p, char * file, int line );
extern size_t Mem_used( void );
extern void Mem_show( FILE * f );
extern void Mem_check( void );
extern void Mem_exit( int n );
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include "mem-private.h"
static unsigned long chunks = 0; /* how many calls to malloc() et al? */
/*
The alignment type: double on most architectures
*/
#define ALIGN_SIZE sizeof( double )
/*
The magic tag value: all our mlist blocks must contain
this as the tag.
*/
#define TAG_VALUE 0xa55a
/* The memory list type */
typedef struct mlist *mlist;
struct mlist
{
unsigned tag;
unsigned long size;
mlist next;
mlist prev;
char * file;
int line;
};
#define HDR_SIZE sizeof( struct mlist )
#define RESERVE_SIZE ( ( (HDR_SIZE+ALIGN_SIZE-1) / ALIGN_SIZE )\
* ALIGN_SIZE )
#define C2H(a) ( (mlist) ( ((char *) (a)) - RESERVE_SIZE ) )
#define H2C(a) ( ((char *) (a)) + RESERVE_SIZE )
/* PUBLIC VARIABLES */
int Mem_crash = 1; /* Should we crash if malloc etc fail - Yes */
/* PRIVATE VARIABLES */
static size_t Mem_size = 0; /* How much memory is used */
static mlist Mem_list = NULL; /* list of all allocated memory */
static void null( char * , char * , int );
static void add( mlist );
static void del( mlist );
void * Mem_malloc( size_t size, char *file, int line )
{
mlist new;
chunks++;
new = (mlist) malloc( (size_t) size + RESERVE_SIZE );
if( new == NULL )
{
null( "Mem_malloc", file, line );
return NULL;
}
new->tag = TAG_VALUE;
new->size = size;
new->file = file;
new->line = line;
add( new );
Mem_size += new->size;
return H2C(new);
}
void * Mem_xmalloc( size_t size, char *file, int line )
{
int temp;
void *result;
/* disable crashing */
temp = Mem_crash;
Mem_crash = 0;
result = Mem_malloc( size, file, line );
assert( result != NULL );
/* reset crashing to previous */
Mem_crash = temp;
return result;
}
void * Mem_xdatadup( size_t size, void *data, char *file, int line )
{
int temp;
void *result;
/* disable crashing */
temp = Mem_crash;
Mem_crash = 0;
result = Mem_malloc( size, file, line );
assert( result != NULL );
memcpy( result, data, size );
/* June 2011: Reenable previous crash setting: thanks to
benjamin.grabham10@imperial.ac.uk
*/
Mem_crash = temp;
return result;
}
void * Mem_calloc( size_t n, size_t size, char *file, int line )
{
mlist new;
chunks++;
new = (mlist) malloc( (size_t) n * size + RESERVE_SIZE );
if( new == NULL )
{
null( "Mem_calloc", file, line );
return NULL;
}
memset( (void *) new, 0, (int) n * size + RESERVE_SIZE );
new->tag = TAG_VALUE;
new->size = n * size;
new->file = file;
new->line = line;
add( new );
Mem_size += new->size;
return H2C(new);
}
void * Mem_realloc( void *p, size_t size, char*file, int line )
{
mlist new;
chunks++;
new = C2H( p );
if( new->tag != TAG_VALUE )
{
fprintf( stderr, "Mem_realloc: invalid pointer %s(%d)\n",
file, line );
Mem_show( stderr );
exit( Mem_crash ? Mem_crash : 1 );
}
/* Invalidate */
new->tag = ! new->tag;
Mem_size -= new->size;
del( new );
new = (mlist) realloc( (void *) new, (size_t) RESERVE_SIZE + size );
if( new == NULL )
{
null( "Mem_realloc", file, line );
return NULL;
}
/* Validate new one */
new->tag = TAG_VALUE;
new->size = size;
new->file = file;
new->line = line;
Mem_size += new->size;
add( new );
return H2C( new );
}
char * Mem_strdup( char *p, char *file, int line )
{
char *s;
chunks++;
s = (char *) Mem_malloc( (size_t) strlen(p)+1, file, line );
if( s != NULL )
{
strcpy( s, p );
}
return s;
}
char * Mem_xstrdup( char *p, char *file, int line )
{
char *s;
chunks++;
s = (char *) Mem_malloc( (size_t) strlen(p)+1, file, line );
assert( s != NULL );
strcpy( s, p );
return s;
}
void Mem_free( void *p, char *file, int line )
{
mlist new;
new = C2H( p );
if( new->tag != TAG_VALUE )
{
fprintf( stderr, "Mem_free: invalid pointer %s(%d)\n",
file, line );
Mem_show( stderr );
exit( Mem_crash ? Mem_crash : 1 );
}
/* Invalidate */
new->tag = ! new->tag;
Mem_size -= new->size;
del( new );
free( new );
}
size_t Mem_used( void )
{
return Mem_size;
}
void Mem_show( FILE *f )
{
mlist m;
fprintf( f, "\nMemory In Use: total size %lu\n\n",
(unsigned long)Mem_size );
fprintf( f, "File\t\tLine\tSize\n" );
for( m = Mem_list; m != NULL; m = m->next )
{
fprintf( f, "%s\t%d\t%6lu\n", m->file, m->line, m->size );
}
}
static void null( char *procname, char *file, int line )
{
if( Mem_crash )
{
fprintf( stderr,
"%s: out of memory %s(%d)\n",
procname, file, line );
Mem_show( stderr );
exit( Mem_crash );
}
}
static void add( mlist p )
{
p->next = Mem_list;
p->prev = NULL;
if( Mem_list != NULL )
{
Mem_list->prev = p;
}
Mem_list = p;
}
static void del( mlist p )
{
if( p->next != NULL )
{
p->next->prev = p->prev;
}
if( p->prev != NULL )
{
p->prev->next = p->next;
} else
{
Mem_list = p->next;
}
}
void Mem_check( void )
{
if( Mem_used() != 0 )
{
fprintf( stderr, "\nMemory list not empty!!\n" );
Mem_show( stderr );
fprintf( stderr,
"\n%lu calls to malloc/realloc/calloc/strdup!!\n",
chunks );
exit( 1 );
}
}
void Mem_exit( int n )
{
Mem_check();
exit( n );
}
/*
memory checking system derived from Dr Dobbs Journal
*/
/* #ifdef MEM_MALLOC */
# define malloc(x) Mem_malloc( (x), __FILE__, __LINE__ )
# define calloc(x,y) Mem_calloc( (x), (y), __FILE__, __LINE__ )
# define realloc(x,y) Mem_realloc( (x), (y), __FILE__, __LINE__ )
# define strdup(x) Mem_strdup( (x), __FILE__, __LINE__ )
# define xstrdup(x) Mem_xstrdup( (x), __FILE__, __LINE__ )
# define xmalloc(x) Mem_xmalloc( (x), __FILE__, __LINE__ )
# define xdatadup(x,y) Mem_xdatadup( (x), (y), __FILE__, __LINE__ )
# define free(x) Mem_free( (x), __FILE__, __LINE__ )
# define exit(n) Mem_exit( n )
/* #endif */
extern int Mem_crash; /* should we crash if malloc et al fail? */
/* No if = 0; Yes, with Mem_crash = exit status */
/* By default; Yes, exit status 1 */
extern void * Mem_malloc( size_t size, char * file, int line );
extern void * Mem_xmalloc( size_t size, char * file, int line );
extern void * Mem_xdatadup( size_t size, void * data, char * file, int line );
extern void * Mem_calloc( size_t n, size_t size, char * file, int line );
extern void * Mem_realloc( void * p, size_t size, char * file, int line );
extern char * Mem_strdup( char * p, char * file, int line );
extern char * Mem_xstrdup( char * p, char * file, int line );
extern void Mem_free( void * p, char * file, int line );
extern size_t Mem_used( void );
extern void Mem_show( FILE * f );
extern void Mem_check( void );
extern void Mem_exit( int n );
CC = gcc
DEST = ..
LIBDIR = $(DEST)/lib/$(ARCH)
INCDIR = $(DEST)/include
OBJS = hash.o
CFLAGS = -I. -I$(INCDIR) -Wall -g
LDLIBS = -L$(LIBDIR) -lmem
all: testhash iterate libhash.a
clean:
rm -f lib* *.o testhash iterate
install: libhash.a
install -m0644 libhash.a $(LIBDIR)
install -m0644 hash.h $(INCDIR)
libhash.a: $(OBJS)
rm -f libhash.a
ar rc libhash.a $(OBJS)
ranlib libhash.a
testhash: testhash.o hash.o
iterate: iterate.o hash.o
testhash.o: hash.h
hash.o: hash.h
iterate.o: hash.h
/*
* hash.c: hash storage for C.. new version of assoc.c with cooler
* name and more features (copying, depth metrics etc)
* we store a hash as a hash table, hashing each key into
* a dynamic array of binary search trees, and then
* search/include/exclude the key,value pair in/from the
* corresponding search tree. The hash also stores 3 func
* ptrs: a (file,key,value) print function, a value free function,
* and a value copy function. These enable you to use values
* that are themselves complex data structures.
*
* (C) Duncan C. White, 1996-2013 although it seems longer:-)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "hash.h"
#define NHASH 32533
typedef struct tree_s *tree;
struct hash_s {
tree * data; /* dynamic array of trees */
printfunc p; /* how to print (k,v) pair */
freefunc f; /* how to free a value */
copyfunc c; /* how to copy a value */
};
struct tree_s {
key k; /* Key */
value v; /* Value */
tree left; /* Left... */
tree right; /* ... and Right trees */
};
/*
* operation
*/
typedef enum { Search, Define } tree_operation;
/* Private functions */
static void foreach_tree( tree, foreachcbfunc, void * );
static void dump_cb( key, value, void * );
static void free_tree( tree, freefunc );
static void freevalue( freefunc, value );
static tree copy_tree( tree, copyfunc );
static int depth_tree( tree );
static tree tree_op( hash, key, value, tree_operation );
static tree talloc( key, value );
static int shash( char * );
/*
* Create an empty hash
*/
hash hashCreate( printfunc p, freefunc f, copyfunc c )
{
int i;
hash h;
h = (hash) malloc( sizeof(struct hash_s) );
h->data = (tree *) malloc( NHASH*sizeof(tree) );