Home | History | Annotate | Download | only in mdb
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 /*
     30  * These routines simply provide wrappers around malloc(3C) and free(3C)
     31  * for now.  In the future we hope to provide a userland equivalent to
     32  * the kmem allocator, including cache allocators.
     33  */
     34 
     35 #include <strings.h>
     36 #include <stdlib.h>
     37 #include <poll.h>
     38 
     39 #ifdef _KMDB
     40 #include <kmdb/kmdb_fault.h>
     41 #endif
     42 #include <mdb/mdb_debug.h>
     43 #include <mdb/mdb_stdlib.h>
     44 #include <mdb/mdb_frame.h>
     45 #include <mdb/mdb_umem.h>
     46 #include <mdb/mdb_err.h>
     47 #include <mdb/mdb.h>
     48 
     49 #define	UMF_DEBUG			0x1
     50 
     51 #ifdef DEBUG
     52 int mdb_umem_flags = UMF_DEBUG;
     53 #else
     54 int mdb_umem_flags = 0;
     55 #endif
     56 
     57 struct mdb_mblk {
     58 	void *blk_addr;			/* address of allocated block */
     59 	size_t blk_size;		/* size of block in bytes */
     60 	struct mdb_mblk *blk_next;	/* link to next block */
     61 };
     62 
     63 /*ARGSUSED*/
     64 static void *
     65 mdb_umem_handler(size_t nbytes, size_t align, uint_t flags)
     66 {
     67 #ifdef _KMDB
     68 
     69 	/*
     70 	 * kmdb has a fixed, dedicated VA range in which to play.  This range
     71 	 * won't change size while the debugger is running, regardless of how
     72 	 * long we wait.  As a result, the only sensible course of action is
     73 	 * to fail the request.  If we're here, however, the request was made
     74 	 * with UM_SLEEP.  The caller is thus not expecting a NULL back.  We'll
     75 	 * have to fail the current dcmd set.
     76 	 */
     77 	if (mdb.m_depth > 0) {
     78 		warn("failed to allocate %lu bytes -- recovering\n",
     79 		    (ulong_t)nbytes);
     80 
     81 		kmdb_print_stack();
     82 
     83 		longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM);
     84 	}
     85 
     86 #else
     87 
     88 	/*
     89 	 * mdb, on the other hand, can afford to wait, as someone may actually
     90 	 * free something.
     91 	 */
     92 	if (errno == EAGAIN) {
     93 		void *ptr = NULL;
     94 		char buf[64];
     95 
     96 		(void) mdb_iob_snprintf(buf, sizeof (buf),
     97 		    "[ sleeping for %lu bytes of free memory ... ]",
     98 		    (ulong_t)nbytes);
     99 
    100 		(void) mdb_iob_puts(mdb.m_err, buf);
    101 		(void) mdb_iob_flush(mdb.m_err);
    102 
    103 		do {
    104 			(void) poll(NULL, 0, 1000);
    105 			if (align != 0)
    106 				ptr = memalign(align, nbytes);
    107 			else
    108 				ptr = malloc(nbytes);
    109 		} while (ptr == NULL && errno == EAGAIN);
    110 
    111 		if (ptr != NULL)
    112 			return (ptr);
    113 
    114 		(void) memset(buf, '\b', strlen(buf));
    115 		(void) mdb_iob_puts(mdb.m_err, buf);
    116 		(void) mdb_iob_flush(mdb.m_err);
    117 
    118 		(void) memset(buf, ' ', strlen(buf));
    119 		(void) mdb_iob_puts(mdb.m_err, buf);
    120 		(void) mdb_iob_flush(mdb.m_err);
    121 
    122 		(void) memset(buf, '\b', strlen(buf));
    123 		(void) mdb_iob_puts(mdb.m_err, buf);
    124 		(void) mdb_iob_flush(mdb.m_err);
    125 	}
    126 #endif
    127 
    128 	die("failed to allocate %lu bytes -- terminating\n", (ulong_t)nbytes);
    129 
    130 	/*NOTREACHED*/
    131 
    132 	return (NULL);
    133 }
    134 
    135 static void
    136 mdb_umem_gc_enter(void *ptr, size_t nbytes)
    137 {
    138 	mdb_mblk_t *blkp = mdb_alloc(sizeof (mdb_mblk_t), UM_SLEEP);
    139 
    140 	blkp->blk_addr = ptr;
    141 	blkp->blk_size = nbytes;
    142 	blkp->blk_next = mdb.m_frame->f_mblks;
    143 
    144 	mdb.m_frame->f_mblks = blkp;
    145 }
    146 
    147 /*
    148  * If we're compiled in debug mode, we use this function (gratuitously
    149  * stolen from kmem.c) to set uninitialized and freed regions to
    150  * special bit patterns.
    151  */
    152 static void
    153 mdb_umem_copy_pattern(uint32_t pattern, void *buf_arg, size_t size)
    154 {
    155 	/* LINTED - alignment of bufend */
    156 	uint32_t *bufend = (uint32_t *)((char *)buf_arg + size);
    157 	uint32_t *buf = buf_arg;
    158 
    159 	while (buf < bufend - 3) {
    160 		buf[3] = buf[2] = buf[1] = buf[0] = pattern;
    161 		buf += 4;
    162 	}
    163 
    164 	while (buf < bufend)
    165 		*buf++ = pattern;
    166 }
    167 
    168 void *
    169 mdb_alloc_align(size_t nbytes, size_t align, uint_t flags)
    170 {
    171 	void *ptr;
    172 
    173 	if (nbytes == 0)
    174 		return (NULL);
    175 
    176 	nbytes = (nbytes + sizeof (uint32_t) - 1) & ~(sizeof (uint32_t) - 1);
    177 
    178 	if (align != 0)
    179 		ptr = memalign(align, nbytes);
    180 	else
    181 		ptr = malloc(nbytes);
    182 
    183 	if (flags & UM_SLEEP) {
    184 		while (ptr == NULL)
    185 			ptr = mdb_umem_handler(nbytes, align, flags);
    186 	}
    187 
    188 	if (ptr != NULL && (mdb_umem_flags & UMF_DEBUG) != 0)
    189 		mdb_umem_copy_pattern(UMEM_UNINITIALIZED_PATTERN, ptr, nbytes);
    190 
    191 	if (flags & UM_GC)
    192 		mdb_umem_gc_enter(ptr, nbytes);
    193 
    194 	return (ptr);
    195 }
    196 
    197 void *
    198 mdb_alloc(size_t nbytes, uint_t flags)
    199 {
    200 	return (mdb_alloc_align(nbytes, 0, flags));
    201 }
    202 
    203 void *
    204 mdb_zalloc(size_t nbytes, uint_t flags)
    205 {
    206 	void *ptr = mdb_alloc(nbytes, flags);
    207 
    208 	if (ptr != NULL)
    209 		bzero(ptr, nbytes);
    210 
    211 	return (ptr);
    212 }
    213 
    214 void
    215 mdb_free(void *ptr, size_t nbytes)
    216 {
    217 	ASSERT(ptr != NULL || nbytes == 0);
    218 
    219 	nbytes = (nbytes + sizeof (uint32_t) - 1) & ~(sizeof (uint32_t) - 1);
    220 
    221 	if (ptr != NULL) {
    222 		if (mdb_umem_flags & UMF_DEBUG)
    223 			mdb_umem_copy_pattern(UMEM_FREE_PATTERN, ptr, nbytes);
    224 		free(ptr);
    225 	}
    226 }
    227 
    228 void
    229 mdb_free_align(void *ptr, size_t nbytes)
    230 {
    231 	mdb_free(ptr, nbytes);
    232 }
    233 
    234 void
    235 mdb_recycle(mdb_mblk_t **blkpp)
    236 {
    237 	mdb_mblk_t *blkp, *nblkp;
    238 
    239 	for (blkp = *blkpp; blkp != NULL; blkp = nblkp) {
    240 		mdb_dprintf(MDB_DBG_UMEM,
    241 		    "garbage collect %p size %lu bytes\n", blkp->blk_addr,
    242 		    (ulong_t)blkp->blk_size);
    243 
    244 		nblkp = blkp->blk_next;
    245 		mdb_free(blkp->blk_addr, blkp->blk_size);
    246 		mdb_free(blkp, sizeof (mdb_mblk_t));
    247 	}
    248 
    249 	*blkpp = NULL;
    250 }
    251