view usr/src/cmd/mdb/common/mdb/mdb_umem.c @ 14155:dcd9e8748b08

3946 ::gcore Reviewed by: Adam Leventhal <ahl@delphix.com> Reviewed by: Matthew Ahrens <mahrens@delphix.com> Approved by: Robert Mustacchi <rm@joyent.com>
author Jeremy Jones <jeremy@delphix.com>
date Wed, 21 Aug 2013 15:45:46 -0800
parents 68f95e015346
children
line wrap: on
line source

/*
 * 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 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * These routines simply provide wrappers around malloc(3C) and free(3C)
 * for now.  In the future we hope to provide a userland equivalent to
 * the kmem allocator, including cache allocators.
 */

#include <strings.h>
#include <stdlib.h>
#include <poll.h>

#ifdef _KMDB
#include <kmdb/kmdb_fault.h>
#endif
#include <mdb/mdb_debug.h>
#include <mdb/mdb_stdlib.h>
#include <mdb/mdb_frame.h>
#include <mdb/mdb_umem.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb.h>

#define	UMF_DEBUG			0x1

#ifdef DEBUG
int mdb_umem_flags = UMF_DEBUG;
#else
int mdb_umem_flags = 0;
#endif

struct mdb_mblk {
	void *blk_addr;			/* address of allocated block */
	size_t blk_size;		/* size of block in bytes */
	struct mdb_mblk *blk_next;	/* link to next block */
};

/*ARGSUSED*/
static void *
mdb_umem_handler(size_t nbytes, size_t align, uint_t flags)
{
#ifdef _KMDB

	/*
	 * kmdb has a fixed, dedicated VA range in which to play.  This range
	 * won't change size while the debugger is running, regardless of how
	 * long we wait.  As a result, the only sensible course of action is
	 * to fail the request.  If we're here, however, the request was made
	 * with UM_SLEEP.  The caller is thus not expecting a NULL back.  We'll
	 * have to fail the current dcmd set.
	 */
	if (mdb.m_depth > 0) {
		warn("failed to allocate %lu bytes -- recovering\n",
		    (ulong_t)nbytes);

		kmdb_print_stack();

		longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM);
	}

#else

	/*
	 * mdb, on the other hand, can afford to wait, as someone may actually
	 * free something.
	 */
	if (errno == EAGAIN) {
		void *ptr = NULL;
		char buf[64];

		(void) mdb_iob_snprintf(buf, sizeof (buf),
		    "[ sleeping for %lu bytes of free memory ... ]",
		    (ulong_t)nbytes);

		(void) mdb_iob_puts(mdb.m_err, buf);
		(void) mdb_iob_flush(mdb.m_err);

		do {
			(void) poll(NULL, 0, 1000);
			if (align != 0)
				ptr = memalign(align, nbytes);
			else
				ptr = malloc(nbytes);
		} while (ptr == NULL && errno == EAGAIN);

		if (ptr != NULL)
			return (ptr);

		(void) memset(buf, '\b', strlen(buf));
		(void) mdb_iob_puts(mdb.m_err, buf);
		(void) mdb_iob_flush(mdb.m_err);

		(void) memset(buf, ' ', strlen(buf));
		(void) mdb_iob_puts(mdb.m_err, buf);
		(void) mdb_iob_flush(mdb.m_err);

		(void) memset(buf, '\b', strlen(buf));
		(void) mdb_iob_puts(mdb.m_err, buf);
		(void) mdb_iob_flush(mdb.m_err);
	}
#endif

	die("failed to allocate %lu bytes -- terminating\n", (ulong_t)nbytes);

	/*NOTREACHED*/

	return (NULL);
}

static void
mdb_umem_gc_enter(void *ptr, size_t nbytes)
{
	mdb_mblk_t *blkp = mdb_alloc(sizeof (mdb_mblk_t), UM_SLEEP);

	blkp->blk_addr = ptr;
	blkp->blk_size = nbytes;
	blkp->blk_next = mdb.m_frame->f_mblks;

	mdb.m_frame->f_mblks = blkp;
}

/*
 * If we're compiled in debug mode, we use this function (gratuitously
 * stolen from kmem.c) to set uninitialized and freed regions to
 * special bit patterns.
 */
static void
mdb_umem_copy_pattern(uint32_t pattern, void *buf_arg, size_t size)
{
	/* LINTED - alignment of bufend */
	uint32_t *bufend = (uint32_t *)((char *)buf_arg + size);
	uint32_t *buf = buf_arg;

	while (buf < bufend - 3) {
		buf[3] = buf[2] = buf[1] = buf[0] = pattern;
		buf += 4;
	}

	while (buf < bufend)
		*buf++ = pattern;
}

void *
mdb_alloc_align(size_t nbytes, size_t align, uint_t flags)
{
	void *ptr;

	if (nbytes == 0)
		return (NULL);

	nbytes = (nbytes + sizeof (uint32_t) - 1) & ~(sizeof (uint32_t) - 1);

	if (align != 0)
		ptr = memalign(align, nbytes);
	else
		ptr = malloc(nbytes);

	if (flags & UM_SLEEP) {
		while (ptr == NULL)
			ptr = mdb_umem_handler(nbytes, align, flags);
	}

	if (ptr != NULL && (mdb_umem_flags & UMF_DEBUG) != 0)
		mdb_umem_copy_pattern(UMEM_UNINITIALIZED_PATTERN, ptr, nbytes);

	if (flags & UM_GC)
		mdb_umem_gc_enter(ptr, nbytes);

	return (ptr);
}

void *
mdb_alloc(size_t nbytes, uint_t flags)
{
	return (mdb_alloc_align(nbytes, 0, flags));
}

void *
mdb_zalloc(size_t nbytes, uint_t flags)
{
	void *ptr = mdb_alloc(nbytes, flags);

	if (ptr != NULL)
		bzero(ptr, nbytes);

	return (ptr);
}

void
mdb_free(void *ptr, size_t nbytes)
{
	ASSERT(ptr != NULL || nbytes == 0);

	nbytes = (nbytes + sizeof (uint32_t) - 1) & ~(sizeof (uint32_t) - 1);

	if (ptr != NULL) {
		if (mdb_umem_flags & UMF_DEBUG)
			mdb_umem_copy_pattern(UMEM_FREE_PATTERN, ptr, nbytes);
		free(ptr);
	}
}

void
mdb_free_align(void *ptr, size_t nbytes)
{
	mdb_free(ptr, nbytes);
}

void
mdb_recycle(mdb_mblk_t **blkpp)
{
	mdb_mblk_t *blkp, *nblkp;

	for (blkp = *blkpp; blkp != NULL; blkp = nblkp) {
		mdb_dprintf(MDB_DBG_UMEM,
		    "garbage collect %p size %lu bytes\n", blkp->blk_addr,
		    (ulong_t)blkp->blk_size);

		nblkp = blkp->blk_next;
		mdb_free(blkp->blk_addr, blkp->blk_size);
		mdb_free(blkp, sizeof (mdb_mblk_t));
	}

	*blkpp = NULL;
}