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