190792Sgshapiro/*
2261363Sgshapiro * Copyright (c) 2000-2001, 2004 Proofpoint, Inc. and its suppliers.
390792Sgshapiro *	All rights reserved.
490792Sgshapiro *
590792Sgshapiro * By using this file, you agree to the terms and conditions set
690792Sgshapiro * forth in the LICENSE file which can be found at the top level of
790792Sgshapiro * the sendmail distribution.
890792Sgshapiro */
990792Sgshapiro
1090792Sgshapiro#include <sm/gen.h>
11266692SgshapiroSM_RCSID("@(#)$Id: heap.c,v 1.52 2013-11-22 20:51:43 ca Exp $")
1290792Sgshapiro
1390792Sgshapiro/*
1490792Sgshapiro**  debugging memory allocation package
1590792Sgshapiro**  See heap.html for documentation.
1690792Sgshapiro*/
1790792Sgshapiro
1890792Sgshapiro#include <string.h>
1990792Sgshapiro
2090792Sgshapiro#include <sm/assert.h>
2190792Sgshapiro#include <sm/debug.h>
2290792Sgshapiro#include <sm/exc.h>
2390792Sgshapiro#include <sm/heap.h>
2490792Sgshapiro#include <sm/io.h>
2590792Sgshapiro#include <sm/signal.h>
2690792Sgshapiro#include <sm/xtrap.h>
2790792Sgshapiro
2890792Sgshapiro/* undef all macro versions of the "functions" so they can be specified here */
2990792Sgshapiro#undef sm_malloc
3090792Sgshapiro#undef sm_malloc_x
3190792Sgshapiro#undef sm_malloc_tagged
3290792Sgshapiro#undef sm_malloc_tagged_x
3390792Sgshapiro#undef sm_free
3490792Sgshapiro#undef sm_free_tagged
3590792Sgshapiro#undef sm_realloc
3690792Sgshapiro#if SM_HEAP_CHECK
3790792Sgshapiro# undef sm_heap_register
3890792Sgshapiro# undef sm_heap_checkptr
3990792Sgshapiro# undef sm_heap_report
4090792Sgshapiro#endif /* SM_HEAP_CHECK */
4190792Sgshapiro
4290792Sgshapiro#if SM_HEAP_CHECK
4390792SgshapiroSM_DEBUG_T SmHeapCheck = SM_DEBUG_INITIALIZER("sm_check_heap",
4490792Sgshapiro    "@(#)$Debug: sm_check_heap - check sm_malloc, sm_realloc, sm_free calls $");
4590792Sgshapiro# define HEAP_CHECK sm_debug_active(&SmHeapCheck, 1)
46141858Sgshapirostatic int	ptrhash __P((void *p));
4790792Sgshapiro#endif /* SM_HEAP_CHECK */
4890792Sgshapiro
4990792Sgshapiroconst SM_EXC_TYPE_T SmHeapOutOfMemoryType =
5090792Sgshapiro{
5190792Sgshapiro	SmExcTypeMagic,
5290792Sgshapiro	"F:sm.heap",
5390792Sgshapiro	"",
5490792Sgshapiro	sm_etype_printf,
5590792Sgshapiro	"out of memory",
5690792Sgshapiro};
5790792Sgshapiro
5890792SgshapiroSM_EXC_T SmHeapOutOfMemory = SM_EXC_INITIALIZER(&SmHeapOutOfMemoryType, NULL);
5990792Sgshapiro
6090792Sgshapiro
6190792Sgshapiro/*
6290792Sgshapiro**  The behaviour of malloc with size==0 is platform dependent (it
6390792Sgshapiro**  says so in the C standard): it can return NULL or non-NULL.  We
6490792Sgshapiro**  don't want sm_malloc_x(0) to raise an exception on some platforms
6590792Sgshapiro**  but not others, so this case requires special handling.  We've got
6690792Sgshapiro**  two choices: "size = 1" or "return NULL". We use the former in the
6790792Sgshapiro**  following.
6890792Sgshapiro**	If we had something like autoconf we could figure out the
6990792Sgshapiro**	behaviour of the platform and either use this hack or just
7090792Sgshapiro**	use size.
7190792Sgshapiro*/
7290792Sgshapiro
7390792Sgshapiro#define MALLOC_SIZE(size)	((size) == 0 ? 1 : (size))
7490792Sgshapiro
7590792Sgshapiro/*
7690792Sgshapiro**  SM_MALLOC_X -- wrapper around malloc(), raises an exception on error.
7790792Sgshapiro**
7890792Sgshapiro**	Parameters:
7990792Sgshapiro**		size -- size of requested memory.
8090792Sgshapiro**
8190792Sgshapiro**	Returns:
8290792Sgshapiro**		Pointer to memory region.
8390792Sgshapiro**
8490792Sgshapiro**	Note:
8590792Sgshapiro**		sm_malloc_x only gets called from source files in which heap
8690792Sgshapiro**		debugging is disabled at compile time.  Otherwise, a call to
8790792Sgshapiro**		sm_malloc_x is macro expanded to a call to sm_malloc_tagged_x.
8890792Sgshapiro**
8990792Sgshapiro**	Exceptions:
9090792Sgshapiro**		F:sm_heap -- out of memory
9190792Sgshapiro*/
9290792Sgshapiro
9390792Sgshapirovoid *
9490792Sgshapirosm_malloc_x(size)
9590792Sgshapiro	size_t size;
9690792Sgshapiro{
9790792Sgshapiro	void *ptr;
9890792Sgshapiro
9990792Sgshapiro	ENTER_CRITICAL();
10090792Sgshapiro	ptr = malloc(MALLOC_SIZE(size));
10190792Sgshapiro	LEAVE_CRITICAL();
10290792Sgshapiro	if (ptr == NULL)
10390792Sgshapiro		sm_exc_raise_x(&SmHeapOutOfMemory);
10490792Sgshapiro	return ptr;
10590792Sgshapiro}
10690792Sgshapiro
10790792Sgshapiro#if !SM_HEAP_CHECK
10890792Sgshapiro
10990792Sgshapiro/*
11090792Sgshapiro**  SM_MALLOC -- wrapper around malloc()
11190792Sgshapiro**
11290792Sgshapiro**	Parameters:
11390792Sgshapiro**		size -- size of requested memory.
11490792Sgshapiro**
11590792Sgshapiro**	Returns:
11690792Sgshapiro**		Pointer to memory region.
11790792Sgshapiro*/
11890792Sgshapiro
11990792Sgshapirovoid *
12090792Sgshapirosm_malloc(size)
12190792Sgshapiro	size_t size;
12290792Sgshapiro{
12390792Sgshapiro	void *ptr;
12490792Sgshapiro
12590792Sgshapiro	ENTER_CRITICAL();
12690792Sgshapiro	ptr = malloc(MALLOC_SIZE(size));
12790792Sgshapiro	LEAVE_CRITICAL();
12890792Sgshapiro	return ptr;
12990792Sgshapiro}
13090792Sgshapiro
13190792Sgshapiro/*
13290792Sgshapiro**  SM_REALLOC -- wrapper for realloc()
13390792Sgshapiro**
13490792Sgshapiro**	Parameters:
13590792Sgshapiro**		ptr -- pointer to old memory area.
13690792Sgshapiro**		size -- size of requested memory.
13790792Sgshapiro**
13890792Sgshapiro**	Returns:
13990792Sgshapiro**		Pointer to new memory area, NULL on failure.
14090792Sgshapiro*/
14190792Sgshapiro
14290792Sgshapirovoid *
14390792Sgshapirosm_realloc(ptr, size)
14490792Sgshapiro	void *ptr;
14590792Sgshapiro	size_t size;
14690792Sgshapiro{
14790792Sgshapiro	void *newptr;
14890792Sgshapiro
14990792Sgshapiro	ENTER_CRITICAL();
15090792Sgshapiro	newptr = realloc(ptr, MALLOC_SIZE(size));
15190792Sgshapiro	LEAVE_CRITICAL();
15290792Sgshapiro	return newptr;
15390792Sgshapiro}
15490792Sgshapiro
15590792Sgshapiro/*
15690792Sgshapiro**  SM_REALLOC_X -- wrapper for realloc()
15790792Sgshapiro**
15890792Sgshapiro**	Parameters:
15990792Sgshapiro**		ptr -- pointer to old memory area.
16090792Sgshapiro**		size -- size of requested memory.
16190792Sgshapiro**
16290792Sgshapiro**	Returns:
16390792Sgshapiro**		Pointer to new memory area.
16490792Sgshapiro**
16590792Sgshapiro**	Exceptions:
16690792Sgshapiro**		F:sm_heap -- out of memory
16790792Sgshapiro*/
16890792Sgshapiro
16990792Sgshapirovoid *
17090792Sgshapirosm_realloc_x(ptr, size)
17190792Sgshapiro	void *ptr;
17290792Sgshapiro	size_t size;
17390792Sgshapiro{
17490792Sgshapiro	void *newptr;
17590792Sgshapiro
17690792Sgshapiro	ENTER_CRITICAL();
17790792Sgshapiro	newptr = realloc(ptr, MALLOC_SIZE(size));
17890792Sgshapiro	LEAVE_CRITICAL();
17990792Sgshapiro	if (newptr == NULL)
18090792Sgshapiro		sm_exc_raise_x(&SmHeapOutOfMemory);
18190792Sgshapiro	return newptr;
18290792Sgshapiro}
18390792Sgshapiro/*
18490792Sgshapiro**  SM_FREE -- wrapper around free()
18590792Sgshapiro**
18690792Sgshapiro**	Parameters:
18790792Sgshapiro**		ptr -- pointer to memory region.
18890792Sgshapiro**
18990792Sgshapiro**	Returns:
19090792Sgshapiro**		none.
19190792Sgshapiro*/
19290792Sgshapiro
19390792Sgshapirovoid
19490792Sgshapirosm_free(ptr)
19590792Sgshapiro	void *ptr;
19690792Sgshapiro{
19790792Sgshapiro	if (ptr == NULL)
19890792Sgshapiro		return;
19990792Sgshapiro	ENTER_CRITICAL();
20090792Sgshapiro	free(ptr);
20190792Sgshapiro	LEAVE_CRITICAL();
20290792Sgshapiro	return;
20390792Sgshapiro}
20490792Sgshapiro
20590792Sgshapiro#else /* !SM_HEAP_CHECK */
20690792Sgshapiro
20790792Sgshapiro/*
20890792Sgshapiro**  Each allocated block is assigned a "group number".
20990792Sgshapiro**  By default, all blocks are assigned to group #1.
21090792Sgshapiro**  By convention, group #0 is for memory that is never freed.
21190792Sgshapiro**  You can use group numbers any way you want, in order to help make
21290792Sgshapiro**  sense of sm_heap_report output.
21390792Sgshapiro*/
21490792Sgshapiro
21590792Sgshapiroint SmHeapGroup = 1;
21690792Sgshapiroint SmHeapMaxGroup = 1;
21790792Sgshapiro
21890792Sgshapiro/*
21990792Sgshapiro**  Total number of bytes allocated.
22090792Sgshapiro**  This is only maintained if the sm_check_heap debug category is active.
22190792Sgshapiro*/
22290792Sgshapiro
22390792Sgshapirosize_t SmHeapTotal = 0;
22490792Sgshapiro
22590792Sgshapiro/*
22690792Sgshapiro**  High water mark: the most that SmHeapTotal has ever been.
22790792Sgshapiro*/
22890792Sgshapiro
22990792Sgshapirosize_t SmHeapMaxTotal = 0;
23090792Sgshapiro
23190792Sgshapiro/*
23290792Sgshapiro**  Maximum number of bytes that may be allocated at any one time.
23390792Sgshapiro**  0 means no limit.
23490792Sgshapiro**  This is only honoured if sm_check_heap is active.
23590792Sgshapiro*/
23690792Sgshapiro
23790792SgshapiroSM_DEBUG_T SmHeapLimit = SM_DEBUG_INITIALIZER("sm_heap_limit",
23890792Sgshapiro    "@(#)$Debug: sm_heap_limit - max # of bytes permitted in heap $");
23990792Sgshapiro
24090792Sgshapiro/*
24190792Sgshapiro**  This is the data structure that keeps track of all currently
24290792Sgshapiro**  allocated blocks of memory known to the heap package.
24390792Sgshapiro*/
24490792Sgshapiro
24590792Sgshapirotypedef struct sm_heap_item SM_HEAP_ITEM_T;
24690792Sgshapirostruct sm_heap_item
24790792Sgshapiro{
24890792Sgshapiro	void		*hi_ptr;
24990792Sgshapiro	size_t		hi_size;
25090792Sgshapiro	char		*hi_tag;
25190792Sgshapiro	int		hi_num;
25290792Sgshapiro	int		hi_group;
25390792Sgshapiro	SM_HEAP_ITEM_T	*hi_next;
25490792Sgshapiro};
25590792Sgshapiro
25690792Sgshapiro#define SM_HEAP_TABLE_SIZE	256
25790792Sgshapirostatic SM_HEAP_ITEM_T *SmHeapTable[SM_HEAP_TABLE_SIZE];
25890792Sgshapiro
25990792Sgshapiro/*
26090792Sgshapiro**  This is a randomly generated table
26190792Sgshapiro**  which contains exactly one occurrence
26290792Sgshapiro**  of each of the numbers between 0 and 255.
26390792Sgshapiro**  It is used by ptrhash.
26490792Sgshapiro*/
26590792Sgshapiro
26690792Sgshapirostatic unsigned char hashtab[SM_HEAP_TABLE_SIZE] =
26790792Sgshapiro{
26890792Sgshapiro	161, 71, 77,187, 15,229,  9,176,221,119,239, 21, 85,138,203, 86,
26990792Sgshapiro	102, 65, 80,199,235, 32,140, 96,224, 78,126,127,144,  0, 11,179,
27090792Sgshapiro	 64, 30,120, 23,225,226, 33, 50,205,167,130,240,174, 99,206, 73,
27190792Sgshapiro	231,210,189,162, 48, 93,246, 54,213,141,135, 39, 41,192,236,193,
27290792Sgshapiro	157, 88, 95,104,188, 63,133,177,234,110,158,214,238,131,233, 91,
27390792Sgshapiro	125, 82, 94, 79, 66, 92,151, 45,252, 98, 26,183,  7,191,171,106,
27490792Sgshapiro	145,154,251,100,113,  5, 74, 62, 76,124, 14,217,200, 75,115,190,
27590792Sgshapiro	103, 28,198,196,169,219, 37,118,150, 18,152,175, 49,136,  6,142,
27690792Sgshapiro	 89, 19,243,254, 47,137, 24,166,180, 10, 40,186,202, 46,184, 67,
27790792Sgshapiro	148,108,181, 81, 25,241, 13,139, 58, 38, 84,253,201, 12,116, 17,
27890792Sgshapiro	195, 22,112, 69,255, 43,147,222,111, 56,194,216,149,244, 42,173,
27990792Sgshapiro	232,220,249,105,207, 51,197,242, 72,211,208, 59,122,230,237,170,
28090792Sgshapiro	165, 44, 68,123,129,245,143,101,  8,209,215,247,185, 57,218, 53,
28190792Sgshapiro	114,121,  3,128,  4,204,212,146,  2,155, 83,250, 87, 29, 31,159,
28290792Sgshapiro	 60, 27,107,156,227,182,  1, 61, 36,160,109, 97, 90, 20,168,132,
28390792Sgshapiro	223,248, 70,164, 55,172, 34, 52,163,117, 35,153,134, 16,178,228
28490792Sgshapiro};
28590792Sgshapiro
28690792Sgshapiro/*
28790792Sgshapiro**  PTRHASH -- hash a pointer value
28890792Sgshapiro**
28990792Sgshapiro**	Parameters:
29090792Sgshapiro**		p -- pointer.
29190792Sgshapiro**
29290792Sgshapiro**	Returns:
29390792Sgshapiro**		hash value.
29490792Sgshapiro**
29590792Sgshapiro**  ptrhash hashes a pointer value to a uniformly distributed random
29690792Sgshapiro**  number between 0 and 255.
29790792Sgshapiro**
29890792Sgshapiro**  This hash algorithm is based on Peter K. Pearson,
29990792Sgshapiro**  "Fast Hashing of Variable-Length Text Strings",
30090792Sgshapiro**  in Communications of the ACM, June 1990, vol 33 no 6.
30190792Sgshapiro*/
30290792Sgshapiro
30390792Sgshapirostatic int
30490792Sgshapiroptrhash(p)
30590792Sgshapiro	void *p;
30690792Sgshapiro{
30790792Sgshapiro	int h;
30890792Sgshapiro
30990792Sgshapiro	if (sizeof(void*) == 4 && sizeof(unsigned long) == 4)
31090792Sgshapiro	{
31190792Sgshapiro		unsigned long n = (unsigned long)p;
31290792Sgshapiro
31390792Sgshapiro		h = hashtab[n & 0xFF];
31490792Sgshapiro		h = hashtab[h ^ ((n >> 8) & 0xFF)];
31590792Sgshapiro		h = hashtab[h ^ ((n >> 16) & 0xFF)];
31690792Sgshapiro		h = hashtab[h ^ ((n >> 24) & 0xFF)];
31790792Sgshapiro	}
31890792Sgshapiro# if 0
31990792Sgshapiro	else if (sizeof(void*) == 8 && sizeof(unsigned long) == 8)
32090792Sgshapiro	{
32190792Sgshapiro		unsigned long n = (unsigned long)p;
32290792Sgshapiro
32390792Sgshapiro		h = hashtab[n & 0xFF];
32490792Sgshapiro		h = hashtab[h ^ ((n >> 8) & 0xFF)];
32590792Sgshapiro		h = hashtab[h ^ ((n >> 16) & 0xFF)];
32690792Sgshapiro		h = hashtab[h ^ ((n >> 24) & 0xFF)];
32790792Sgshapiro		h = hashtab[h ^ ((n >> 32) & 0xFF)];
32890792Sgshapiro		h = hashtab[h ^ ((n >> 40) & 0xFF)];
32990792Sgshapiro		h = hashtab[h ^ ((n >> 48) & 0xFF)];
33090792Sgshapiro		h = hashtab[h ^ ((n >> 56) & 0xFF)];
33190792Sgshapiro	}
33290792Sgshapiro# endif /* 0 */
33390792Sgshapiro	else
33490792Sgshapiro	{
33590792Sgshapiro		unsigned char *cp = (unsigned char *)&p;
33690792Sgshapiro		int i;
33790792Sgshapiro
33890792Sgshapiro		h = 0;
33990792Sgshapiro		for (i = 0; i < sizeof(void*); ++i)
34090792Sgshapiro			h = hashtab[h ^ cp[i]];
34190792Sgshapiro	}
34290792Sgshapiro	return h;
34390792Sgshapiro}
34490792Sgshapiro
34590792Sgshapiro/*
34690792Sgshapiro**  SM_MALLOC_TAGGED -- wrapper around malloc(), debugging version.
34790792Sgshapiro**
34890792Sgshapiro**	Parameters:
34990792Sgshapiro**		size -- size of requested memory.
35090792Sgshapiro**		tag -- tag for debugging.
35190792Sgshapiro**		num -- additional value for debugging.
35290792Sgshapiro**		group -- heap group for debugging.
35390792Sgshapiro**
35490792Sgshapiro**	Returns:
35590792Sgshapiro**		Pointer to memory region.
35690792Sgshapiro*/
35790792Sgshapiro
35890792Sgshapirovoid *
35990792Sgshapirosm_malloc_tagged(size, tag, num, group)
36090792Sgshapiro	size_t size;
36190792Sgshapiro	char *tag;
36290792Sgshapiro	int num;
36390792Sgshapiro	int group;
36490792Sgshapiro{
36590792Sgshapiro	void *ptr;
36690792Sgshapiro
36790792Sgshapiro	if (!HEAP_CHECK)
36890792Sgshapiro	{
36990792Sgshapiro		ENTER_CRITICAL();
37090792Sgshapiro		ptr = malloc(MALLOC_SIZE(size));
37190792Sgshapiro		LEAVE_CRITICAL();
37290792Sgshapiro		return ptr;
37390792Sgshapiro	}
37490792Sgshapiro
37590792Sgshapiro	if (sm_xtrap_check())
37690792Sgshapiro		return NULL;
37790792Sgshapiro	if (sm_debug_active(&SmHeapLimit, 1)
37890792Sgshapiro	    && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size)
37990792Sgshapiro		return NULL;
38090792Sgshapiro	ENTER_CRITICAL();
38190792Sgshapiro	ptr = malloc(MALLOC_SIZE(size));
38290792Sgshapiro	LEAVE_CRITICAL();
38390792Sgshapiro	if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group))
38490792Sgshapiro	{
38590792Sgshapiro		ENTER_CRITICAL();
38690792Sgshapiro		free(ptr);
38790792Sgshapiro		LEAVE_CRITICAL();
38890792Sgshapiro		ptr = NULL;
38990792Sgshapiro	}
39090792Sgshapiro	SmHeapTotal += size;
39190792Sgshapiro	if (SmHeapTotal > SmHeapMaxTotal)
39290792Sgshapiro		SmHeapMaxTotal = SmHeapTotal;
39390792Sgshapiro	return ptr;
39490792Sgshapiro}
39590792Sgshapiro
39690792Sgshapiro/*
39790792Sgshapiro**  SM_MALLOC_TAGGED_X -- wrapper around malloc(), debugging version.
39890792Sgshapiro**
39990792Sgshapiro**	Parameters:
40090792Sgshapiro**		size -- size of requested memory.
40190792Sgshapiro**		tag -- tag for debugging.
40290792Sgshapiro**		num -- additional value for debugging.
40390792Sgshapiro**		group -- heap group for debugging.
40490792Sgshapiro**
40590792Sgshapiro**	Returns:
40690792Sgshapiro**		Pointer to memory region.
40790792Sgshapiro**
40890792Sgshapiro**	Exceptions:
40990792Sgshapiro**		F:sm_heap -- out of memory
41090792Sgshapiro*/
41190792Sgshapiro
41290792Sgshapirovoid *
41390792Sgshapirosm_malloc_tagged_x(size, tag, num, group)
41490792Sgshapiro	size_t size;
41590792Sgshapiro	char *tag;
41690792Sgshapiro	int num;
41790792Sgshapiro	int group;
41890792Sgshapiro{
41990792Sgshapiro	void *ptr;
42090792Sgshapiro
42190792Sgshapiro	if (!HEAP_CHECK)
42290792Sgshapiro	{
42390792Sgshapiro		ENTER_CRITICAL();
42490792Sgshapiro		ptr = malloc(MALLOC_SIZE(size));
42590792Sgshapiro		LEAVE_CRITICAL();
42690792Sgshapiro		if (ptr == NULL)
42790792Sgshapiro			sm_exc_raise_x(&SmHeapOutOfMemory);
42890792Sgshapiro		return ptr;
42990792Sgshapiro	}
43090792Sgshapiro
43190792Sgshapiro	sm_xtrap_raise_x(&SmHeapOutOfMemory);
43290792Sgshapiro	if (sm_debug_active(&SmHeapLimit, 1)
43390792Sgshapiro	    && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size)
43490792Sgshapiro	{
43590792Sgshapiro		sm_exc_raise_x(&SmHeapOutOfMemory);
43690792Sgshapiro	}
43790792Sgshapiro	ENTER_CRITICAL();
43890792Sgshapiro	ptr = malloc(MALLOC_SIZE(size));
43990792Sgshapiro	LEAVE_CRITICAL();
44090792Sgshapiro	if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group))
44190792Sgshapiro	{
44290792Sgshapiro		ENTER_CRITICAL();
44390792Sgshapiro		free(ptr);
44490792Sgshapiro		LEAVE_CRITICAL();
44590792Sgshapiro		ptr = NULL;
44690792Sgshapiro	}
44790792Sgshapiro	if (ptr == NULL)
44890792Sgshapiro		sm_exc_raise_x(&SmHeapOutOfMemory);
44990792Sgshapiro	SmHeapTotal += size;
45090792Sgshapiro	if (SmHeapTotal > SmHeapMaxTotal)
45190792Sgshapiro		SmHeapMaxTotal = SmHeapTotal;
45290792Sgshapiro	return ptr;
45390792Sgshapiro}
45490792Sgshapiro
45590792Sgshapiro/*
45690792Sgshapiro**  SM_HEAP_REGISTER -- register a pointer into the heap for debugging.
45790792Sgshapiro**
45890792Sgshapiro**	Parameters:
45990792Sgshapiro**		ptr -- pointer to register.
46090792Sgshapiro**		size -- size of requested memory.
46190792Sgshapiro**		tag -- tag for debugging.
46290792Sgshapiro**		num -- additional value for debugging.
46390792Sgshapiro**		group -- heap group for debugging.
46490792Sgshapiro**
46590792Sgshapiro**	Returns:
46690792Sgshapiro**		true iff successfully registered (not yet in table).
46790792Sgshapiro*/
46890792Sgshapiro
46990792Sgshapirobool
47090792Sgshapirosm_heap_register(ptr, size, tag, num, group)
47190792Sgshapiro	void *ptr;
47290792Sgshapiro	size_t size;
47390792Sgshapiro	char *tag;
47490792Sgshapiro	int num;
47590792Sgshapiro	int group;
47690792Sgshapiro{
47790792Sgshapiro	int i;
47890792Sgshapiro	SM_HEAP_ITEM_T *hi;
47990792Sgshapiro
48090792Sgshapiro	if (!HEAP_CHECK)
48190792Sgshapiro		return true;
48290792Sgshapiro	SM_REQUIRE(ptr != NULL);
48390792Sgshapiro	i = ptrhash(ptr);
48490792Sgshapiro# if SM_CHECK_REQUIRE
48590792Sgshapiro
48690792Sgshapiro	/*
48790792Sgshapiro	** We require that ptr is not already in SmHeapTable.
48890792Sgshapiro	*/
48990792Sgshapiro
49090792Sgshapiro	for (hi = SmHeapTable[i]; hi != NULL; hi = hi->hi_next)
49190792Sgshapiro	{
49290792Sgshapiro		if (hi->hi_ptr == ptr)
49390792Sgshapiro			sm_abort("sm_heap_register: ptr %p is already registered (%s:%d)",
49490792Sgshapiro				 ptr, hi->hi_tag, hi->hi_num);
49590792Sgshapiro	}
49690792Sgshapiro# endif /* SM_CHECK_REQUIRE */
49790792Sgshapiro	ENTER_CRITICAL();
49890792Sgshapiro	hi = (SM_HEAP_ITEM_T *) malloc(sizeof(SM_HEAP_ITEM_T));
49990792Sgshapiro	LEAVE_CRITICAL();
50090792Sgshapiro	if (hi == NULL)
50190792Sgshapiro		return false;
50290792Sgshapiro	hi->hi_ptr = ptr;
50390792Sgshapiro	hi->hi_size = size;
50490792Sgshapiro	hi->hi_tag = tag;
50590792Sgshapiro	hi->hi_num = num;
50690792Sgshapiro	hi->hi_group = group;
50790792Sgshapiro	hi->hi_next = SmHeapTable[i];
50890792Sgshapiro	SmHeapTable[i] = hi;
50990792Sgshapiro	return true;
51090792Sgshapiro}
51190792Sgshapiro/*
51290792Sgshapiro**  SM_REALLOC -- wrapper for realloc(), debugging version.
51390792Sgshapiro**
51490792Sgshapiro**	Parameters:
51590792Sgshapiro**		ptr -- pointer to old memory area.
51690792Sgshapiro**		size -- size of requested memory.
51790792Sgshapiro**
51890792Sgshapiro**	Returns:
51990792Sgshapiro**		Pointer to new memory area, NULL on failure.
52090792Sgshapiro*/
52190792Sgshapiro
52290792Sgshapirovoid *
52390792Sgshapirosm_realloc(ptr, size)
52490792Sgshapiro	void *ptr;
52590792Sgshapiro	size_t size;
52690792Sgshapiro{
52790792Sgshapiro	void *newptr;
52890792Sgshapiro	SM_HEAP_ITEM_T *hi, **hp;
52990792Sgshapiro
53090792Sgshapiro	if (!HEAP_CHECK)
53190792Sgshapiro	{
53290792Sgshapiro		ENTER_CRITICAL();
53390792Sgshapiro		newptr = realloc(ptr, MALLOC_SIZE(size));
53490792Sgshapiro		LEAVE_CRITICAL();
53590792Sgshapiro		return newptr;
53690792Sgshapiro	}
53790792Sgshapiro
53890792Sgshapiro	if (ptr == NULL)
53990792Sgshapiro		return sm_malloc_tagged(size, "realloc", 0, SmHeapGroup);
54090792Sgshapiro
54190792Sgshapiro	for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
54290792Sgshapiro	{
54390792Sgshapiro		if ((**hp).hi_ptr == ptr)
54490792Sgshapiro		{
54590792Sgshapiro			if (sm_xtrap_check())
54690792Sgshapiro				return NULL;
54790792Sgshapiro			hi = *hp;
54890792Sgshapiro			if (sm_debug_active(&SmHeapLimit, 1)
54990792Sgshapiro			    && sm_debug_level(&SmHeapLimit)
55090792Sgshapiro			       < SmHeapTotal - hi->hi_size + size)
55190792Sgshapiro			{
55290792Sgshapiro				return NULL;
55390792Sgshapiro			}
55490792Sgshapiro			ENTER_CRITICAL();
55590792Sgshapiro			newptr = realloc(ptr, MALLOC_SIZE(size));
55690792Sgshapiro			LEAVE_CRITICAL();
55790792Sgshapiro			if (newptr == NULL)
55890792Sgshapiro				return NULL;
55990792Sgshapiro			SmHeapTotal = SmHeapTotal - hi->hi_size + size;
56090792Sgshapiro			if (SmHeapTotal > SmHeapMaxTotal)
56190792Sgshapiro				SmHeapMaxTotal = SmHeapTotal;
56290792Sgshapiro			*hp = hi->hi_next;
56390792Sgshapiro			hi->hi_ptr = newptr;
56490792Sgshapiro			hi->hi_size = size;
56590792Sgshapiro			hp = &SmHeapTable[ptrhash(newptr)];
56690792Sgshapiro			hi->hi_next = *hp;
56790792Sgshapiro			*hp = hi;
56890792Sgshapiro			return newptr;
56990792Sgshapiro		}
57090792Sgshapiro	}
57190792Sgshapiro	sm_abort("sm_realloc: bad argument (%p)", ptr);
57290792Sgshapiro	/* NOTREACHED */
57390792Sgshapiro	return NULL;	/* keep Irix compiler happy */
57490792Sgshapiro}
57590792Sgshapiro
57690792Sgshapiro/*
57790792Sgshapiro**  SM_REALLOC_X -- wrapper for realloc(), debugging version.
57890792Sgshapiro**
57990792Sgshapiro**	Parameters:
58090792Sgshapiro**		ptr -- pointer to old memory area.
58190792Sgshapiro**		size -- size of requested memory.
58290792Sgshapiro**
58390792Sgshapiro**	Returns:
58490792Sgshapiro**		Pointer to new memory area.
58590792Sgshapiro**
58690792Sgshapiro**	Exceptions:
58790792Sgshapiro**		F:sm_heap -- out of memory
58890792Sgshapiro*/
58990792Sgshapiro
59090792Sgshapirovoid *
59190792Sgshapirosm_realloc_x(ptr, size)
59290792Sgshapiro	void *ptr;
59390792Sgshapiro	size_t size;
59490792Sgshapiro{
59590792Sgshapiro	void *newptr;
59690792Sgshapiro	SM_HEAP_ITEM_T *hi, **hp;
59790792Sgshapiro
59890792Sgshapiro	if (!HEAP_CHECK)
59990792Sgshapiro	{
60090792Sgshapiro		ENTER_CRITICAL();
60190792Sgshapiro		newptr = realloc(ptr, MALLOC_SIZE(size));
60290792Sgshapiro		LEAVE_CRITICAL();
60390792Sgshapiro		if (newptr == NULL)
60490792Sgshapiro			sm_exc_raise_x(&SmHeapOutOfMemory);
60590792Sgshapiro		return newptr;
60690792Sgshapiro	}
60790792Sgshapiro
60890792Sgshapiro	if (ptr == NULL)
60990792Sgshapiro		return sm_malloc_tagged_x(size, "realloc", 0, SmHeapGroup);
61090792Sgshapiro
61190792Sgshapiro	for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
61290792Sgshapiro	{
61390792Sgshapiro		if ((**hp).hi_ptr == ptr)
61490792Sgshapiro		{
61590792Sgshapiro			sm_xtrap_raise_x(&SmHeapOutOfMemory);
61690792Sgshapiro			hi = *hp;
61790792Sgshapiro			if (sm_debug_active(&SmHeapLimit, 1)
61890792Sgshapiro			    && sm_debug_level(&SmHeapLimit)
61990792Sgshapiro			       < SmHeapTotal - hi->hi_size + size)
62090792Sgshapiro			{
62190792Sgshapiro				sm_exc_raise_x(&SmHeapOutOfMemory);
62290792Sgshapiro			}
62390792Sgshapiro			ENTER_CRITICAL();
62490792Sgshapiro			newptr = realloc(ptr, MALLOC_SIZE(size));
62590792Sgshapiro			LEAVE_CRITICAL();
62690792Sgshapiro			if (newptr == NULL)
62790792Sgshapiro				sm_exc_raise_x(&SmHeapOutOfMemory);
62890792Sgshapiro			SmHeapTotal = SmHeapTotal - hi->hi_size + size;
62990792Sgshapiro			if (SmHeapTotal > SmHeapMaxTotal)
63090792Sgshapiro				SmHeapMaxTotal = SmHeapTotal;
63190792Sgshapiro			*hp = hi->hi_next;
63290792Sgshapiro			hi->hi_ptr = newptr;
63390792Sgshapiro			hi->hi_size = size;
63490792Sgshapiro			hp = &SmHeapTable[ptrhash(newptr)];
63590792Sgshapiro			hi->hi_next = *hp;
63690792Sgshapiro			*hp = hi;
63790792Sgshapiro			return newptr;
63890792Sgshapiro		}
63990792Sgshapiro	}
64090792Sgshapiro	sm_abort("sm_realloc_x: bad argument (%p)", ptr);
64190792Sgshapiro	/* NOTREACHED */
64290792Sgshapiro	return NULL;	/* keep Irix compiler happy */
64390792Sgshapiro}
64490792Sgshapiro
64590792Sgshapiro/*
64690792Sgshapiro**  SM_FREE_TAGGED -- wrapper around free(), debugging version.
64790792Sgshapiro**
64890792Sgshapiro**	Parameters:
64990792Sgshapiro**		ptr -- pointer to memory region.
65090792Sgshapiro**		tag -- tag for debugging.
65190792Sgshapiro**		num -- additional value for debugging.
65290792Sgshapiro**
65390792Sgshapiro**	Returns:
65490792Sgshapiro**		none.
65590792Sgshapiro*/
65690792Sgshapiro
65790792Sgshapirovoid
65890792Sgshapirosm_free_tagged(ptr, tag, num)
65990792Sgshapiro	void *ptr;
66090792Sgshapiro	char *tag;
66190792Sgshapiro	int num;
66290792Sgshapiro{
66390792Sgshapiro	SM_HEAP_ITEM_T **hp;
66490792Sgshapiro
66590792Sgshapiro	if (ptr == NULL)
66690792Sgshapiro		return;
66790792Sgshapiro	if (!HEAP_CHECK)
66890792Sgshapiro	{
66990792Sgshapiro		ENTER_CRITICAL();
67090792Sgshapiro		free(ptr);
67190792Sgshapiro		LEAVE_CRITICAL();
67290792Sgshapiro		return;
67390792Sgshapiro	}
67490792Sgshapiro	for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next)
67590792Sgshapiro	{
67690792Sgshapiro		if ((**hp).hi_ptr == ptr)
67790792Sgshapiro		{
67890792Sgshapiro			SM_HEAP_ITEM_T *hi = *hp;
67990792Sgshapiro
68090792Sgshapiro			*hp = hi->hi_next;
68190792Sgshapiro
68290792Sgshapiro			/*
68390792Sgshapiro			**  Fill the block with zeros before freeing.
68490792Sgshapiro			**  This is intended to catch problems with
68590792Sgshapiro			**  dangling pointers.  The block is filled with
68690792Sgshapiro			**  zeros, not with some non-zero value, because
68790792Sgshapiro			**  it is common practice in some C code to store
68890792Sgshapiro			**  a zero in a structure member before freeing the
68990792Sgshapiro			**  structure, as a defense against dangling pointers.
69090792Sgshapiro			*/
69190792Sgshapiro
69290792Sgshapiro			(void) memset(ptr, 0, hi->hi_size);
69390792Sgshapiro			SmHeapTotal -= hi->hi_size;
69490792Sgshapiro			ENTER_CRITICAL();
69590792Sgshapiro			free(ptr);
69690792Sgshapiro			free(hi);
69790792Sgshapiro			LEAVE_CRITICAL();
69890792Sgshapiro			return;
69990792Sgshapiro		}
70090792Sgshapiro	}
70190792Sgshapiro	sm_abort("sm_free: bad argument (%p) (%s:%d)", ptr, tag, num);
70290792Sgshapiro}
70390792Sgshapiro
70490792Sgshapiro/*
70590792Sgshapiro**  SM_HEAP_CHECKPTR_TAGGED -- check whether ptr is a valid argument to sm_free
70690792Sgshapiro**
70790792Sgshapiro**	Parameters:
70890792Sgshapiro**		ptr -- pointer to memory region.
70990792Sgshapiro**		tag -- tag for debugging.
71090792Sgshapiro**		num -- additional value for debugging.
71190792Sgshapiro**
71290792Sgshapiro**	Returns:
71390792Sgshapiro**		none.
71490792Sgshapiro**
71590792Sgshapiro**	Side Effects:
71690792Sgshapiro**		aborts if check fails.
71790792Sgshapiro*/
71890792Sgshapiro
71990792Sgshapirovoid
72090792Sgshapirosm_heap_checkptr_tagged(ptr, tag, num)
72190792Sgshapiro	void *ptr;
72290792Sgshapiro	char *tag;
72390792Sgshapiro	int num;
72490792Sgshapiro{
72590792Sgshapiro	SM_HEAP_ITEM_T *hp;
72690792Sgshapiro
72790792Sgshapiro	if (!HEAP_CHECK)
72890792Sgshapiro		return;
72990792Sgshapiro	if (ptr == NULL)
73090792Sgshapiro		return;
73190792Sgshapiro	for (hp = SmHeapTable[ptrhash(ptr)]; hp != NULL; hp = hp->hi_next)
73290792Sgshapiro	{
73390792Sgshapiro		if (hp->hi_ptr == ptr)
73490792Sgshapiro			return;
73590792Sgshapiro	}
73690792Sgshapiro	sm_abort("sm_heap_checkptr(%p): bad ptr (%s:%d)", ptr, tag, num);
73790792Sgshapiro}
73890792Sgshapiro
73990792Sgshapiro/*
74090792Sgshapiro**  SM_HEAP_REPORT -- output "map" of used heap.
74190792Sgshapiro**
74290792Sgshapiro**	Parameters:
74390792Sgshapiro**		stream -- the file pointer to write to.
74490792Sgshapiro**		verbosity -- how much info?
74590792Sgshapiro**
74690792Sgshapiro**	Returns:
74790792Sgshapiro**		none.
74890792Sgshapiro*/
74990792Sgshapiro
75090792Sgshapirovoid
75190792Sgshapirosm_heap_report(stream, verbosity)
75290792Sgshapiro	SM_FILE_T *stream;
75390792Sgshapiro	int verbosity;
75490792Sgshapiro{
75590792Sgshapiro	int i;
75690792Sgshapiro	unsigned long group0total, group1total, otherstotal, grandtotal;
75790792Sgshapiro
75890792Sgshapiro	if (!HEAP_CHECK || verbosity <= 0)
75990792Sgshapiro		return;
76090792Sgshapiro	group0total = group1total = otherstotal = grandtotal = 0;
76190792Sgshapiro	for (i = 0; i < sizeof(SmHeapTable) / sizeof(SmHeapTable[0]); ++i)
76290792Sgshapiro	{
76390792Sgshapiro		SM_HEAP_ITEM_T *hi = SmHeapTable[i];
76490792Sgshapiro
76590792Sgshapiro		while (hi != NULL)
76690792Sgshapiro		{
76790792Sgshapiro			if (verbosity > 2
76890792Sgshapiro			    || (verbosity > 1 && hi->hi_group != 0))
76990792Sgshapiro			{
77090792Sgshapiro				sm_io_fprintf(stream, SM_TIME_DEFAULT,
77190792Sgshapiro					"%4d %*lx %7lu bytes",
77290792Sgshapiro					hi->hi_group,
77390792Sgshapiro					(int) sizeof(void *) * 2,
77490792Sgshapiro					(long)hi->hi_ptr,
77590792Sgshapiro					(unsigned long)hi->hi_size);
77690792Sgshapiro				if (hi->hi_tag != NULL)
77790792Sgshapiro				{
77890792Sgshapiro					sm_io_fprintf(stream, SM_TIME_DEFAULT,
77990792Sgshapiro						"  %s",
78090792Sgshapiro						hi->hi_tag);
78190792Sgshapiro					if (hi->hi_num)
78290792Sgshapiro					{
78390792Sgshapiro						sm_io_fprintf(stream,
78490792Sgshapiro							SM_TIME_DEFAULT,
78590792Sgshapiro							":%d",
78690792Sgshapiro							hi->hi_num);
78790792Sgshapiro					}
78890792Sgshapiro				}
78990792Sgshapiro				sm_io_fprintf(stream, SM_TIME_DEFAULT, "\n");
79090792Sgshapiro			}
79190792Sgshapiro			switch (hi->hi_group)
79290792Sgshapiro			{
79390792Sgshapiro			  case 0:
79490792Sgshapiro				group0total += hi->hi_size;
79590792Sgshapiro				break;
79690792Sgshapiro			  case 1:
79790792Sgshapiro				group1total += hi->hi_size;
79890792Sgshapiro				break;
79990792Sgshapiro			  default:
80090792Sgshapiro				otherstotal += hi->hi_size;
80190792Sgshapiro				break;
80290792Sgshapiro			}
80390792Sgshapiro			grandtotal += hi->hi_size;
80490792Sgshapiro			hi = hi->hi_next;
80590792Sgshapiro		}
80690792Sgshapiro	}
80790792Sgshapiro	sm_io_fprintf(stream, SM_TIME_DEFAULT,
80890792Sgshapiro		"heap max=%lu, total=%lu, ",
80990792Sgshapiro		(unsigned long) SmHeapMaxTotal, grandtotal);
81090792Sgshapiro	sm_io_fprintf(stream, SM_TIME_DEFAULT,
81190792Sgshapiro		"group 0=%lu, group 1=%lu, others=%lu\n",
81290792Sgshapiro		group0total, group1total, otherstotal);
81390792Sgshapiro	if (grandtotal != SmHeapTotal)
81490792Sgshapiro	{
81590792Sgshapiro		sm_io_fprintf(stream, SM_TIME_DEFAULT,
81690792Sgshapiro			"BUG => SmHeapTotal: got %lu, expected %lu\n",
81790792Sgshapiro			(unsigned long) SmHeapTotal, grandtotal);
81890792Sgshapiro	}
81990792Sgshapiro}
82090792Sgshapiro#endif /* !SM_HEAP_CHECK */
821