Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / sendmail / libsm / heap.c
1 /*
2  * Copyright (c) 2000-2001 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.50 2001/09/11 04:04:48 gshapiro 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 #endif /* SM_HEAP_CHECK */
47
48 const SM_EXC_TYPE_T SmHeapOutOfMemoryType =
49 {
50         SmExcTypeMagic,
51         "F:sm.heap",
52         "",
53         sm_etype_printf,
54         "out of memory",
55 };
56
57 SM_EXC_T SmHeapOutOfMemory = SM_EXC_INITIALIZER(&SmHeapOutOfMemoryType, NULL);
58
59
60 /*
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
66 **  following.
67 **      If we had something like autoconf we could figure out the
68 **      behaviour of the platform and either use this hack or just
69 **      use size.
70 */
71
72 #define MALLOC_SIZE(size)       ((size) == 0 ? 1 : (size))
73
74 /*
75 **  SM_MALLOC_X -- wrapper around malloc(), raises an exception on error.
76 **
77 **      Parameters:
78 **              size -- size of requested memory.
79 **
80 **      Returns:
81 **              Pointer to memory region.
82 **
83 **      Note:
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.
87 **
88 **      Exceptions:
89 **              F:sm_heap -- out of memory
90 */
91
92 void *
93 sm_malloc_x(size)
94         size_t size;
95 {
96         void *ptr;
97
98         ENTER_CRITICAL();
99         ptr = malloc(MALLOC_SIZE(size));
100         LEAVE_CRITICAL();
101         if (ptr == NULL)
102                 sm_exc_raise_x(&SmHeapOutOfMemory);
103         return ptr;
104 }
105
106 #if !SM_HEAP_CHECK
107
108 /*
109 **  SM_MALLOC -- wrapper around malloc()
110 **
111 **      Parameters:
112 **              size -- size of requested memory.
113 **
114 **      Returns:
115 **              Pointer to memory region.
116 */
117
118 void *
119 sm_malloc(size)
120         size_t size;
121 {
122         void *ptr;
123
124         ENTER_CRITICAL();
125         ptr = malloc(MALLOC_SIZE(size));
126         LEAVE_CRITICAL();
127         return ptr;
128 }
129
130 /*
131 **  SM_REALLOC -- wrapper for realloc()
132 **
133 **      Parameters:
134 **              ptr -- pointer to old memory area.
135 **              size -- size of requested memory.
136 **
137 **      Returns:
138 **              Pointer to new memory area, NULL on failure.
139 */
140
141 void *
142 sm_realloc(ptr, size)
143         void *ptr;
144         size_t size;
145 {
146         void *newptr;
147
148         ENTER_CRITICAL();
149         newptr = realloc(ptr, MALLOC_SIZE(size));
150         LEAVE_CRITICAL();
151         return newptr;
152 }
153
154 /*
155 **  SM_REALLOC_X -- wrapper for realloc()
156 **
157 **      Parameters:
158 **              ptr -- pointer to old memory area.
159 **              size -- size of requested memory.
160 **
161 **      Returns:
162 **              Pointer to new memory area.
163 **
164 **      Exceptions:
165 **              F:sm_heap -- out of memory
166 */
167
168 void *
169 sm_realloc_x(ptr, size)
170         void *ptr;
171         size_t size;
172 {
173         void *newptr;
174
175         ENTER_CRITICAL();
176         newptr = realloc(ptr, MALLOC_SIZE(size));
177         LEAVE_CRITICAL();
178         if (newptr == NULL)
179                 sm_exc_raise_x(&SmHeapOutOfMemory);
180         return newptr;
181 }
182 /*
183 **  SM_FREE -- wrapper around free()
184 **
185 **      Parameters:
186 **              ptr -- pointer to memory region.
187 **
188 **      Returns:
189 **              none.
190 */
191
192 void
193 sm_free(ptr)
194         void *ptr;
195 {
196         if (ptr == NULL)
197                 return;
198         ENTER_CRITICAL();
199         free(ptr);
200         LEAVE_CRITICAL();
201         return;
202 }
203
204 #else /* !SM_HEAP_CHECK */
205
206 /*
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.
212 */
213
214 int SmHeapGroup = 1;
215 int SmHeapMaxGroup = 1;
216
217 /*
218 **  Total number of bytes allocated.
219 **  This is only maintained if the sm_check_heap debug category is active.
220 */
221
222 size_t SmHeapTotal = 0;
223
224 /*
225 **  High water mark: the most that SmHeapTotal has ever been.
226 */
227
228 size_t SmHeapMaxTotal = 0;
229
230 /*
231 **  Maximum number of bytes that may be allocated at any one time.
232 **  0 means no limit.
233 **  This is only honoured if sm_check_heap is active.
234 */
235
236 SM_DEBUG_T SmHeapLimit = SM_DEBUG_INITIALIZER("sm_heap_limit",
237     "@(#)$Debug: sm_heap_limit - max # of bytes permitted in heap $");
238
239 /*
240 **  This is the data structure that keeps track of all currently
241 **  allocated blocks of memory known to the heap package.
242 */
243
244 typedef struct sm_heap_item SM_HEAP_ITEM_T;
245 struct sm_heap_item
246 {
247         void            *hi_ptr;
248         size_t          hi_size;
249         char            *hi_tag;
250         int             hi_num;
251         int             hi_group;
252         SM_HEAP_ITEM_T  *hi_next;
253 };
254
255 #define SM_HEAP_TABLE_SIZE      256
256 static SM_HEAP_ITEM_T *SmHeapTable[SM_HEAP_TABLE_SIZE];
257
258 /*
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.
263 */
264
265 static unsigned char hashtab[SM_HEAP_TABLE_SIZE] =
266 {
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
283 };
284
285 /*
286 **  PTRHASH -- hash a pointer value
287 **
288 **      Parameters:
289 **              p -- pointer.
290 **
291 **      Returns:
292 **              hash value.
293 **
294 **  ptrhash hashes a pointer value to a uniformly distributed random
295 **  number between 0 and 255.
296 **
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.
300 */
301
302 static int
303 ptrhash(p)
304         void *p;
305 {
306         int h;
307
308         if (sizeof(void*) == 4 && sizeof(unsigned long) == 4)
309         {
310                 unsigned long n = (unsigned long)p;
311
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)];
316         }
317 # if 0
318         else if (sizeof(void*) == 8 && sizeof(unsigned long) == 8)
319         {
320                 unsigned long n = (unsigned long)p;
321
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)];
330         }
331 # endif /* 0 */
332         else
333         {
334                 unsigned char *cp = (unsigned char *)&p;
335                 int i;
336
337                 h = 0;
338                 for (i = 0; i < sizeof(void*); ++i)
339                         h = hashtab[h ^ cp[i]];
340         }
341         return h;
342 }
343
344 /*
345 **  SM_MALLOC_TAGGED -- wrapper around malloc(), debugging version.
346 **
347 **      Parameters:
348 **              size -- size of requested memory.
349 **              tag -- tag for debugging.
350 **              num -- additional value for debugging.
351 **              group -- heap group for debugging.
352 **
353 **      Returns:
354 **              Pointer to memory region.
355 */
356
357 void *
358 sm_malloc_tagged(size, tag, num, group)
359         size_t size;
360         char *tag;
361         int num;
362         int group;
363 {
364         void *ptr;
365
366         if (!HEAP_CHECK)
367         {
368                 ENTER_CRITICAL();
369                 ptr = malloc(MALLOC_SIZE(size));
370                 LEAVE_CRITICAL();
371                 return ptr;
372         }
373
374         if (sm_xtrap_check())
375                 return NULL;
376         if (sm_debug_active(&SmHeapLimit, 1)
377             && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size)
378                 return NULL;
379         ENTER_CRITICAL();
380         ptr = malloc(MALLOC_SIZE(size));
381         LEAVE_CRITICAL();
382         if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group))
383         {
384                 ENTER_CRITICAL();
385                 free(ptr);
386                 LEAVE_CRITICAL();
387                 ptr = NULL;
388         }
389         SmHeapTotal += size;
390         if (SmHeapTotal > SmHeapMaxTotal)
391                 SmHeapMaxTotal = SmHeapTotal;
392         return ptr;
393 }
394
395 /*
396 **  SM_MALLOC_TAGGED_X -- wrapper around malloc(), debugging version.
397 **
398 **      Parameters:
399 **              size -- size of requested memory.
400 **              tag -- tag for debugging.
401 **              num -- additional value for debugging.
402 **              group -- heap group for debugging.
403 **
404 **      Returns:
405 **              Pointer to memory region.
406 **
407 **      Exceptions:
408 **              F:sm_heap -- out of memory
409 */
410
411 void *
412 sm_malloc_tagged_x(size, tag, num, group)
413         size_t size;
414         char *tag;
415         int num;
416         int group;
417 {
418         void *ptr;
419
420         if (!HEAP_CHECK)
421         {
422                 ENTER_CRITICAL();
423                 ptr = malloc(MALLOC_SIZE(size));
424                 LEAVE_CRITICAL();
425                 if (ptr == NULL)
426                         sm_exc_raise_x(&SmHeapOutOfMemory);
427                 return ptr;
428         }
429
430         sm_xtrap_raise_x(&SmHeapOutOfMemory);
431         if (sm_debug_active(&SmHeapLimit, 1)
432             && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size)
433         {
434                 sm_exc_raise_x(&SmHeapOutOfMemory);
435         }
436         ENTER_CRITICAL();
437         ptr = malloc(MALLOC_SIZE(size));
438         LEAVE_CRITICAL();
439         if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group))
440         {
441                 ENTER_CRITICAL();
442                 free(ptr);
443                 LEAVE_CRITICAL();
444                 ptr = NULL;
445         }
446         if (ptr == NULL)
447                 sm_exc_raise_x(&SmHeapOutOfMemory);
448         SmHeapTotal += size;
449         if (SmHeapTotal > SmHeapMaxTotal)
450                 SmHeapMaxTotal = SmHeapTotal;
451         return ptr;
452 }
453
454 /*
455 **  SM_HEAP_REGISTER -- register a pointer into the heap for debugging.
456 **
457 **      Parameters:
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.
463 **
464 **      Returns:
465 **              true iff successfully registered (not yet in table).
466 */
467
468 bool
469 sm_heap_register(ptr, size, tag, num, group)
470         void *ptr;
471         size_t size;
472         char *tag;
473         int num;
474         int group;
475 {
476         int i;
477         SM_HEAP_ITEM_T *hi;
478
479         if (!HEAP_CHECK)
480                 return true;
481         SM_REQUIRE(ptr != NULL);
482         i = ptrhash(ptr);
483 # if SM_CHECK_REQUIRE
484
485         /*
486         ** We require that ptr is not already in SmHeapTable.
487         */
488
489         for (hi = SmHeapTable[i]; hi != NULL; hi = hi->hi_next)
490         {
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);
494         }
495 # endif /* SM_CHECK_REQUIRE */
496         ENTER_CRITICAL();
497         hi = (SM_HEAP_ITEM_T *) malloc(sizeof(SM_HEAP_ITEM_T));
498         LEAVE_CRITICAL();
499         if (hi == NULL)
500                 return false;
501         hi->hi_ptr = ptr;
502         hi->hi_size = size;
503         hi->hi_tag = tag;
504         hi->hi_num = num;
505         hi->hi_group = group;
506         hi->hi_next = SmHeapTable[i];
507         SmHeapTable[i] = hi;
508         return true;
509 }
510 /*
511 **  SM_REALLOC -- wrapper for realloc(), debugging version.
512 **
513 **      Parameters:
514 **              ptr -- pointer to old memory area.
515 **              size -- size of requested memory.
516 **
517 **      Returns:
518 **              Pointer to new memory area, NULL on failure.
519 */
520
521 void *
522 sm_realloc(ptr, size)
523         void *ptr;
524         size_t size;
525 {
526         void *newptr;
527         SM_HEAP_ITEM_T *hi, **hp;
528
529         if (!HEAP_CHECK)
530         {
531                 ENTER_CRITICAL();
532                 newptr = realloc(ptr, MALLOC_SIZE(size));
533                 LEAVE_CRITICAL();
534                 return newptr;
535         }
536
537         if (ptr == NULL)
538                 return sm_malloc_tagged(size, "realloc", 0, SmHeapGroup);
539
540         for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
541         {
542                 if ((**hp).hi_ptr == ptr)
543                 {
544                         if (sm_xtrap_check())
545                                 return NULL;
546                         hi = *hp;
547                         if (sm_debug_active(&SmHeapLimit, 1)
548                             && sm_debug_level(&SmHeapLimit)
549                                < SmHeapTotal - hi->hi_size + size)
550                         {
551                                 return NULL;
552                         }
553                         ENTER_CRITICAL();
554                         newptr = realloc(ptr, MALLOC_SIZE(size));
555                         LEAVE_CRITICAL();
556                         if (newptr == NULL)
557                                 return NULL;
558                         SmHeapTotal = SmHeapTotal - hi->hi_size + size;
559                         if (SmHeapTotal > SmHeapMaxTotal)
560                                 SmHeapMaxTotal = SmHeapTotal;
561                         *hp = hi->hi_next;
562                         hi->hi_ptr = newptr;
563                         hi->hi_size = size;
564                         hp = &SmHeapTable[ptrhash(newptr)];
565                         hi->hi_next = *hp;
566                         *hp = hi;
567                         return newptr;
568                 }
569         }
570         sm_abort("sm_realloc: bad argument (%p)", ptr);
571         /* NOTREACHED */
572         return NULL;    /* keep Irix compiler happy */
573 }
574
575 /*
576 **  SM_REALLOC_X -- wrapper for realloc(), debugging version.
577 **
578 **      Parameters:
579 **              ptr -- pointer to old memory area.
580 **              size -- size of requested memory.
581 **
582 **      Returns:
583 **              Pointer to new memory area.
584 **
585 **      Exceptions:
586 **              F:sm_heap -- out of memory
587 */
588
589 void *
590 sm_realloc_x(ptr, size)
591         void *ptr;
592         size_t size;
593 {
594         void *newptr;
595         SM_HEAP_ITEM_T *hi, **hp;
596
597         if (!HEAP_CHECK)
598         {
599                 ENTER_CRITICAL();
600                 newptr = realloc(ptr, MALLOC_SIZE(size));
601                 LEAVE_CRITICAL();
602                 if (newptr == NULL)
603                         sm_exc_raise_x(&SmHeapOutOfMemory);
604                 return newptr;
605         }
606
607         if (ptr == NULL)
608                 return sm_malloc_tagged_x(size, "realloc", 0, SmHeapGroup);
609
610         for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
611         {
612                 if ((**hp).hi_ptr == ptr)
613                 {
614                         sm_xtrap_raise_x(&SmHeapOutOfMemory);
615                         hi = *hp;
616                         if (sm_debug_active(&SmHeapLimit, 1)
617                             && sm_debug_level(&SmHeapLimit)
618                                < SmHeapTotal - hi->hi_size + size)
619                         {
620                                 sm_exc_raise_x(&SmHeapOutOfMemory);
621                         }
622                         ENTER_CRITICAL();
623                         newptr = realloc(ptr, MALLOC_SIZE(size));
624                         LEAVE_CRITICAL();
625                         if (newptr == NULL)
626                                 sm_exc_raise_x(&SmHeapOutOfMemory);
627                         SmHeapTotal = SmHeapTotal - hi->hi_size + size;
628                         if (SmHeapTotal > SmHeapMaxTotal)
629                                 SmHeapMaxTotal = SmHeapTotal;
630                         *hp = hi->hi_next;
631                         hi->hi_ptr = newptr;
632                         hi->hi_size = size;
633                         hp = &SmHeapTable[ptrhash(newptr)];
634                         hi->hi_next = *hp;
635                         *hp = hi;
636                         return newptr;
637                 }
638         }
639         sm_abort("sm_realloc_x: bad argument (%p)", ptr);
640         /* NOTREACHED */
641         return NULL;    /* keep Irix compiler happy */
642 }
643
644 /*
645 **  SM_FREE_TAGGED -- wrapper around free(), debugging version.
646 **
647 **      Parameters:
648 **              ptr -- pointer to memory region.
649 **              tag -- tag for debugging.
650 **              num -- additional value for debugging.
651 **
652 **      Returns:
653 **              none.
654 */
655
656 void
657 sm_free_tagged(ptr, tag, num)
658         void *ptr;
659         char *tag;
660         int num;
661 {
662         SM_HEAP_ITEM_T **hp;
663
664         if (ptr == NULL)
665                 return;
666         if (!HEAP_CHECK)
667         {
668                 ENTER_CRITICAL();
669                 free(ptr);
670                 LEAVE_CRITICAL();
671                 return;
672         }
673         for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
674         {
675                 if ((**hp).hi_ptr == ptr)
676                 {
677                         SM_HEAP_ITEM_T *hi = *hp;
678
679                         *hp = hi->hi_next;
680
681                         /*
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.
689                         */
690
691                         (void) memset(ptr, 0, hi->hi_size);
692                         SmHeapTotal -= hi->hi_size;
693                         ENTER_CRITICAL();
694                         free(ptr);
695                         free(hi);
696                         LEAVE_CRITICAL();
697                         return;
698                 }
699         }
700         sm_abort("sm_free: bad argument (%p) (%s:%d)", ptr, tag, num);
701 }
702
703 /*
704 **  SM_HEAP_CHECKPTR_TAGGED -- check whether ptr is a valid argument to sm_free
705 **
706 **      Parameters:
707 **              ptr -- pointer to memory region.
708 **              tag -- tag for debugging.
709 **              num -- additional value for debugging.
710 **
711 **      Returns:
712 **              none.
713 **
714 **      Side Effects:
715 **              aborts if check fails.
716 */
717
718 void
719 sm_heap_checkptr_tagged(ptr, tag, num)
720         void *ptr;
721         char *tag;
722         int num;
723 {
724         SM_HEAP_ITEM_T *hp;
725
726         if (!HEAP_CHECK)
727                 return;
728         if (ptr == NULL)
729                 return;
730         for (hp = SmHeapTable[ptrhash(ptr)]; hp != NULL; hp = hp->hi_next)
731         {
732                 if (hp->hi_ptr == ptr)
733                         return;
734         }
735         sm_abort("sm_heap_checkptr(%p): bad ptr (%s:%d)", ptr, tag, num);
736 }
737
738 /*
739 **  SM_HEAP_REPORT -- output "map" of used heap.
740 **
741 **      Parameters:
742 **              stream -- the file pointer to write to.
743 **              verbosity -- how much info?
744 **
745 **      Returns:
746 **              none.
747 */
748
749 void
750 sm_heap_report(stream, verbosity)
751         SM_FILE_T *stream;
752         int verbosity;
753 {
754         int i;
755         unsigned long group0total, group1total, otherstotal, grandtotal;
756
757         if (!HEAP_CHECK || verbosity <= 0)
758                 return;
759         group0total = group1total = otherstotal = grandtotal = 0;
760         for (i = 0; i < sizeof(SmHeapTable) / sizeof(SmHeapTable[0]); ++i)
761         {
762                 SM_HEAP_ITEM_T *hi = SmHeapTable[i];
763
764                 while (hi != NULL)
765                 {
766                         if (verbosity > 2
767                             || (verbosity > 1 && hi->hi_group != 0))
768                         {
769                                 sm_io_fprintf(stream, SM_TIME_DEFAULT,
770                                         "%4d %*lx %7lu bytes",
771                                         hi->hi_group,
772                                         (int) sizeof(void *) * 2,
773                                         (long)hi->hi_ptr,
774                                         (unsigned long)hi->hi_size);
775                                 if (hi->hi_tag != NULL)
776                                 {
777                                         sm_io_fprintf(stream, SM_TIME_DEFAULT,
778                                                 "  %s",
779                                                 hi->hi_tag);
780                                         if (hi->hi_num)
781                                         {
782                                                 sm_io_fprintf(stream,
783                                                         SM_TIME_DEFAULT,
784                                                         ":%d",
785                                                         hi->hi_num);
786                                         }
787                                 }
788                                 sm_io_fprintf(stream, SM_TIME_DEFAULT, "\n");
789                         }
790                         switch (hi->hi_group)
791                         {
792                           case 0:
793                                 group0total += hi->hi_size;
794                                 break;
795                           case 1:
796                                 group1total += hi->hi_size;
797                                 break;
798                           default:
799                                 otherstotal += hi->hi_size;
800                                 break;
801                         }
802                         grandtotal += hi->hi_size;
803                         hi = hi->hi_next;
804                 }
805         }
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)
813         {
814                 sm_io_fprintf(stream, SM_TIME_DEFAULT,
815                         "BUG => SmHeapTotal: got %lu, expected %lu\n",
816                         (unsigned long) SmHeapTotal, grandtotal);
817         }
818 }
819 #endif /* !SM_HEAP_CHECK */