Merge from vendor branch GCC:
[dragonfly.git] / contrib / sendmail-8.13.4 / libsm / heap.c
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 *
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 *
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 *
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 *
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
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
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 *
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 *
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
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 *
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 *
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
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
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
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 */