[net.unix] Debugging version of malloc/free

keith@cecil.UUCP (keith gorlen) (11/22/85)

I vaguely remember reading somewhere about a set of routines to replace
malloc and free which assisted one in debugging a program that was
mis-managing memory (i.e., using deallocated blocks, deallocating the
same block more than once, not deallocating blocks, etc.) by logging
calls to malloc/free to a file which could then be analyzed for
problems.  I'd greatly appreciate any pointers to this software.  Please
reply by mail and I'll re-post to the net if there is general interest.
Thanks.
-- 
---
	Keith Gorlen
	Computer Systems Laboratory
	Division of Computer Research and Technology
	National Institutes of Health
	Bethseda, MD 20892
	phone:	(301) 496-5363
	uucp:	{decvax!}seismo!elsie!cecil!keith

mike@BRL.ARPA (Mike Muuss) (11/25/85)

I often write my code to call vmalloc() and vfree(), so that
I can trace my memory allocations.  It is rarely useful, but
the one time you need it, it certainly is invaluable to have
built right in!  Also, watching the memory allocation patterns
can help you understand better how different parts of a big
program interact, memory wise.

Here is the source code;  you may need to tailor it some.

Notes:  The RES_ALLOC() and RES_FREE() routines are used
for P & V type locking on parallel machines, and are noops on
conventional serial machines.  Also, the get_seg() and get_pt()
routines are included as an example of a good way of using the
byte_roundup() routine.

/*
 *			S T O R A G E . C
 *
 * Ray Tracing program, storage manager.
 *
 *  Functions -
 *
 *  Author -
 *	Michael John Muuss
 *  
 *  Source -
 *	SECAD/VLD Computing Consortium, Bldg 394
 *	The U. S. Army Ballistic Research Laboratory
 *	Aberdeen Proving Ground, Maryland  21005
 */
#ifndef lint
static char RCSid[] = "@(#)$Header: storage.c,v 2.3 85/09/06 02:29:35 mike Exp $";
#endif

#include <stdio.h>
#include "../h/machine.h"
#include "../h/vmath.h"
#include "raytrace.h"
#include "debug.h"


/*
 *  malloc/Free wrappers, for debugging
 */
char *
vmalloc(cnt, str)
unsigned int cnt;
char *str;
{
	register char *ptr;
	extern char *malloc();

	RES_ACQUIRE( &res_malloc );		/* lock */
	ptr = malloc(cnt);
	RES_RELEASE( &res_malloc );		/* unlock */

	if( ptr==(char *)0 || debug&DEBUG_MEM )
		rtlog("%x=malloc(%d) %s\n", ptr, cnt, str);
	if( ptr==(char *)0 )
		rtbomb("vmalloc: malloc failure");
	return(ptr);
}

void
vfree(ptr,str)
char *ptr;
{
	extern void free();

	RES_ACQUIRE( &res_malloc );		/* lock */
	*((int *)ptr) = -1;	/* zappo! */
	free(ptr);
	RES_RELEASE( &res_malloc );		/* unlock */
	if(debug&DEBUG_MEM) rtlog("%x freed %s\n", ptr, str);
}

/*
 *  			G E T _ S E G
 *  
 *  This routine is called by the GET_SEG macro when the freelist
 *  is exhausted.  Rather than simply getting one additional structure,
 *  we get a whole batch, saving overhead.  When this routine is called,
 *  the seg resource must already be locked.
 *  malloc() locking is done in vmalloc.
 */
void
get_seg()  {
	register char *cp;
	register struct seg *sp;
	register int bytes;

	bytes = byte_roundup(64*sizeof(struct seg));
	if( (cp = vmalloc(bytes, "get_seg")) == (char *)0 )  {
		rtlog("get_seg: malloc failure\n");
		exit(17);
	}
	sp = (struct seg *)cp;
	while( bytes >= sizeof(struct seg) )  {
		sp->seg_next = FreeSeg;
		FreeSeg = sp++;
		bytes -= sizeof(struct seg);
	}
}

/*
 *  			G E T _ P T
 *  
 *  This routine is called by the GET_PT macro when the freelist
 *  is exhausted.  Rather than simply getting one additional structure,
 *  we get a whole batch, saving overhead.  When this routine is called,
 *  the partition resource must already be locked.
 *  malloc() locking is done in vmalloc.
 *
 *  Also note that there is a bit of trickery going on here:
 *  the *real* size of pt_solhit[] array is determined at runtime, here.
 */
void
get_pt()  {
	register char *cp;
	register int bytes;
	register int size;		/* size of structure to really get */

	size = PT_BYTES;		/* depends on nsolids */
	size = (size + sizeof(long) -1) & (~(sizeof(long)-1));
	bytes = byte_roundup(64*size);
	if( (cp = vmalloc(bytes, "get_pt")) == (char *)0 )  {
		rtlog("get_pt: malloc failure\n");
		exit(17);
	}
	while( bytes >= size )  {
		((struct partition *)cp)->pt_forw = FreePart;
		FreePart = (struct partition *)cp;
		cp += size;
		bytes -= size;
	}
}

/*
 *  			B Y T E _ R O U N D U P
 *  
 *  On systems with the CalTech malloc(), the amount of storage
 *  ACTUALLY ALLOCATED is the amount requested rounded UP to the
 *  nearest power of two.  For structures which are acquired and
 *  released often, this works well, but for structures which will
 *  remain unchanged for the duration of the program, this wastes
 *  as much as 50% of the address space (and usually memory as well).
 *  Here, we round up a byte size to the nearest power of two,
 *  leaving off the malloc header, so as to ask for storage without
 *  wasting any.
 *  
 *  On systems with the traditional malloc(), this strategy will just
 *  consume the memory in somewhat larger chunks, but overall little
 *  unused memory will be consumed.
 */
int
byte_roundup(nbytes)
register int nbytes;
{
	static int pagesz;
	register int n;
	register int amt;

	if (pagesz == 0)
		pagesz = 1024;		/* getpagesize(); */

#define OVERHEAD	(4*sizeof(unsigned char) + \
			2*sizeof(unsigned short) + \
			sizeof(unsigned int) )
	n = pagesz - OVERHEAD;
	if (nbytes <= n)
		return(n);
	amt = pagesz;

	while (nbytes > amt + n) {
		amt <<= 1;
	}
	return(amt-OVERHEAD-sizeof(int));
}