summaryrefslogtreecommitdiff
path: root/usr/src/lib/watchmalloc/common/malloc.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/watchmalloc/common/malloc.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/watchmalloc/common/malloc.c')
-rw-r--r--usr/src/lib/watchmalloc/common/malloc.c1480
1 files changed, 1480 insertions, 0 deletions
diff --git a/usr/src/lib/watchmalloc/common/malloc.c b/usr/src/lib/watchmalloc/common/malloc.c
new file mode 100644
index 0000000000..8ea421b939
--- /dev/null
+++ b/usr/src/lib/watchmalloc/common/malloc.c
@@ -0,0 +1,1480 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1996-1997, 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/* Copyright (c) 1988 AT&T */
+/* All Rights Reserved */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.30 */
+
+/*
+ * Memory management: malloc(), realloc(), free(), memalign().
+ *
+ * The following #-parameters may be redefined:
+ * GETCORE: a function to get more core memory.
+ * GETCORE(0) is assumed to return the next available
+ * address. Default is 'sbrk'.
+ * ERRCORE: the error code as returned by GETCORE.
+ * Default is ((char *)(-1)).
+ * CORESIZE: a desired unit (measured in bytes) to be used
+ * with GETCORE. Default is (1024*ALIGN).
+ *
+ * This algorithm is based on a best fit strategy with lists of
+ * free elts maintained in a self-adjusting binary tree. Each list
+ * contains all elts of the same size. The tree is ordered by size.
+ * For results on self-adjusting trees, see the paper:
+ * Self-Adjusting Binary Trees,
+ * DD Sleator & RE Tarjan, JACM 1985.
+ *
+ * The header of a block contains the size of the data part in bytes.
+ * Since the size of a block is 0%4, the low two bits of the header
+ * are free and used as follows:
+ *
+ * BIT0: 1 for busy (block is in use), 0 for free.
+ * BIT1: if the block is busy, this bit is 1 if the
+ * preceding block in contiguous memory is free.
+ * Otherwise, it is always 0.
+ */
+
+#include "mallint.h"
+
+static mutex_t __watch_malloc_lock = DEFAULTMUTEX;
+
+static TREE *Root; /* root of the free tree */
+static TREE *Bottom; /* the last free chunk in the arena */
+static char *Baddr; /* current high address of the arena */
+
+static void t_delete(TREE *);
+static void t_splay(TREE *);
+static void realfree(void *);
+static void *malloc_unlocked(size_t);
+static void free_unlocked(void *);
+static TREE *morecore(size_t);
+
+static void protect(TREE *);
+static void unprotect(TREE *);
+
+#define FREEPAT 0
+#define LIVEPAT 1
+
+/*
+ * Patterns to be copied into freed blocks and allocated blocks.
+ * 0xfeedbeef and 0xfeedface are invalid pointer values in all programs.
+ */
+static uint64_t patterns[2] = {
+ 0xdeadbeefdeadbeef, /* pattern in a freed block */
+ 0xbaddcafebaddcafe /* pattern in an allocated block */
+};
+
+static void
+copy_pattern(int pat, TREE *tp)
+{
+ uint64_t pattern = patterns[pat];
+ size_t sz = SIZE(tp) / sizeof (uint64_t);
+ /* LINTED improper alignment */
+ uint64_t *datap = (uint64_t *)DATA(tp);
+
+ while (sz--)
+ *datap++ = pattern;
+}
+
+/*
+ * Keep lists of small blocks, LIFO order.
+ */
+static TREE *List[MINSIZE/WORDSIZE-1];
+static TREE *Last[MINSIZE/WORDSIZE-1];
+
+/* number of blocks to get at one time */
+#define NPS (WORDSIZE*8)
+
+static void *
+smalloc(size_t size)
+{
+ TREE *tp;
+ size_t i;
+
+ ASSERT(size % WORDSIZE == 0);
+ /* want to return a unique pointer on malloc(0) */
+ if (size == 0)
+ size = WORDSIZE;
+
+ /* list to use */
+ i = size / WORDSIZE - 1;
+
+ if (List[i] == NULL) {
+ TREE *np;
+ int n;
+ ASSERT((size + WORDSIZE) * NPS >= MINSIZE);
+
+ /* get NPS of these block types */
+ if ((np = malloc_unlocked((size + WORDSIZE)*NPS)) == NULL)
+ return (NULL);
+
+ /* make them into a link list */
+ for (n = 0, List[i] = np; n < NPS; ++n) {
+ tp = np;
+ SIZE(tp) = size;
+ copy_pattern(FREEPAT, tp);
+ if (n == NPS - 1) {
+ Last[i] = tp;
+ np = NULL;
+ } else {
+ /* LINTED improper alignment */
+ np = NEXT(tp);
+ }
+ AFTER(tp) = np;
+ protect(tp);
+ }
+ }
+
+ /* allocate from the head of the queue */
+ tp = List[i];
+ unprotect(tp);
+ if ((List[i] = AFTER(tp)) == NULL)
+ Last[i] = NULL;
+ copy_pattern(LIVEPAT, tp);
+ SETBIT0(SIZE(tp));
+ protect(tp);
+ return (DATA(tp));
+}
+
+void *
+malloc(size_t size)
+{
+ void *ret;
+ _mutex_lock(&__watch_malloc_lock);
+ ret = malloc_unlocked(size);
+ _mutex_unlock(&__watch_malloc_lock);
+ return (ret);
+}
+
+static void *
+malloc_unlocked(size_t size)
+{
+ size_t n;
+ TREE *tp, *sp, *tmp;
+
+ COUNT(nmalloc);
+ ASSERT(WORDSIZE == ALIGN);
+
+ /* check for size that could overflow calculations */
+ if (size > MAX_MALLOC) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ /* make sure that size is 0 mod ALIGN */
+ ROUND(size);
+
+ /* small blocks */
+ if (size < MINSIZE)
+ return (smalloc(size));
+
+ /* search for an elt of the right size */
+ sp = NULL;
+ n = 0;
+ if (Root) {
+ tp = Root;
+ for (;;) {
+ unprotect(tp);
+ if (SIZE(tp) >= size) { /* branch left */
+ if (n == 0 || n >= SIZE(tp)) {
+ sp = tp;
+ n = SIZE(tp);
+ }
+ if ((tmp = LEFT(tp)) != NULL) {
+ protect(tp);
+ tp = tmp;
+ } else {
+ protect(tp);
+ break;
+ }
+ } else { /* branch right */
+ if ((tmp = RIGHT(tp)) != NULL) {
+ protect(tp);
+ tp = tmp;
+ } else {
+ protect(tp);
+ break;
+ }
+ }
+ }
+
+ if (sp) {
+ unprotect(sp);
+ t_delete(sp);
+ } else if (tp != Root) {
+ /* make the searched-to element the root */
+ unprotect(tp);
+ t_splay(tp);
+ protect(tp);
+ Root = tp;
+ }
+ }
+
+ /* if found none fitted in the tree */
+ if (sp == NULL) {
+ if (Bottom) {
+ unprotect(Bottom);
+ if (size <= SIZE(Bottom)) {
+ sp = Bottom;
+ CLRBITS01(SIZE(sp));
+ } else {
+ protect(Bottom);
+ if ((sp = morecore(size)) == NULL)
+ return (NULL);
+ }
+ } else {
+ if ((sp = morecore(size)) == NULL)
+ return (NULL);
+ }
+ }
+
+ /* tell the forward neighbor that we're busy */
+ /* LINTED improper alignment */
+ tmp = NEXT(sp);
+ unprotect(tmp);
+ CLRBIT1(SIZE(tmp));
+ ASSERT(ISBIT0(SIZE(tmp)));
+ protect(tmp);
+
+leftover:
+ /* if the leftover is enough for a new free piece */
+ if ((n = (SIZE(sp) - size)) >= MINSIZE + WORDSIZE) {
+ n -= WORDSIZE;
+ SIZE(sp) = size;
+ /* LINTED improper alignment */
+ tp = NEXT(sp);
+ SIZE(tp) = n | BIT0;
+ realfree(DATA(tp));
+ } else if (BOTTOM(sp))
+ Bottom = NULL;
+
+ /* return the allocated space */
+ copy_pattern(LIVEPAT, sp);
+ SIZE(sp) |= BIT0;
+ protect(sp);
+ return (DATA(sp));
+}
+
+/*
+ * realloc().
+ * If the block size is increasing, we try forward merging first.
+ * This is not best-fit but it avoids some data recopying.
+ */
+void *
+realloc(void *old, size_t size)
+{
+ TREE *tp, *np;
+ size_t ts;
+ char *new;
+
+ COUNT(nrealloc);
+
+ /* check for size that could overflow calculations */
+ if (size > MAX_MALLOC) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ /* pointer to the block */
+ _mutex_lock(&__watch_malloc_lock);
+ if (old == NULL) {
+ new = malloc_unlocked(size);
+ _mutex_unlock(&__watch_malloc_lock);
+ return (new);
+ }
+
+ /* make sure that size is 0 mod ALIGN */
+ ROUND(size);
+
+ /* LINTED improper alignment */
+ tp = BLOCK(old);
+ unprotect(tp);
+ ts = SIZE(tp);
+
+ /* if the block was freed, data has been destroyed. */
+ if (!ISBIT0(ts)) {
+ /* XXX; complain here! */
+ protect(tp);
+ _mutex_unlock(&__watch_malloc_lock);
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ CLRBITS01(SIZE(tp));
+ if (size == SIZE(tp)) { /* nothing to do */
+ SIZE(tp) = ts;
+ protect(tp);
+ _mutex_unlock(&__watch_malloc_lock);
+ return (old);
+ }
+
+ /* special cases involving small blocks */
+ if (size < MINSIZE || SIZE(tp) < MINSIZE) {
+ if (size == 0) {
+ SETOLD01(SIZE(tp), ts);
+ free_unlocked(old);
+ _mutex_unlock(&__watch_malloc_lock);
+ return (NULL);
+ }
+ goto call_malloc;
+ }
+
+ /* block is increasing in size, try merging the next block */
+ if (size > SIZE(tp)) {
+ /* LINTED improper alignment */
+ np = NEXT(tp);
+ unprotect(np);
+ if (ISBIT0(SIZE(np)))
+ protect(np);
+ else {
+ TREE *tmp;
+ ASSERT(SIZE(np) >= MINSIZE);
+ ASSERT(!ISBIT1(SIZE(np)));
+ SIZE(tp) += SIZE(np) + WORDSIZE;
+ if (np != Bottom)
+ t_delete(np);
+ else
+ Bottom = NULL;
+ /* LINTED improper alignment */
+ tmp = NEXT(np);
+ unprotect(tmp);
+ CLRBIT1(SIZE(tmp));
+ protect(tmp);
+ }
+
+ /* not enough & at TRUE end of memory, try extending core */
+ if (size > SIZE(tp) && BOTTOM(tp) && GETCORE(0) == Baddr) {
+ Bottom = tp;
+ protect(Bottom);
+ if ((tp = morecore(size)) == NULL) {
+ tp = Bottom;
+ Bottom = NULL;
+ unprotect(tp);
+ }
+ }
+ }
+
+ /* got enough space to use */
+ if (size <= SIZE(tp)) {
+ size_t n;
+chop_big:
+ if ((n = (SIZE(tp) - size)) >= MINSIZE + WORDSIZE) {
+ n -= WORDSIZE;
+ SIZE(tp) = size;
+ /* LINTED improper alignment */
+ np = NEXT(tp);
+ SIZE(np) = n | BIT0;
+ realfree(DATA(np));
+ } else if (BOTTOM(tp))
+ Bottom = NULL;
+
+ /* the previous block may be free */
+ SETOLD01(SIZE(tp), ts);
+ protect(tp);
+ _mutex_unlock(&__watch_malloc_lock);
+ return (old);
+ }
+
+call_malloc: /* call malloc to get a new block */
+ SETOLD01(SIZE(tp), ts);
+ if ((new = malloc_unlocked(size)) != NULL) {
+ CLRBITS01(ts);
+ if (ts > size)
+ ts = size;
+ (void) memcpy(new, old, ts);
+ free_unlocked(old);
+ _mutex_unlock(&__watch_malloc_lock);
+ return (new);
+ }
+
+ /*
+ * Attempt special case recovery allocations since malloc() failed:
+ *
+ * 1. size <= SIZE(tp) < MINSIZE
+ * Simply return the existing block
+ * 2. SIZE(tp) < size < MINSIZE
+ * malloc() may have failed to allocate the chunk of
+ * small blocks. Try asking for MINSIZE bytes.
+ * 3. size < MINSIZE <= SIZE(tp)
+ * malloc() may have failed as with 2. Change to
+ * MINSIZE allocation which is taken from the beginning
+ * of the current block.
+ * 4. MINSIZE <= SIZE(tp) < size
+ * If the previous block is free and the combination of
+ * these two blocks has at least size bytes, then merge
+ * the two blocks copying the existing contents backwards.
+ */
+ CLRBITS01(SIZE(tp));
+ if (SIZE(tp) < MINSIZE) {
+ if (size < SIZE(tp)) /* case 1. */ {
+ SETOLD01(SIZE(tp), ts);
+ protect(tp);
+ _mutex_unlock(&__watch_malloc_lock);
+ return (old);
+ } else if (size < MINSIZE) /* case 2. */ {
+ size = MINSIZE;
+ goto call_malloc;
+ }
+ } else if (size < MINSIZE) /* case 3. */ {
+ size = MINSIZE;
+ goto chop_big;
+ } else if (ISBIT1(ts)) {
+ np = LAST(tp);
+ unprotect(np);
+ if ((SIZE(np) + SIZE(tp) + WORDSIZE) >= size) {
+ ASSERT(!ISBIT0(SIZE(np)));
+ t_delete(np);
+ SIZE(np) += SIZE(tp) + WORDSIZE;
+ /*
+ * Since the copy may overlap, use memmove().
+ */
+ (void) memmove(DATA(np), old, SIZE(tp));
+ old = DATA(np);
+ tp = np;
+ CLRBIT1(ts);
+ goto chop_big;
+ }
+ protect(np);
+ }
+ SETOLD01(SIZE(tp), ts);
+ protect(tp);
+ _mutex_unlock(&__watch_malloc_lock);
+ /* malloc() sets errno */
+ return (NULL);
+}
+
+/*
+ * realfree().
+ * Coalescing of adjacent free blocks is done first.
+ * Then, the new free block is leaf-inserted into the free tree
+ * without splaying. This strategy does not guarantee the amortized
+ * O(nlogn) behaviour for the insert/delete/find set of operations
+ * on the tree. In practice, however, free is much more infrequent
+ * than malloc/realloc and the tree searches performed by these
+ * functions adequately keep the tree in balance.
+ */
+static void
+realfree(void *old)
+{
+ TREE *tp, *sp, *np, *tmp;
+ size_t ts, size;
+
+ COUNT(nfree);
+
+ /* pointer to the block */
+ /* LINTED improper alignment */
+ tp = BLOCK(old);
+ unprotect(tp);
+ ts = SIZE(tp);
+ if (!ISBIT0(ts)) { /* block is not busy; previously freed? */
+ protect(tp); /* force a watchpoint trap */
+ CLRBIT0(SIZE(tp));
+ return;
+ }
+ CLRBITS01(SIZE(tp));
+ copy_pattern(FREEPAT, tp);
+
+ /* small block, return it to the tail of its queue */
+ if (SIZE(tp) < MINSIZE) {
+ ASSERT(SIZE(tp) / WORDSIZE >= 1);
+ ts = SIZE(tp) / WORDSIZE - 1;
+ AFTER(tp) = NULL;
+ protect(tp);
+ if (List[ts] == NULL) {
+ List[ts] = tp;
+ Last[ts] = tp;
+ } else {
+ sp = Last[ts];
+ unprotect(sp);
+ AFTER(sp) = tp;
+ protect(sp);
+ Last[ts] = tp;
+ }
+ return;
+ }
+
+ /* see if coalescing with next block is warranted */
+ /* LINTED improper alignment */
+ np = NEXT(tp);
+ unprotect(np);
+ if (ISBIT0(SIZE(np)))
+ protect(np);
+ else {
+ if (np != Bottom)
+ t_delete(np);
+ SIZE(tp) += SIZE(np) + WORDSIZE;
+ }
+
+ /* the same with the preceding block */
+ if (ISBIT1(ts)) {
+ np = LAST(tp);
+ unprotect(np);
+ ASSERT(!ISBIT0(SIZE(np)));
+ ASSERT(np != Bottom);
+ t_delete(np);
+ SIZE(np) += SIZE(tp) + WORDSIZE;
+ tp = np;
+ }
+
+ /* initialize tree info */
+ PARENT(tp) = LEFT(tp) = RIGHT(tp) = LINKFOR(tp) = NULL;
+
+ /* set bottom block, or insert in the free tree */
+ if (BOTTOM(tp))
+ Bottom = tp;
+ else {
+ /* search for the place to insert */
+ if (Root) {
+ size = SIZE(tp);
+ np = Root;
+ for (;;) {
+ unprotect(np);
+ if (SIZE(np) > size) {
+ if ((tmp = LEFT(np)) != NULL) {
+ protect(np);
+ np = tmp;
+ } else {
+ LEFT(np) = tp;
+ PARENT(tp) = np;
+ protect(np);
+ break;
+ }
+ } else if (SIZE(np) < size) {
+ if ((tmp = RIGHT(np)) != NULL) {
+ protect(np);
+ np = tmp;
+ } else {
+ RIGHT(np) = tp;
+ PARENT(tp) = np;
+ protect(np);
+ break;
+ }
+ } else {
+ if ((sp = PARENT(np)) != NULL) {
+ unprotect(sp);
+ if (np == LEFT(sp))
+ LEFT(sp) = tp;
+ else
+ RIGHT(sp) = tp;
+ PARENT(tp) = sp;
+ protect(sp);
+ } else
+ Root = tp;
+
+ /* insert to head of list */
+ if ((sp = LEFT(np)) != NULL) {
+ unprotect(sp);
+ PARENT(sp) = tp;
+ protect(sp);
+ }
+ LEFT(tp) = sp;
+
+ if ((sp = RIGHT(np)) != NULL) {
+ unprotect(sp);
+ PARENT(sp) = tp;
+ protect(sp);
+ }
+ RIGHT(tp) = sp;
+
+ /* doubly link list */
+ LINKFOR(tp) = np;
+ LINKBAK(np) = tp;
+ SETNOTREE(np);
+ protect(np);
+
+ break;
+ }
+ }
+ } else {
+ Root = tp;
+ }
+ }
+
+ /*
+ * Tell next block that this one is free.
+ * The first WORD of the next block contains self's address.
+ */
+ /* LINTED improper alignment */
+ tmp = NEXT(tp);
+ unprotect(tmp);
+ /* LINTED improper alignment */
+ *(SELFP(tp)) = tp;
+ SETBIT1(SIZE(tmp));
+ ASSERT(ISBIT0(SIZE(tmp)));
+ protect(tmp);
+ protect(tp);
+}
+
+/*
+ * Get more core. Gaps in memory are noted as busy blocks.
+ */
+static TREE *
+morecore(size_t size)
+{
+ TREE *tp;
+ size_t n, offset, requestsize;
+ char *addr;
+
+ /* compute new amount of memory to get */
+ tp = Bottom;
+ n = size + 2 * WORDSIZE;
+ addr = GETCORE(0);
+
+ if (addr == ERRCORE)
+ /* errno set by GETCORE sbrk */
+ return (NULL);
+
+ /* need to pad size out so that addr is aligned */
+ if ((((size_t)addr) % ALIGN) != 0)
+ offset = ALIGN - (size_t)addr % ALIGN;
+ else
+ offset = 0;
+
+ if (tp)
+ unprotect(tp);
+
+ /* if not segmented memory, what we need may be smaller */
+ if (addr == Baddr) {
+ n -= WORDSIZE;
+ if (tp != NULL)
+ n -= SIZE(tp);
+ }
+
+ /* get a multiple of CORESIZE */
+ n = ((n - 1) / CORESIZE + 1) * CORESIZE;
+ requestsize = n + offset;
+
+ /* check if nsize request could overflow in GETCORE */
+ if (requestsize > MAX_MALLOC - (size_t)addr) {
+ if (tp)
+ protect(tp);
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ if (requestsize > MAX_GETCORE) {
+ intptr_t delta;
+ /*
+ * the value required is too big for GETCORE() to deal with
+ * in one go, so use GETCORE() at most 2 times instead.
+ * Argument to GETCORE() must be multiple of ALIGN.
+ * If not, GETCORE(-MAX_GETCORE) will not return brk point
+ * to previous value, but will be ALIGN more.
+ * This would leave a small hole.
+ */
+ delta = MAX_GETCORE;
+ while (delta > 0) {
+ if (GETCORE(delta) == ERRCORE) {
+ if (tp)
+ protect(tp);
+ if (addr != GETCORE(0))
+ (void) GETCORE(-MAX_GETCORE);
+ return (NULL);
+ }
+ requestsize -= MAX_GETCORE;
+ delta = requestsize;
+ }
+ } else if (GETCORE(requestsize) == ERRCORE) {
+ if (tp)
+ protect(tp);
+ return (NULL);
+ }
+
+ /* contiguous memory */
+ if (addr == Baddr) {
+ ASSERT(offset == 0);
+ if (tp) {
+ addr = ((char *)tp);
+ n += SIZE(tp) + 2 * WORDSIZE;
+ } else {
+ addr = Baddr - WORDSIZE;
+ n += WORDSIZE;
+ }
+ } else {
+ addr += offset;
+ }
+
+ /* new bottom address */
+ Baddr = addr + n;
+
+ /* new bottom block */
+ /* LINTED improper alignment */
+ tp = ((TREE *)addr);
+ SIZE(tp) = n - 2 * WORDSIZE;
+ ASSERT((SIZE(tp) % ALIGN) == 0);
+
+ /* reserved the last word to head any noncontiguous memory */
+ /* LINTED improper alignment */
+ SETBIT0(SIZE(NEXT(tp)));
+
+ /* non-contiguous memory, free old bottom block */
+ if (Bottom && Bottom != tp) {
+ SETBIT0(SIZE(Bottom));
+ realfree(DATA(Bottom));
+ }
+
+ return (tp);
+}
+
+/*
+ * Utility function to avoid protecting a tree node twice.
+ * Return true if tp is in the NULL-terminated array of tree nodes.
+ */
+static int
+in_list(TREE *tp, TREE **npp)
+{
+ TREE *sp;
+
+ while ((sp = *npp++) != NULL)
+ if (tp == sp)
+ return (1);
+ return (0);
+}
+
+/*
+ * Tree rotation functions (BU: bottom-up, TD: top-down).
+ * All functions are entered with the arguments unprotected.
+ * They must return in the same condition, with all other elements
+ * that have been unprotected during the operation re-protected.
+ */
+static void
+LEFT1(TREE *x, TREE *y)
+{
+ TREE *node[3];
+ TREE **npp = node;
+ TREE *tp;
+
+ if ((RIGHT(x) = LEFT(y)) != NULL) {
+ unprotect(*npp++ = RIGHT(x));
+ PARENT(RIGHT(x)) = x;
+ }
+ if ((PARENT(y) = PARENT(x)) != NULL) {
+ unprotect(*npp++ = PARENT(x));
+ if (LEFT(PARENT(x)) == x)
+ LEFT(PARENT(y)) = y;
+ else
+ RIGHT(PARENT(y)) = y;
+ }
+ LEFT(y) = x;
+ PARENT(x) = y;
+
+ *npp = NULL;
+ npp = node;
+ while ((tp = *npp++) != NULL)
+ if (tp != x && tp != y && !in_list(tp, npp))
+ protect(tp);
+}
+
+static void
+RIGHT1(TREE *x, TREE *y)
+{
+ TREE *node[3];
+ TREE **npp = node;
+ TREE *tp;
+
+ if ((LEFT(x) = RIGHT(y)) != NULL) {
+ unprotect(*npp++ = LEFT(x));
+ PARENT(LEFT(x)) = x;
+ }
+ if ((PARENT(y) = PARENT(x)) != NULL) {
+ unprotect(*npp++ = PARENT(x));
+ if (LEFT(PARENT(x)) == x)
+ LEFT(PARENT(y)) = y;
+ else
+ RIGHT(PARENT(y)) = y;
+ }
+ RIGHT(y) = x;
+ PARENT(x) = y;
+
+ *npp = NULL;
+ npp = node;
+ while ((tp = *npp++) != NULL)
+ if (tp != x && tp != y && !in_list(tp, npp))
+ protect(tp);
+}
+
+static void
+BULEFT2(TREE *x, TREE *y, TREE *z)
+{
+ TREE *node[4];
+ TREE **npp = node;
+ TREE *tp;
+
+ if ((RIGHT(x) = LEFT(y)) != NULL) {
+ unprotect(*npp++ = RIGHT(x));
+ PARENT(RIGHT(x)) = x;
+ }
+ if ((RIGHT(y) = LEFT(z)) != NULL) {
+ unprotect(*npp++ = RIGHT(y));
+ PARENT(RIGHT(y)) = y;
+ }
+ if ((PARENT(z) = PARENT(x)) != NULL) {
+ unprotect(*npp++ = PARENT(x));
+ if (LEFT(PARENT(x)) == x)
+ LEFT(PARENT(z)) = z;
+ else
+ RIGHT(PARENT(z)) = z;
+ }
+ LEFT(z) = y;
+ PARENT(y) = z;
+ LEFT(y) = x;
+ PARENT(x) = y;
+
+ *npp = NULL;
+ npp = node;
+ while ((tp = *npp++) != NULL)
+ if (tp != x && tp != y && tp != z && !in_list(tp, npp))
+ protect(tp);
+}
+
+static void
+BURIGHT2(TREE *x, TREE *y, TREE *z)
+{
+ TREE *node[4];
+ TREE **npp = node;
+ TREE *tp;
+
+ if ((LEFT(x) = RIGHT(y)) != NULL) {
+ unprotect(*npp++ = LEFT(x));
+ PARENT(LEFT(x)) = x;
+ }
+ if ((LEFT(y) = RIGHT(z)) != NULL) {
+ unprotect(*npp++ = LEFT(y));
+ PARENT(LEFT(y)) = y;
+ }
+ if ((PARENT(z) = PARENT(x)) != NULL) {
+ unprotect(*npp++ = PARENT(x));
+ if (LEFT(PARENT(x)) == x)
+ LEFT(PARENT(z)) = z;
+ else
+ RIGHT(PARENT(z)) = z;
+ }
+ RIGHT(z) = y;
+ PARENT(y) = z;
+ RIGHT(y) = x;
+ PARENT(x) = y;
+
+ *npp = NULL;
+ npp = node;
+ while ((tp = *npp++) != NULL)
+ if (tp != x && tp != y && tp != z && !in_list(tp, npp))
+ protect(tp);
+}
+
+static void
+TDLEFT2(TREE *x, TREE *y, TREE *z)
+{
+ TREE *node[3];
+ TREE **npp = node;
+ TREE *tp;
+
+ if ((RIGHT(y) = LEFT(z)) != NULL) {
+ unprotect(*npp++ = RIGHT(y));
+ PARENT(RIGHT(y)) = y;
+ }
+ if ((PARENT(z) = PARENT(x)) != NULL) {
+ unprotect(*npp++ = PARENT(x));
+ if (LEFT(PARENT(x)) == x)
+ LEFT(PARENT(z)) = z;
+ else
+ RIGHT(PARENT(z)) = z;
+ }
+ PARENT(x) = z;
+ LEFT(z) = x;
+
+ *npp = NULL;
+ npp = node;
+ while ((tp = *npp++) != NULL)
+ if (tp != x && tp != y && tp != z && !in_list(tp, npp))
+ protect(tp);
+}
+
+#if 0 /* Not used, for now */
+static void
+TDRIGHT2(TREE *x, TREE *y, TREE *z)
+{
+ TREE *node[3];
+ TREE **npp = node;
+ TREE *tp;
+
+ if ((LEFT(y) = RIGHT(z)) != NULL) {
+ unprotect(*npp++ = LEFT(y));
+ PARENT(LEFT(y)) = y;
+ }
+ if ((PARENT(z) = PARENT(x)) != NULL) {
+ unprotect(*npp++ = PARENT(x));
+ if (LEFT(PARENT(x)) == x)
+ LEFT(PARENT(z)) = z;
+ else
+ RIGHT(PARENT(z)) = z;
+ }
+ PARENT(x) = z;
+ RIGHT(z) = x;
+
+ *npp = NULL;
+ npp = node;
+ while ((tp = *npp++) != NULL)
+ if (tp != x && tp != y && tp != z && !in_list(tp, npp))
+ protect(tp);
+}
+#endif
+
+/*
+ * Delete a tree element
+ */
+static void
+t_delete(TREE *op)
+{
+ TREE *tp, *sp, *gp;
+
+ /* if this is a non-tree node */
+ if (ISNOTREE(op)) {
+ tp = LINKBAK(op);
+ unprotect(tp);
+ if ((sp = LINKFOR(op)) != NULL) {
+ unprotect(sp);
+ LINKBAK(sp) = tp;
+ protect(sp);
+ }
+ LINKFOR(tp) = sp;
+ protect(tp);
+ return;
+ }
+
+ /* make op the root of the tree */
+ if (PARENT(op))
+ t_splay(op);
+
+ /* if this is the start of a list */
+ if ((tp = LINKFOR(op)) != NULL) {
+ unprotect(tp);
+ PARENT(tp) = NULL;
+ if ((sp = LEFT(op)) != NULL) {
+ unprotect(sp);
+ PARENT(sp) = tp;
+ protect(sp);
+ }
+ LEFT(tp) = sp;
+
+ if ((sp = RIGHT(op)) != NULL) {
+ unprotect(sp);
+ PARENT(sp) = tp;
+ protect(sp);
+ }
+ RIGHT(tp) = sp;
+
+ Root = tp;
+ protect(tp);
+ return;
+ }
+
+ /* if op has a non-null left subtree */
+ if ((tp = LEFT(op)) != NULL) {
+ unprotect(tp);
+ PARENT(tp) = NULL;
+ if (RIGHT(op)) {
+ /* make the right-end of the left subtree its root */
+ while ((sp = RIGHT(tp)) != NULL) {
+ unprotect(sp);
+ if ((gp = RIGHT(sp)) != NULL) {
+ unprotect(gp);
+ TDLEFT2(tp, sp, gp);
+ protect(sp);
+ protect(tp);
+ tp = gp;
+ } else {
+ LEFT1(tp, sp);
+ protect(tp);
+ tp = sp;
+ }
+ }
+
+ /* hook the right subtree of op to the above elt */
+ RIGHT(tp) = sp = RIGHT(op);
+ unprotect(sp);
+ PARENT(sp) = tp;
+ protect(sp);
+ }
+ protect(tp);
+ } else if ((tp = RIGHT(op)) != NULL) { /* no left subtree */
+ unprotect(tp);
+ PARENT(tp) = NULL;
+ protect(tp);
+ }
+
+ Root = tp;
+}
+
+/*
+ * Bottom up splaying (simple version).
+ * The basic idea is to roughly cut in half the
+ * path from Root to tp and make tp the new root.
+ */
+static void
+t_splay(TREE *tp)
+{
+ TREE *pp, *gp;
+
+ /* iterate until tp is the root */
+ while ((pp = PARENT(tp)) != NULL) {
+ unprotect(pp);
+ /* grandparent of tp */
+ gp = PARENT(pp);
+ if (gp)
+ unprotect(gp);
+
+ /* x is a left child */
+ if (LEFT(pp) == tp) {
+ if (gp && LEFT(gp) == pp) {
+ BURIGHT2(gp, pp, tp);
+ protect(gp);
+ } else {
+ if (gp)
+ protect(gp);
+ RIGHT1(pp, tp);
+ }
+ } else {
+ ASSERT(RIGHT(pp) == tp);
+ if (gp && RIGHT(gp) == pp) {
+ BULEFT2(gp, pp, tp);
+ protect(gp);
+ } else {
+ if (gp)
+ protect(gp);
+ LEFT1(pp, tp);
+ }
+ }
+ protect(pp);
+ unprotect(tp); /* just in case */
+ }
+}
+
+void
+free(void *old)
+{
+ _mutex_lock(&__watch_malloc_lock);
+ free_unlocked(old);
+ _mutex_unlock(&__watch_malloc_lock);
+}
+
+
+static void
+free_unlocked(void *old)
+{
+ if (old != NULL)
+ realfree(old);
+}
+
+
+/*
+ * memalign(align,nbytes)
+ *
+ * Description:
+ * Returns a block of specified size on a specified alignment boundary.
+ *
+ * Algorithm:
+ * Malloc enough to ensure that a block can be aligned correctly.
+ * Find the alignment point and return the fragments
+ * before and after the block.
+ *
+ * Errors:
+ * Returns NULL and sets errno as follows:
+ * [EINVAL]
+ * if nbytes = 0,
+ * or if alignment is misaligned,
+ * or if the heap has been detectably corrupted.
+ * [ENOMEM]
+ * if the requested memory could not be allocated.
+ */
+
+#pragma weak memalign = _memalign
+
+#define misaligned(p) ((unsigned)(p) & 3)
+ /* 4-byte "word" alignment is considered ok in LP64 */
+#define nextblk(p, size) ((TREE *)((char *)(p) + (size)))
+
+void *
+_memalign(size_t align, size_t nbytes)
+{
+ size_t reqsize; /* Num of bytes to get from malloc() */
+ TREE *p; /* Ptr returned from malloc() */
+ TREE *blk; /* For addressing fragment blocks */
+ size_t blksize; /* Current (shrinking) block size */
+ TREE *alignedp; /* Ptr to properly aligned boundary */
+ TREE *aligned_blk; /* The block to be returned */
+ size_t frag_size; /* size of fragments fore and aft */
+ size_t x;
+
+ /*
+ * check for valid size and alignment parameters
+ * MAX_ALIGN check prevents overflow in later calculation.
+ */
+ if (nbytes == 0 || misaligned(align) || align == 0 ||
+ align > MAX_ALIGN) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ /*
+ * Malloc enough memory to guarantee that the result can be
+ * aligned correctly. The worst case is when malloc returns
+ * a block so close to the next alignment boundary that a
+ * fragment of minimum size cannot be created. In order to
+ * make sure we can handle this, we need to force the
+ * alignment to be at least as large as the minimum frag size
+ * (MINSIZE + WORDSIZE).
+ */
+
+ /* check for size that could overflow ROUND calculation */
+ if (nbytes > MAX_MALLOC) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ ROUND(nbytes);
+ if (nbytes < MINSIZE)
+ nbytes = MINSIZE;
+ ROUND(align);
+ while (align < MINSIZE + WORDSIZE)
+ align <<= 1;
+ reqsize = nbytes + align + (MINSIZE + WORDSIZE);
+ /* check for overflow */
+ if (reqsize < nbytes) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ p = (TREE *) malloc(reqsize);
+ if (p == (TREE *) NULL) {
+ /* malloc sets errno */
+ return (NULL);
+ }
+ _mutex_lock(&__watch_malloc_lock);
+
+ /*
+ * get size of the entire block (overhead and all)
+ */
+ /* LINTED improper alignment */
+ blk = BLOCK(p); /* back up to get length word */
+ unprotect(blk);
+ blksize = SIZE(blk);
+ CLRBITS01(blksize);
+
+ /*
+ * locate the proper alignment boundary within the block.
+ */
+ x = (size_t)p;
+ if (x % align != 0)
+ x += align - (x % align);
+ alignedp = (TREE *)x;
+ /* LINTED improper alignment */
+ aligned_blk = BLOCK(alignedp);
+
+ /*
+ * Check out the space to the left of the alignment
+ * boundary, and split off a fragment if necessary.
+ */
+ frag_size = (size_t)aligned_blk - (size_t)blk;
+ if (frag_size != 0) {
+ /*
+ * Create a fragment to the left of the aligned block.
+ */
+ if (frag_size < MINSIZE + WORDSIZE) {
+ /*
+ * Not enough space. So make the split
+ * at the other end of the alignment unit.
+ * We know this yields enough space, because
+ * we forced align >= MINSIZE + WORDSIZE above.
+ */
+ frag_size += align;
+ /* LINTED improper alignment */
+ aligned_blk = nextblk(aligned_blk, align);
+ }
+ blksize -= frag_size;
+ SIZE(aligned_blk) = blksize | BIT0;
+ frag_size -= WORDSIZE;
+ SIZE(blk) = frag_size | BIT0 | ISBIT1(SIZE(blk));
+ free_unlocked(DATA(blk));
+ /*
+ * free_unlocked(DATA(blk)) has the side-effect of calling
+ * protect() on the block following blk, that is, aligned_blk.
+ * We recover from this by unprotect()ing it here.
+ */
+ unprotect(aligned_blk);
+ }
+
+ /*
+ * Is there a (sufficiently large) fragment to the
+ * right of the aligned block?
+ */
+ frag_size = blksize - nbytes;
+ if (frag_size >= MINSIZE + WORDSIZE) {
+ /*
+ * split and free a fragment on the right
+ */
+ blksize = SIZE(aligned_blk);
+ SIZE(aligned_blk) = nbytes;
+ /* LINTED improper alignment */
+ blk = NEXT(aligned_blk);
+ SETOLD01(SIZE(aligned_blk), blksize);
+ frag_size -= WORDSIZE;
+ SIZE(blk) = frag_size | BIT0;
+ free_unlocked(DATA(blk));
+ }
+ copy_pattern(LIVEPAT, aligned_blk);
+ protect(aligned_blk);
+ _mutex_unlock(&__watch_malloc_lock);
+ return (DATA(aligned_blk));
+}
+
+#pragma weak valloc = _valloc
+
+void *
+_valloc(size_t size)
+{
+ static unsigned pagesize;
+ if (!pagesize)
+ pagesize = _sysconf(_SC_PAGESIZE);
+ return (memalign(pagesize, size));
+}
+
+/*
+ * libc does not define a weak calloc as _calloc
+ */
+void *
+calloc(size_t num, size_t size)
+{
+ void *mp;
+ size_t total;
+
+ total = num * size;
+
+ /* check for overflow */
+ if (num != 0 && total / num != size) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ if ((mp = malloc(total)) != NULL)
+ (void) memset(mp, 0, total);
+ return (mp);
+}
+
+#pragma weak cfree = _cfree
+
+/* ARGSUSED1 */
+void
+_cfree(void *p, size_t num, size_t size)
+{
+ free(p);
+}
+
+/*
+ * mallopt -- Do nothing
+ */
+
+#pragma weak mallopt = _mallopt
+
+/* ARGSUSED */
+int
+_mallopt(int cmd, int value)
+{
+ return (0);
+}
+
+/*
+ * mallinfo -- Do nothing
+ */
+
+#pragma weak mallinfo = _mallinfo
+
+struct mallinfo
+_mallinfo()
+{
+ static struct mallinfo __mallinfo;
+
+ return (__mallinfo);
+}
+
+
+typedef struct {
+ long cmd;
+ prwatch_t prwatch;
+} ctl_t;
+
+static pid_t my_pid = 0; /* to check for whether we fork()d */
+static int dont_watch = 0;
+static int do_stop = 0;
+static int ctlfd = -1;
+struct stat ctlstatb;
+static int wflags = WA_WRITE;
+
+static void
+init_watch()
+{
+ char str[80];
+ char *s;
+
+ my_pid = getpid();
+
+ dont_watch = 1;
+
+ if ((s = getenv("MALLOC_DEBUG")) == NULL)
+ return;
+
+ s = strncpy(str, s, sizeof (str));
+ while (s != NULL) {
+ char *e = strchr(s, ',');
+ if (e)
+ *e++ = '\0';
+ if (strcmp(s, "STOP") == 0)
+ do_stop = 1;
+ else if (strcmp(s, "WATCH") == 0)
+ dont_watch = 0;
+ else if (strcmp(s, "RW") == 0) {
+ dont_watch = 0;
+ wflags = WA_READ|WA_WRITE;
+ }
+ s = e;
+ }
+
+ if (dont_watch)
+ return;
+
+ if ((ctlfd = open("/proc/self/ctl", O_WRONLY)) < 0 ||
+ fstat(ctlfd, &ctlstatb) != 0) {
+ if (ctlfd >= 0)
+ (void) close(ctlfd);
+ ctlfd = -1;
+ dont_watch = 1;
+ return;
+ }
+ /* close-on-exec */
+ (void) fcntl(ctlfd, F_SETFD, 1);
+
+ if (do_stop) {
+ int pfd;
+ pstatus_t pstatus;
+ struct {
+ long cmd;
+ fltset_t fltset;
+ } ctl;
+
+ /*
+ * Play together with some /proc controller
+ * that has set other stop-on-fault flags.
+ */
+ premptyset(&ctl.fltset);
+ if ((pfd = open("/proc/self/status", O_RDONLY)) >= 0) {
+ if (read(pfd, &pstatus, sizeof (pstatus))
+ == sizeof (pstatus))
+ ctl.fltset = pstatus.pr_flttrace;
+ (void) close(pfd);
+ }
+ praddset(&ctl.fltset, FLTWATCH);
+ ctl.cmd = PCSFAULT;
+ (void) write(ctlfd, &ctl, sizeof (ctl));
+ }
+}
+
+static int
+nowatch()
+{
+ struct stat statb;
+
+ if (dont_watch)
+ return (1);
+ if (ctlfd < 0) /* first time */
+ init_watch();
+ else if (fstat(ctlfd, &statb) != 0 ||
+ statb.st_dev != ctlstatb.st_dev ||
+ statb.st_ino != ctlstatb.st_ino) {
+ /*
+ * Someone closed our file descriptor.
+ * Just open another one.
+ */
+ if ((ctlfd = open("/proc/self/ctl", O_WRONLY)) < 0 ||
+ fstat(ctlfd, &ctlstatb) != 0) {
+ if (ctlfd >= 0)
+ (void) close(ctlfd);
+ ctlfd = -1;
+ dont_watch = 1;
+ return (1);
+ }
+ /* close-on-exec */
+ (void) fcntl(ctlfd, F_SETFD, 1);
+ }
+ if (my_pid != getpid()) {
+ /*
+ * We fork()d since the last call to the allocator.
+ * watchpoints are not inherited across fork().
+ * XXX: how to recover from this ???
+ */
+ dont_watch = 1;
+ (void) close(ctlfd);
+ ctlfd = -1;
+ }
+ return (dont_watch);
+}
+
+static void
+protect(TREE *tp)
+{
+ ctl_t ctl;
+ size_t size, sz;
+
+ if (nowatch())
+ return;
+ if (tp == NULL || DATA(tp) == Baddr)
+ return;
+
+ sz = size = SIZE(tp);
+ CLRBITS01(size);
+ if (size == 0)
+ return;
+ if (ISBIT0(sz)) /* block is busy, protect only the head */
+ size = 0;
+ ctl.cmd = PCWATCH;
+ ctl.prwatch.pr_vaddr = (uintptr_t)tp;
+ ctl.prwatch.pr_size = size + WORDSIZE;
+ ctl.prwatch.pr_wflags = wflags;
+ (void) write(ctlfd, &ctl, sizeof (ctl));
+}
+
+static void
+unprotect(TREE *tp)
+{
+ ctl_t ctl;
+
+ if (nowatch())
+ return;
+ if (tp == NULL || DATA(tp) == Baddr)
+ return;
+
+ ctl.cmd = PCWATCH;
+ ctl.prwatch.pr_vaddr = (uintptr_t)tp;
+ ctl.prwatch.pr_size = WORDSIZE; /* size is arbitrary */
+ ctl.prwatch.pr_wflags = 0; /* clear the watched area */
+ (void) write(ctlfd, &ctl, sizeof (ctl));
+}