2 * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
11 SM_RCSID("@(#)$Id: heap.c,v 1.50 2001/09/11 04:04:48 gshapiro Exp $")
14 ** debugging memory allocation package
15 ** See heap.html for documentation.
20 #include <sm/assert.h>
25 #include <sm/signal.h>
28 /* undef all macro versions of the "functions" so they can be specified here */
31 #undef sm_malloc_tagged
32 #undef sm_malloc_tagged_x
37 # undef sm_heap_register
38 # undef sm_heap_checkptr
39 # undef sm_heap_report
40 #endif /* SM_HEAP_CHECK */
43 SM_DEBUG_T SmHeapCheck = SM_DEBUG_INITIALIZER("sm_check_heap",
44 "@(#)$Debug: sm_check_heap - check sm_malloc, sm_realloc, sm_free calls $");
45 # define HEAP_CHECK sm_debug_active(&SmHeapCheck, 1)
46 #endif /* SM_HEAP_CHECK */
48 const SM_EXC_TYPE_T SmHeapOutOfMemoryType =
57 SM_EXC_T SmHeapOutOfMemory = SM_EXC_INITIALIZER(&SmHeapOutOfMemoryType, NULL);
61 ** The behaviour of malloc with size==0 is platform dependent (it
62 ** says so in the C standard): it can return NULL or non-NULL. We
63 ** don't want sm_malloc_x(0) to raise an exception on some platforms
64 ** but not others, so this case requires special handling. We've got
65 ** two choices: "size = 1" or "return NULL". We use the former in the
67 ** If we had something like autoconf we could figure out the
68 ** behaviour of the platform and either use this hack or just
72 #define MALLOC_SIZE(size) ((size) == 0 ? 1 : (size))
75 ** SM_MALLOC_X -- wrapper around malloc(), raises an exception on error.
78 ** size -- size of requested memory.
81 ** Pointer to memory region.
84 ** sm_malloc_x only gets called from source files in which heap
85 ** debugging is disabled at compile time. Otherwise, a call to
86 ** sm_malloc_x is macro expanded to a call to sm_malloc_tagged_x.
89 ** F:sm_heap -- out of memory
99 ptr = malloc(MALLOC_SIZE(size));
102 sm_exc_raise_x(&SmHeapOutOfMemory);
109 ** SM_MALLOC -- wrapper around malloc()
112 ** size -- size of requested memory.
115 ** Pointer to memory region.
125 ptr = malloc(MALLOC_SIZE(size));
131 ** SM_REALLOC -- wrapper for realloc()
134 ** ptr -- pointer to old memory area.
135 ** size -- size of requested memory.
138 ** Pointer to new memory area, NULL on failure.
142 sm_realloc(ptr, size)
149 newptr = realloc(ptr, MALLOC_SIZE(size));
155 ** SM_REALLOC_X -- wrapper for realloc()
158 ** ptr -- pointer to old memory area.
159 ** size -- size of requested memory.
162 ** Pointer to new memory area.
165 ** F:sm_heap -- out of memory
169 sm_realloc_x(ptr, size)
176 newptr = realloc(ptr, MALLOC_SIZE(size));
179 sm_exc_raise_x(&SmHeapOutOfMemory);
183 ** SM_FREE -- wrapper around free()
186 ** ptr -- pointer to memory region.
204 #else /* !SM_HEAP_CHECK */
207 ** Each allocated block is assigned a "group number".
208 ** By default, all blocks are assigned to group #1.
209 ** By convention, group #0 is for memory that is never freed.
210 ** You can use group numbers any way you want, in order to help make
211 ** sense of sm_heap_report output.
215 int SmHeapMaxGroup = 1;
218 ** Total number of bytes allocated.
219 ** This is only maintained if the sm_check_heap debug category is active.
222 size_t SmHeapTotal = 0;
225 ** High water mark: the most that SmHeapTotal has ever been.
228 size_t SmHeapMaxTotal = 0;
231 ** Maximum number of bytes that may be allocated at any one time.
233 ** This is only honoured if sm_check_heap is active.
236 SM_DEBUG_T SmHeapLimit = SM_DEBUG_INITIALIZER("sm_heap_limit",
237 "@(#)$Debug: sm_heap_limit - max # of bytes permitted in heap $");
240 ** This is the data structure that keeps track of all currently
241 ** allocated blocks of memory known to the heap package.
244 typedef struct sm_heap_item SM_HEAP_ITEM_T;
252 SM_HEAP_ITEM_T *hi_next;
255 #define SM_HEAP_TABLE_SIZE 256
256 static SM_HEAP_ITEM_T *SmHeapTable[SM_HEAP_TABLE_SIZE];
259 ** This is a randomly generated table
260 ** which contains exactly one occurrence
261 ** of each of the numbers between 0 and 255.
262 ** It is used by ptrhash.
265 static unsigned char hashtab[SM_HEAP_TABLE_SIZE] =
267 161, 71, 77,187, 15,229, 9,176,221,119,239, 21, 85,138,203, 86,
268 102, 65, 80,199,235, 32,140, 96,224, 78,126,127,144, 0, 11,179,
269 64, 30,120, 23,225,226, 33, 50,205,167,130,240,174, 99,206, 73,
270 231,210,189,162, 48, 93,246, 54,213,141,135, 39, 41,192,236,193,
271 157, 88, 95,104,188, 63,133,177,234,110,158,214,238,131,233, 91,
272 125, 82, 94, 79, 66, 92,151, 45,252, 98, 26,183, 7,191,171,106,
273 145,154,251,100,113, 5, 74, 62, 76,124, 14,217,200, 75,115,190,
274 103, 28,198,196,169,219, 37,118,150, 18,152,175, 49,136, 6,142,
275 89, 19,243,254, 47,137, 24,166,180, 10, 40,186,202, 46,184, 67,
276 148,108,181, 81, 25,241, 13,139, 58, 38, 84,253,201, 12,116, 17,
277 195, 22,112, 69,255, 43,147,222,111, 56,194,216,149,244, 42,173,
278 232,220,249,105,207, 51,197,242, 72,211,208, 59,122,230,237,170,
279 165, 44, 68,123,129,245,143,101, 8,209,215,247,185, 57,218, 53,
280 114,121, 3,128, 4,204,212,146, 2,155, 83,250, 87, 29, 31,159,
281 60, 27,107,156,227,182, 1, 61, 36,160,109, 97, 90, 20,168,132,
282 223,248, 70,164, 55,172, 34, 52,163,117, 35,153,134, 16,178,228
286 ** PTRHASH -- hash a pointer value
294 ** ptrhash hashes a pointer value to a uniformly distributed random
295 ** number between 0 and 255.
297 ** This hash algorithm is based on Peter K. Pearson,
298 ** "Fast Hashing of Variable-Length Text Strings",
299 ** in Communications of the ACM, June 1990, vol 33 no 6.
308 if (sizeof(void*) == 4 && sizeof(unsigned long) == 4)
310 unsigned long n = (unsigned long)p;
312 h = hashtab[n & 0xFF];
313 h = hashtab[h ^ ((n >> 8) & 0xFF)];
314 h = hashtab[h ^ ((n >> 16) & 0xFF)];
315 h = hashtab[h ^ ((n >> 24) & 0xFF)];
318 else if (sizeof(void*) == 8 && sizeof(unsigned long) == 8)
320 unsigned long n = (unsigned long)p;
322 h = hashtab[n & 0xFF];
323 h = hashtab[h ^ ((n >> 8) & 0xFF)];
324 h = hashtab[h ^ ((n >> 16) & 0xFF)];
325 h = hashtab[h ^ ((n >> 24) & 0xFF)];
326 h = hashtab[h ^ ((n >> 32) & 0xFF)];
327 h = hashtab[h ^ ((n >> 40) & 0xFF)];
328 h = hashtab[h ^ ((n >> 48) & 0xFF)];
329 h = hashtab[h ^ ((n >> 56) & 0xFF)];
334 unsigned char *cp = (unsigned char *)&p;
338 for (i = 0; i < sizeof(void*); ++i)
339 h = hashtab[h ^ cp[i]];
345 ** SM_MALLOC_TAGGED -- wrapper around malloc(), debugging version.
348 ** size -- size of requested memory.
349 ** tag -- tag for debugging.
350 ** num -- additional value for debugging.
351 ** group -- heap group for debugging.
354 ** Pointer to memory region.
358 sm_malloc_tagged(size, tag, num, group)
369 ptr = malloc(MALLOC_SIZE(size));
374 if (sm_xtrap_check())
376 if (sm_debug_active(&SmHeapLimit, 1)
377 && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size)
380 ptr = malloc(MALLOC_SIZE(size));
382 if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group))
390 if (SmHeapTotal > SmHeapMaxTotal)
391 SmHeapMaxTotal = SmHeapTotal;
396 ** SM_MALLOC_TAGGED_X -- wrapper around malloc(), debugging version.
399 ** size -- size of requested memory.
400 ** tag -- tag for debugging.
401 ** num -- additional value for debugging.
402 ** group -- heap group for debugging.
405 ** Pointer to memory region.
408 ** F:sm_heap -- out of memory
412 sm_malloc_tagged_x(size, tag, num, group)
423 ptr = malloc(MALLOC_SIZE(size));
426 sm_exc_raise_x(&SmHeapOutOfMemory);
430 sm_xtrap_raise_x(&SmHeapOutOfMemory);
431 if (sm_debug_active(&SmHeapLimit, 1)
432 && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size)
434 sm_exc_raise_x(&SmHeapOutOfMemory);
437 ptr = malloc(MALLOC_SIZE(size));
439 if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group))
447 sm_exc_raise_x(&SmHeapOutOfMemory);
449 if (SmHeapTotal > SmHeapMaxTotal)
450 SmHeapMaxTotal = SmHeapTotal;
455 ** SM_HEAP_REGISTER -- register a pointer into the heap for debugging.
458 ** ptr -- pointer to register.
459 ** size -- size of requested memory.
460 ** tag -- tag for debugging.
461 ** num -- additional value for debugging.
462 ** group -- heap group for debugging.
465 ** true iff successfully registered (not yet in table).
469 sm_heap_register(ptr, size, tag, num, group)
481 SM_REQUIRE(ptr != NULL);
483 # if SM_CHECK_REQUIRE
486 ** We require that ptr is not already in SmHeapTable.
489 for (hi = SmHeapTable[i]; hi != NULL; hi = hi->hi_next)
491 if (hi->hi_ptr == ptr)
492 sm_abort("sm_heap_register: ptr %p is already registered (%s:%d)",
493 ptr, hi->hi_tag, hi->hi_num);
495 # endif /* SM_CHECK_REQUIRE */
497 hi = (SM_HEAP_ITEM_T *) malloc(sizeof(SM_HEAP_ITEM_T));
505 hi->hi_group = group;
506 hi->hi_next = SmHeapTable[i];
511 ** SM_REALLOC -- wrapper for realloc(), debugging version.
514 ** ptr -- pointer to old memory area.
515 ** size -- size of requested memory.
518 ** Pointer to new memory area, NULL on failure.
522 sm_realloc(ptr, size)
527 SM_HEAP_ITEM_T *hi, **hp;
532 newptr = realloc(ptr, MALLOC_SIZE(size));
538 return sm_malloc_tagged(size, "realloc", 0, SmHeapGroup);
540 for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
542 if ((**hp).hi_ptr == ptr)
544 if (sm_xtrap_check())
547 if (sm_debug_active(&SmHeapLimit, 1)
548 && sm_debug_level(&SmHeapLimit)
549 < SmHeapTotal - hi->hi_size + size)
554 newptr = realloc(ptr, MALLOC_SIZE(size));
558 SmHeapTotal = SmHeapTotal - hi->hi_size + size;
559 if (SmHeapTotal > SmHeapMaxTotal)
560 SmHeapMaxTotal = SmHeapTotal;
564 hp = &SmHeapTable[ptrhash(newptr)];
570 sm_abort("sm_realloc: bad argument (%p)", ptr);
572 return NULL; /* keep Irix compiler happy */
576 ** SM_REALLOC_X -- wrapper for realloc(), debugging version.
579 ** ptr -- pointer to old memory area.
580 ** size -- size of requested memory.
583 ** Pointer to new memory area.
586 ** F:sm_heap -- out of memory
590 sm_realloc_x(ptr, size)
595 SM_HEAP_ITEM_T *hi, **hp;
600 newptr = realloc(ptr, MALLOC_SIZE(size));
603 sm_exc_raise_x(&SmHeapOutOfMemory);
608 return sm_malloc_tagged_x(size, "realloc", 0, SmHeapGroup);
610 for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
612 if ((**hp).hi_ptr == ptr)
614 sm_xtrap_raise_x(&SmHeapOutOfMemory);
616 if (sm_debug_active(&SmHeapLimit, 1)
617 && sm_debug_level(&SmHeapLimit)
618 < SmHeapTotal - hi->hi_size + size)
620 sm_exc_raise_x(&SmHeapOutOfMemory);
623 newptr = realloc(ptr, MALLOC_SIZE(size));
626 sm_exc_raise_x(&SmHeapOutOfMemory);
627 SmHeapTotal = SmHeapTotal - hi->hi_size + size;
628 if (SmHeapTotal > SmHeapMaxTotal)
629 SmHeapMaxTotal = SmHeapTotal;
633 hp = &SmHeapTable[ptrhash(newptr)];
639 sm_abort("sm_realloc_x: bad argument (%p)", ptr);
641 return NULL; /* keep Irix compiler happy */
645 ** SM_FREE_TAGGED -- wrapper around free(), debugging version.
648 ** ptr -- pointer to memory region.
649 ** tag -- tag for debugging.
650 ** num -- additional value for debugging.
657 sm_free_tagged(ptr, tag, num)
673 for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
675 if ((**hp).hi_ptr == ptr)
677 SM_HEAP_ITEM_T *hi = *hp;
682 ** Fill the block with zeros before freeing.
683 ** This is intended to catch problems with
684 ** dangling pointers. The block is filled with
685 ** zeros, not with some non-zero value, because
686 ** it is common practice in some C code to store
687 ** a zero in a structure member before freeing the
688 ** structure, as a defense against dangling pointers.
691 (void) memset(ptr, 0, hi->hi_size);
692 SmHeapTotal -= hi->hi_size;
700 sm_abort("sm_free: bad argument (%p) (%s:%d)", ptr, tag, num);
704 ** SM_HEAP_CHECKPTR_TAGGED -- check whether ptr is a valid argument to sm_free
707 ** ptr -- pointer to memory region.
708 ** tag -- tag for debugging.
709 ** num -- additional value for debugging.
715 ** aborts if check fails.
719 sm_heap_checkptr_tagged(ptr, tag, num)
730 for (hp = SmHeapTable[ptrhash(ptr)]; hp != NULL; hp = hp->hi_next)
732 if (hp->hi_ptr == ptr)
735 sm_abort("sm_heap_checkptr(%p): bad ptr (%s:%d)", ptr, tag, num);
739 ** SM_HEAP_REPORT -- output "map" of used heap.
742 ** stream -- the file pointer to write to.
743 ** verbosity -- how much info?
750 sm_heap_report(stream, verbosity)
755 unsigned long group0total, group1total, otherstotal, grandtotal;
757 if (!HEAP_CHECK || verbosity <= 0)
759 group0total = group1total = otherstotal = grandtotal = 0;
760 for (i = 0; i < sizeof(SmHeapTable) / sizeof(SmHeapTable[0]); ++i)
762 SM_HEAP_ITEM_T *hi = SmHeapTable[i];
767 || (verbosity > 1 && hi->hi_group != 0))
769 sm_io_fprintf(stream, SM_TIME_DEFAULT,
770 "%4d %*lx %7lu bytes",
772 (int) sizeof(void *) * 2,
774 (unsigned long)hi->hi_size);
775 if (hi->hi_tag != NULL)
777 sm_io_fprintf(stream, SM_TIME_DEFAULT,
782 sm_io_fprintf(stream,
788 sm_io_fprintf(stream, SM_TIME_DEFAULT, "\n");
790 switch (hi->hi_group)
793 group0total += hi->hi_size;
796 group1total += hi->hi_size;
799 otherstotal += hi->hi_size;
802 grandtotal += hi->hi_size;
806 sm_io_fprintf(stream, SM_TIME_DEFAULT,
807 "heap max=%lu, total=%lu, ",
808 (unsigned long) SmHeapMaxTotal, grandtotal);
809 sm_io_fprintf(stream, SM_TIME_DEFAULT,
810 "group 0=%lu, group 1=%lu, others=%lu\n",
811 group0total, group1total, otherstotal);
812 if (grandtotal != SmHeapTotal)
814 sm_io_fprintf(stream, SM_TIME_DEFAULT,
815 "BUG => SmHeapTotal: got %lu, expected %lu\n",
816 (unsigned long) SmHeapTotal, grandtotal);
819 #endif /* !SM_HEAP_CHECK */