[comp.lang.c] Tracking Memory Leaks

naim@eecs.nwu.edu (Naim Abdullah) (09/09/88)

I want to check sections of a big program for memory leaks. My basic
strategy was to define functions xmalloc() and xfree() that would keep
track of the amount allocated, and to see if the amount went down to
zero where it should (I wanted to use "#define malloc xmalloc" in the
main header file to replace occurrences of malloc() and free()).

The problem with this scheme is that I don't know how much space free()
will actually free. Here is some sample code, illustrating what I mean:
===============================Cut Here============================
#include <stdio.h>
extern char *malloc();

static unsigned int alloc_size = 0;

char *xmalloc(size)
unsigned int size;
{
    char *p;
    if ((p = malloc(size)) != NULL)
        alloc_size += size;
    return p;
}

xfree(p)
char *p;
{
    unsigned int size_being_freed;
    size_being_freed = *((unsigned int *)(p - OFFSET)); /* probably wrong */
    alloc_size -= size_being_freed;
    free(p);
}

unsigned int bytes_allocated()
{
    return alloc_size;
}

main()
{
    char *p1, *p2;
    p1 = xmalloc(10);
    p2 = xmalloc(99);
    xfree(p1);
    printf("%u bytes still allocated.\n", bytes_allocated());
}
=====================================================================

Now, I don't know what to define OFFSET to be. I would like this to be
portable between VAXen running 4.3bsd and Sun3s running SunOS 3.x, but if
that is not possible, having it work on any one machine would be ok too.

Any suggestions for the value of OFFSET or what the function xfree() should
look like ?

Thank you.

		      Naim Abdullah
		      Dept. of EECS,
		      Northwestern University

		      Internet: naim@eecs.nwu.edu
		      Uucp: {oddjob, chinet, att}!nucsrl!naim

P.S: There was a paper on "mprof" in the latest USENIX. That can be used to
track down memory leaks as well as give you a lot of other information.
Hopefully, it will be in the next BSD but until then,..

jeff@amsdsg.UUCP (Jeff Barr) (09/09/88)

In article <3950011@eecs.nwu.edu>, naim@eecs.nwu.edu (Naim Abdullah) writes:
> I want to check sections of a big program for memory leaks. My basic
> strategy was to define functions xmalloc() and xfree() that would keep
> track of the amount allocated, and to see if the amount went down to
> zero where it should (I wanted to use "#define malloc xmalloc" in the
> main header file to replace occurrences of malloc() and free()).
> 
> The problem with this scheme is that I don't know how much space free()
> will actually free. 

You can allocate space for an extra 'int' in xmalloc, store the size
yourself at the beginning of the allocated block (being sure to return a
pointer PAST the 'int'), and then retrieve it in xfree.

						Jeff
	+---------------------------------------------------------+
	|  Jeff Barr   AMS-DSG   uunet!amsdsg!jeff   800-832-8668 |
	+---------------------------------------------------------+
-- 
						Jeff
	+---------------------------------------------------------+
	|  Jeff Barr   AMS-DSG   uunet!amsdsg!jeff   800-832-8668 |
	+---------------------------------------------------------+

greggy@infmx.UUCP (greg yachuk) (09/10/88)

In article <3950011@eecs.nwu.edu> naim@eecs.nwu.edu (Naim Abdullah) writes:
>Any suggestions for the value of OFFSET or what the function xfree() should
>look like ?
For both Sun and MSC, OFFSET is 2.  I don't know about Vaxen, though

>		      Naim Abdullah

Greg Yachuk		Informix Software Inc., Menlo Park, CA	(415) 322-4100
{uunet,pyramid}!infmx!greggy		why yes, I DID choose that login myself
--------------------------------------------------------------------------------
Yes, yes hammerheads; swimming, kissing; we are big and clever and we don't know
				anything!
God save hammerheads; keeping going; we are sleek and special, and we're sure of
				something

g-rh@cca.CCA.COM (Richard Harter) (09/10/88)

In article <3950011@eecs.nwu.edu> naim@eecs.nwu.edu (Naim Abdullah) writes:
>I want to check sections of a big program for memory leaks. My basic
>strategy was to define functions xmalloc() and xfree() that would keep
>track of the amount allocated, and to see if the amount went down to
>zero where it should (I wanted to use "#define malloc xmalloc" in the
>main header file to replace occurrences of malloc() and free()).

