xref: /illumos-gate/usr/src/cmd/sendmail/libsm/heap.c (revision 7c478bd9)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
11 
12 #include <sm/gen.h>
13 SM_RCSID("@(#)$Id: heap.c,v 1.51 2004/08/03 20:32:00 ca Exp $")
14 
15 /*
16 **  debugging memory allocation package
17 **  See heap.html for documentation.
18 */
19 
20 #include <string.h>
21 
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>
29 
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 */
43 
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 */
50 
51 const SM_EXC_TYPE_T SmHeapOutOfMemoryType =
52 {
53 	SmExcTypeMagic,
54 	"F:sm.heap",
55 	"",
56 	sm_etype_printf,
57 	"out of memory",
58 };
59 
60 SM_EXC_T SmHeapOutOfMemory = SM_EXC_INITIALIZER(&SmHeapOutOfMemoryType, NULL);
61 
62 
63 /*
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.
73 */
74 
75 #define MALLOC_SIZE(size)	((size) == 0 ? 1 : (size))
76 
77 /*
78 **  SM_MALLOC_X -- wrapper around malloc(), raises an exception on error.
79 **
80 **	Parameters:
81 **		size -- size of requested memory.
82 **
83 **	Returns:
84 **		Pointer to memory region.
85 **
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.
90 **
91 **	Exceptions:
92 **		F:sm_heap -- out of memory
93 */
94 
95 void *
96 sm_malloc_x(size)
97 	size_t size;
98 {
99 	void *ptr;
100 
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;
107 }
108 
109 #if !SM_HEAP_CHECK
110 
111 /*
112 **  SM_MALLOC -- wrapper around malloc()
113 **
114 **	Parameters:
115 **		size -- size of requested memory.
116 **
117 **	Returns:
118 **		Pointer to memory region.
119 */
120 
121 void *
122 sm_malloc(size)
123 	size_t size;
124 {
125 	void *ptr;
126 
127 	ENTER_CRITICAL();
128 	ptr = malloc(MALLOC_SIZE(size));
129 	LEAVE_CRITICAL();
130 	return ptr;
131 }
132 
133 /*
134 **  SM_REALLOC -- wrapper for realloc()
135 **
136 **	Parameters:
137 **		ptr -- pointer to old memory area.
138 **		size -- size of requested memory.
139 **
140 **	Returns:
141 **		Pointer to new memory area, NULL on failure.
142 */
143 
144 void *
145 sm_realloc(ptr, size)
146 	void *ptr;
147 	size_t size;
148 {
149 	void *newptr;
150 
151 	ENTER_CRITICAL();
152 	newptr = realloc(ptr, MALLOC_SIZE(size));
153 	LEAVE_CRITICAL();
154 	return newptr;
155 }
156 
157 /*
158 **  SM_REALLOC_X -- wrapper for realloc()
159 **
160 **	Parameters:
161 **		ptr -- pointer to old memory area.
162 **		size -- size of requested memory.
163 **
164 **	Returns:
165 **		Pointer to new memory area.
166 **
167 **	Exceptions:
168 **		F:sm_heap -- out of memory
169 */
170 
171 void *
172 sm_realloc_x(ptr, size)
173 	void *ptr;
174 	size_t size;
175 {
176 	void *newptr;
177 
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;
184 }
185 /*
186 **  SM_FREE -- wrapper around free()
187 **
188 **	Parameters:
189 **		ptr -- pointer to memory region.
190 **
191 **	Returns:
192 **		none.
193 */
194 
195 void
196 sm_free(ptr)
197 	void *ptr;
198 {
199 	if (ptr == NULL)
200 		return;
201 	ENTER_CRITICAL();
202 	free(ptr);
203 	LEAVE_CRITICAL();
204 	return;
205 }
206 
207 #else /* !SM_HEAP_CHECK */
208 
209 /*
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.
215 */
216 
217 int SmHeapGroup = 1;
218 int SmHeapMaxGroup = 1;
219 
220 /*
221 **  Total number of bytes allocated.
222 **  This is only maintained if the sm_check_heap debug category is active.
223 */
224 
225 size_t SmHeapTotal = 0;
226 
227 /*
228 **  High water mark: the most that SmHeapTotal has ever been.
229 */
230 
231 size_t SmHeapMaxTotal = 0;
232 
233 /*
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.
237 */
238 
239 SM_DEBUG_T SmHeapLimit = SM_DEBUG_INITIALIZER("sm_heap_limit",
240     "@(#)$Debug: sm_heap_limit - max # of bytes permitted in heap $");
241 
242 /*
243 **  This is the data structure that keeps track of all currently
244 **  allocated blocks of memory known to the heap package.
245 */
246 
247 typedef struct sm_heap_item SM_HEAP_ITEM_T;
248 struct sm_heap_item
249 {
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;
256 };
257 
258 #define SM_HEAP_TABLE_SIZE	256
259 static SM_HEAP_ITEM_T *SmHeapTable[SM_HEAP_TABLE_SIZE];
260 
261 /*
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.
266 */
267 
268 static unsigned char hashtab[SM_HEAP_TABLE_SIZE] =
269 {
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
286 };
287 
288 /*
289 **  PTRHASH -- hash a pointer value
290 **
291 **	Parameters:
292 **		p -- pointer.
293 **
294 **	Returns:
295 **		hash value.
296 **
297 **  ptrhash hashes a pointer value to a uniformly distributed random
298 **  number between 0 and 255.
299 **
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.
303 */
304 
305 static int
306 ptrhash(p)
307 	void *p;
308 {
309 	int h;
310 
311 	if (sizeof(void*) == 4 && sizeof(unsigned long) == 4)
312 	{
313 		unsigned long n = (unsigned long)p;
314 
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)];
319 	}
320 # if 0
321 	else if (sizeof(void*) == 8 && sizeof(unsigned long) == 8)
322 	{
323 		unsigned long n = (unsigned long)p;
324 
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)];
333 	}
334 # endif /* 0 */
335 	else
336 	{
337 		unsigned char *cp = (unsigned char *)&p;
338 		int i;
339 
340 		h = 0;
341 		for (i = 0; i < sizeof(void*); ++i)
342 			h = hashtab[h ^ cp[i]];
343 	}
344 	return h;
345 }
346 
347 /*
348 **  SM_MALLOC_TAGGED -- wrapper around malloc(), debugging version.
349 **
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.
355 **
356 **	Returns:
357 **		Pointer to memory region.
358 */
359 
360 void *
361 sm_malloc_tagged(size, tag, num, group)
362 	size_t size;
363 	char *tag;
364 	int num;
365 	int group;
366 {
367 	void *ptr;
368 
369 	if (!HEAP_CHECK)
370 	{
371 		ENTER_CRITICAL();
372 		ptr = malloc(MALLOC_SIZE(size));
373 		LEAVE_CRITICAL();
374 		return ptr;
375 	}
376 
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))
386 	{
387 		ENTER_CRITICAL();
388 		free(ptr);
389 		LEAVE_CRITICAL();
390 		ptr = NULL;
391 	}
392 	SmHeapTotal += size;
393 	if (SmHeapTotal > SmHeapMaxTotal)
394 		SmHeapMaxTotal = SmHeapTotal;
395 	return ptr;
396 }
397 
398 /*
399 **  SM_MALLOC_TAGGED_X -- wrapper around malloc(), debugging version.
400 **
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.
406 **
407 **	Returns:
408 **		Pointer to memory region.
409 **
410 **	Exceptions:
411 **		F:sm_heap -- out of memory
412 */
413 
414 void *
415 sm_malloc_tagged_x(size, tag, num, group)
416 	size_t size;
417 	char *tag;
418 	int num;
419 	int group;
420 {
421 	void *ptr;
422 
423 	if (!HEAP_CHECK)
424 	{
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;
431 	}
432 
433 	sm_xtrap_raise_x(&SmHeapOutOfMemory);
434 	if (sm_debug_active(&SmHeapLimit, 1)
435 	    && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size)
436 	{
437 		sm_exc_raise_x(&SmHeapOutOfMemory);
438 	}
439 	ENTER_CRITICAL();
440 	ptr = malloc(MALLOC_SIZE(size));
441 	LEAVE_CRITICAL();
442 	if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group))
443 	{
444 		ENTER_CRITICAL();
445 		free(ptr);
446 		LEAVE_CRITICAL();
447 		ptr = NULL;
448 	}
449 	if (ptr == NULL)
450 		sm_exc_raise_x(&SmHeapOutOfMemory);
451 	SmHeapTotal += size;
452 	if (SmHeapTotal > SmHeapMaxTotal)
453 		SmHeapMaxTotal = SmHeapTotal;
454 	return ptr;
455 }
456 
457 /*
458 **  SM_HEAP_REGISTER -- register a pointer into the heap for debugging.
459 **
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.
466 **
467 **	Returns:
468 **		true iff successfully registered (not yet in table).
469 */
470 
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;
478 {
479 	int i;
480 	SM_HEAP_ITEM_T *hi;
481 
482 	if (!HEAP_CHECK)
483 		return true;
484 	SM_REQUIRE(ptr != NULL);
485 	i = ptrhash(ptr);
486 # if SM_CHECK_REQUIRE
487 
488 	/*
489 	** We require that ptr is not already in SmHeapTable.
490 	*/
491 
492 	for (hi = SmHeapTable[i]; hi != NULL; hi = hi->hi_next)
493 	{
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);
497 	}
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;
512 }
513 /*
514 **  SM_REALLOC -- wrapper for realloc(), debugging version.
515 **
516 **	Parameters:
517 **		ptr -- pointer to old memory area.
518 **		size -- size of requested memory.
519 **
520 **	Returns:
521 **		Pointer to new memory area, NULL on failure.
522 */
523 
524 void *
525 sm_realloc(ptr, size)
526 	void *ptr;
527 	size_t size;
528 {
529 	void *newptr;
530 	SM_HEAP_ITEM_T *hi, **hp;
531 
532 	if (!HEAP_CHECK)
533 	{
534 		ENTER_CRITICAL();
535 		newptr = realloc(ptr, MALLOC_SIZE(size));
536 		LEAVE_CRITICAL();
537 		return newptr;
538 	}
539 
540 	if (ptr == NULL)
541 		return sm_malloc_tagged(size, "realloc", 0, SmHeapGroup);
542 
543 	for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
544 	{
545 		if ((**hp).hi_ptr == ptr)
546 		{
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)
553 			{
554 				return NULL;
555 			}
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;
571 		}
572 	}
573 	sm_abort("sm_realloc: bad argument (%p)", ptr);
574 	/* NOTREACHED */
575 	return NULL;	/* keep Irix compiler happy */
576 }
577 
578 /*
579 **  SM_REALLOC_X -- wrapper for realloc(), debugging version.
580 **
581 **	Parameters:
582 **		ptr -- pointer to old memory area.
583 **		size -- size of requested memory.
584 **
585 **	Returns:
586 **		Pointer to new memory area.
587 **
588 **	Exceptions:
589 **		F:sm_heap -- out of memory
590 */
591 
592 void *
593 sm_realloc_x(ptr, size)
594 	void *ptr;
595 	size_t size;
596 {
597 	void *newptr;
598 	SM_HEAP_ITEM_T *hi, **hp;
599 
600 	if (!HEAP_CHECK)
601 	{
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;
608 	}
609 
610 	if (ptr == NULL)
611 		return sm_malloc_tagged_x(size, "realloc", 0, SmHeapGroup);
612 
613 	for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
614 	{
615 		if ((**hp).hi_ptr == ptr)
616 		{
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)
622 			{
623 				sm_exc_raise_x(&SmHeapOutOfMemory);
624 			}
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;
640 		}
641 	}
642 	sm_abort("sm_realloc_x: bad argument (%p)", ptr);
643 	/* NOTREACHED */
644 	return NULL;	/* keep Irix compiler happy */
645 }
646 
647 /*
648 **  SM_FREE_TAGGED -- wrapper around free(), debugging version.
649 **
650 **	Parameters:
651 **		ptr -- pointer to memory region.
652 **		tag -- tag for debugging.
653 **		num -- additional value for debugging.
654 **
655 **	Returns:
656 **		none.
657 */
658 
659 void
660 sm_free_tagged(ptr, tag, num)
661 	void *ptr;
662 	char *tag;
663 	int num;
664 {
665 	SM_HEAP_ITEM_T **hp;
666 
667 	if (ptr == NULL)
668 		return;
669 	if (!HEAP_CHECK)
670 	{
671 		ENTER_CRITICAL();
672 		free(ptr);
673 		LEAVE_CRITICAL();
674 		return;
675 	}
676 	for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
677 	{
678 		if ((**hp).hi_ptr == ptr)
679 		{
680 			SM_HEAP_ITEM_T *hi = *hp;
681 
682 			*hp = hi->hi_next;
683 
684 			/*
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.
692 			*/
693 
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;
701 		}
702 	}
703 	sm_abort("sm_free: bad argument (%p) (%s:%d)", ptr, tag, num);
704 }
705 
706 /*
707 **  SM_HEAP_CHECKPTR_TAGGED -- check whether ptr is a valid argument to sm_free
708 **
709 **	Parameters:
710 **		ptr -- pointer to memory region.
711 **		tag -- tag for debugging.
712 **		num -- additional value for debugging.
713 **
714 **	Returns:
715 **		none.
716 **
717 **	Side Effects:
718 **		aborts if check fails.
719 */
720 
721 void
722 sm_heap_checkptr_tagged(ptr, tag, num)
723 	void *ptr;
724 	char *tag;
725 	int num;
726 {
727 	SM_HEAP_ITEM_T *hp;
728 
729 	if (!HEAP_CHECK)
730 		return;
731 	if (ptr == NULL)
732 		return;
733 	for (hp = SmHeapTable[ptrhash(ptr)]; hp != NULL; hp = hp->hi_next)
734 	{
735 		if (hp->hi_ptr == ptr)
736 			return;
737 	}
738 	sm_abort("sm_heap_checkptr(%p): bad ptr (%s:%d)", ptr, tag, num);
739 }
740 
741 /*
742 **  SM_HEAP_REPORT -- output "map" of used heap.
743 **
744 **	Parameters:
745 **		stream -- the file pointer to write to.
746 **		verbosity -- how much info?
747 **
748 **	Returns:
749 **		none.
750 */
751 
752 void
753 sm_heap_report(stream, verbosity)
754 	SM_FILE_T *stream;
755 	int verbosity;
756 {
757 	int i;
758 	unsigned long group0total, group1total, otherstotal, grandtotal;
759 
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)
764 	{
765 		SM_HEAP_ITEM_T *hi = SmHeapTable[i];
766 
767 		while (hi != NULL)
768 		{
769 			if (verbosity > 2
770 			    || (verbosity > 1 && hi->hi_group != 0))
771 			{
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)
779 				{
780 					sm_io_fprintf(stream, SM_TIME_DEFAULT,
781 						"  %s",
782 						hi->hi_tag);
783 					if (hi->hi_num)
784 					{
785 						sm_io_fprintf(stream,
786 							SM_TIME_DEFAULT,
787 							":%d",
788 							hi->hi_num);
789 					}
790 				}
791 				sm_io_fprintf(stream, SM_TIME_DEFAULT, "\n");
792 			}
793 			switch (hi->hi_group)
794 			{
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;
804 			}
805 			grandtotal += hi->hi_size;
806 			hi = hi->hi_next;
807 		}
808 	}
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)
816 	{
817 		sm_io_fprintf(stream, SM_TIME_DEFAULT,
818 			"BUG => SmHeapTotal: got %lu, expected %lu\n",
819 			(unsigned long) SmHeapTotal, grandtotal);
820 	}
821 }
822 #endif /* !SM_HEAP_CHECK */
823