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 *
sm_malloc_x(size)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 *
sm_malloc(size)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 *
sm_realloc(ptr,size)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 *
sm_realloc_x(ptr,size)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
sm_free(ptr)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
ptrhash(p)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 *
sm_malloc_tagged(size,tag,num,group)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 *
sm_malloc_tagged_x(size,tag,num,group)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
sm_heap_register(ptr,size,tag,num,group)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 *
sm_realloc(ptr,size)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 *
sm_realloc_x(ptr,size)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
sm_free_tagged(ptr,tag,num)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
sm_heap_checkptr_tagged(ptr,tag,num)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
sm_heap_report(stream,verbosity)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 */
838