xref: /freebsd/contrib/sendmail/libsm/heap.c (revision 783d3ff6)
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 */
838