/*
* Copyright (c) 2000-2001, 2004 Proofpoint, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
/*
** debugging memory allocation package
** See heap.html for documentation.
*/
#include <string.h>
/* undef all macro versions of the "functions" so they can be specified here */
#if SM_HEAP_CHECK
#endif /* SM_HEAP_CHECK */
#if SM_HEAP_CHECK
"@(#)$Debug: sm_check_heap - check sm_malloc, sm_realloc, sm_free calls $");
#endif /* SM_HEAP_CHECK */
{
"F:sm.heap",
"",
"out of memory",
};
/*
** The behaviour of malloc with size==0 is platform dependent (it
** says so in the C standard): it can return NULL or non-NULL. We
** don't want sm_malloc_x(0) to raise an exception on some platforms
** but not others, so this case requires special handling. We've got
** two choices: "size = 1" or "return NULL". We use the former in the
** following.
** If we had something like autoconf we could figure out the
** behaviour of the platform and either use this hack or just
** use size.
*/
/*
** SM_MALLOC_X -- wrapper around malloc(), raises an exception on error.
**
** Parameters:
** size -- size of requested memory.
**
** Returns:
** Pointer to memory region.
**
** Note:
** sm_malloc_x only gets called from source files in which heap
** debugging is disabled at compile time. Otherwise, a call to
** sm_malloc_x is macro expanded to a call to sm_malloc_tagged_x.
**
** Exceptions:
** F:sm_heap -- out of memory
*/
void *
{
void *ptr;
return ptr;
}
#if !SM_HEAP_CHECK
/*
** SM_MALLOC -- wrapper around malloc()
**
** Parameters:
** size -- size of requested memory.
**
** Returns:
** Pointer to memory region.
*/
void *
{
void *ptr;
return ptr;
}
/*
** SM_REALLOC -- wrapper for realloc()
**
** Parameters:
** ptr -- pointer to old memory area.
** size -- size of requested memory.
**
** Returns:
** Pointer to new memory area, NULL on failure.
*/
void *
void *ptr;
{
void *newptr;
return newptr;
}
/*
** SM_REALLOC_X -- wrapper for realloc()
**
** Parameters:
** ptr -- pointer to old memory area.
** size -- size of requested memory.
**
** Returns:
** Pointer to new memory area.
**
** Exceptions:
** F:sm_heap -- out of memory
*/
void *
void *ptr;
{
void *newptr;
return newptr;
}
/*
** SM_FREE -- wrapper around free()
**
** Parameters:
** ptr -- pointer to memory region.
**
** Returns:
** none.
*/
void
void *ptr;
{
return;
return;
}
#else /* !SM_HEAP_CHECK */
/*
** Each allocated block is assigned a "group number".
** By default, all blocks are assigned to group #1.
** By convention, group #0 is for memory that is never freed.
** You can use group numbers any way you want, in order to help make
** sense of sm_heap_report output.
*/
/*
** Total number of bytes allocated.
** This is only maintained if the sm_check_heap debug category is active.
*/
/*
** High water mark: the most that SmHeapTotal has ever been.
*/
/*
** Maximum number of bytes that may be allocated at any one time.
** 0 means no limit.
** This is only honoured if sm_check_heap is active.
*/
"@(#)$Debug: sm_heap_limit - max # of bytes permitted in heap $");
/*
** This is the data structure that keeps track of all currently
** allocated blocks of memory known to the heap package.
*/
struct sm_heap_item
{
void *hi_ptr;
char *hi_tag;
int hi_num;
int hi_group;
};
/*
** This is a randomly generated table
** which contains exactly one occurrence
** of each of the numbers between 0 and 255.
** It is used by ptrhash.
*/
{
161, 71, 77,187, 15,229, 9,176,221,119,239, 21, 85,138,203, 86,
102, 65, 80,199,235, 32,140, 96,224, 78,126,127,144, 0, 11,179,
64, 30,120, 23,225,226, 33, 50,205,167,130,240,174, 99,206, 73,
231,210,189,162, 48, 93,246, 54,213,141,135, 39, 41,192,236,193,
157, 88, 95,104,188, 63,133,177,234,110,158,214,238,131,233, 91,
125, 82, 94, 79, 66, 92,151, 45,252, 98, 26,183, 7,191,171,106,
145,154,251,100,113, 5, 74, 62, 76,124, 14,217,200, 75,115,190,
103, 28,198,196,169,219, 37,118,150, 18,152,175, 49,136, 6,142,
89, 19,243,254, 47,137, 24,166,180, 10, 40,186,202, 46,184, 67,
148,108,181, 81, 25,241, 13,139, 58, 38, 84,253,201, 12,116, 17,
195, 22,112, 69,255, 43,147,222,111, 56,194,216,149,244, 42,173,
232,220,249,105,207, 51,197,242, 72,211,208, 59,122,230,237,170,
165, 44, 68,123,129,245,143,101, 8,209,215,247,185, 57,218, 53,
114,121, 3,128, 4,204,212,146, 2,155, 83,250, 87, 29, 31,159,
60, 27,107,156,227,182, 1, 61, 36,160,109, 97, 90, 20,168,132,
223,248, 70,164, 55,172, 34, 52,163,117, 35,153,134, 16,178,228
};
/*
** PTRHASH -- hash a pointer value
**
** Parameters:
** p -- pointer.
**
** Returns:
** hash value.
**
** ptrhash hashes a pointer value to a uniformly distributed random
** number between 0 and 255.
**
** This hash algorithm is based on Peter K. Pearson,
** "Fast Hashing of Variable-Length Text Strings",
** in Communications of the ACM, June 1990, vol 33 no 6.
*/
static int
ptrhash(p)
void *p;
{
int h;
if (sizeof(void*) == 4 && sizeof(unsigned long) == 4)
{
unsigned long n = (unsigned long)p;
h = hashtab[n & 0xFF];
}
# if 0
else if (sizeof(void*) == 8 && sizeof(unsigned long) == 8)
{
unsigned long n = (unsigned long)p;
h = hashtab[n & 0xFF];
}
# endif /* 0 */
else
{
unsigned char *cp = (unsigned char *)&p;
int i;
h = 0;
for (i = 0; i < sizeof(void*); ++i)
}
return h;
}
/*
** SM_MALLOC_TAGGED -- wrapper around malloc(), debugging version.
**
** Parameters:
** size -- size of requested memory.
** tag -- tag for debugging.
** num -- additional value for debugging.
** group -- heap group for debugging.
**
** Returns:
** Pointer to memory region.
*/
void *
char *tag;
int num;
int group;
{
void *ptr;
if (!HEAP_CHECK)
{
return ptr;
}
if (sm_xtrap_check())
return NULL;
return NULL;
{
}
SmHeapTotal += size;
if (SmHeapTotal > SmHeapMaxTotal)
return ptr;
}
/*
** SM_MALLOC_TAGGED_X -- wrapper around malloc(), debugging version.
**
** Parameters:
** size -- size of requested memory.
** tag -- tag for debugging.
** num -- additional value for debugging.
** group -- heap group for debugging.
**
** Returns:
** Pointer to memory region.
**
** Exceptions:
** F:sm_heap -- out of memory
*/
void *
char *tag;
int num;
int group;
{
void *ptr;
if (!HEAP_CHECK)
{
return ptr;
}
{
}
{
}
SmHeapTotal += size;
if (SmHeapTotal > SmHeapMaxTotal)
return ptr;
}
/*
** SM_HEAP_REGISTER -- register a pointer into the heap for debugging.
**
** Parameters:
** ptr -- pointer to register.
** size -- size of requested memory.
** tag -- tag for debugging.
** num -- additional value for debugging.
** group -- heap group for debugging.
**
** Returns:
** true iff successfully registered (not yet in table).
*/
bool
void *ptr;
char *tag;
int num;
int group;
{
int i;
if (!HEAP_CHECK)
return true;
# if SM_CHECK_REQUIRE
/*
** We require that ptr is not already in SmHeapTable.
*/
{
sm_abort("sm_heap_register: ptr %p is already registered (%s:%d)",
}
# endif /* SM_CHECK_REQUIRE */
return false;
SmHeapTable[i] = hi;
return true;
}
/*
** SM_REALLOC -- wrapper for realloc(), debugging version.
**
** Parameters:
** ptr -- pointer to old memory area.
** size -- size of requested memory.
**
** Returns:
** Pointer to new memory area, NULL on failure.
*/
void *
void *ptr;
{
void *newptr;
if (!HEAP_CHECK)
{
return newptr;
}
{
{
if (sm_xtrap_check())
return NULL;
{
return NULL;
}
return NULL;
if (SmHeapTotal > SmHeapMaxTotal)
return newptr;
}
}
/* NOTREACHED */
return NULL; /* keep Irix compiler happy */
}
/*
** SM_REALLOC_X -- wrapper for realloc(), debugging version.
**
** Parameters:
** ptr -- pointer to old memory area.
** size -- size of requested memory.
**
** Returns:
** Pointer to new memory area.
**
** Exceptions:
** F:sm_heap -- out of memory
*/
void *
void *ptr;
{
void *newptr;
if (!HEAP_CHECK)
{
return newptr;
}
{
{
{
}
if (SmHeapTotal > SmHeapMaxTotal)
return newptr;
}
}
/* NOTREACHED */
return NULL; /* keep Irix compiler happy */
}
/*
** SM_FREE_TAGGED -- wrapper around free(), debugging version.
**
** Parameters:
** ptr -- pointer to memory region.
** tag -- tag for debugging.
** num -- additional value for debugging.
**
** Returns:
** none.
*/
void
void *ptr;
char *tag;
int num;
{
return;
if (!HEAP_CHECK)
{
return;
}
{
{
/*
** Fill the block with zeros before freeing.
** This is intended to catch problems with
** dangling pointers. The block is filled with
** zeros, not with some non-zero value, because
** it is common practice in some C code to store
** a zero in a structure member before freeing the
** structure, as a defense against dangling pointers.
*/
return;
}
}
}
/*
** SM_HEAP_CHECKPTR_TAGGED -- check whether ptr is a valid argument to sm_free
**
** Parameters:
** ptr -- pointer to memory region.
** tag -- tag for debugging.
** num -- additional value for debugging.
**
** Returns:
** none.
**
** Side Effects:
** aborts if check fails.
*/
void
void *ptr;
char *tag;
int num;
{
if (!HEAP_CHECK)
return;
return;
{
return;
}
}
/*
** SM_HEAP_REPORT -- output "map" of used heap.
**
** Parameters:
** stream -- the file pointer to write to.
** verbosity -- how much info?
**
** Returns:
** none.
*/
void
int verbosity;
{
int i;
if (!HEAP_CHECK || verbosity <= 0)
return;
for (i = 0; i < sizeof(SmHeapTable) / sizeof(SmHeapTable[0]); ++i)
{
{
if (verbosity > 2
{
"%4d %*lx %7lu bytes",
(int) sizeof(void *) * 2,
{
" %s",
{
":%d",
}
}
}
{
case 0:
break;
case 1:
break;
default:
break;
}
}
}
"heap max=%lu, total=%lu, ",
(unsigned long) SmHeapMaxTotal, grandtotal);
"group 0=%lu, group 1=%lu, others=%lu\n",
if (grandtotal != SmHeapTotal)
{
"BUG => SmHeapTotal: got %lu, expected %lu\n",
(unsigned long) SmHeapTotal, grandtotal);
}
}
#endif /* !SM_HEAP_CHECK */