summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjwadams <none@none>2006-03-02 10:48:56 -0800
committerjwadams <none@none>2006-03-02 10:48:56 -0800
commit789d94c2889bedf502063bc22addcabfa798a438 (patch)
treeb12cfa99b625d5683fae676bb7162a4011c7d9fe
parent14d56903b5e712ea59206c206e57d3731107fcf1 (diff)
downloadillumos-joyent-789d94c2889bedf502063bc22addcabfa798a438.tar.gz
4720206 ::findleaks shouldn't cache results across state changes
4743353 libumem's module fails to load on idle targets 6304072 libumem seems to use more heap than it needs 6336202 d4fc7824::typegraph made mdb crash
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/kmem.c71
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/leaky.c27
-rw-r--r--usr/src/cmd/mdb/common/modules/libumem/leaky_subr.c29
-rw-r--r--usr/src/cmd/mdb/common/modules/libumem/libumem.c30
-rw-r--r--usr/src/cmd/mdb/common/modules/libumem/misc.c40
-rw-r--r--usr/src/cmd/mdb/common/modules/libumem/misc.h9
-rw-r--r--usr/src/cmd/mdb/common/modules/libumem/umem.c807
-rw-r--r--usr/src/cmd/mdb/common/modules/libumem/umem.h16
-rw-r--r--usr/src/cmd/mdb/intel/amd64/libumem/Makefile9
-rw-r--r--usr/src/cmd/mdb/intel/ia32/libumem/Makefile9
-rw-r--r--usr/src/cmd/mdb/sparc/v7/libumem/Makefile9
-rw-r--r--usr/src/cmd/mdb/sparc/v9/libumem/Makefile9
-rw-r--r--usr/src/lib/libumem/common/envvar.c73
-rw-r--r--usr/src/lib/libumem/common/umem.c117
-rw-r--r--usr/src/lib/libumem/common/umem_base.h11
-rw-r--r--usr/src/lib/libumem/common/vmem_base.h8
-rw-r--r--usr/src/lib/libumem/common/vmem_sbrk.c15
17 files changed, 1119 insertions, 170 deletions
diff --git a/usr/src/cmd/mdb/common/modules/genunix/kmem.c b/usr/src/cmd/mdb/common/modules/genunix/kmem.c
index 897413891b..ad94418fc3 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/kmem.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/kmem.c
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -41,6 +40,7 @@
#include <vm/page.h>
#include "kmem.h"
+#include "leaky.h"
#define dprintf(x) if (mdb_debug_level) { \
mdb_printf("kmem debug: "); \
@@ -56,8 +56,6 @@
static int mdb_debug_level = 0;
-static void *kmem_ready_cbhdl;
-
/*ARGSUSED*/
static int
kmem_init_walkers(uintptr_t addr, const kmem_cache_t *c, void *ignored)
@@ -551,8 +549,12 @@ kmem_read_magazines(kmem_cache_t *cp, uintptr_t addr, int ncpus,
* correctness.
*/
magsize = kmem_get_magsize(cp);
- if (magsize == 0)
- magsize = 1;
+ if (magsize == 0) {
+ *maglistp = NULL;
+ *magcntp = 0;
+ *magmaxp = 0;
+ return (WALK_NEXT);
+ }
/*
* There are several places where we need to go buffer hunting:
@@ -572,7 +574,7 @@ kmem_read_magazines(kmem_cache_t *cp, uintptr_t addr, int ncpus,
if (magbsize >= PAGESIZE / 2) {
mdb_warn("magazine size for cache %p unreasonable (%x)\n",
addr, magbsize);
- goto fail;
+ return (WALK_ERR);
}
maglist = mdb_alloc(magmax * sizeof (void *), alloc_flags);
@@ -693,6 +695,7 @@ kmem_walk_init_common(mdb_walk_state_t *wsp, int type)
kmem_walk_t *kmw;
int ncpus, csize;
kmem_cache_t *cp;
+ size_t vm_quantum;
size_t magmax, magcnt;
void **maglist = NULL;
@@ -724,6 +727,28 @@ kmem_walk_init_common(mdb_walk_state_t *wsp, int type)
goto out2;
}
+ /*
+ * It's easy for someone to hand us an invalid cache address.
+ * Unfortunately, it is hard for this walker to survive an
+ * invalid cache cleanly. So we make sure that:
+ *
+ * 1. the vmem arena for the cache is readable,
+ * 2. the vmem arena's quantum is a power of 2,
+ * 3. our slabsize is a multiple of the quantum, and
+ * 4. our chunksize is >0 and less than our slabsize.
+ */
+ if (mdb_vread(&vm_quantum, sizeof (vm_quantum),
+ (uintptr_t)&cp->cache_arena->vm_quantum) == -1 ||
+ vm_quantum == 0 ||
+ (vm_quantum & (vm_quantum - 1)) != 0 ||
+ cp->cache_slabsize < vm_quantum ||
+ P2PHASE(cp->cache_slabsize, vm_quantum) != 0 ||
+ cp->cache_chunksize == 0 ||
+ cp->cache_chunksize > cp->cache_slabsize) {
+ mdb_warn("%p is not a valid kmem_cache_t\n", addr);
+ goto out2;
+ }
+
dprintf(("buf total is %d\n", cp->cache_buftotal));
if (cp->cache_buftotal == 0) {
@@ -832,7 +857,10 @@ out1:
mdb_free(kmw->kmw_ubase, slabsize +
sizeof (kmem_bufctl_t));
- mdb_free(kmw->kmw_maglist, kmw->kmw_max * sizeof (uintptr_t));
+ if (kmw->kmw_maglist)
+ mdb_free(kmw->kmw_maglist,
+ kmw->kmw_max * sizeof (uintptr_t));
+
mdb_free(kmw, sizeof (kmem_walk_t));
wsp->walk_data = NULL;
}
@@ -3784,16 +3812,19 @@ kmem_ready_check(void)
/*ARGSUSED*/
static void
-kmem_ready_cb(void *arg)
+kmem_statechange_cb(void *arg)
{
- if (kmem_ready_check() <= 0)
+ static int been_ready = 0;
+
+ leaky_cleanup(1); /* state changes invalidate leaky state */
+
+ if (been_ready)
return;
- if (kmem_ready_cbhdl != NULL) {
- mdb_callback_remove(kmem_ready_cbhdl);
- kmem_ready_cbhdl = NULL;
- }
+ if (kmem_ready_check() <= 0)
+ return;
+ been_ready = 1;
(void) mdb_walk("kmem_cache", (mdb_walk_cb_t)kmem_init_walkers, NULL);
}
@@ -3818,12 +3849,8 @@ kmem_init(void)
return;
}
- if (kmem_ready_check() > 0) {
- kmem_ready_cb(NULL);
- } else {
- kmem_ready_cbhdl = mdb_callback_add(MDB_CALLBACK_STCHG,
- kmem_ready_cb, NULL);
- }
+ (void) mdb_callback_add(MDB_CALLBACK_STCHG, kmem_statechange_cb, NULL);
+ kmem_statechange_cb(NULL);
}
typedef struct whatthread {
diff --git a/usr/src/cmd/mdb/common/modules/genunix/leaky.c b/usr/src/cmd/mdb/common/modules/genunix/leaky.c
index 9d0a30e687..187c132b76 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/leaky.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/leaky.c
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -603,7 +602,11 @@ leaky_cleanup(int force)
*/
lk_free_state = NULL;
- if (lk_state == LK_CLEANING) {
+ switch (lk_state) {
+ case LK_CLEAN:
+ return; /* nothing to do */
+
+ case LK_CLEANING:
mdb_warn("interrupted during ::findleaks cleanup; some mdb "
"memory will be leaked\n");
@@ -618,10 +621,16 @@ leaky_cleanup(int force)
bzero(&lk_beans, sizeof (lk_beans));
lk_state = LK_CLEAN;
return;
- }
- if (!force && lk_state != LK_SWEEPING)
- return;
+ case LK_SWEEPING:
+ break; /* must clean up */
+
+ case LK_DONE:
+ default:
+ if (!force)
+ return;
+ break; /* only clean up if forced */
+ }
lk_state = LK_CLEANING;
@@ -797,7 +806,7 @@ findleaks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
if (lk_state == LK_DONE) {
if (lk_verbose)
mdb_printf("findleaks: using cached results "
- "(-f will force a full run)\n");
+ "(use '-f' to force a full run)\n");
goto dump;
}
diff --git a/usr/src/cmd/mdb/common/modules/libumem/leaky_subr.c b/usr/src/cmd/mdb/common/modules/libumem/leaky_subr.c
index f999a06d8a..0142b19698 100644
--- a/usr/src/cmd/mdb/common/modules/libumem/leaky_subr.c
+++ b/usr/src/cmd/mdb/common/modules/libumem/leaky_subr.c
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -706,30 +705,24 @@ leaky_subr_bufctl_cmp(const leak_bufctl_t *lhs, const leak_bufctl_t *rhs)
int
leaky_subr_estimate(size_t *estp)
{
- int umem_flags;
- int umem_ready;
-
- if (umem_readvar(&umem_ready, "umem_ready") == -1) {
- mdb_warn("couldn't read 'umem_ready'");
+ if (umem_ready == 0) {
+ mdb_warn(
+ "findleaks: umem is not loaded in the address space\n");
return (DCMD_ERR);
}
- if (umem_ready != UMEM_READY) {
- mdb_warn("findleaks: No allocations have occured -- no "
+ if (umem_ready == UMEM_READY_INIT_FAILED) {
+ mdb_warn("findleaks: umem initialization failed -- no "
"possible leaks.\n");
return (DCMD_ERR);
}
- if (umem_readvar(&umem_flags, "umem_flags") == -1) {
- mdb_warn("couldn't read 'umem_flags'");
+ if (umem_ready != UMEM_READY) {
+ mdb_warn("findleaks: No allocations have occured -- no "
+ "possible leaks.\n");
return (DCMD_ERR);
}
- if (umem_flags & UMF_RANDOMIZE) {
- mdb_warn("findleaks: might not work with "
- "UMEM_DEBUG=randomize\n");
- }
-
if (mdb_walk("umem_cache", (mdb_walk_cb_t)leaky_estimate, estp) == -1) {
mdb_warn("couldn't walk 'umem_cache'");
return (DCMD_ERR);
diff --git a/usr/src/cmd/mdb/common/modules/libumem/libumem.c b/usr/src/cmd/mdb/common/modules/libumem/libumem.c
index 5b521c2447..2a309207b1 100644
--- a/usr/src/cmd/mdb/common/modules/libumem/libumem.c
+++ b/usr/src/cmd/mdb/common/modules/libumem/libumem.c
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -343,6 +342,12 @@ static const mdb_dcmd_t dcmds[] = {
"users of the umem allocator", umausers },
{ "umem_cache", "?", "print a umem cache", umem_cache },
{ "umem_log", "?", "dump umem transaction log", umem_log },
+ { "umem_malloc_dist", "[-dg] [-b maxbins] [-B minbinsize]",
+ "report distribution of outstanding malloc()s",
+ umem_malloc_dist, umem_malloc_dist_help },
+ { "umem_malloc_info", "?[-dg] [-b maxbins] [-B minbinsize]",
+ "report information about malloc()s by cache",
+ umem_malloc_info, umem_malloc_info_help },
{ "umem_verify", "?", "check integrity of umem-managed memory",
umem_verify },
{ "vmem", "?", "print a vmem_t", vmem },
@@ -424,26 +429,9 @@ static const mdb_modinfo_t modinfo = {MDB_API_VERSION, dcmds, walkers};
const mdb_modinfo_t *
_mdb_init(void)
{
- mdb_walker_t w = {
- "umem_cache", "walk list of umem caches", umem_cache_walk_init,
- umem_cache_walk_step, umem_cache_walk_fini
- };
-
if (umem_init() != 0)
return (NULL);
- /*
- * Load the umem_cache walker manually, and then invoke it to add
- * named walks for each cache. This walker needs to be added by
- * hand since walkers in the linkage structure cannot be loaded
- * until _mdb_init returns the pointer to the linkage structure.
- */
- if (mdb_add_walker(&w) == 0) {
- (void) mdb_walk("umem_cache", (mdb_walk_cb_t)
- umem_init_walkers, NULL);
- } else
- mdb_warn("failed to add umem_cache walker");
-
return (&modinfo);
}
diff --git a/usr/src/cmd/mdb/common/modules/libumem/misc.c b/usr/src/cmd/mdb/common/modules/libumem/misc.c
index 2abca1cc76..bedb166aee 100644
--- a/usr/src/cmd/mdb/common/modules/libumem/misc.c
+++ b/usr/src/cmd/mdb/common/modules/libumem/misc.c
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -45,10 +44,37 @@ umem_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_OK);
}
-void
+/*
+ * To further confuse the issue, this dmod can run against either
+ * libumem.so.1 *or* the libstandumem.so linked into kmdb(1M). To figure
+ * out which one we are working against, we look up "umem_alloc" in both
+ * libumem.so and the executable.
+ *
+ * A further wrinkle is that libumem.so may not yet be loaded into the
+ * process' address space. That can lead to either the lookup failing, or
+ * being unable to read from the data segment. We treat either case as
+ * an error.
+ */
+int
umem_set_standalone(void)
{
- umem_is_standalone = 1;
+ GElf_Sym sym;
+ int ready;
+
+ if (mdb_lookup_by_obj(UMEM_OBJNAME, "umem_alloc", &sym) == 0)
+ umem_is_standalone = 0;
+ else if (mdb_lookup_by_obj(MDB_OBJ_EXEC, "umem_alloc", &sym) == 0)
+ umem_is_standalone = 1;
+ else
+ return (-1);
+
+ /*
+ * now that we know where things should be, make sure we can actually
+ * read things out.
+ */
+ if (umem_readvar(&ready, "umem_ready") == -1)
+ return (-1);
+ return (0);
}
ssize_t
@@ -80,5 +106,5 @@ is_umem_sym(const char *sym, const char *prefix)
char *tick_p = strrchr(sym, '`');
return (strncmp(sym, "libumem", 7) == 0 && tick_p != NULL &&
- strncmp(tick_p + 1, prefix, strlen(prefix)) == 0);
+ strncmp(tick_p + 1, prefix, strlen(prefix)) == 0);
}
diff --git a/usr/src/cmd/mdb/common/modules/libumem/misc.h b/usr/src/cmd/mdb/common/modules/libumem/misc.h
index 9b112ca418..73981fffeb 100644
--- a/usr/src/cmd/mdb/common/modules/libumem/misc.h
+++ b/usr/src/cmd/mdb/common/modules/libumem/misc.h
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -39,7 +38,7 @@ extern "C" {
extern int umem_debug(uintptr_t, uint_t, int, const mdb_arg_t *);
-extern void umem_set_standalone(void);
+extern int umem_set_standalone(void);
extern ssize_t umem_lookup_by_name(const char *, GElf_Sym *);
extern ssize_t umem_readvar(void *, const char *);
diff --git a/usr/src/cmd/mdb/common/modules/libumem/umem.c b/usr/src/cmd/mdb/common/modules/libumem/umem.c
index a052ed2270..ea9b67a63f 100644
--- a/usr/src/cmd/mdb/common/modules/libumem/umem.c
+++ b/usr/src/cmd/mdb/common/modules/libumem/umem.c
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -32,8 +31,10 @@
#include <umem_impl.h>
#include <alloca.h>
+#include <limits.h>
#include "misc.h"
+#include "leaky.h"
#include "umem_pagesize.h"
@@ -42,46 +43,64 @@
#define UM_BUFCTL 0x4
#define UM_HASH 0x8
-uint32_t umem_max_ncpus;
+int umem_ready;
+static int umem_stack_depth_warned;
+static uint32_t umem_max_ncpus;
uint32_t umem_stack_depth;
+
size_t umem_pagesize;
#define UMEM_READVAR(var) \
(umem_readvar(&(var), #var) == -1 && \
- ((void) mdb_warn("failed to read "#var), 1))
+ (mdb_warn("failed to read "#var), 1))
int
-umem_init(void)
+umem_update_variables(void)
{
size_t pagesize;
- GElf_Sym sym;
/*
- * Figure out which type of umem is being used
+ * Figure out which type of umem is being used; if it's not there
+ * yet, succeed quietly.
*/
- if (mdb_lookup_by_obj("libumem.so.1", "umem_alloc", &sym) != 0)
- umem_set_standalone();
+ if (umem_set_standalone() == -1) {
+ umem_ready = 0;
+ return (0); /* umem not there yet */
+ }
- if (UMEM_READVAR(umem_max_ncpus))
+ /*
+ * Solaris 9 used a different name for umem_max_ncpus. It's
+ * cheap backwards compatibility to check for both names.
+ */
+ if (umem_readvar(&umem_max_ncpus, "umem_max_ncpus") == -1 &&
+ umem_readvar(&umem_max_ncpus, "max_ncpus") == -1) {
+ mdb_warn("unable to read umem_max_ncpus or max_ncpus");
+ return (-1);
+ }
+ if (UMEM_READVAR(umem_ready))
return (-1);
if (UMEM_READVAR(umem_stack_depth))
return (-1);
if (UMEM_READVAR(pagesize))
return (-1);
- umem_pagesize = pagesize;
-
if (umem_stack_depth > UMEM_MAX_STACK_DEPTH) {
- mdb_warn("umem_stack_depth corrupted (%d > %d)\n",
- umem_stack_depth, UMEM_MAX_STACK_DEPTH);
+ if (umem_stack_depth_warned == 0) {
+ mdb_warn("umem_stack_depth corrupted (%d > %d)\n",
+ umem_stack_depth, UMEM_MAX_STACK_DEPTH);
+ umem_stack_depth_warned = 1;
+ }
umem_stack_depth = 0;
}
+
+ umem_pagesize = pagesize;
+
return (0);
}
/*ARGSUSED*/
-int
+static int
umem_init_walkers(uintptr_t addr, const umem_cache_t *c, void *ignored)
{
mdb_walker_t w;
@@ -103,6 +122,52 @@ umem_init_walkers(uintptr_t addr, const umem_cache_t *c, void *ignored)
return (WALK_NEXT);
}
+/*ARGSUSED*/
+static void
+umem_statechange_cb(void *arg)
+{
+ static int been_ready = 0;
+
+#ifndef _KMDB
+ leaky_cleanup(1); /* state changes invalidate leaky state */
+#endif
+
+ if (umem_update_variables() == -1)
+ return;
+
+ if (been_ready)
+ return;
+
+ if (umem_ready != UMEM_READY)
+ return;
+
+ been_ready = 1;
+ (void) mdb_walk("umem_cache", (mdb_walk_cb_t)umem_init_walkers, NULL);
+}
+
+int
+umem_init(void)
+{
+ mdb_walker_t w = {
+ "umem_cache", "walk list of umem caches", umem_cache_walk_init,
+ umem_cache_walk_step, umem_cache_walk_fini
+ };
+
+ if (mdb_add_walker(&w) == -1) {
+ mdb_warn("failed to add umem_cache walker");
+ return (-1);
+ }
+
+ if (umem_update_variables() == -1)
+ return (-1);
+
+ /* install a callback so that our variables are always up-to-date */
+ (void) mdb_callback_add(MDB_CALLBACK_STCHG, umem_statechange_cb, NULL);
+ umem_statechange_cb(NULL);
+
+ return (0);
+}
+
int
umem_abort_messages(void)
{
@@ -190,7 +255,6 @@ umem_debug_flags_t umem_status_flags[] = {
int
umem_status(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv)
{
- int umem_ready;
int umem_logging;
umem_log_header_t *umem_transaction_log;
@@ -198,16 +262,17 @@ umem_status(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv)
umem_log_header_t *umem_failure_log;
umem_log_header_t *umem_slab_log;
- if (UMEM_READVAR(umem_ready))
- goto err;
-
mdb_printf("Status:\t\t%s\n",
umem_ready == UMEM_READY_INIT_FAILED ? "initialization failed" :
umem_ready == UMEM_READY_STARTUP ? "uninitialized" :
umem_ready == UMEM_READY_INITING ? "initialization in process" :
umem_ready == UMEM_READY ? "ready and active" :
+ umem_ready == 0 ? "not loaded into address space" :
"unknown (umem_ready invalid)");
+ if (umem_ready == 0)
+ return (DCMD_OK);
+
mdb_printf("Concurrency:\t%d\n", umem_max_ncpus);
if (UMEM_READVAR(umem_logging))
@@ -673,7 +738,7 @@ umem_get_magsize(const umem_cache_t *cp)
(cp->cache_flags & UMF_NOMAGAZINE))
return (res);
- if (mdb_lookup_by_name("umem_magtype", &mt_sym) == -1) {
+ if (umem_lookup_by_name("umem_magtype", &mt_sym) == -1) {
mdb_warn("unable to read 'umem_magtype'");
} else if (addr < mt_sym.st_value ||
addr + sizeof (mt) - 1 > mt_sym.st_value + mt_sym.st_size - 1 ||
@@ -742,7 +807,7 @@ umem_estimate_allocated(uintptr_t addr, const umem_cache_t *cp)
}
int
-umem_read_magazines(umem_cache_t *cp, uintptr_t addr, int ncpus,
+umem_read_magazines(umem_cache_t *cp, uintptr_t addr,
void ***maglistp, size_t *magcntp, size_t *magmaxp, int alloc_flags)
{
umem_magazine_t *ump, *mp;
@@ -756,8 +821,12 @@ umem_read_magazines(umem_cache_t *cp, uintptr_t addr, int ncpus,
* correctness.
*/
magsize = umem_get_magsize(cp);
- if (magsize == 0)
- magsize = 1;
+ if (magsize == 0) {
+ *maglistp = NULL;
+ *magcntp = 0;
+ *magmaxp = 0;
+ return (WALK_NEXT);
+ }
/*
* There are several places where we need to go buffer hunting:
@@ -771,13 +840,13 @@ umem_read_magazines(umem_cache_t *cp, uintptr_t addr, int ncpus,
* is live (the number "100" comes from the same fudge factor in
* crash(1M)).
*/
- magmax = (cp->cache_full.ml_total + 2 * ncpus + 100) * magsize;
+ magmax = (cp->cache_full.ml_total + 2 * umem_max_ncpus + 100) * magsize;
magbsize = offsetof(umem_magazine_t, mag_round[magsize]);
if (magbsize >= PAGESIZE / 2) {
mdb_warn("magazine size for cache %p unreasonable (%x)\n",
addr, magbsize);
- goto fail;
+ return (WALK_ERR);
}
maglist = mdb_alloc(magmax * sizeof (void *), alloc_flags);
@@ -802,7 +871,7 @@ umem_read_magazines(umem_cache_t *cp, uintptr_t addr, int ncpus,
* Now whip through the CPUs, snagging the loaded magazines
* and full spares.
*/
- for (cpu = 0; cpu < ncpus; cpu++) {
+ for (cpu = 0; cpu < umem_max_ncpus; cpu++) {
umem_cpu_cache_t *ccp = &cp->cache_cpu[cpu];
dprintf(("reading cpu cache %p\n",
@@ -897,8 +966,9 @@ static int
umem_walk_init_common(mdb_walk_state_t *wsp, int type)
{
umem_walk_t *umw;
- int ncpus, csize;
+ int csize;
umem_cache_t *cp;
+ size_t vm_quantum;
size_t magmax, magcnt;
void **maglist = NULL;
@@ -917,12 +987,9 @@ umem_walk_init_common(mdb_walk_state_t *wsp, int type)
dprintf(("walking %p\n", addr));
/*
- * First we need to figure out how many CPUs are configured in the
- * system to know how much to slurp out.
+ * The number of "cpus" determines how large the cache is.
*/
- umem_readvar(&ncpus, "umem_max_ncpus");
-
- csize = UMEM_CACHE_SIZE(ncpus);
+ csize = UMEM_CACHE_SIZE(umem_max_ncpus);
cp = mdb_alloc(csize, UM_SLEEP);
if (mdb_vread(cp, csize, addr) == -1) {
@@ -930,6 +997,28 @@ umem_walk_init_common(mdb_walk_state_t *wsp, int type)
goto out2;
}
+ /*
+ * It's easy for someone to hand us an invalid cache address.
+ * Unfortunately, it is hard for this walker to survive an
+ * invalid cache cleanly. So we make sure that:
+ *
+ * 1. the vmem arena for the cache is readable,
+ * 2. the vmem arena's quantum is a power of 2,
+ * 3. our slabsize is a multiple of the quantum, and
+ * 4. our chunksize is >0 and less than our slabsize.
+ */
+ if (mdb_vread(&vm_quantum, sizeof (vm_quantum),
+ (uintptr_t)&cp->cache_arena->vm_quantum) == -1 ||
+ vm_quantum == 0 ||
+ (vm_quantum & (vm_quantum - 1)) != 0 ||
+ cp->cache_slabsize < vm_quantum ||
+ P2PHASE(cp->cache_slabsize, vm_quantum) != 0 ||
+ cp->cache_chunksize == 0 ||
+ cp->cache_chunksize > cp->cache_slabsize) {
+ mdb_warn("%p is not a valid umem_cache_t\n", addr);
+ goto out2;
+ }
+
dprintf(("buf total is %d\n", cp->cache_buftotal));
if (cp->cache_buftotal == 0) {
@@ -951,8 +1040,8 @@ umem_walk_init_common(mdb_walk_state_t *wsp, int type)
/*
* Read in the contents of the magazine layer
*/
- if (umem_read_magazines(cp, addr, ncpus, &maglist, &magcnt,
- &magmax, UM_SLEEP) == WALK_ERR)
+ if (umem_read_magazines(cp, addr, &maglist, &magcnt, &magmax,
+ UM_SLEEP) == WALK_ERR)
goto out2;
/*
@@ -1027,7 +1116,10 @@ out1:
mdb_free(umw->umw_ubase, slabsize +
sizeof (umem_bufctl_t));
- mdb_free(umw->umw_maglist, umw->umw_max * sizeof (uintptr_t));
+ if (umw->umw_maglist)
+ mdb_free(umw->umw_maglist, umw->umw_max *
+ sizeof (uintptr_t));
+
mdb_free(umw, sizeof (umem_walk_t));
wsp->walk_data = NULL;
}
@@ -3520,3 +3612,644 @@ umausers(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_OK);
}
+
+struct malloc_data {
+ uint32_t malloc_size;
+ uint32_t malloc_stat; /* == UMEM_MALLOC_ENCODE(state, malloc_size) */
+};
+
+#ifdef _LP64
+#define UMI_MAX_BUCKET (UMEM_MAXBUF - 2*sizeof (struct malloc_data))
+#else
+#define UMI_MAX_BUCKET (UMEM_MAXBUF - sizeof (struct malloc_data))
+#endif
+
+typedef struct umem_malloc_info {
+ size_t um_total; /* total allocated buffers */
+ size_t um_malloc; /* malloc buffers */
+ size_t um_malloc_size; /* sum of malloc buffer sizes */
+ size_t um_malloc_overhead; /* sum of in-chunk overheads */
+
+ umem_cache_t *um_cp;
+
+ uint_t *um_bucket;
+} umem_malloc_info_t;
+
+static const int *
+dist_linear(int buckets, int beg, int end)
+{
+ int *out = mdb_alloc((buckets + 1) * sizeof (*out), UM_SLEEP | UM_GC);
+ int pos;
+ int dist = end - beg + 1;
+
+ for (pos = 0; pos < buckets; pos++)
+ out[pos] = beg + (pos * dist)/buckets;
+ out[buckets] = end + 1;
+
+ return (out);
+}
+
+/*
+ * We want the bins to be a constant ratio:
+ *
+ * b_0 = beg;
+ * b_idx = b_{idx-1} * r;
+ * b_buckets = end + 1;
+ *
+ * That is:
+ *
+ * buckets
+ * beg * r = end
+ *
+ * Which reduces to:
+ *
+ * buckets ___________________
+ * r = -------/ ((end + 1) / beg)
+ *
+ * log ((end + 1) / beg)
+ * log r = ---------------------
+ * buckets
+ *
+ * (log ((end + 1) / beg)) / buckets
+ * r = e
+ */
+static const int *
+dist_geometric(int buckets, int beg, int end, int minbucketsize)
+{
+#ifdef _KMDB
+ return (dist_linear(buckets, beg, end));
+#else
+ int *out = mdb_alloc((buckets + 1) * sizeof (*out), UM_SLEEP | UM_GC);
+
+ extern double log(double);
+ extern double exp(double);
+
+ double r;
+ double b;
+ int idx = 0;
+ int last;
+ int begzero;
+
+ if (minbucketsize == 0)
+ minbucketsize = 1;
+
+ if (buckets == 1) {
+ out[0] = beg;
+ out[1] = end + 1;
+ return (out);
+ }
+
+ begzero = (beg == 0);
+ if (begzero)
+ beg = 1;
+
+ r = exp(log((double)(end + 1) / beg) / buckets);
+
+ /*
+ * We've now computed r, using the previously derived formula. We
+ * now need to generate the array of bucket bounds. There are
+ * two major variables:
+ *
+ * b holds b_idx, the current index, as a double.
+ * last holds the integer which goes into out[idx]
+ *
+ * Our job is to transform the smooth function b_idx, defined
+ * above, into integer-sized buckets, with a specified minimum
+ * bucket size. Since b_idx is an exponentially growing function,
+ * any inadequate buckets must be at the beginning. To deal
+ * with this, we make buckets of minimum size until b catches up
+ * with last.
+ *
+ * A final wrinkle is that beg *can* be zero. We compute r and b
+ * as if beg was 1, then start last as 0. This can lead to a bit
+ * of oddness around the 0 bucket, but it's mostly reasonable.
+ */
+
+ b = last = beg;
+ if (begzero)
+ last = 0;
+
+ for (idx = 0; idx < buckets; idx++) {
+ int next;
+
+ out[idx] = last;
+
+ b *= r;
+ next = (int)b;
+
+ if (next > last + minbucketsize - 1)
+ last = next;
+ else
+ last += minbucketsize;
+ }
+ out[buckets] = end + 1;
+
+ return (out);
+#endif
+}
+
+#define NCHARS 50
+static void
+umem_malloc_print_dist(uint_t *um_bucket, size_t minmalloc, size_t maxmalloc,
+ size_t maxbuckets, size_t minbucketsize, int geometric)
+{
+ size_t um_malloc;
+ int minb = -1;
+ int maxb = -1;
+ int buckets;
+ int nbucks;
+ int i;
+ int n;
+ int b;
+ const char *dist = " Distribution ";
+ char dashes[NCHARS + 1];
+ const int *distarray;
+
+ minb = (int)minmalloc;
+ maxb = (int)maxmalloc;
+
+ nbucks = buckets = maxb - minb + 1;
+
+ um_malloc = 0;
+ for (b = minb; b <= maxb; b++)
+ um_malloc += um_bucket[b];
+ if (um_malloc == 0)
+ um_malloc = 1; /* avoid divide-by-zero */
+
+ if (maxbuckets != 0)
+ buckets = MIN(buckets, maxbuckets);
+
+ if (minbucketsize > 1) {
+ buckets = MIN(buckets, nbucks/minbucketsize);
+ if (buckets == 0) {
+ buckets = 1;
+ minbucketsize = nbucks;
+ }
+ }
+
+
+ if (geometric)
+ distarray = dist_geometric(buckets, minb, maxb, minbucketsize);
+ else
+ distarray = dist_linear(buckets, minb, maxb);
+
+ n = (NCHARS - strlen(dist)) / 2;
+ (void) memset(dashes, '-', n);
+ dashes[n] = 0;
+
+ mdb_printf("%11s %s%s%s %s\n",
+ "malloc size", dashes, dist, dashes, "count");
+
+ for (i = 0; i < buckets; i++) {
+ int bb = distarray[i];
+ int be = distarray[i+1] - 1;
+ uint64_t amount = 0;
+
+ int nats;
+ char ats[NCHARS + 1], spaces[NCHARS + 1];
+ char range[40];
+
+ for (b = bb; b <= be; b++)
+ amount += um_bucket[b];
+
+ nats = (NCHARS * amount)/um_malloc;
+ (void) memset(ats, '@', nats);
+ ats[nats] = 0;
+ (void) memset(spaces, ' ', NCHARS - nats);
+ spaces[NCHARS - nats] = 0;
+
+ if (bb == be)
+ mdb_snprintf(range, sizeof (range), "%d", bb);
+ else
+ mdb_snprintf(range, sizeof (range), "%d-%d", bb, be);
+ mdb_printf("%11s |%s%s %lld\n", range, ats, spaces, amount);
+ }
+ mdb_printf("\n");
+}
+#undef NCHARS
+
+/*
+ * A malloc()ed buffer looks like:
+ *
+ * <----------- mi.malloc_size --->
+ * <----------- cp.cache_bufsize ------------------>
+ * <----------- cp.cache_chunksize -------------------------------->
+ * +-------+-----------------------+---------------+---------------+
+ * |/tag///| mallocsz |/round-off/////|/debug info////|
+ * +-------+---------------------------------------+---------------+
+ * <-- usable space ------>
+ *
+ * mallocsz is the argument to malloc(3C).
+ * mi.malloc_size is the actual size passed to umem_alloc(), which
+ * is rounded up to the smallest available cache size, which is
+ * cache_bufsize. If there is debugging or alignment overhead in
+ * the cache, that is reflected in a larger cache_chunksize.
+ *
+ * The tag at the beginning of the buffer is either 8-bytes or 16-bytes,
+ * depending upon the ISA's alignment requirements. For 32-bit allocations,
+ * it is always a 8-byte tag. For 64-bit allocations larger than 8 bytes,
+ * the tag has 8 bytes of padding before it.
+ *
+ * 32-byte, 64-byte buffers <= 8 bytes:
+ * +-------+-------+--------- ...
+ * |/size//|/stat//| mallocsz ...
+ * +-------+-------+--------- ...
+ * ^
+ * pointer returned from malloc(3C)
+ *
+ * 64-byte buffers > 8 bytes:
+ * +---------------+-------+-------+--------- ...
+ * |/padding///////|/size//|/stat//| mallocsz ...
+ * +---------------+-------+-------+--------- ...
+ * ^
+ * pointer returned from malloc(3C)
+ *
+ * The "size" field is "malloc_size", which is mallocsz + the padding.
+ * The "stat" field is derived from malloc_size, and functions as a
+ * validation that this buffer is actually from malloc(3C).
+ */
+/*ARGSUSED*/
+static int
+um_umem_buffer_cb(uintptr_t addr, void *buf, umem_malloc_info_t *ump)
+{
+ struct malloc_data md;
+ size_t m_addr = addr;
+ size_t overhead = sizeof (md);
+ size_t mallocsz;
+
+ ump->um_total++;
+
+#ifdef _LP64
+ if (ump->um_cp->cache_bufsize > UMEM_SECOND_ALIGN) {
+ m_addr += overhead;
+ overhead += sizeof (md);
+ }
+#endif
+
+ if (mdb_vread(&md, sizeof (md), m_addr) == -1) {
+ mdb_warn("unable to read malloc header at %p", m_addr);
+ return (WALK_NEXT);
+ }
+
+ switch (UMEM_MALLOC_DECODE(md.malloc_stat, md.malloc_size)) {
+ case MALLOC_MAGIC:
+#ifdef _LP64
+ case MALLOC_SECOND_MAGIC:
+#endif
+ mallocsz = md.malloc_size - overhead;
+
+ ump->um_malloc++;
+ ump->um_malloc_size += mallocsz;
+ ump->um_malloc_overhead += overhead;
+
+ /* include round-off and debug overhead */
+ ump->um_malloc_overhead +=
+ ump->um_cp->cache_chunksize - md.malloc_size;
+
+ if (ump->um_bucket != NULL && mallocsz <= UMI_MAX_BUCKET)
+ ump->um_bucket[mallocsz]++;
+
+ break;
+ default:
+ break;
+ }
+
+ return (WALK_NEXT);
+}
+
+int
+get_umem_alloc_sizes(int **out, size_t *out_num)
+{
+ GElf_Sym sym;
+
+ if (umem_lookup_by_name("umem_alloc_sizes", &sym) == -1) {
+ mdb_warn("unable to look up umem_alloc_sizes");
+ return (-1);
+ }
+
+ *out = mdb_alloc(sym.st_size, UM_SLEEP | UM_GC);
+ *out_num = sym.st_size / sizeof (int);
+
+ if (mdb_vread(*out, sym.st_size, sym.st_value) == -1) {
+ mdb_warn("unable to read umem_alloc_sizes (%p)", sym.st_value);
+ *out = NULL;
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+static int
+um_umem_cache_cb(uintptr_t addr, umem_cache_t *cp, umem_malloc_info_t *ump)
+{
+ if (strncmp(cp->cache_name, "umem_alloc_", strlen("umem_alloc_")) != 0)
+ return (WALK_NEXT);
+
+ ump->um_cp = cp;
+
+ if (mdb_pwalk("umem", (mdb_walk_cb_t)um_umem_buffer_cb, ump, addr) ==
+ -1) {
+ mdb_warn("can't walk 'umem' for cache %p", addr);
+ return (WALK_ERR);
+ }
+
+ return (WALK_NEXT);
+}
+
+void
+umem_malloc_dist_help(void)
+{
+ mdb_printf("%s\n",
+ "report distribution of outstanding malloc()s");
+ mdb_dec_indent(2);
+ mdb_printf("%<b>OPTIONS%</b>\n");
+ mdb_inc_indent(2);
+ mdb_printf("%s",
+" -b maxbins\n"
+" Use at most maxbins bins for the data\n"
+" -B minbinsize\n"
+" Make the bins at least minbinsize bytes apart\n"
+" -d dump the raw data out, without binning\n"
+" -g use geometric binning instead of linear binning\n");
+}
+
+/*ARGSUSED*/
+int
+umem_malloc_dist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ umem_malloc_info_t mi;
+ uint_t geometric = 0;
+ uint_t dump = 0;
+ size_t maxbuckets = 0;
+ size_t minbucketsize = 0;
+
+ size_t minalloc = 0;
+ size_t maxalloc = UMI_MAX_BUCKET;
+
+ if (flags & DCMD_ADDRSPEC)
+ return (DCMD_USAGE);
+
+ if (mdb_getopts(argc, argv,
+ 'd', MDB_OPT_SETBITS, TRUE, &dump,
+ 'g', MDB_OPT_SETBITS, TRUE, &geometric,
+ 'b', MDB_OPT_UINTPTR, &maxbuckets,
+ 'B', MDB_OPT_UINTPTR, &minbucketsize,
+ 0) != argc)
+ return (DCMD_USAGE);
+
+ bzero(&mi, sizeof (mi));
+ mi.um_bucket = mdb_zalloc((UMI_MAX_BUCKET + 1) * sizeof (*mi.um_bucket),
+ UM_SLEEP | UM_GC);
+
+ if (mdb_walk("umem_cache", (mdb_walk_cb_t)um_umem_cache_cb,
+ &mi) == -1) {
+ mdb_warn("unable to walk 'umem_cache'");
+ return (DCMD_ERR);
+ }
+
+ if (dump) {
+ int i;
+ for (i = minalloc; i <= maxalloc; i++)
+ mdb_printf("%d\t%d\n", i, mi.um_bucket[i]);
+
+ return (DCMD_OK);
+ }
+
+ umem_malloc_print_dist(mi.um_bucket, minalloc, maxalloc,
+ maxbuckets, minbucketsize, geometric);
+
+ return (DCMD_OK);
+}
+
+void
+umem_malloc_info_help(void)
+{
+ mdb_printf("%s\n",
+ "report information about malloc()s by cache. ");
+ mdb_dec_indent(2);
+ mdb_printf("%<b>OPTIONS%</b>\n");
+ mdb_inc_indent(2);
+ mdb_printf("%s",
+" -b maxbins\n"
+" Use at most maxbins bins for the data\n"
+" -B minbinsize\n"
+" Make the bins at least minbinsize bytes apart\n"
+" -d dump the raw distribution data without binning\n"
+#ifndef _KMDB
+" -g use geometric binning instead of linear binning\n"
+#endif
+ "");
+}
+int
+umem_malloc_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ umem_cache_t c;
+ umem_malloc_info_t mi;
+
+ int skip = 0;
+
+ size_t maxmalloc;
+ size_t overhead;
+ size_t allocated;
+ size_t avg_malloc;
+ size_t overhead_pct; /* 1000 * overhead_percent */
+
+ uint_t verbose = 0;
+ uint_t dump = 0;
+ uint_t geometric = 0;
+ size_t maxbuckets = 0;
+ size_t minbucketsize = 0;
+
+ int *alloc_sizes;
+ int idx;
+ size_t num;
+ size_t minmalloc;
+
+ if (mdb_getopts(argc, argv,
+ 'd', MDB_OPT_SETBITS, TRUE, &dump,
+ 'g', MDB_OPT_SETBITS, TRUE, &geometric,
+ 'b', MDB_OPT_UINTPTR, &maxbuckets,
+ 'B', MDB_OPT_UINTPTR, &minbucketsize,
+ 0) != argc)
+ return (DCMD_USAGE);
+
+ if (dump || geometric || (maxbuckets != 0) || (minbucketsize != 0))
+ verbose = 1;
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ if (mdb_walk_dcmd("umem_cache", "umem_malloc_info",
+ argc, argv) == -1) {
+ mdb_warn("can't walk umem_cache");
+ return (DCMD_ERR);
+ }
+ return (DCMD_OK);
+ }
+
+ if (!mdb_vread(&c, sizeof (c), addr)) {
+ mdb_warn("unable to read cache at %p", addr);
+ return (DCMD_ERR);
+ }
+
+ if (strncmp(c.cache_name, "umem_alloc_", strlen("umem_alloc_")) != 0) {
+ if (!(flags & DCMD_LOOP))
+ mdb_warn("umem_malloc_info: cache \"%s\" is not used "
+ "by malloc()\n", c.cache_name);
+ skip = 1;
+ }
+
+ /*
+ * normally, print the header only the first time. In verbose mode,
+ * print the header on every non-skipped buffer
+ */
+ if ((!verbose && DCMD_HDRSPEC(flags)) || (verbose && !skip))
+ mdb_printf("%<ul>%-?s %6s %6s %8s %8s %10s %10s %6s%</ul>\n",
+ "CACHE", "BUFSZ", "MAXMAL",
+ "BUFMALLC", "AVG_MAL", "MALLOCED", "OVERHEAD", "%OVER");
+
+ if (skip)
+ return (DCMD_OK);
+
+ maxmalloc = c.cache_bufsize - sizeof (struct malloc_data);
+#ifdef _LP64
+ if (c.cache_bufsize > UMEM_SECOND_ALIGN)
+ maxmalloc -= sizeof (struct malloc_data);
+#endif
+
+ bzero(&mi, sizeof (mi));
+ mi.um_cp = &c;
+ if (verbose)
+ mi.um_bucket =
+ mdb_zalloc((UMI_MAX_BUCKET + 1) * sizeof (*mi.um_bucket),
+ UM_SLEEP | UM_GC);
+
+ if (mdb_pwalk("umem", (mdb_walk_cb_t)um_umem_buffer_cb, &mi, addr) ==
+ -1) {
+ mdb_warn("can't walk 'umem'");
+ return (DCMD_ERR);
+ }
+
+ overhead = mi.um_malloc_overhead;
+ allocated = mi.um_malloc_size;
+
+ /* do integer round off for the average */
+ if (mi.um_malloc != 0)
+ avg_malloc = (allocated + (mi.um_malloc - 1)/2) / mi.um_malloc;
+ else
+ avg_malloc = 0;
+
+ /*
+ * include per-slab overhead
+ *
+ * Each slab in a given cache is the same size, and has the same
+ * number of chunks in it; we read in the first slab on the
+ * slab list to get the number of chunks for all slabs. To
+ * compute the per-slab overhead, we just subtract the chunk usage
+ * from the slabsize:
+ *
+ * +------------+-------+-------+ ... --+-------+-------+-------+
+ * |////////////| | | ... | |///////|///////|
+ * |////color///| chunk | chunk | ... | chunk |/color/|/slab//|
+ * |////////////| | | ... | |///////|///////|
+ * +------------+-------+-------+ ... --+-------+-------+-------+
+ * | \_______chunksize * chunks_____/ |
+ * \__________________________slabsize__________________________/
+ *
+ * For UMF_HASH caches, there is an additional source of overhead;
+ * the external umem_slab_t and per-chunk bufctl structures. We
+ * include those in our per-slab overhead.
+ *
+ * Once we have a number for the per-slab overhead, we estimate
+ * the actual overhead by treating the malloc()ed buffers as if
+ * they were densely packed:
+ *
+ * additional overhead = (# mallocs) * (per-slab) / (chunks);
+ *
+ * carefully ordering the multiply before the divide, to avoid
+ * round-off error.
+ */
+ if (mi.um_malloc != 0) {
+ umem_slab_t slab;
+ uintptr_t saddr = (uintptr_t)c.cache_nullslab.slab_next;
+
+ if (mdb_vread(&slab, sizeof (slab), saddr) == -1) {
+ mdb_warn("unable to read slab at %p\n", saddr);
+ } else {
+ long chunks = slab.slab_chunks;
+ if (chunks != 0 && c.cache_chunksize != 0 &&
+ chunks <= c.cache_slabsize / c.cache_chunksize) {
+ uintmax_t perslab =
+ c.cache_slabsize -
+ (c.cache_chunksize * chunks);
+
+ if (c.cache_flags & UMF_HASH) {
+ perslab += sizeof (umem_slab_t) +
+ chunks *
+ ((c.cache_flags & UMF_AUDIT) ?
+ sizeof (umem_bufctl_audit_t) :
+ sizeof (umem_bufctl_t));
+ }
+ overhead +=
+ (perslab * (uintmax_t)mi.um_malloc)/chunks;
+ } else {
+ mdb_warn("invalid #chunks (%d) in slab %p\n",
+ chunks, saddr);
+ }
+ }
+ }
+
+ if (allocated != 0)
+ overhead_pct = (1000ULL * overhead) / allocated;
+ else
+ overhead_pct = 0;
+
+ mdb_printf("%0?p %6ld %6ld %8ld %8ld %10ld %10ld %3ld.%01ld%%\n",
+ addr, c.cache_bufsize, maxmalloc,
+ mi.um_malloc, avg_malloc, allocated, overhead,
+ overhead_pct / 10, overhead_pct % 10);
+
+ if (!verbose)
+ return (DCMD_OK);
+
+ if (!dump)
+ mdb_printf("\n");
+
+ if (get_umem_alloc_sizes(&alloc_sizes, &num) == -1)
+ return (DCMD_ERR);
+
+ for (idx = 0; idx < num; idx++) {
+ if (alloc_sizes[idx] == c.cache_bufsize)
+ break;
+ if (alloc_sizes[idx] == 0) {
+ idx = num; /* 0-terminated array */
+ break;
+ }
+ }
+ if (idx == num) {
+ mdb_warn(
+ "cache %p's size (%d) not in umem_alloc_sizes\n",
+ addr, c.cache_bufsize);
+ return (DCMD_ERR);
+ }
+
+ minmalloc = (idx == 0)? 0 : alloc_sizes[idx - 1];
+ if (minmalloc > 0) {
+#ifdef _LP64
+ if (minmalloc > UMEM_SECOND_ALIGN)
+ minmalloc -= sizeof (struct malloc_data);
+#endif
+ minmalloc -= sizeof (struct malloc_data);
+ minmalloc += 1;
+ }
+
+ if (dump) {
+ for (idx = minmalloc; idx <= maxmalloc; idx++)
+ mdb_printf("%d\t%d\n", idx, mi.um_bucket[idx]);
+ mdb_printf("\n");
+ } else {
+ umem_malloc_print_dist(mi.um_bucket, minmalloc, maxmalloc,
+ maxbuckets, minbucketsize, geometric);
+ }
+
+ return (DCMD_OK);
+}
diff --git a/usr/src/cmd/mdb/common/modules/libumem/umem.h b/usr/src/cmd/mdb/common/modules/libumem/umem.h
index 17d1f6a6e1..f282b7ce97 100644
--- a/usr/src/cmd/mdb/common/modules/libumem/umem.h
+++ b/usr/src/cmd/mdb/common/modules/libumem/umem.h
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -36,8 +35,7 @@
extern "C" {
#endif
-extern uint32_t umem_max_ncpus;
-
+extern int umem_ready;
extern uint32_t umem_stack_depth;
extern int umem_cache_walk_init(mdb_walk_state_t *);
@@ -113,6 +111,8 @@ extern int umalog(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int umausers(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int umem_cache(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int umem_log(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int umem_malloc_dist(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int umem_malloc_info(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int umem_status(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int umem_verify(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int umem_verify_alloc(uintptr_t, uint_t, int, const mdb_arg_t *);
@@ -120,14 +120,16 @@ extern int umem_verify_free(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int vmem(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int vmem_seg(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int whatis(uintptr_t, uint_t, int, const mdb_arg_t *);
+
extern void bufctl_help(void);
+extern void umem_malloc_dist_help(void);
+extern void umem_malloc_info_help(void);
extern void vmem_seg_help(void);
/*
* utility functions for the rest of libumem
*/
extern int umem_init(void);
-extern int umem_init_walkers(uintptr_t, const umem_cache_t *, void *);
extern int umem_get_magsize(const umem_cache_t *);
extern size_t umem_estimate_allocated(uintptr_t, const umem_cache_t *);
diff --git a/usr/src/cmd/mdb/intel/amd64/libumem/Makefile b/usr/src/cmd/mdb/intel/amd64/libumem/Makefile
index 3a7236341a..5d5c2a9530 100644
--- a/usr/src/cmd/mdb/intel/amd64/libumem/Makefile
+++ b/usr/src/cmd/mdb/intel/amd64/libumem/Makefile
@@ -2,9 +2,8 @@
# 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.
+# Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -48,6 +47,8 @@ CPPFLAGS += -I$(MODSRCS_DIR)
include ../../Makefile.amd64
include ../../../Makefile.module
+dmod/$(MODULE) := LDLIBS += -lm
+
%.o: $(MODSRCS_DIR)/%.c
$(COMPILE.c) $<
$(CTFCONVERT_O)
diff --git a/usr/src/cmd/mdb/intel/ia32/libumem/Makefile b/usr/src/cmd/mdb/intel/ia32/libumem/Makefile
index 3948a50c80..be5b322348 100644
--- a/usr/src/cmd/mdb/intel/ia32/libumem/Makefile
+++ b/usr/src/cmd/mdb/intel/ia32/libumem/Makefile
@@ -2,9 +2,8 @@
# 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.
+# Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -47,6 +46,8 @@ CPPFLAGS += -I$(MODSRCS_DIR)
include ../../Makefile.ia32
include ../../../Makefile.module
+dmod/$(MODULE) := LDLIBS += -lm
+
%.o: $(MODSRCS_DIR)/%.c
$(COMPILE.c) $<
$(CTFCONVERT_O)
diff --git a/usr/src/cmd/mdb/sparc/v7/libumem/Makefile b/usr/src/cmd/mdb/sparc/v7/libumem/Makefile
index dc5739732a..f2f5d64f65 100644
--- a/usr/src/cmd/mdb/sparc/v7/libumem/Makefile
+++ b/usr/src/cmd/mdb/sparc/v7/libumem/Makefile
@@ -2,9 +2,8 @@
# 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.
+# Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -47,6 +46,8 @@ CPPFLAGS += -I$(MODSRCS_DIR)
include ../../Makefile.sparcv7
include ../../../Makefile.module
+dmod/$(MODULE) := LDLIBS += -lm
+
%.o: $(MODSRCS_DIR)/%.c
$(COMPILE.c) $<
$(CTFCONVERT_O)
diff --git a/usr/src/cmd/mdb/sparc/v9/libumem/Makefile b/usr/src/cmd/mdb/sparc/v9/libumem/Makefile
index 948324bd0a..3d5e987796 100644
--- a/usr/src/cmd/mdb/sparc/v9/libumem/Makefile
+++ b/usr/src/cmd/mdb/sparc/v9/libumem/Makefile
@@ -2,9 +2,8 @@
# 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.
+# Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -61,6 +60,8 @@ include ../../../../Makefile.cmd.64
include ../../Makefile.sparcv9
include ../../../Makefile.module
+dmod/$(MODULE) := LDLIBS += -lm
+
%.o: $(MODSRCS_DIR)/%.c
$(COMPILE.c) $<
$(CTFCONVERT_O)
diff --git a/usr/src/lib/libumem/common/envvar.c b/usr/src/lib/libumem/common/envvar.c
index 82afede143..bbdaf3a7e8 100644
--- a/usr/src/lib/libumem/common/envvar.c
+++ b/usr/src/lib/libumem/common/envvar.c
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -89,6 +88,9 @@ static arg_process_t umem_backend_process;
static arg_process_t umem_log_process;
+static size_t umem_size_tempval;
+static arg_process_t umem_size_process;
+
const char *____umem_environ_msg_options = "-- UMEM_OPTIONS --";
static umem_env_item_t umem_options_items[] = {
@@ -118,7 +120,27 @@ static umem_env_item_t umem_options_items[] = {
NULL, 0, &umem_reap_interval
},
+ { "size_add", "Private", ITEM_SPECIAL,
+ "add a size to the cache size table",
+ NULL, 0, NULL,
+ &umem_size_tempval, &umem_size_process
+ },
+ { "size_clear", "Private", ITEM_SPECIAL,
+ "clear all but the largest size from the cache size table",
+ NULL, 0, NULL,
+ &umem_size_tempval, &umem_size_process
+ },
+ { "size_remove", "Private", ITEM_SPECIAL,
+ "remove a size from the cache size table",
+ NULL, 0, NULL,
+ &umem_size_tempval, &umem_size_process
+ },
+
#ifndef UMEM_STANDALONE
+ { "sbrk_minalloc", "Private", ITEM_SIZE,
+ "The minimum allocation chunk for the sbrk(2) heap.",
+ NULL, 0, NULL, &vmem_sbrk_minalloc
+ },
{ "sbrk_pagesize", "Private", ITEM_SIZE,
"The preferred page size for the sbrk(2) heap.",
NULL, 0, NULL, &vmem_sbrk_pagesize
@@ -385,6 +407,49 @@ umem_log_process(const umem_env_item_t *item, const char *item_arg)
return (ARG_SUCCESS);
}
+static int
+umem_size_process(const umem_env_item_t *item, const char *item_arg)
+{
+ const char *name = item->item_name;
+ void (*action_func)(size_t);
+
+ size_t result;
+
+ int ret;
+
+ if (strcmp(name, "size_clear") == 0) {
+ if (item_arg != NULL) {
+ log_message("%s: %s: does not take a value. ignored\n",
+ CURRENT, name);
+ return (ARG_BAD);
+ }
+ umem_alloc_sizes_clear();
+ return (ARG_SUCCESS);
+ } else if (strcmp(name, "size_add") == 0) {
+ action_func = umem_alloc_sizes_add;
+ } else if (strcmp(name, "size_remove") == 0) {
+ action_func = umem_alloc_sizes_remove;
+ } else {
+ log_message("%s: %s: internally unrecognized\n",
+ CURRENT, name, name, name);
+ return (ARG_BAD);
+ }
+
+ if (item_arg == NULL) {
+ log_message("%s: %s: requires a value. ignored\n",
+ CURRENT, name);
+ return (ARG_BAD);
+ }
+
+ ret = item_size_process(item, item_arg);
+ if (ret != ARG_SUCCESS)
+ return (ret);
+
+ result = *item->item_size_target;
+ action_func(result);
+ return (ARG_SUCCESS);
+}
+
#ifndef UMEM_STANDALONE
static int
umem_backend_process(const umem_env_item_t *item, const char *item_arg)
diff --git a/usr/src/lib/libumem/common/umem.c b/usr/src/lib/libumem/common/umem.c
index d69b245e97..9e762377ae 100644
--- a/usr/src/lib/libumem/common/umem.c
+++ b/usr/src/lib/libumem/common/umem.c
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -344,7 +343,7 @@
* vmem_nosleep_lock.vmpl_mutex
* vmem_t's:
* vm_lock
- * sbrk_faillock
+ * sbrk_lock
*
* umem_cache_lock
* umem_update_lock
@@ -393,8 +392,12 @@ size_t pagesize;
* bytes, so that it will be 64-byte aligned. For all multiples of 64,
* the next kmem_cache_size greater than or equal to it must be a
* multiple of 64.
+ *
+ * This table must be in sorted order, from smallest to highest. The
+ * highest slot must be UMEM_MAXBUF, and every slot afterwards must be
+ * zero.
*/
-static const int umem_alloc_sizes[] = {
+static int umem_alloc_sizes[] = {
#ifdef _LP64
1 * 8,
1 * 16,
@@ -413,17 +416,19 @@ static const int umem_alloc_sizes[] = {
P2ALIGN(8192 / 7, 64),
P2ALIGN(8192 / 6, 64),
P2ALIGN(8192 / 5, 64),
- P2ALIGN(8192 / 4, 64),
+ P2ALIGN(8192 / 4, 64), 2304,
P2ALIGN(8192 / 3, 64),
- P2ALIGN(8192 / 2, 64),
- P2ALIGN(8192 / 1, 64),
+ P2ALIGN(8192 / 2, 64), 4544,
+ P2ALIGN(8192 / 1, 64), 9216,
4096 * 3,
- 8192 * 2,
+ UMEM_MAXBUF, /* = 8192 * 2 */
+ /* 24 slots for user expansion */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
};
#define NUM_ALLOC_SIZES (sizeof (umem_alloc_sizes) / sizeof (*umem_alloc_sizes))
-#define UMEM_MAXBUF 16384
-
static umem_magtype_t umem_magtype[] = {
{ 1, 8, 3200, 65536 },
{ 3, 16, 256, 32768 },
@@ -2728,6 +2733,88 @@ umem_cache_destroy(umem_cache_t *cp)
vmem_free(umem_cache_arena, cp, UMEM_CACHE_SIZE(umem_max_ncpus));
}
+void
+umem_alloc_sizes_clear(void)
+{
+ int i;
+
+ umem_alloc_sizes[0] = UMEM_MAXBUF;
+ for (i = 1; i < NUM_ALLOC_SIZES; i++)
+ umem_alloc_sizes[i] = 0;
+}
+
+void
+umem_alloc_sizes_add(size_t size_arg)
+{
+ int i, j;
+ size_t size = size_arg;
+
+ if (size == 0) {
+ log_message("size_add: cannot add zero-sized cache\n",
+ size, UMEM_MAXBUF);
+ return;
+ }
+
+ if (size > UMEM_MAXBUF) {
+ log_message("size_add: %ld > %d, cannot add\n", size,
+ UMEM_MAXBUF);
+ return;
+ }
+
+ if (umem_alloc_sizes[NUM_ALLOC_SIZES - 1] != 0) {
+ log_message("size_add: no space in alloc_table for %d\n",
+ size);
+ return;
+ }
+
+ if (P2PHASE(size, UMEM_ALIGN) != 0) {
+ size = P2ROUNDUP(size, UMEM_ALIGN);
+ log_message("size_add: rounding %d up to %d\n", size_arg,
+ size);
+ }
+
+ for (i = 0; i < NUM_ALLOC_SIZES; i++) {
+ int cur = umem_alloc_sizes[i];
+ if (cur == size) {
+ log_message("size_add: %ld already in table\n",
+ size);
+ return;
+ }
+ if (cur > size)
+ break;
+ }
+
+ for (j = NUM_ALLOC_SIZES - 1; j > i; j--)
+ umem_alloc_sizes[j] = umem_alloc_sizes[j-1];
+ umem_alloc_sizes[i] = size;
+}
+
+void
+umem_alloc_sizes_remove(size_t size)
+{
+ int i;
+
+ if (size == UMEM_MAXBUF) {
+ log_message("size_remove: cannot remove %ld\n", size);
+ return;
+ }
+
+ for (i = 0; i < NUM_ALLOC_SIZES; i++) {
+ int cur = umem_alloc_sizes[i];
+ if (cur == size)
+ break;
+ else if (cur > size || cur == 0) {
+ log_message("size_remove: %ld not found in table\n",
+ size);
+ return;
+ }
+ }
+
+ for (; i + 1 < NUM_ALLOC_SIZES; i++)
+ umem_alloc_sizes[i] = umem_alloc_sizes[i+1];
+ umem_alloc_sizes[i] = 0;
+}
+
static int
umem_cache_init(void)
{
@@ -2820,6 +2907,10 @@ umem_cache_init(void)
for (i = 0; i < NUM_ALLOC_SIZES; i++) {
size_t cache_size = umem_alloc_sizes[i];
size_t align = 0;
+
+ if (cache_size == 0)
+ break; /* 0 terminates the list */
+
/*
* If they allocate a multiple of the coherency granularity,
* they get a coherency-granularity-aligned address.
@@ -2847,6 +2938,9 @@ umem_cache_init(void)
for (i = 0; i < NUM_ALLOC_SIZES; i++) {
size_t cache_size = umem_alloc_sizes[i];
+ if (cache_size == 0)
+ break; /* 0 terminates the list */
+
cp = umem_alloc_caches[i];
while (size <= cache_size) {
@@ -2854,6 +2948,7 @@ umem_cache_init(void)
size += UMEM_ALIGN;
}
}
+ ASSERT(size - UMEM_ALIGN == UMEM_MAXBUF);
return (1);
}
diff --git a/usr/src/lib/libumem/common/umem_base.h b/usr/src/lib/libumem/common/umem_base.h
index b853222907..e78bebfb58 100644
--- a/usr/src/lib/libumem/common/umem_base.h
+++ b/usr/src/lib/libumem/common/umem_base.h
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -120,6 +119,10 @@ extern void umem_process_updates(void);
extern void umem_cache_applyall(void (*)(umem_cache_t *));
extern void umem_cache_update(umem_cache_t *);
+extern void umem_alloc_sizes_add(size_t);
+extern void umem_alloc_sizes_clear(void);
+extern void umem_alloc_sizes_remove(size_t);
+
/*
* umem_fork.c: private interfaces
*/
diff --git a/usr/src/lib/libumem/common/vmem_base.h b/usr/src/lib/libumem/common/vmem_base.h
index 4af0a50539..46ed397343 100644
--- a/usr/src/lib/libumem/common/vmem_base.h
+++ b/usr/src/lib/libumem/common/vmem_base.h
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -60,6 +59,7 @@ extern void vmem_reap(void); /* vmem_populate()-safe reap */
extern size_t pagesize;
extern size_t vmem_sbrk_pagesize;
+extern size_t vmem_sbrk_minalloc;
extern uint_t vmem_backend;
#define VMEM_BACKEND_SBRK 0x0000001
diff --git a/usr/src/lib/libumem/common/vmem_sbrk.c b/usr/src/lib/libumem/common/vmem_sbrk.c
index 3c59372fbe..f07c543988 100644
--- a/usr/src/lib/libumem/common/vmem_sbrk.c
+++ b/usr/src/lib/libumem/common/vmem_sbrk.c
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -65,7 +64,8 @@
size_t vmem_sbrk_pagesize = 0; /* the preferred page size of the heap */
-#define MIN_ALLOC (64*1024)
+#define VMEM_SBRK_MINALLOC (64 * 1024)
+size_t vmem_sbrk_minalloc = VMEM_SBRK_MINALLOC; /* minimum allocation */
static size_t real_pagesize;
static vmem_t *sbrk_heap;
@@ -176,7 +176,7 @@ vmem_sbrk_alloc(vmem_t *src, size_t size, int vmflags)
(ret = vmem_sbrk_tryfail(src, size, vmflags)) != NULL)
return (ret);
- buf_size = MAX(size, MIN_ALLOC);
+ buf_size = MAX(size, vmem_sbrk_minalloc);
/*
* buf_size gets overwritten with the actual allocated size
@@ -249,6 +249,11 @@ vmem_sbrk_arena(vmem_alloc_t **a_out, vmem_free_t **f_out)
}
vmem_sbrk_pagesize = heap_size;
+ /* validate vmem_sbrk_minalloc */
+ if (vmem_sbrk_minalloc < VMEM_SBRK_MINALLOC)
+ vmem_sbrk_minalloc = VMEM_SBRK_MINALLOC;
+ vmem_sbrk_minalloc = P2ROUNDUP(vmem_sbrk_minalloc, heap_size);
+
sbrk_heap = vmem_init("sbrk_top", real_pagesize,
vmem_sbrk_alloc, vmem_free,
"sbrk_heap", NULL, 0, real_pagesize,