xref: /illumos-gate/usr/src/cmd/sendmail/libsm/heap.c (revision 86d949f9497332fe19be6b5d711d265eb957439f)
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 */
821