>The problem with this scheme is that I don't know how much space free()
>will actually free. Here is some sample code, illustrating what I mean:

	If you merely want to know if the amount of space allocated has
gone to zero a much simpler device is to maintain a counter in xmalloc/xfree.
Increment every time you allocate space, decrement it every time you free it.
The value of the counter at any time is the number of allocates for which
there is no corresponding free.  If this number is 0 there are no leaks.
If you want more (i.e. you want to know where the leaks occur) you need
to tie more information to the block.  A quick and dirty way to do this
is to allocate some extra space at the beginning of the block, two words
for a doubly linked list, and one for an address.  When you allocate a 
block link it into the list and load the address xmalloc was called from
in the address field (it is easy to get this address, albeit in a machine
dependent way).  When you free the block delink it.  The result is that
the list gives you, at any time, a list of all addresses of calls to xmalloc
of blocks that have not been deallocated.

-- 

In the fields of Hell where the grass grows high
Are the graves of dreams allowed to die.
	Richard Harter, SMDS  Inc.

tps@chem.ucsd.edu (Tom Stockfisch) (09/11/88)

In article <164@amsdsg.UUCP> jeff@amsdsg.UUCP (Jeff Barr) writes:
>In article <3950011@eecs.nwu.edu>, naim@eecs.nwu.edu (Naim Abdullah) writes:
>> I want to check sections of a big program for memory leaks....

>You can allocate space for an extra 'int' in xmalloc, store the size
>yourself at the beginning of the allocated block (being sure to return a
>pointer PAST the 'int'), and then retrieve it in xfree.

This assumes that "int" has the most stringent alignment requirements.
Doesn't draft-ANS have a MAXALIGN_T or something like that?
-- 

|| Tom Stockfisch, UCSD Chemistry	tps@chem.ucsd.edu

duane@cg-atla.UUCP (Andrew Duane) (09/12/88)

In article <3950011@eecs.nwu.edu>, naim@eecs.nwu.edu (Naim Abdullah) writes:
> I want to check sections of a big program for memory leaks. My basic
> strategy was to define functions xmalloc() and xfree() that would keep
> track of the amount allocated, and to see if the amount went down to
> zero where it should (I wanted to use "#define malloc xmalloc" in the
> main header file to replace occurrences of malloc() and free()).

In order to find memory leaks here, I wrote some routines to
put into your program to track and report bad mallocs, bad
frees, mallocs without frees, etc.

The architecture of them is a new set of malloc/realloc/free
routines that get spliced into your C library (using ar). The
new routines generate entries into an audit file and then call
the "real" routines (which are renamed). There are new calls
added to start the audit file wherever you want, and to turn
loggin on and off at will. Then, there is a separate program
called NMPP (namelist postprocessor) which sifts through the
audit file and generates a stack trace of the program at each
point a malloc/free routine was called. It can weed out
"properly matching" pairs, and just report the problems, or it
can report all calls.

The routines and programs run on a Sun-3, using SunOS 3.x.
I don't know if they will run elsewhere (there is a little
assembler "magic" to get the stack trace, and it relies on the
format of the Sun stack frame).


Andrew L. Duane (JOT-7)  w:(508)-658-5600 X5993  h:(603)-434-7934
Compugraphic Corp.			 decvax!cg-atla!duane
200 Ballardvale St.		       ulowell/ \laidback
Wilmington, Mass. 01887		   cbosgd!ima/   \cgeuro
Mail Stop 200II-3-5S		     ism780c/     \wizvax

Only my cat shares my opinions, and she's breaking in the new help.

jfh@rpp386.Dallas.TX.US (The Beach Bum) (09/14/88)

In article <164@amsdsg.UUCP> jeff@amsdsg.UUCP (Jeff Barr) writes:
>You can allocate space for an extra 'int' in xmalloc, store the size
>yourself at the beginning of the allocated block (being sure to return a
>pointer PAST the 'int'), and then retrieve it in xfree.

the number of bytes being allocated and freed is not important.  you
need to keep a count of the number of memory regions which have been
allocated and freed.

another problem is keeping the allocated region maximally aligned.
if you are using a system where doubles must be aligned modulo
sizeof (double), allocating an int before the region will introduce
alignment (and portability) problems.

