2 * Copyright (c) 2000-2001, 2004 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.
10 #pragma ident "%Z%%M% %I% %E% SMI"
13 SM_RCSID("@(#)$Id: heap.c,v 1.51 2004/08/03 20:32:00 ca Exp $")
16 ** debugging memory allocation package
17 ** See heap.html for documentation.
22 #include <sm/assert.h>
27 #include <sm/signal.h>
30 /* undef all macro versions of the "functions" so they can be specified here */
33 #undef sm_malloc_tagged
34 #undef sm_malloc_tagged_x
39 # undef sm_heap_register
40 # undef sm_heap_checkptr
41 # undef sm_heap_report
42 #endif /* SM_HEAP_CHECK */
45 SM_DEBUG_T SmHeapCheck
= SM_DEBUG_INITIALIZER("sm_check_heap",
46 "@(#)$Debug: sm_check_heap - check sm_malloc, sm_realloc, sm_free calls $");
47 # define HEAP_CHECK sm_debug_active(&SmHeapCheck, 1)
48 static int ptrhash
__P((void *p
));
49 #endif /* SM_HEAP_CHECK */
51 const SM_EXC_TYPE_T SmHeapOutOfMemoryType
=
60 SM_EXC_T SmHeapOutOfMemory
= SM_EXC_INITIALIZER(&SmHeapOutOfMemoryType
, NULL
);
64 ** The behaviour of malloc with size==0 is platform dependent (it
65 ** says so in the C standard): it can return NULL or non-NULL. We
66 ** don't want sm_malloc_x(0) to raise an exception on some platforms
67 ** but not others, so this case requires special handling. We've got
68 ** two choices: "size = 1" or "return NULL". We use the former in the
70 ** If we had something like autoconf we could figure out the
71 ** behaviour of the platform and either use this hack or just
75 #define MALLOC_SIZE(size) ((size) == 0 ? 1 : (size))
78 ** SM_MALLOC_X -- wrapper around malloc(), raises an exception on error.
81 ** size -- size of requested memory.
84 ** Pointer to memory region.
87 ** sm_malloc_x only gets called from source files in which heap
88 ** debugging is disabled at compile time. Otherwise, a call to
89 ** sm_malloc_x is macro expanded to a call to sm_malloc_tagged_x.
92 ** F:sm_heap -- out of memory
102 ptr
= malloc(MALLOC_SIZE(size
));
105 sm_exc_raise_x(&SmHeapOutOfMemory
);
112 ** SM_MALLOC -- wrapper around malloc()
115 ** size -- size of requested memory.
118 ** Pointer to memory region.
128 ptr
= malloc(MALLOC_SIZE(size
));
134 ** SM_REALLOC -- wrapper for realloc()
137 ** ptr -- pointer to old memory area.
138 ** size -- size of requested memory.
141 ** Pointer to new memory area, NULL on failure.
145 sm_realloc(ptr
, size
)
152 newptr
= realloc(ptr
, MALLOC_SIZE(size
));
158 ** SM_REALLOC_X -- wrapper for realloc()
161 ** ptr -- pointer to old memory area.
162 ** size -- size of requested memory.
165 ** Pointer to new memory area.
168 ** F:sm_heap -- out of memory
172 sm_realloc_x(ptr
, size
)
179 newptr
= realloc(ptr
, MALLOC_SIZE(size
));
182 sm_exc_raise_x(&SmHeapOutOfMemory
);
186 ** SM_FREE -- wrapper around free()
189 ** ptr -- pointer to memory region.
207 #else /* !SM_HEAP_CHECK */
210 ** Each allocated block is assigned a "group number".
211 ** By default, all blocks are assigned to group #1.
212 ** By convention, group #0 is for memory that is never freed.
213 ** You can use group numbers any way you want, in order to help make
214 ** sense of sm_heap_report output.
218 int SmHeapMaxGroup
= 1;
221 ** Total number of bytes allocated.
222 ** This is only maintained if the sm_check_heap debug category is active.
225 size_t SmHeapTotal
= 0;
228 ** High water mark: the most that SmHeapTotal has ever been.
231 size_t SmHeapMaxTotal
= 0;
234 ** Maximum number of bytes that may be allocated at any one time.
236 ** This is only honoured if sm_check_heap is active.
239 SM_DEBUG_T SmHeapLimit
= SM_DEBUG_INITIALIZER("sm_heap_limit",
240 "@(#)$Debug: sm_heap_limit - max # of bytes permitted in heap $");
243 ** This is the data structure that keeps track of all currently
244 ** allocated blocks of memory known to the heap package.
247 typedef struct sm_heap_item SM_HEAP_ITEM_T
;
255 SM_HEAP_ITEM_T
*hi_next
;
258 #define SM_HEAP_TABLE_SIZE 256
259 static SM_HEAP_ITEM_T
*SmHeapTable
[SM_HEAP_TABLE_SIZE
];
262 ** This is a randomly generated table
263 ** which contains exactly one occurrence
264 ** of each of the numbers between 0 and 255.
265 ** It is used by ptrhash.
268 static unsigned char hashtab
[SM_HEAP_TABLE_SIZE
] =
270 161, 71, 77,187, 15,229, 9,176,221,119,239, 21, 85,138,203, 86,
271 102, 65, 80,199,235, 32,140, 96,224, 78,126,127,144, 0, 11,179,
272 64, 30,120, 23,225,226, 33, 50,205,167,130,240,174, 99,206, 73,
273 231,210,189,162, 48, 93,246, 54,213,141,135, 39, 41,192,236,193,
274 157, 88, 95,104,188, 63,133,177,234,110,158,214,238,131,233, 91,
275 125, 82, 94, 79, 66, 92,151, 45,252, 98, 26,183, 7,191,171,106,
276 145,154,251,100,113, 5, 74, 62, 76,124, 14,217,200, 75,115,190,
277 103, 28,198,196,169,219, 37,118,150, 18,152,175, 49,136, 6,142,
278 89, 19,243,254, 47,137, 24,166,180, 10, 40,186,202, 46,184, 67,
279 148,108,181, 81, 25,241, 13,139, 58, 38, 84,253,201, 12,116, 17,
280 195, 22,112, 69,255, 43,147,222,111, 56,194,216,149,244, 42,173,
281 232,220,249,105,207, 51,197,242, 72,211,208, 59,122,230,237,170,
282 165, 44, 68,123,129,245,143,101, 8,209,215,247,185, 57,218, 53,
283 114,121, 3,128, 4,204,212,146, 2,155, 83,250, 87, 29, 31,159,
284 60, 27,107,156,227,182, 1, 61, 36,160,109, 97, 90, 20,168,132,
285 223,248, 70,164, 55,172, 34, 52,163,117, 35,153,134, 16,178,228
289 ** PTRHASH -- hash a pointer value
297 ** ptrhash hashes a pointer value to a uniformly distributed random
298 ** number between 0 and 255.
300 ** This hash algorithm is based on Peter K. Pearson,
301 ** "Fast Hashing of Variable-Length Text Strings",
302 ** in Communications of the ACM, June 1990, vol 33 no 6.
311 if (sizeof(void*) == 4 && sizeof(unsigned long) == 4)
313 unsigned long n
= (unsigned long)p
;
315 h
= hashtab
[n
& 0xFF];
316 h
= hashtab
[h
^ ((n
>> 8) & 0xFF)];
317 h
= hashtab
[h
^ ((n
>> 16) & 0xFF)];
318 h
= hashtab
[h
^ ((n
>> 24) & 0xFF)];
321 else if (sizeof(void*) == 8 && sizeof(unsigned long) == 8)
323 unsigned long n
= (unsigned long)p
;
325 h
= hashtab
[n
& 0xFF];
326 h
= hashtab
[h
^ ((n
>> 8) & 0xFF)];
327 h
= hashtab
[h
^ ((n
>> 16) & 0xFF)];
328 h
= hashtab
[h
^ ((n
>> 24) & 0xFF)];
329 h
= hashtab
[h
^ ((n
>> 32) & 0xFF)];
330 h
= hashtab
[h
^ ((n
>> 40) & 0xFF)];
331 h
= hashtab
[h
^ ((n
>> 48) & 0xFF)];
332 h
= hashtab
[h
^ ((n
>> 56) & 0xFF)];
337 unsigned char *cp
= (unsigned char *)&p
;
341 for (i
= 0; i
< sizeof(void*); ++i
)
342 h
= hashtab
[h
^ cp
[i
]];
348 ** SM_MALLOC_TAGGED -- wrapper around malloc(), debugging version.
351 ** size -- size of requested memory.
352 ** tag -- tag for debugging.
353 ** num -- additional value for debugging.
354 ** group -- heap group for debugging.
357 ** Pointer to memory region.
361 sm_malloc_tagged(size
, tag
, num
, group
)
372 ptr
= malloc(MALLOC_SIZE(size
));
377 if (sm_xtrap_check())
379 if (sm_debug_active(&SmHeapLimit
, 1)
380 && sm_debug_level(&SmHeapLimit
) < SmHeapTotal
+ size
)
383 ptr
= malloc(MALLOC_SIZE(size
));
385 if (ptr
!= NULL
&& !sm_heap_register(ptr
, size
, tag
, num
, group
))
393 if (SmHeapTotal
> SmHeapMaxTotal
)
394 SmHeapMaxTotal
= SmHeapTotal
;
399 ** SM_MALLOC_TAGGED_X -- wrapper around malloc(), debugging version.
402 ** size -- size of requested memory.
403 ** tag -- tag for debugging.
404 ** num -- additional value for debugging.
405 ** group -- heap group for debugging.
408 ** Pointer to memory region.
411 ** F:sm_heap -- out of memory
415 sm_malloc_tagged_x(size
, tag
, num
, group
)
426 ptr
= malloc(MALLOC_SIZE(size
));
429 sm_exc_raise_x(&SmHeapOutOfMemory
);
433 sm_xtrap_raise_x(&SmHeapOutOfMemory
);
434 if (sm_debug_active(&SmHeapLimit
, 1)
435 && sm_debug_level(&SmHeapLimit
) < SmHeapTotal
+ size
)
437 sm_exc_raise_x(&SmHeapOutOfMemory
);
440 ptr
= malloc(MALLOC_SIZE(size
));
442 if (ptr
!= NULL
&& !sm_heap_register(ptr
, size
, tag
, num
, group
))
450 sm_exc_raise_x(&SmHeapOutOfMemory
);
452 if (SmHeapTotal
> SmHeapMaxTotal
)
453 SmHeapMaxTotal
= SmHeapTotal
;
458 ** SM_HEAP_REGISTER -- register a pointer into the heap for debugging.
461 ** ptr -- pointer to register.
462 ** size -- size of requested memory.
463 ** tag -- tag for debugging.
464 ** num -- additional value for debugging.
465 ** group -- heap group for debugging.
468 ** true iff successfully registered (not yet in table).
472 sm_heap_register(ptr
, size
, tag
, num
, group
)
484 SM_REQUIRE(ptr
!= NULL
);
486 # if SM_CHECK_REQUIRE
489 ** We require that ptr is not already in SmHeapTable.
492 for (hi
= SmHeapTable
[i
]; hi
!= NULL
; hi
= hi
->hi_next
)
494 if (hi
->hi_ptr
== ptr
)
495 sm_abort("sm_heap_register: ptr %p is already registered (%s:%d)",
496 ptr
, hi
->hi_tag
, hi
->hi_num
);
498 # endif /* SM_CHECK_REQUIRE */
500 hi
= (SM_HEAP_ITEM_T
*) malloc(sizeof(SM_HEAP_ITEM_T
));
508 hi
->hi_group
= group
;
509 hi
->hi_next
= SmHeapTable
[i
];
514 ** SM_REALLOC -- wrapper for realloc(), debugging version.
517 ** ptr -- pointer to old memory area.
518 ** size -- size of requested memory.
521 ** Pointer to new memory area, NULL on failure.
525 sm_realloc(ptr
, size
)
530 SM_HEAP_ITEM_T
*hi
, **hp
;
535 newptr
= realloc(ptr
, MALLOC_SIZE(size
));
541 return sm_malloc_tagged(size
, "realloc", 0, SmHeapGroup
);
543 for (hp
= &SmHeapTable
[ptrhash(ptr
)]; *hp
!= NULL
; hp
= &(**hp
).hi_next
)
545 if ((**hp
).hi_ptr
== ptr
)
547 if (sm_xtrap_check())
550 if (sm_debug_active(&SmHeapLimit
, 1)
551 && sm_debug_level(&SmHeapLimit
)
552 < SmHeapTotal
- hi
->hi_size
+ size
)
557 newptr
= realloc(ptr
, MALLOC_SIZE(size
));
561 SmHeapTotal
= SmHeapTotal
- hi
->hi_size
+ size
;
562 if (SmHeapTotal
> SmHeapMaxTotal
)
563 SmHeapMaxTotal
= SmHeapTotal
;
567 hp
= &SmHeapTable
[ptrhash(newptr
)];
573 sm_abort("sm_realloc: bad argument (%p)", ptr
);
575 return NULL
; /* keep Irix compiler happy */
579 ** SM_REALLOC_X -- wrapper for realloc(), debugging version.
582 ** ptr -- pointer to old memory area.
583 ** size -- size of requested memory.
586 ** Pointer to new memory area.
589 ** F:sm_heap -- out of memory
593 sm_realloc_x(ptr
, size
)
598 SM_HEAP_ITEM_T
*hi
, **hp
;
603 newptr
= realloc(ptr
, MALLOC_SIZE(size
));
606 sm_exc_raise_x(&SmHeapOutOfMemory
);
611 return sm_malloc_tagged_x(size
, "realloc", 0, SmHeapGroup
);
613 for (hp
= &SmHeapTable
[ptrhash(ptr
)]; *hp
!= NULL
; hp
= &(**hp
).hi_next
)
615 if ((**hp
).hi_ptr
== ptr
)
617 sm_xtrap_raise_x(&SmHeapOutOfMemory
);
619 if (sm_debug_active(&SmHeapLimit
, 1)
620 && sm_debug_level(&SmHeapLimit
)
621 < SmHeapTotal
- hi
->hi_size
+ size
)
623 sm_exc_raise_x(&SmHeapOutOfMemory
);
626 newptr
= realloc(ptr
, MALLOC_SIZE(size
));
629 sm_exc_raise_x(&SmHeapOutOfMemory
);
630 SmHeapTotal
= SmHeapTotal
- hi
->hi_size
+ size
;
631 if (SmHeapTotal
> SmHeapMaxTotal
)
632 SmHeapMaxTotal
= SmHeapTotal
;
636 hp
= &SmHeapTable
[ptrhash(newptr
)];
642 sm_abort("sm_realloc_x: bad argument (%p)", ptr
);
644 return NULL
; /* keep Irix compiler happy */
648 ** SM_FREE_TAGGED -- wrapper around free(), debugging version.
651 ** ptr -- pointer to memory region.
652 ** tag -- tag for debugging.
653 ** num -- additional value for debugging.
660 sm_free_tagged(ptr
, tag
, num
)
676 for (hp
= &SmHeapTable
[ptrhash(ptr
)]; *hp
!= NULL
; hp
= &(**hp
).hi_next
)
678 if ((**hp
).hi_ptr
== ptr
)
680 SM_HEAP_ITEM_T
*hi
= *hp
;
685 ** Fill the block with zeros before freeing.
686 ** This is intended to catch problems with
687 ** dangling pointers. The block is filled with
688 ** zeros, not with some non-zero value, because
689 ** it is common practice in some C code to store
690 ** a zero in a structure member before freeing the
691 ** structure, as a defense against dangling pointers.
694 (void) memset(ptr
, 0, hi
->hi_size
);
695 SmHeapTotal
-= hi
->hi_size
;
703 sm_abort("sm_free: bad argument (%p) (%s:%d)", ptr
, tag
, num
);
707 ** SM_HEAP_CHECKPTR_TAGGED -- check whether ptr is a valid argument to sm_free
710 ** ptr -- pointer to memory region.
711 ** tag -- tag for debugging.
712 ** num -- additional value for debugging.
718 ** aborts if check fails.
722 sm_heap_checkptr_tagged(ptr
, tag
, num
)
733 for (hp
= SmHeapTable
[ptrhash(ptr
)]; hp
!= NULL
; hp
= hp
->hi_next
)
735 if (hp
->hi_ptr
== ptr
)
738 sm_abort("sm_heap_checkptr(%p): bad ptr (%s:%d)", ptr
, tag
, num
);
742 ** SM_HEAP_REPORT -- output "map" of used heap.
745 ** stream -- the file pointer to write to.
746 ** verbosity -- how much info?
753 sm_heap_report(stream
, verbosity
)
758 unsigned long group0total
, group1total
, otherstotal
, grandtotal
;
760 if (!HEAP_CHECK
|| verbosity
<= 0)
762 group0total
= group1total
= otherstotal
= grandtotal
= 0;
763 for (i
= 0; i
< sizeof(SmHeapTable
) / sizeof(SmHeapTable
[0]); ++i
)
765 SM_HEAP_ITEM_T
*hi
= SmHeapTable
[i
];
770 || (verbosity
> 1 && hi
->hi_group
!= 0))
772 sm_io_fprintf(stream
, SM_TIME_DEFAULT
,
773 "%4d %*lx %7lu bytes",
775 (int) sizeof(void *) * 2,
777 (unsigned long)hi
->hi_size
);
778 if (hi
->hi_tag
!= NULL
)
780 sm_io_fprintf(stream
, SM_TIME_DEFAULT
,
785 sm_io_fprintf(stream
,
791 sm_io_fprintf(stream
, SM_TIME_DEFAULT
, "\n");
793 switch (hi
->hi_group
)
796 group0total
+= hi
->hi_size
;
799 group1total
+= hi
->hi_size
;
802 otherstotal
+= hi
->hi_size
;
805 grandtotal
+= hi
->hi_size
;
809 sm_io_fprintf(stream
, SM_TIME_DEFAULT
,
810 "heap max=%lu, total=%lu, ",
811 (unsigned long) SmHeapMaxTotal
, grandtotal
);
812 sm_io_fprintf(stream
, SM_TIME_DEFAULT
,
813 "group 0=%lu, group 1=%lu, others=%lu\n",
814 group0total
, group1total
, otherstotal
);
815 if (grandtotal
!= SmHeapTotal
)
817 sm_io_fprintf(stream
, SM_TIME_DEFAULT
,
818 "BUG => SmHeapTotal: got %lu, expected %lu\n",
819 (unsigned long) SmHeapTotal
, grandtotal
);
822 #endif /* !SM_HEAP_CHECK */