1 /*
2 * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
3 * All rights reserved.
4 *
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.
8 */
9
10 #include <sm/gen.h>
11 SM_RCSID("@(#)$Id: heap.c,v 1.51 2004/08/03 20:32:00 ca Exp $")
12
13 /*
14 ** debugging memory allocation package
15 ** See heap.html for documentation.
16 */
17
18 #include <string.h>
19
20 #include <sm/assert.h>
21 #include <sm/debug.h>
22 #include <sm/exc.h>
23 #include <sm/heap.h>
24 #include <sm/io.h>
25 #include <sm/signal.h>
26 #include <sm/xtrap.h>
27
28 /* undef all macro versions of the "functions" so they can be specified here */
29 #undef sm_malloc
30 #undef sm_malloc_x
31 #undef sm_malloc_tagged
32 #undef sm_malloc_tagged_x
33 #undef sm_free
34 #undef sm_free_tagged
35 #undef sm_realloc
36 #if SM_HEAP_CHECK
37 # undef sm_heap_register
38 # undef sm_heap_checkptr
39 # undef sm_heap_report
40 #endif /* SM_HEAP_CHECK */
41
42 #if 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 static int ptrhash __P((void *p));
47 #endif /* SM_HEAP_CHECK */
48
49 const SM_EXC_TYPE_T SmHeapOutOfMemoryType =
50 {
51 SmExcTypeMagic,
52 "F:sm.heap",
53 "",
54 sm_etype_printf,
55 "out of memory",
56 };
57
58 SM_EXC_T SmHeapOutOfMemory = SM_EXC_INITIALIZER(&SmHeapOutOfMemoryType, NULL);
59
60
61 /*
62 ** The behaviour of malloc with size==0 is platform dependent (it
63 ** says so in the C standard): it can return NULL or non-NULL. We
64 ** don't want sm_malloc_x(0) to raise an exception on some platforms
65 ** but not others, so this case requires special handling. We've got
66 ** two choices: "size = 1" or "return NULL". We use the former in the
67 ** following.
68 ** If we had something like autoconf we could figure out the
69 ** behaviour of the platform and either use this hack or just
70 ** use size.
71 */
72
73 #define MALLOC_SIZE(size) ((size) == 0 ? 1 : (size))
74
75 /*
76 ** SM_MALLOC_X -- wrapper around malloc(), raises an exception on error.
77 **
78 ** Parameters:
79 ** size -- size of requested memory.
80 **
81 ** Returns:
82 ** Pointer to memory region.
83 **
84 ** Note:
85 ** sm_malloc_x only gets called from source files in which heap
86 ** debugging is disabled at compile time. Otherwise, a call to
87 ** sm_malloc_x is macro expanded to a call to sm_malloc_tagged_x.
88 **
89 ** Exceptions:
90 ** F:sm_heap -- out of memory
91 */
92
93 void *
sm_malloc_x(size)94 sm_malloc_x(size)
95 size_t size;
96 {
97 void *ptr;
98
99 ENTER_CRITICAL();
100 ptr = malloc(MALLOC_SIZE(size));
101 LEAVE_CRITICAL();
102 if (ptr == NULL)
103 sm_exc_raise_x(&SmHeapOutOfMemory);
104 return ptr;
105 }
106
107 #if !SM_HEAP_CHECK
108
109 /*
110 ** SM_MALLOC -- wrapper around malloc()
111 **
112 ** Parameters:
113 ** size -- size of requested memory.
114 **
115 ** Returns:
116 ** Pointer to memory region.
117 */
118
119 void *
sm_malloc(size)120 sm_malloc(size)
121 size_t size;
122 {
123 void *ptr;
124
125 ENTER_CRITICAL();
126 ptr = malloc(MALLOC_SIZE(size));
127 LEAVE_CRITICAL();
128 return ptr;
129 }
130
131 /*
132 ** SM_REALLOC -- wrapper for realloc()
133 **
134 ** Parameters:
135 ** ptr -- pointer to old memory area.
136 ** size -- size of requested memory.
137 **
138 ** Returns:
139 ** Pointer to new memory area, NULL on failure.
140 */
141
142 void *
sm_realloc(ptr,size)143 sm_realloc(ptr, size)
144 void *ptr;
145 size_t size;
146 {
147 void *newptr;
148
149 ENTER_CRITICAL();
150 newptr = realloc(ptr, MALLOC_SIZE(size));
151 LEAVE_CRITICAL();
152 return newptr;
153 }
154
155 /*
156 ** SM_REALLOC_X -- wrapper for realloc()
157 **
158 ** Parameters:
159 ** ptr -- pointer to old memory area.
160 ** size -- size of requested memory.
161 **
162 ** Returns:
163 ** Pointer to new memory area.
164 **
165 ** Exceptions:
166 ** F:sm_heap -- out of memory
167 */
168
169 void *
sm_realloc_x(ptr,size)170 sm_realloc_x(ptr, size)
171 void *ptr;
172 size_t size;
173 {
174 void *newptr;
175
176 ENTER_CRITICAL();
177 newptr = realloc(ptr, MALLOC_SIZE(size));
178 LEAVE_CRITICAL();
179 if (newptr == NULL)
180 sm_exc_raise_x(&SmHeapOutOfMemory);
181 return newptr;
182 }
183 /*
184 ** SM_FREE -- wrapper around free()
185 **
186 ** Parameters:
187 ** ptr -- pointer to memory region.
188 **
189 ** Returns:
190 ** none.
191 */
192
193 void
sm_free(ptr)194 sm_free(ptr)
195 void *ptr;
196 {
197 if (ptr == NULL)
198 return;
199 ENTER_CRITICAL();
200 free(ptr);
201 LEAVE_CRITICAL();
202 return;
203 }
204
205 #else /* !SM_HEAP_CHECK */
206
207 /*
208 ** Each allocated block is assigned a "group number".
209 ** By default, all blocks are assigned to group #1.
210 ** By convention, group #0 is for memory that is never freed.
211 ** You can use group numbers any way you want, in order to help make
212 ** sense of sm_heap_report output.
213 */
214
215 int SmHeapGroup = 1;
216 int SmHeapMaxGroup = 1;
217
218 /*
219 ** Total number of bytes allocated.
220 ** This is only maintained if the sm_check_heap debug category is active.
221 */
222
223 size_t SmHeapTotal = 0;
224
225 /*
226 ** High water mark: the most that SmHeapTotal has ever been.
227 */
228
229 size_t SmHeapMaxTotal = 0;
230
231 /*
232 ** Maximum number of bytes that may be allocated at any one time.
233 ** 0 means no limit.
234 ** This is only honoured if sm_check_heap is active.
235 */
236
237 SM_DEBUG_T SmHeapLimit = SM_DEBUG_INITIALIZER("sm_heap_limit",
238 "@(#)$Debug: sm_heap_limit - max # of bytes permitted in heap $");
239
240 /*
241 ** This is the data structure that keeps track of all currently
242 ** allocated blocks of memory known to the heap package.
243 */
244
245 typedef struct sm_heap_item SM_HEAP_ITEM_T;
246 struct sm_heap_item
247 {
248 void *hi_ptr;
249 size_t hi_size;
250 char *hi_tag;
251 int hi_num;
252 int hi_group;
253 SM_HEAP_ITEM_T *hi_next;
254 };
255
256 #define SM_HEAP_TABLE_SIZE 256
257 static SM_HEAP_ITEM_T *SmHeapTable[SM_HEAP_TABLE_SIZE];
258
259 /*
260 ** This is a randomly generated table
261 ** which contains exactly one occurrence
262 ** of each of the numbers between 0 and 255.
263 ** It is used by ptrhash.
264 */
265
266 static unsigned char hashtab[SM_HEAP_TABLE_SIZE] =
267 {
268 161, 71, 77,187, 15,229, 9,176,221,119,239, 21, 85,138,203, 86,
269 102, 65, 80,199,235, 32,140, 96,224, 78,126,127,144, 0, 11,179,
270 64, 30,120, 23,225,226, 33, 50,205,167,130,240,174, 99,206, 73,
271 231,210,189,162, 48, 93,246, 54,213,141,135, 39, 41,192,236,193,
272 157, 88, 95,104,188, 63,133,177,234,110,158,214,238,131,233, 91,
273 125, 82, 94, 79, 66, 92,151, 45,252, 98, 26,183, 7,191,171,106,
274 145,154,251,100,113, 5, 74, 62, 76,124, 14,217,200, 75,115,190,
275 103, 28,198,196,169,219, 37,118,150, 18,152,175, 49,136, 6,142,
276 89, 19,243,254, 47,137, 24,166,180, 10, 40,186,202, 46,184, 67,
277 148,108,181, 81, 25,241, 13,139, 58, 38, 84,253,201, 12,116, 17,
278 195, 22,112, 69,255, 43,147,222,111, 56,194,216,149,244, 42,173,
279 232,220,249,105,207, 51,197,242, 72,211,208, 59,122,230,237,170,
280 165, 44, 68,123,129,245,143,101, 8,209,215,247,185, 57,218, 53,
281 114,121, 3,128, 4,204,212,146, 2,155, 83,250, 87, 29, 31,159,
282 60, 27,107,156,227,182, 1, 61, 36,160,109, 97, 90, 20,168,132,
283 223,248, 70,164, 55,172, 34, 52,163,117, 35,153,134, 16,178,228
284 };
285
286 /*
287 ** PTRHASH -- hash a pointer value
288 **
289 ** Parameters:
290 ** p -- pointer.
291 **
292 ** Returns:
293 ** hash value.
294 **
295 ** ptrhash hashes a pointer value to a uniformly distributed random
296 ** number between 0 and 255.
297 **
298 ** This hash algorithm is based on Peter K. Pearson,
299 ** "Fast Hashing of Variable-Length Text Strings",
300 ** in Communications of the ACM, June 1990, vol 33 no 6.
301 */
302
303 static int
ptrhash(p)304 ptrhash(p)
305 void *p;
306 {
307 int h;
308
309 if (sizeof(void*) == 4 && sizeof(unsigned long) == 4)
310 {
311 unsigned long n = (unsigned long)p;
312
313 h = hashtab[n & 0xFF];
314 h = hashtab[h ^ ((n >> 8) & 0xFF)];
315 h = hashtab[h ^ ((n >> 16) & 0xFF)];
316 h = hashtab[h ^ ((n >> 24) & 0xFF)];
317 }
318 # if 0
319 else if (sizeof(void*) == 8 && sizeof(unsigned long) == 8)
320 {
321 unsigned long n = (unsigned long)p;
322
323 h = hashtab[n & 0xFF];
324 h = hashtab[h ^ ((n >> 8) & 0xFF)];
325 h = hashtab[h ^ ((n >> 16) & 0xFF)];
326 h = hashtab[h ^ ((n >> 24) & 0xFF)];
327 h = hashtab[h ^ ((n >> 32) & 0xFF)];
328 h = hashtab[h ^ ((n >> 40) & 0xFF)];
329 h = hashtab[h ^ ((n >> 48) & 0xFF)];
330 h = hashtab[h ^ ((n >> 56) & 0xFF)];
331 }
332 # endif /* 0 */
333 else
334 {
335 unsigned char *cp = (unsigned char *)&p;
336 int i;
337
338 h = 0;
339 for (i = 0; i < sizeof(void*); ++i)
340 h = hashtab[h ^ cp[i]];
341 }
342 return h;
343 }
344
345 /*
346 ** SM_MALLOC_TAGGED -- wrapper around malloc(), debugging version.
347 **
348 ** Parameters:
349 ** size -- size of requested memory.
350 ** tag -- tag for debugging.
351 ** num -- additional value for debugging.
352 ** group -- heap group for debugging.
353 **
354 ** Returns:
355 ** Pointer to memory region.
356 */
357
358 void *
sm_malloc_tagged(size,tag,num,group)359 sm_malloc_tagged(size, tag, num, group)
360 size_t size;
361 char *tag;
362 int num;
363 int group;
364 {
365 void *ptr;
366
367 if (!HEAP_CHECK)
368 {
369 ENTER_CRITICAL();
370 ptr = malloc(MALLOC_SIZE(size));
371 LEAVE_CRITICAL();
372 return ptr;
373 }
374
375 if (sm_xtrap_check())
376 return NULL;
377 if (sm_debug_active(&SmHeapLimit, 1)
378 && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size)
379 return NULL;
380 ENTER_CRITICAL();
381 ptr = malloc(MALLOC_SIZE(size));
382 LEAVE_CRITICAL();
383 if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group))
384 {
385 ENTER_CRITICAL();
386 free(ptr);
387 LEAVE_CRITICAL();
388 ptr = NULL;
389 }
390 SmHeapTotal += size;
391 if (SmHeapTotal > SmHeapMaxTotal)
392 SmHeapMaxTotal = SmHeapTotal;
393 return ptr;
394 }
395
396 /*
397 ** SM_MALLOC_TAGGED_X -- wrapper around malloc(), debugging version.
398 **
399 ** Parameters:
400 ** size -- size of requested memory.
401 ** tag -- tag for debugging.
402 ** num -- additional value for debugging.
403 ** group -- heap group for debugging.
404 **
405 ** Returns:
406 ** Pointer to memory region.
407 **
408 ** Exceptions:
409 ** F:sm_heap -- out of memory
410 */
411
412 void *
sm_malloc_tagged_x(size,tag,num,group)413 sm_malloc_tagged_x(size, tag, num, group)
414 size_t size;
415 char *tag;
416 int num;
417 int group;
418 {
419 void *ptr;
420
421 if (!HEAP_CHECK)
422 {
423 ENTER_CRITICAL();
424 ptr = malloc(MALLOC_SIZE(size));
425 LEAVE_CRITICAL();
426 if (ptr == NULL)
427 sm_exc_raise_x(&SmHeapOutOfMemory);
428 return ptr;
429 }
430
431 sm_xtrap_raise_x(&SmHeapOutOfMemory);
432 if (sm_debug_active(&SmHeapLimit, 1)
433 && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size)
434 {
435 sm_exc_raise_x(&SmHeapOutOfMemory);
436 }
437 ENTER_CRITICAL();
438 ptr = malloc(MALLOC_SIZE(size));
439 LEAVE_CRITICAL();
440 if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group))
441 {
442 ENTER_CRITICAL();
443 free(ptr);
444 LEAVE_CRITICAL();
445 ptr = NULL;
446 }
447 if (ptr == NULL)
448 sm_exc_raise_x(&SmHeapOutOfMemory);
449 SmHeapTotal += size;
450 if (SmHeapTotal > SmHeapMaxTotal)
451 SmHeapMaxTotal = SmHeapTotal;
452 return ptr;
453 }
454
455 /*
456 ** SM_HEAP_REGISTER -- register a pointer into the heap for debugging.
457 **
458 ** Parameters:
459 ** ptr -- pointer to register.
460 ** size -- size of requested memory.
461 ** tag -- tag for debugging.
462 ** num -- additional value for debugging.
463 ** group -- heap group for debugging.
464 **
465 ** Returns:
466 ** true iff successfully registered (not yet in table).
467 */
468
469 bool
sm_heap_register(ptr,size,tag,num,group)470 sm_heap_register(ptr, size, tag, num, group)
471 void *ptr;
472 size_t size;
473 char *tag;
474 int num;
475 int group;
476 {
477 int i;
478 SM_HEAP_ITEM_T *hi;
479
480 if (!HEAP_CHECK)
481 return true;
482 SM_REQUIRE(ptr != NULL);
483 i = ptrhash(ptr);
484 # if SM_CHECK_REQUIRE
485
486 /*
487 ** We require that ptr is not already in SmHeapTable.
488 */
489
490 for (hi = SmHeapTable[i]; hi != NULL; hi = hi->hi_next)
491 {
492 if (hi->hi_ptr == ptr)
493 sm_abort("sm_heap_register: ptr %p is already registered (%s:%d)",
494 ptr, hi->hi_tag, hi->hi_num);
495 }
496 # endif /* SM_CHECK_REQUIRE */
497 ENTER_CRITICAL();
498 hi = (SM_HEAP_ITEM_T *) malloc(sizeof(SM_HEAP_ITEM_T));
499 LEAVE_CRITICAL();
500 if (hi == NULL)
501 return false;
502 hi->hi_ptr = ptr;
503 hi->hi_size = size;
504 hi->hi_tag = tag;
505 hi->hi_num = num;
506 hi->hi_group = group;
507 hi->hi_next = SmHeapTable[i];
508 SmHeapTable[i] = hi;
509 return true;
510 }
511 /*
512 ** SM_REALLOC -- wrapper for realloc(), debugging version.
513 **
514 ** Parameters:
515 ** ptr -- pointer to old memory area.
516 ** size -- size of requested memory.
517 **
518 ** Returns:
519 ** Pointer to new memory area, NULL on failure.
520 */
521
522 void *
sm_realloc(ptr,size)523 sm_realloc(ptr, size)
524 void *ptr;
525 size_t size;
526 {
527 void *newptr;
528 SM_HEAP_ITEM_T *hi, **hp;
529
530 if (!HEAP_CHECK)
531 {
532 ENTER_CRITICAL();
533 newptr = realloc(ptr, MALLOC_SIZE(size));
534 LEAVE_CRITICAL();
535 return newptr;
536 }
537
538 if (ptr == NULL)
539 return sm_malloc_tagged(size, "realloc", 0, SmHeapGroup);
540
541 for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
542 {
543 if ((**hp).hi_ptr == ptr)
544 {
545 if (sm_xtrap_check())
546 return NULL;
547 hi = *hp;
548 if (sm_debug_active(&SmHeapLimit, 1)
549 && sm_debug_level(&SmHeapLimit)
550 < SmHeapTotal - hi->hi_size + size)
551 {
552 return NULL;
553 }
554 ENTER_CRITICAL();
555 newptr = realloc(ptr, MALLOC_SIZE(size));
556 LEAVE_CRITICAL();
557 if (newptr == NULL)
558 return NULL;
559 SmHeapTotal = SmHeapTotal - hi->hi_size + size;
560 if (SmHeapTotal > SmHeapMaxTotal)
561 SmHeapMaxTotal = SmHeapTotal;
562 *hp = hi->hi_next;
563 hi->hi_ptr = newptr;
564 hi->hi_size = size;
565 hp = &SmHeapTable[ptrhash(newptr)];
566 hi->hi_next = *hp;
567 *hp = hi;
568 return newptr;
569 }
570 }
571 sm_abort("sm_realloc: bad argument (%p)", ptr);
572 /* NOTREACHED */
573 return NULL; /* keep Irix compiler happy */
574 }
575
576 /*
577 ** SM_REALLOC_X -- wrapper for realloc(), debugging version.
578 **
579 ** Parameters:
580 ** ptr -- pointer to old memory area.
581 ** size -- size of requested memory.
582 **
583 ** Returns:
584 ** Pointer to new memory area.
585 **
586 ** Exceptions:
587 ** F:sm_heap -- out of memory
588 */
589
590 void *
sm_realloc_x(ptr,size)591 sm_realloc_x(ptr, size)
592 void *ptr;
593 size_t size;
594 {
595 void *newptr;
596 SM_HEAP_ITEM_T *hi, **hp;
597
598 if (!HEAP_CHECK)
599 {
600 ENTER_CRITICAL();
601 newptr = realloc(ptr, MALLOC_SIZE(size));
602 LEAVE_CRITICAL();
603 if (newptr == NULL)
604 sm_exc_raise_x(&SmHeapOutOfMemory);
605 return newptr;
606 }
607
608 if (ptr == NULL)
609 return sm_malloc_tagged_x(size, "realloc", 0, SmHeapGroup);
610
611 for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
612 {
613 if ((**hp).hi_ptr == ptr)
614 {
615 sm_xtrap_raise_x(&SmHeapOutOfMemory);
616 hi = *hp;
617 if (sm_debug_active(&SmHeapLimit, 1)
618 && sm_debug_level(&SmHeapLimit)
619 < SmHeapTotal - hi->hi_size + size)
620 {
621 sm_exc_raise_x(&SmHeapOutOfMemory);
622 }
623 ENTER_CRITICAL();
624 newptr = realloc(ptr, MALLOC_SIZE(size));
625 LEAVE_CRITICAL();
626 if (newptr == NULL)
627 sm_exc_raise_x(&SmHeapOutOfMemory);
628 SmHeapTotal = SmHeapTotal - hi->hi_size + size;
629 if (SmHeapTotal > SmHeapMaxTotal)
630 SmHeapMaxTotal = SmHeapTotal;
631 *hp = hi->hi_next;
632 hi->hi_ptr = newptr;
633 hi->hi_size = size;
634 hp = &SmHeapTable[ptrhash(newptr)];
635 hi->hi_next = *hp;
636 *hp = hi;
637 return newptr;
638 }
639 }
640 sm_abort("sm_realloc_x: bad argument (%p)", ptr);
641 /* NOTREACHED */
642 return NULL; /* keep Irix compiler happy */
643 }
644
645 /*
646 ** SM_FREE_TAGGED -- wrapper around free(), debugging version.
647 **
648 ** Parameters:
649 ** ptr -- pointer to memory region.
650 ** tag -- tag for debugging.
651 ** num -- additional value for debugging.
652 **
653 ** Returns:
654 ** none.
655 */
656
657 void
sm_free_tagged(ptr,tag,num)658 sm_free_tagged(ptr, tag, num)
659 void *ptr;
660 char *tag;
661 int num;
662 {
663 SM_HEAP_ITEM_T **hp;
664
665 if (ptr == NULL)
666 return;
667 if (!HEAP_CHECK)
668 {
669 ENTER_CRITICAL();
670 free(ptr);
671 LEAVE_CRITICAL();
672 return;
673 }
674 for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
675 {
676 if ((**hp).hi_ptr == ptr)
677 {
678 SM_HEAP_ITEM_T *hi = *hp;
679
680 *hp = hi->hi_next;
681
682 /*
683 ** Fill the block with zeros before freeing.
684 ** This is intended to catch problems with
685 ** dangling pointers. The block is filled with
686 ** zeros, not with some non-zero value, because
687 ** it is common practice in some C code to store
688 ** a zero in a structure member before freeing the
689 ** structure, as a defense against dangling pointers.
690 */
691
692 (void) memset(ptr, 0, hi->hi_size);
693 SmHeapTotal -= hi->hi_size;
694 ENTER_CRITICAL();
695 free(ptr);
696 free(hi);
697 LEAVE_CRITICAL();
698 return;
699 }
700 }
701 sm_abort("sm_free: bad argument (%p) (%s:%d)", ptr, tag, num);
702 }
703
704 /*
705 ** SM_HEAP_CHECKPTR_TAGGED -- check whether ptr is a valid argument to sm_free
706 **
707 ** Parameters:
708 ** ptr -- pointer to memory region.
709 ** tag -- tag for debugging.
710 ** num -- additional value for debugging.
711 **
712 ** Returns:
713 ** none.
714 **
715 ** Side Effects:
716 ** aborts if check fails.
717 */
718
719 void
sm_heap_checkptr_tagged(ptr,tag,num)720 sm_heap_checkptr_tagged(ptr, tag, num)
721 void *ptr;
722 char *tag;
723 int num;
724 {
725 SM_HEAP_ITEM_T *hp;
726
727 if (!HEAP_CHECK)
728 return;
729 if (ptr == NULL)
730 return;
731 for (hp = SmHeapTable[ptrhash(ptr)]; hp != NULL; hp = hp->hi_next)
732 {
733 if (hp->hi_ptr == ptr)
734 return;
735 }
736 sm_abort("sm_heap_checkptr(%p): bad ptr (%s:%d)", ptr, tag, num);
737 }
738
739 /*
740 ** SM_HEAP_REPORT -- output "map" of used heap.
741 **
742 ** Parameters:
743 ** stream -- the file pointer to write to.
744 ** verbosity -- how much info?
745 **
746 ** Returns:
747 ** none.
748 */
749
750 void
sm_heap_report(stream,verbosity)751 sm_heap_report(stream, verbosity)
752 SM_FILE_T *stream;
753 int verbosity;
754 {
755 int i;
756 unsigned long group0total, group1total, otherstotal, grandtotal;
757
758 if (!HEAP_CHECK || verbosity <= 0)
759 return;
760 group0total = group1total = otherstotal = grandtotal = 0;
761 for (i = 0; i < sizeof(SmHeapTable) / sizeof(SmHeapTable[0]); ++i)
762 {
763 SM_HEAP_ITEM_T *hi = SmHeapTable[i];
764
765 while (hi != NULL)
766 {
767 if (verbosity > 2
768 || (verbosity > 1 && hi->hi_group != 0))
769 {
770 sm_io_fprintf(stream, SM_TIME_DEFAULT,
771 "%4d %*lx %7lu bytes",
772 hi->hi_group,
773 (int) sizeof(void *) * 2,
774 (long)hi->hi_ptr,
775 (unsigned long)hi->hi_size);
776 if (hi->hi_tag != NULL)
777 {
778 sm_io_fprintf(stream, SM_TIME_DEFAULT,
779 " %s",
780 hi->hi_tag);
781 if (hi->hi_num)
782 {
783 sm_io_fprintf(stream,
784 SM_TIME_DEFAULT,
785 ":%d",
786 hi->hi_num);
787 }
788 }
789 sm_io_fprintf(stream, SM_TIME_DEFAULT, "\n");
790 }
791 switch (hi->hi_group)
792 {
793 case 0:
794 group0total += hi->hi_size;
795 break;
796 case 1:
797 group1total += hi->hi_size;
798 break;
799 default:
800 otherstotal += hi->hi_size;
801 break;
802 }
803 grandtotal += hi->hi_size;
804 hi = hi->hi_next;
805 }
806 }
807 sm_io_fprintf(stream, SM_TIME_DEFAULT,
808 "heap max=%lu, total=%lu, ",
809 (unsigned long) SmHeapMaxTotal, grandtotal);
810 sm_io_fprintf(stream, SM_TIME_DEFAULT,
811 "group 0=%lu, group 1=%lu, others=%lu\n",
812 group0total, group1total, otherstotal);
813 if (grandtotal != SmHeapTotal)
814 {
815 sm_io_fprintf(stream, SM_TIME_DEFAULT,
816 "BUG => SmHeapTotal: got %lu, expected %lu\n",
817 (unsigned long) SmHeapTotal, grandtotal);
818 }
819 }
820 #endif /* !SM_HEAP_CHECK */
821