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