the pointer returned by malloc() can be cast to both a (double *) and
a (char *).  so, store the address of the region at the beginning of
the region, then return the address of the double following the
beginning of the region cast (void *).
-- 
John F. Haugh II (jfh@rpp386.Dallas.TX.US)                   HASA, "S" Division

    "If the code and the comments disagree, then both are probably wrong."
                -- Norm Schryer

naim@eecs.nwu.edu (Naim Abdullah) (09/14/88)

A couple of days ago I asked how to detect memory leaks (not
free'ing what you had malloc'ed earlier). My question was how
to write functions xmalloc() and xfree() which could replace the
calls to malloc() and free().

Many people replied with samples of code and suggestions. My thanks
to all of you. There were two basic approaches suggested:

1) Look at the header of the malloc'ed block to get the size.
   This is what I was (unsuccessfully) trying to do in the code 
   I posted. People pointed out that the size allocated by
   malloc() might be greater than what you asked for, so the correct
   thing to do in xmalloc() is:
   
	alloc_size += *((unsigned int *)(p-sizeof(int)));

   and in xfree 

	size_being_freed = *((unsigned int *)(p - sizeof(int)));
	
   The disadvantage of this approach is that it is not portable
 and might fail if your malloc() has been compiled with error checking.
 (The above works on stock 4.3bsd and SunOS 3.5). The advantage of this
 approach is that no extra memory is used.
 
2) Ask the real malloc for the size being asked plus 4 extra bytes. Store
the size in an int at the beginning of the block, advance the pointer by
four bytes and return it to the caller.

   In free(), back up the pointer by four bytes and get the number stored
there as the size of the block. Give this pointer to the real free() to
free the block.

   I feel this approach is very elegant. Using it, I was able to find the
memory leak I was looking for.

   Here is some code from Karl Heuer (karl@haddock.isc.com). It implements
xmalloc(), xcalloc(), xfree(), xrealloc() and bytes_allocated(). Karl
gave me permission to post this so you may want to put it in your
toolbox.

		      Naim Abdullah
		      Dept. of EECS,
		      Northwestern University

		      Internet: naim@eecs.nwu.edu
		      Uucp: {oddjob, chinet, att}!nucsrl!naim
============================Cut Here==================================
#define ALIGN   8       /* system-dependent; may need to be tuned */
#define SLOP    ((sizeof(size_t)+ALIGN-1)/ALIGN*ALIGN)

#if defined(__STDC__)
#include <stddef.h>
#include <stdlib.h>
#define Void    void
#else
#define NULL    0
#define Void    char
extern Void *malloc();
extern Void *realloc();
extern Void *calloc();
extern void  free();
typedef unsigned int size_t;
#endif

static size_t alloc_size = 0;

Void *xmalloc(size) size_t size; {
    Void *p = malloc(SLOP + size);
    if (p != NULL) {
        *(size_t *)p = size;
        alloc_size += size;
        p = (Void *)((char *)p + SLOP);
    }
    return (p);
}

Void *xcalloc(nmemb, size) size_t nmemb; size_t size; {
    Void *p = calloc(SLOP + (size *= nmemb), 1);
    if (p != NULL) {
        *(size_t *)p = size;
        alloc_size += size;
        p = (Void *)((char *)p + SLOP);
    }
    return (p);
}

void xfree(p) Void *p; {
    if (p != NULL) {
        p = (Void *)((char *)p - SLOP);
        alloc_size -= *(size_t *)p;
    }
    free(p);
}

Void *xrealloc(p, newsize) Void *p; size_t newsize; {
    if (p != NULL) {
        p = (Void *)((char *)p - SLOP);
        alloc_size -= *(size_t *)p;
    }
    p = realloc(p, SLOP + newsize);
    if (p != NULL) {
        *(size_t *)p = newsize;
        alloc_size += newsize;
        p = (Void *)((char *)p + SLOP);
    }
    return (p);
}

size_t bytes_allocated() {
    return (alloc_size);
}

/* Now to test it */

#include <stdio.h>
main() {
    Void *p1, *p2;
    p1 = xmalloc(10);
    p2 = xmalloc(99);
    xfree(p1);
    printf("%lu bytes still allocated\n", (unsigned long)bytes_allocated());
    return (0);
}