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