/*
An interface to malloc() and free(). The code allows for
logging of memory usage to check for memory leaks and some
error checking. Only really used on the SUNs for memory
error checking. Borrowed from ptools and PETSc.
*/
#include "BSprivate.h"
#if defined(HAVE_STDLIB_H)
#include <stdlib.h>
#endif
#if defined(HAVE_MALLOC_H) && !defined(__cplusplus)
#include <malloc.h>
#endif
void *TrMalloc(unsigned int, int, char *);
int TrFree( void *, int, char * );
/*
Code for checking if a pointer is out of the range of the
previously malloced memory. This approach will not always
work, it's just a check.
*/
void *Low_address = (void *) 0x0 , *High_address = (void *) 0xEEEEEEEE;
int TrMallocUsed = 0;
/*
TrSpace - Routines for tracing space usage.
Description:
TrMalloc replaces malloc and TrFree replaces free. These routines
have the same syntax and semantics as the routines that they replace,
In addition, there are routines to report statistics on the memory
usage, and to report the currently allocated space. These routines
are built on top of malloc and free, and can be used together with
them as long as any space allocated with TrMalloc is only freed with
TrFree.
*/
/* HEADER_DOUBLES is the number of doubles in a TrSpace header */
/* We have to be careful about alignment rules here */
#if defined(HAVE_64BITS)
#define TR_ALIGN_BYTES 8
#define TR_ALIGN_MASK 0x7
#define TR_FNAME_LEN 16
#define HEADER_DOUBLES 8
#else
#define TR_ALIGN_BYTES 4
#define TR_ALIGN_MASK 0x3
#define TR_FNAME_LEN 12
#define HEADER_DOUBLES 5
#endif
#define COOKIE_VALUE 0xf0e0d0c9
#define ALREADY_FREED 0x0f0e0d9c
#define MAX_TR_STACK 20
#define TR_MALLOC 0x1
#define TR_FREE 0x2
typedef struct _trSPACE {
unsigned long size;
int id;
int lineno;
char fname[TR_FNAME_LEN];
unsigned long cookie;
struct _trSPACE *next, *prev;
} TRSPACE;
/* This union is used to insure that the block passed to the user is
aligned on a double boundary */
typedef union {
TRSPACE sp;
double v[HEADER_DOUBLES];
} TrSPACE;
static long allocated = 0, frags = 0;
static TRSPACE *TRhead = 0;
static int TRid = 0;
static int TRdebugLevel = 0;
static long TRMaxMem = 0;
static long TRMaxMemId = 0;
#if defined(PARCH_sun4) && defined(__cplusplus)
extern "C" {
extern int malloc_verify();
}
#elif defined(PARCH_sun4)
extern int malloc_verify();
#endif
/*
TrValid - Test the allocated blocks for validity. This can be used to
check for memory overwrites.
Input Parameter:
. line, file - line number and filename where call originated.
Return value:
The number of errors detected.
Output Effect:
Error messages are written to stdout. These have the form of either
$ Block [id=%d(%d)] at address %lx is corrupted (probably write past end)
$ Block allocated in <filename>[<linenumber>]
if the sentinal at the end of the block has been corrupted, and
$ Block at address %lx is corrupted
if the sentinal at the begining of the block has been corrupted.
The address is the actual address of the block. The id is the
value of TRID.
No output is generated if there are no problems detected.
*/
int TrValid(int line,char *file )
{
TRSPACE *head;
char *a;
unsigned long *nend;
head = TRhead;
while (head) {
if (head->cookie != COOKIE_VALUE) {
fprintf( stderr, "called from %s line %d \n",file,line );
fprintf( stderr, "Block at address %p is corrupted\n", head );
MY_SETERRC(1,"TrValid");
}
a = (char *)(((TrSPACE*)head) + 1);
nend = (unsigned long *)(a + head->size);
if (nend[0] != COOKIE_VALUE) {
fprintf( stderr, "called from %s line %d\n",file,line );
head->fname[TR_FNAME_LEN-1]= 0; /* Just in case */
if (nend[0] == ALREADY_FREED) {
fprintf(stderr,"Block [id=%d(%lx)] at address %p already freed\n",
head->id, head->size, a );
MY_SETERRC(1,"TrValid:Freed block in memory list, corrupted memory");
} else {
fprintf( stderr,
"Block [id=%d(%lx)] at address %p is corrupted (probably write past end)\n",
head->id, head->size, a );
fprintf(stderr,"Block allocated in %s[%d]\n",head->fname,head->lineno);
MY_SETERRC(1,"TrValid:Corrupted memory");
}
}
head = head->next;
}
#if defined(PARCH_sun4)
malloc_verify();
#endif
return 0;
}
/*
TrMalloc - Malloc with tracing.
Input Parameters:
. a - number of bytes to allocate
. lineno - line number where used. Use __LINE__ for this
. fname - file name where used. Use __FILE__ for this
Returns:
double aligned pointer to requested storage, or null if not
available.
*/
void *TrMalloc(unsigned int a, int lineno, char *fname )
{
TRSPACE *head;
char *inew;
unsigned long *nend;
unsigned int nsize;
int l,ierr;
if (TRdebugLevel > 0) {
ierr = TrValid(lineno,fname); if (ierr) return 0;
}
if (a == 0) {
fprintf(stderr,"TrMalloc ERROR: malloc zero length, this is illegal!\n");
return 0;
}
nsize = a;
if (nsize & TR_ALIGN_MASK) nsize += (TR_ALIGN_BYTES - (nsize & TR_ALIGN_MASK));
inew = (char *) malloc( (unsigned)(nsize+sizeof(TrSPACE)+sizeof(unsigned long)));
if (!inew) return 0;
/*
Keep track of range of memory locations we have malloced in
*/
#if !defined(PETSC_INSIGHT)
if (Low_address > (void *) inew) Low_address = (void *) inew;
if (High_address < (void *) (inew+nsize+sizeof(TrSPACE)+sizeof(unsigned long)))
High_address = (void *) (inew+nsize+sizeof(TrSPACE)+sizeof(unsigned long));
#endif
head = (TRSPACE *)inew;
inew += sizeof(TrSPACE);
if (TRhead) TRhead->prev = head;
head->next = TRhead;
TRhead = head;
head->prev = 0;
head->size = nsize;
head->id = TRid;
head->lineno = lineno;
if ((l = strlen(fname)) > TR_FNAME_LEN-1) fname += (l - (TR_FNAME_LEN-1));
strncpy( head->fname, fname, (TR_FNAME_LEN-1) );
head->fname[TR_FNAME_LEN-1] = 0;
head->cookie = COOKIE_VALUE;
nend = (unsigned long *)(inew + nsize);
nend[0] = COOKIE_VALUE;
allocated += nsize;
if (allocated > TRMaxMem) {
TRMaxMem = allocated;
TRMaxMemId = TRid;
}
frags ++;
return (void *)inew;
}
/*
TrFree - Free with tracing.
Input Parameters:
. a - pointer to a block allocated with TrMalloc
. line - line in file where called
. file - Name of file where called
*/
int TrFree( void *aa, int line, char *file )
{
char *a = (char *) aa;
TRSPACE *head;
char *ahead;
unsigned long *nend;
int ierr;
/* Don't try to handle empty blocks */
if (!a) {
fprintf(stderr,"TrFree called from line %d in %s\n",line,file);
MY_SETERRC(1,"TrFree:Trying to free null block");
}
if (TRdebugLevel > 0) {
ierr = TrValid(line,file); CHKERR(ierr);
}
#if !defined(PETSC_INSIGHT)
if (Low_address > aa || High_address < aa){
fprintf(stderr,"TrFree called with address not allocated by TrMalloc\n");
MY_SETERRC(1,"TrFree:Invalid Address");
}
#endif
ahead = a;
a = a - sizeof(TrSPACE);
head = (TRSPACE *)a;
if (head->cookie != COOKIE_VALUE) {
/* Damaged header */
fprintf( stderr, "Block at address %p is corrupted; cannot free;\n\
may be block not allocated with TrMalloc or Malloc\n", a );
MY_SETERRC(1,"TrFree:Bad location or corrupted memory");
}
nend = (unsigned long *)(ahead + head->size);
if (*nend != COOKIE_VALUE) {
if (*nend == ALREADY_FREED) {
fprintf(stderr,"Block [id=%d(%lx)] at address %p was already freed\n",
head->id, head->size, a + sizeof(TrSPACE) );
head->fname[TR_FNAME_LEN-1]= 0; /* Just in case */
if (head->lineno > 0)
fprintf( stderr, "Block freed in %s[%d]\n", head->fname, head->lineno );
else
fprintf( stderr, "Block allocated at %s[%d]\n",head->fname,-head->lineno);
MY_SETERRC(1,"TrFree:Memory already freed");
}
else {
/* Damaged tail */
fprintf( stderr,
"Block [id=%d(%lx)] at address %p is corrupted (probably write past end)\n",
head->id, head->size, a );
head->fname[TR_FNAME_LEN-1]= 0; /* Just in case */
fprintf( stderr, "Block allocated in %s[%d]\n", head->fname, head->lineno );
MY_SETERRC(1,"TrFree:Corrupted memory");
}
}
/* Mark the location freed */
*nend = ALREADY_FREED;
/* Save location where freed. If we suspect the line number, mark as
allocated location */
if (line > 0 && line < 5000) {
head->lineno = line;
strncpy( head->fname, file, (TR_FNAME_LEN-1) );
}
else {
head->lineno = - head->lineno;
}
allocated -= head->size;
frags --;
if (head->prev) head->prev->next = head->next;
else TRhead = head->next;
if (head->next) head->next->prev = head->prev;
free( a );
return 0;
}
/*
TrSpace - Returns space statistics.
Output Parameters:
. space - number of bytes currently allocated
. frags - number of blocks currently allocated
. maxs - maximum number of bytes ever allocated
.keywords: memory, allocation, tracing, space, statistics
.seealso: TrDump()
*/
int TrSpace( double *space, double *fr, double *maxs )
{
if (space) *space = (double) allocated;
if (fr) *fr = (double) frags;
if (maxs) *maxs = (double) TRMaxMem;
return 0;
}
/*
TrDump - Dumps the allocated memory blocks to a file. The information
printed is: size of space (in bytes), address of space, id of space,
file in which space was allocated, and line number at which it was
allocated.
Input Parameter:
. fp - file pointer. If fp is NULL, stderr is assumed.
Options Database Key:
$ -trdump : dumps unfreed memory during call to BSfinalize()
.keywords: memory, allocation, tracing, space, statistics
.seealso: TrSpace()
*/
int TrDump( FILE *fp )
{
TRSPACE *head;
int rank;
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
if (fp == 0) fp = stderr;
if (allocated > 0) {
fprintf(fp,"[%d]Total space allocated %d\n",rank,(int)allocated);
}
head = TRhead;
while (head) {
fprintf( fp, "[%d]%d bytes at address [%p], id = ",rank,
(int) head->size, head + sizeof(TrSPACE) );
head->fname[TR_FNAME_LEN-1] = 0;
fprintf(fp, "%d, %s line number %d\n",head->id,head->fname,head->lineno);
head = head->next;
}
return 0;
}
/*
TrDebugLevel - Set the level of debugging for the space management
routines.
Input Parameter:
. level - level of debugging. Currently, either 0 (no checking) or 1
(use TrValid at each TrMalloc or TrFree).
*/
int TrDebugLevel(int level )
{
TRdebugLevel = level;
return 0;
}
#define TR_MAX_DUMP 100
/*
The following routine attempts to give useful information about the
memory usage when an "out-of-memory" error is encountered. The rules are:
If there are less than TR_MAX_DUMP blocks, output those.
Otherwise, try to find multiple instances of the same routine/line #, and
print a summary by number:
file line number-of-blocks total-number-of-blocks
We have to do a sort-in-place for this
*/
/*
Sort by file/line number. Do this without calling a system routine or
allocating ANY space (space is being optimized here).
We do this by first recursively sorting halves of the list and then
merging them.
*/
/* Merge two lists, returning the head of the merged list */
TRSPACE *TrImerge(TRSPACE * l1,TRSPACE * l2 )
{
TRSPACE *head = 0, *tail = 0;
int sign;
while (l1 && l2) {
sign = strcmp(l1->fname, l2->fname);
if (sign > 0 || (sign == 0 && l1->lineno >= l2->lineno)) {
if (head) tail->next = l1;
else head = tail = l1;
tail = l1;
l1 = l1->next;
}
else {
if (head) tail->next = l2;
else head = tail = l2;
tail = l2;
l2 = l2->next;
}
}
/* Add the remaining elements to the end */
if (l1) tail->next = l1;
if (l2) tail->next = l2;
return head;
}
/* Sort head with n elements, returning the head */
TRSPACE *TrIsort( TRSPACE * head,int n )
{
TRSPACE *p, *l1, *l2;
int m, i;
if (n <= 1) return head;
/* This guarentees that m, n are both > 0 */
m = n / 2;
p = head;
for (i=0; i<m-1; i++) p = p->next;
/* p now points to the END of the first list */
l2 = p->next;
p->next = 0;
l1 = TrIsort( head, m );
l2 = TrIsort( l2, n - m );
return TrImerge( l1, l2 );
}
int TrSortBlocks()
{
TRSPACE *head;
int cnt;
head = TRhead;
cnt = 0;
while (head) {
cnt ++;
head = head->next;
}
TRhead = TrIsort( TRhead, cnt );
return 0;
}
/* Takes sorted input and dumps as an aggregate */
int TrDumpGrouped(FILE *fp )
{
TRSPACE *head, *cur;
int nblocks;
unsigned long nbytes;
if (fp == 0) fp = stderr;
TrSortBlocks();
head = TRhead;
cur = 0;
while (head) {
cur = head->next;
nblocks = 1;
nbytes = head->size;
while (cur && !strcmp(cur->fname,head->fname) && cur->lineno == head->lineno){
nblocks++;
nbytes += cur->size;
cur = cur->next;
}
fprintf( fp, "File %13s line %5d: %ld bytes in %d allocation%c\n",
head->fname, head->lineno, nbytes, nblocks,(nblocks > 1) ? 's' : ' ');
head = cur;
}
fflush( fp );
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1