summaryrefslogtreecommitdiff
path: root/usr/src/cmd/mdb/common/modules
diff options
context:
space:
mode:
authorJonathan Adams <Jonathan.Adams@Sun.COM>2009-10-27 13:44:45 -0700
committerJonathan Adams <Jonathan.Adams@Sun.COM>2009-10-27 13:44:45 -0700
commite0ad97e30ea0a9af63c42d71690b5f387c763420 (patch)
tree506c19fe9e8d888deada1b6b17b8be1af1cd200c /usr/src/cmd/mdb/common/modules
parentf717cf302ad33ac8dcb06cab149e63a1f07618c5 (diff)
downloadillumos-gate-e0ad97e30ea0a9af63c42d71690b5f387c763420.tar.gz
6888456 zfs command line utilities should have CTF data
6887356 ::stacks is slow in pipelines on live kernels 6884079 mdb's enum p2 printing should elide common prefixes 6887160 ::offsetof and ::sizeof still can't handle forward decls 6551113 ::taskq would be nice --HG-- rename : usr/src/cmd/svc/Makefile.ctf => usr/src/cmd/Makefile.ctf
Diffstat (limited to 'usr/src/cmd/mdb/common/modules')
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/Makefile.files1
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/findstack.c88
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/genunix.c128
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/taskq.c483
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/taskq.h49
5 files changed, 607 insertions, 142 deletions
diff --git a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
index 1d45564e4b..b52992c8d0 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
+++ b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
@@ -66,6 +66,7 @@ GENUNIX_SRCS = \
sobj.c \
streams.c \
sysevent.c \
+ taskq.c \
thread.c \
tsd.c \
tsol.c \
diff --git a/usr/src/cmd/mdb/common/modules/genunix/findstack.c b/usr/src/cmd/mdb/common/modules/genunix/findstack.c
index 655451f780..ccb50ae26e 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/findstack.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/findstack.c
@@ -690,7 +690,36 @@ stacks_thread_cb(uintptr_t addr, const void *ignored, void *cbarg)
}
int
-stacks_run(int verbose)
+stacks_run_tlist(mdb_pipe_t *tlist, stacks_info_t *si)
+{
+ size_t idx;
+ size_t found = 0;
+ kthread_t kt;
+ int ret;
+
+ for (idx = 0; idx < tlist->pipe_len; idx++) {
+ uintptr_t addr = tlist->pipe_data[idx];
+
+ if (mdb_vread(&kt, sizeof (kt), addr) == -1) {
+ mdb_warn("unable to read kthread_t at %p", addr);
+ continue;
+ }
+ found++;
+
+ ret = stacks_thread_cb(addr, &kt, si);
+ if (ret == WALK_DONE)
+ break;
+ if (ret != WALK_NEXT)
+ return (-1);
+ }
+
+ if (found)
+ return (0);
+ return (-1);
+}
+
+int
+stacks_run(int verbose, mdb_pipe_t *tlist)
{
stacks_info_t si;
findstack_info_t *fsip = &si.si_fsi;
@@ -714,9 +743,14 @@ stacks_run(int verbose)
if (verbose)
mdb_warn("stacks: processing kernel threads\n");
- if (mdb_walk("thread", stacks_thread_cb, &si) != 0) {
- mdb_warn("cannot walk \"thread\"");
- return (DCMD_ERR);
+ if (tlist != NULL) {
+ if (stacks_run_tlist(tlist, &si))
+ return (DCMD_ERR);
+ } else {
+ if (mdb_walk("thread", stacks_thread_cb, &si) != 0) {
+ mdb_warn("cannot walk \"thread\"");
+ return (DCMD_ERR);
+ }
}
if (verbose)
@@ -745,7 +779,8 @@ stacks_run(int verbose)
stacks_hash = NULL;
mdb_free(si.si_hash, STACKS_HSIZE * sizeof (*si.si_hash));
- stacks_state = STACKS_STATE_DONE;
+ if (tlist == NULL)
+ stacks_state = STACKS_STATE_DONE;
if (verbose)
mdb_warn("stacks: done\n");
@@ -959,6 +994,7 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
const char *excl_tstate_str = NULL;
uint_t tstate = -1U;
uint_t excl_tstate = -1U;
+ uint_t printed = 0;
uint_t all = 0;
uint_t force = 0;
@@ -1066,27 +1102,6 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
}
/*
- * Force a cleanup if we're connected to a live system. Never
- * do a cleanup after the first invocation around the loop.
- */
- force |= (mdb_get_state() == MDB_STATE_RUNNING);
- if (force && (flags & (DCMD_LOOPFIRST|DCMD_LOOP)) == DCMD_LOOP)
- force = 0;
-
- stacks_cleanup(force);
-
- if (stacks_state == STACKS_STATE_CLEAN) {
- int res = stacks_run(verbose);
- if (res != DCMD_OK)
- return (res);
- }
-
- if (!all && DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
- mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n",
- "THREAD", "STATE", "SOBJ", "COUNT");
- }
-
- /*
* If there's an address specified, we're going to further filter
* to only entries which have an address in the input. To reduce
* overhead (and make the sorted output come out right), we
@@ -1120,6 +1135,22 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
seen = mdb_zalloc(p.pipe_len, UM_SLEEP | UM_GC);
}
+ /*
+ * Force a cleanup if we're connected to a live system. Never
+ * do a cleanup after the first invocation around the loop.
+ */
+ force |= (mdb_get_state() == MDB_STATE_RUNNING);
+ if (force && (flags & (DCMD_LOOPFIRST|DCMD_LOOP)) == DCMD_LOOP)
+ force = 0;
+
+ stacks_cleanup(force);
+
+ if (stacks_state == STACKS_STATE_CLEAN) {
+ int res = stacks_run(verbose, addrspec ? &p : NULL);
+ if (res != DCMD_OK)
+ return (res);
+ }
+
for (idx = 0; idx < stacks_array_size; idx++) {
stacks_entry_t *sep = stacks_array[idx];
stacks_entry_t *cur = sep;
@@ -1209,9 +1240,10 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
continue;
}
- if (all) {
+ if (all || !printed) {
mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n",
- "THREAD", "STATE", "SOBJTYPE", "COUNT");
+ "THREAD", "STATE", "SOBJ", "COUNT");
+ printed = 1;
}
do {
diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
index 912d5f7ce7..f27684baf6 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
@@ -58,8 +58,6 @@
#include <sys/sysconf.h>
#include <sys/task.h>
#include <sys/project.h>
-#include <sys/taskq.h>
-#include <sys/taskq_impl.h>
#include <sys/errorq_impl.h>
#include <sys/cred_impl.h>
#include <sys/zone.h>
@@ -101,6 +99,7 @@
#include "sobj.h"
#include "streams.h"
#include "sysevent.h"
+#include "taskq.h"
#include "thread.h"
#include "tsd.h"
#include "tsol.h"
@@ -3853,115 +3852,6 @@ sysfile(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_OK);
}
-/*
- * Dump a taskq_ent_t given its address.
- */
-/*ARGSUSED*/
-int
-taskq_ent(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
-{
- taskq_ent_t taskq_ent;
- GElf_Sym sym;
- char buf[MDB_SYM_NAMLEN+1];
-
-
- if (!(flags & DCMD_ADDRSPEC)) {
- mdb_warn("expected explicit taskq_ent_t address before ::\n");
- return (DCMD_USAGE);
- }
-
- if (mdb_vread(&taskq_ent, sizeof (taskq_ent_t), addr) == -1) {
- mdb_warn("failed to read taskq_ent_t at %p", addr);
- return (DCMD_ERR);
- }
-
- if (DCMD_HDRSPEC(flags)) {
- mdb_printf("%<u>%-?s %-?s %-s%</u>\n",
- "ENTRY", "ARG", "FUNCTION");
- }
-
- if (mdb_lookup_by_addr((uintptr_t)taskq_ent.tqent_func, MDB_SYM_EXACT,
- buf, sizeof (buf), &sym) == -1) {
- (void) strcpy(buf, "????");
- }
-
- mdb_printf("%-?p %-?p %s\n", addr, taskq_ent.tqent_arg, buf);
-
- return (DCMD_OK);
-}
-
-/*
- * Given the address of the (taskq_t) task queue head, walk the queue listing
- * the address of every taskq_ent_t.
- */
-int
-taskq_walk_init(mdb_walk_state_t *wsp)
-{
- taskq_t tq_head;
-
-
- if (wsp->walk_addr == NULL) {
- mdb_warn("start address required\n");
- return (WALK_ERR);
- }
-
-
- /*
- * Save the address of the list head entry. This terminates the list.
- */
- wsp->walk_data = (void *)
- ((size_t)wsp->walk_addr + offsetof(taskq_t, tq_task));
-
-
- /*
- * Read in taskq head, set walk_addr to point to first taskq_ent_t.
- */
- if (mdb_vread((void *)&tq_head, sizeof (taskq_t), wsp->walk_addr) ==
- -1) {
- mdb_warn("failed to read taskq list head at %p",
- wsp->walk_addr);
- }
- wsp->walk_addr = (uintptr_t)tq_head.tq_task.tqent_next;
-
-
- /*
- * Check for null list (next=head)
- */
- if (wsp->walk_addr == (uintptr_t)wsp->walk_data) {
- return (WALK_DONE);
- }
-
- return (WALK_NEXT);
-}
-
-
-int
-taskq_walk_step(mdb_walk_state_t *wsp)
-{
- taskq_ent_t tq_ent;
- int status;
-
-
- if (mdb_vread((void *)&tq_ent, sizeof (taskq_ent_t), wsp->walk_addr) ==
- -1) {
- mdb_warn("failed to read taskq_ent_t at %p", wsp->walk_addr);
- return (DCMD_ERR);
- }
-
- status = wsp->walk_callback(wsp->walk_addr, (void *)&tq_ent,
- wsp->walk_cbdata);
-
- wsp->walk_addr = (uintptr_t)tq_ent.tqent_next;
-
-
- /* Check if we're at the last element (next=head) */
- if (wsp->walk_addr == (uintptr_t)wsp->walk_data) {
- return (WALK_DONE);
- }
-
- return (status);
-}
-
int
didmatch(uintptr_t addr, const kthread_t *thr, kt_did_t *didp)
{
@@ -4325,7 +4215,6 @@ static const mdb_dcmd_t dcmds[] = {
"print sysevent subclass list", sysevent_subclass_list},
{ "system", NULL, "print contents of /etc/system file", sysfile },
{ "task", NULL, "display kernel task(s)", task },
- { "taskq_entry", ":", "display a taskq_ent_t", taskq_ent },
{ "vnode2path", ":[-F]", "vnode address to pathname", vnode2path },
{ "vnode2smap", ":[offset]", "translate vnode to smap", vnode2smap },
{ "whereopen", ":", "given a vnode, dumps procs which have it open",
@@ -4571,6 +4460,11 @@ static const mdb_dcmd_t dcmds[] = {
"filter and display STREAM sync queue", syncq, syncq_help },
{ "syncq2q", ":", "print queue for a given syncq", syncq2q },
+ /* from taskq.c */
+ { "taskq", ":[-atT] [-m min_maxq] [-n name]",
+ "display a taskq", taskq, taskq_help },
+ { "taskq_entry", ":", "display a taskq_ent_t", taskq_ent },
+
/* from thread.c */
{ "thread", "?[-bdfimps]", "display a summarized kthread_t", thread,
thread_help },
@@ -4672,8 +4566,6 @@ static const mdb_walker_t walkers[] = {
sysevent_subclass_list_walk_fini},
{ "task", "given a task pointer, walk its processes",
task_walk_init, task_walk_step, NULL },
- { "taskq_entry", "given a taskq_t*, list all taskq_ent_t in the list",
- taskq_walk_init, taskq_walk_step, NULL, NULL },
/* from avl.c */
{ AVL_WALK_NAME, AVL_WALK_DESC,
@@ -4929,6 +4821,14 @@ static const mdb_walker_t walkers[] = {
{ "writeq", "walk write queue side of stdata",
str_walk_init, strw_walk_step, str_walk_fini },
+ /* from taskq.c */
+ { "taskq_thread", "given a taskq_t, list all of its threads",
+ taskq_thread_walk_init,
+ taskq_thread_walk_step,
+ taskq_thread_walk_fini },
+ { "taskq_entry", "given a taskq_t*, list all taskq_ent_t in the list",
+ taskq_ent_walk_init, taskq_ent_walk_step, NULL },
+
/* from thread.c */
{ "deathrow", "walk threads on both lwp_ and thread_deathrow",
deathrow_walk_init, deathrow_walk_step, NULL },
diff --git a/usr/src/cmd/mdb/common/modules/genunix/taskq.c b/usr/src/cmd/mdb/common/modules/genunix/taskq.c
new file mode 100644
index 0000000000..03e199cc3b
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/genunix/taskq.c
@@ -0,0 +1,483 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * 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.
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <mdb/mdb_param.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_ks.h>
+#include <sys/taskq.h>
+#include <sys/taskq_impl.h>
+
+#include "taskq.h"
+
+typedef struct tqarray_ent {
+ uintptr_t tq_addr;
+ char tq_name[TASKQ_NAMELEN + 1];
+ int tq_instance;
+ uint_t tq_flags;
+} tqarray_ent_t;
+
+typedef struct tq_info {
+ tqarray_ent_t *tqi_array;
+ size_t tqi_count;
+ size_t tqi_size;
+} tq_info_t;
+
+/*
+ * We sort taskqs as follows:
+ *
+ * DYNAMIC last
+ * NOINSTANCE first
+ * within NOINSTANCE, sort by order of creation (instance #)
+ * within non-NOINSTANCE, sort by name (case-insensitive) then instance #
+ */
+int
+tqcmp(const void *lhs, const void *rhs)
+{
+ const tqarray_ent_t *l = lhs;
+ const tqarray_ent_t *r = rhs;
+ uint_t lflags = l->tq_flags;
+ uint_t rflags = r->tq_flags;
+ int ret;
+
+ if ((lflags & TASKQ_DYNAMIC) && !(rflags & TASKQ_DYNAMIC))
+ return (1);
+ if (!(lflags & TASKQ_DYNAMIC) && (rflags & TASKQ_DYNAMIC))
+ return (-1);
+
+ if ((lflags & TASKQ_NOINSTANCE) && !(rflags & TASKQ_NOINSTANCE))
+ return (-1);
+ if (!(lflags & TASKQ_NOINSTANCE) && (rflags & TASKQ_NOINSTANCE))
+ return (1);
+
+ if (!(lflags & TASKQ_NOINSTANCE) &&
+ (ret = strcasecmp(l->tq_name, r->tq_name)) != 0)
+ return (ret);
+
+ if (l->tq_instance < r->tq_instance)
+ return (-1);
+ if (l->tq_instance > r->tq_instance)
+ return (1);
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+tq_count(uintptr_t addr, const void *ignored, void *arg)
+{
+ tq_info_t *ti = arg;
+
+ ti->tqi_size++;
+ return (WALK_NEXT);
+}
+
+/*ARGSUSED*/
+int
+tq_fill(uintptr_t addr, const void *ignored, tq_info_t *ti)
+{
+ int idx = ti->tqi_count;
+ taskq_t tq;
+ tqarray_ent_t *tqe = &ti->tqi_array[idx];
+
+ if (idx == ti->tqi_size) {
+ mdb_warn("taskq: inadequate slop\n");
+ return (WALK_ERR);
+ }
+ if (mdb_vread(&tq, sizeof (tq), addr) == -1) {
+ mdb_warn("unable to read taskq_t at %p", addr);
+ return (WALK_NEXT);
+ }
+
+ ti->tqi_count++;
+ tqe->tq_addr = addr;
+ strncpy(tqe->tq_name, tq.tq_name, TASKQ_NAMELEN);
+ tqe->tq_instance = tq.tq_instance;
+ tqe->tq_flags = tq.tq_flags;
+
+ return (WALK_NEXT);
+}
+
+void
+taskq_help(void)
+{
+ mdb_printf("%s",
+ " -a Only show taskqs with active threads.\n"
+ " -t Display active thread stacks in each taskq.\n"
+ " -T Display all thread stacks in each taskq.\n"
+ " -m min_maxq\n"
+ " Only show Dynamic taskqs and taskqs with a MAXQ of at\n"
+ " least min_maxq.\n"
+ " -n name\n"
+ " Only show taskqs which contain name somewhere in their\n"
+ " name.\n");
+}
+
+int
+taskq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ taskq_t tq;
+
+ const char *name = NULL;
+ uintptr_t minmaxq = 0;
+ uint_t active = FALSE;
+ uint_t print_threads = FALSE;
+ uint_t print_threads_all = FALSE;
+
+ size_t tact, tcount, queued, maxq;
+
+ if (mdb_getopts(argc, argv,
+ 'a', MDB_OPT_SETBITS, TRUE, &active,
+ 'm', MDB_OPT_UINTPTR, &minmaxq,
+ 'n', MDB_OPT_STR, &name,
+ 't', MDB_OPT_SETBITS, TRUE, &print_threads,
+ 'T', MDB_OPT_SETBITS, TRUE, &print_threads_all,
+ NULL) != argc)
+ return (DCMD_USAGE);
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ size_t idx;
+ tq_info_t tqi;
+
+ bzero(&tqi, sizeof (tqi));
+
+ if (mdb_walk("taskq_cache", tq_count, &tqi) == -1) {
+ mdb_warn("unable to walk taskq_cache");
+ return (DCMD_ERR);
+ }
+ tqi.tqi_size += 10; /* slop */
+ tqi.tqi_array = mdb_zalloc(
+ sizeof (*tqi.tqi_array) * tqi.tqi_size, UM_SLEEP|UM_GC);
+
+ if (mdb_walk("taskq_cache", (mdb_walk_cb_t)tq_fill,
+ &tqi) == -1) {
+ mdb_warn("unable to walk taskq_cache");
+ return (DCMD_ERR);
+ }
+ qsort(tqi.tqi_array, tqi.tqi_count, sizeof (*tqi.tqi_array),
+ tqcmp);
+
+ flags &= ~DCMD_PIPE;
+ flags |= DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC;
+ for (idx = 0; idx < tqi.tqi_count; idx++) {
+ int ret = taskq(tqi.tqi_array[idx].tq_addr, flags,
+ argc, argv);
+ if (ret != DCMD_OK)
+ return (ret);
+ flags &= ~DCMD_LOOPFIRST;
+ }
+
+ return (DCMD_OK);
+ }
+
+ if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
+ mdb_printf("%<u>%-?s %-31s %4s/%4s %4s %5s %4s%</u>\n",
+ "ADDR", "NAME", "ACT", "THDS",
+ "Q'ED", "MAXQ", "INST");
+ }
+
+ if (mdb_vread(&tq, sizeof (tq), addr) == -1) {
+ mdb_warn("failed to read taskq_t at %p", addr);
+ return (DCMD_ERR);
+ }
+
+ /* terminate the name, just in case */
+ tq.tq_name[sizeof (tq.tq_name) - 1] = 0;
+
+ tact = tq.tq_active;
+ tcount = tq.tq_nthreads;
+ queued = tq.tq_tasks - tq.tq_executed;
+ maxq = tq.tq_maxtasks;
+
+ if (tq.tq_flags & TASKQ_DYNAMIC) {
+ size_t bsize = tq.tq_nbuckets * sizeof (*tq.tq_buckets);
+ size_t idx;
+ taskq_bucket_t *b = mdb_zalloc(bsize, UM_SLEEP | UM_GC);
+
+ if (mdb_vread(b, bsize, (uintptr_t)tq.tq_buckets) == -1) {
+ mdb_warn("unable to read buckets for taskq %p", addr);
+ return (DCMD_ERR);
+ }
+
+ tcount += (tq.tq_tcreates - tq.tq_tdeaths);
+
+ for (idx = 0; idx < tq.tq_nbuckets; idx++) {
+ tact += b[idx].tqbucket_nalloc;
+ }
+ }
+
+ /* filter out taskqs that aren't of interest. */
+ if (name != NULL && strstr(tq.tq_name, name) == NULL)
+ return (DCMD_OK);
+ if (active && tact == 0 && queued == 0)
+ return (DCMD_OK);
+ if (!(tq.tq_flags & TASKQ_DYNAMIC) && maxq < minmaxq)
+ return (DCMD_OK);
+
+ if (flags & DCMD_PIPE_OUT) {
+ mdb_printf("%#lr\n", addr);
+ return (DCMD_OK);
+ }
+
+ mdb_printf("%?p %-31s %4d/%4d %4d ",
+ addr, tq.tq_name, tact, tcount, queued);
+
+ if (tq.tq_flags & TASKQ_DYNAMIC)
+ mdb_printf("%5s ", "-");
+ else
+ mdb_printf("%5d ", maxq);
+
+ if (tq.tq_flags & TASKQ_NOINSTANCE)
+ mdb_printf("%4s", "-");
+ else
+ mdb_printf("%4x", tq.tq_instance);
+
+ mdb_printf("\n");
+
+ if (print_threads || print_threads_all) {
+ int ret;
+ char strbuf[128];
+ const char *arg =
+ print_threads_all ? "" : "-C \"taskq_thread_wait\"";
+
+ /*
+ * We can't use mdb_pwalk_dcmd() here, because ::stacks needs
+ * to get the full pipeline.
+ */
+ mdb_snprintf(strbuf, sizeof (strbuf),
+ "%p::walk taskq_thread | ::stacks -a %s",
+ addr, arg);
+
+ (void) mdb_inc_indent(4);
+ ret = mdb_eval(strbuf);
+ (void) mdb_dec_indent(4);
+
+ /* abort, since they could have control-Ced the eval */
+ if (ret == -1)
+ return (DCMD_ABORT);
+ }
+
+ return (DCMD_OK);
+}
+
+/*
+ * Dump a taskq_ent_t given its address.
+ */
+/*ARGSUSED*/
+int
+taskq_ent(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ taskq_ent_t taskq_ent;
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ return (DCMD_USAGE);
+ }
+
+ if (mdb_vread(&taskq_ent, sizeof (taskq_ent_t), addr) == -1) {
+ mdb_warn("failed to read taskq_ent_t at %p", addr);
+ return (DCMD_ERR);
+ }
+
+ if (DCMD_HDRSPEC(flags)) {
+ mdb_printf("%<u>%-?s %-?s %-s%</u>\n",
+ "ENTRY", "ARG", "FUNCTION");
+ }
+
+ mdb_printf("%-?p %-?p %a\n", addr, taskq_ent.tqent_arg,
+ taskq_ent.tqent_func);
+
+ return (DCMD_OK);
+}
+
+
+/*
+ * Given the address of the (taskq_t) task queue head, walk the queue listing
+ * the address of every taskq_ent_t.
+ */
+int
+taskq_ent_walk_init(mdb_walk_state_t *wsp)
+{
+ taskq_t tq_head;
+
+
+ if (wsp->walk_addr == NULL) {
+ mdb_warn("start address required\n");
+ return (WALK_ERR);
+ }
+
+
+ /*
+ * Save the address of the list head entry. This terminates the list.
+ */
+ wsp->walk_data = (void *)
+ ((size_t)wsp->walk_addr + OFFSETOF(taskq_t, tq_task));
+
+
+ /*
+ * Read in taskq head, set walk_addr to point to first taskq_ent_t.
+ */
+ if (mdb_vread((void *)&tq_head, sizeof (taskq_t), wsp->walk_addr) ==
+ -1) {
+ mdb_warn("failed to read taskq list head at %p",
+ wsp->walk_addr);
+ }
+ wsp->walk_addr = (uintptr_t)tq_head.tq_task.tqent_next;
+
+
+ /*
+ * Check for null list (next=head)
+ */
+ if (wsp->walk_addr == (uintptr_t)wsp->walk_data) {
+ return (WALK_DONE);
+ }
+
+ return (WALK_NEXT);
+}
+
+
+int
+taskq_ent_walk_step(mdb_walk_state_t *wsp)
+{
+ taskq_ent_t tq_ent;
+ int status;
+
+
+ if (mdb_vread((void *)&tq_ent, sizeof (taskq_ent_t), wsp->walk_addr) ==
+ -1) {
+ mdb_warn("failed to read taskq_ent_t at %p", wsp->walk_addr);
+ return (DCMD_ERR);
+ }
+
+ status = wsp->walk_callback(wsp->walk_addr, (void *)&tq_ent,
+ wsp->walk_cbdata);
+
+ wsp->walk_addr = (uintptr_t)tq_ent.tqent_next;
+
+
+ /* Check if we're at the last element (next=head) */
+ if (wsp->walk_addr == (uintptr_t)wsp->walk_data) {
+ return (WALK_DONE);
+ }
+
+ return (status);
+}
+
+typedef struct taskq_thread_info {
+ uintptr_t tti_addr;
+ uintptr_t *tti_tlist;
+ size_t tti_nthreads;
+ size_t tti_idx;
+
+ kthread_t tti_thread;
+} taskq_thread_info_t;
+
+int
+taskq_thread_walk_init(mdb_walk_state_t *wsp)
+{
+ taskq_thread_info_t *tti;
+ taskq_t tq;
+ uintptr_t *tlist;
+ size_t nthreads;
+
+ tti = wsp->walk_data = mdb_zalloc(sizeof (*tti), UM_SLEEP);
+ tti->tti_addr = wsp->walk_addr;
+
+ if (wsp->walk_addr != NULL &&
+ mdb_vread(&tq, sizeof (tq), wsp->walk_addr) != -1 &&
+ !(tq.tq_flags & TASKQ_DYNAMIC)) {
+
+ nthreads = tq.tq_nthreads;
+ tlist = mdb_alloc(nthreads * sizeof (*tlist), UM_SLEEP);
+ if (tq.tq_nthreads_max == 1) {
+ tlist[0] = (uintptr_t)tq.tq_thread;
+
+ } else if (mdb_vread(tlist, nthreads * sizeof (*tlist),
+ (uintptr_t)tq.tq_threadlist) == -1) {
+ mdb_warn("unable to read threadlist for taskq_t %p",
+ wsp->walk_addr);
+ mdb_free(tlist, nthreads * sizeof (*tlist));
+ return (WALK_ERR);
+ }
+
+ tti->tti_tlist = tlist;
+ tti->tti_nthreads = nthreads;
+ return (WALK_NEXT);
+ }
+
+ wsp->walk_addr = 0;
+ if (mdb_layered_walk("thread", wsp) == -1) {
+ mdb_warn("can't walk \"thread\"");
+ return (WALK_ERR);
+ }
+ return (0);
+}
+
+int
+taskq_thread_walk_step(mdb_walk_state_t *wsp)
+{
+ taskq_thread_info_t *tti = wsp->walk_data;
+
+ const kthread_t *kt = wsp->walk_layer;
+ taskq_t *tq = (taskq_t *)tti->tti_addr;
+
+ if (kt == NULL) {
+ uintptr_t addr;
+
+ if (tti->tti_idx >= tti->tti_nthreads)
+ return (WALK_DONE);
+
+ addr = tti->tti_tlist[tti->tti_idx];
+ tti->tti_idx++;
+
+ if (addr == NULL)
+ return (WALK_NEXT);
+
+ if (mdb_vread(&tti->tti_thread, sizeof (kthread_t),
+ addr) == -1) {
+ mdb_warn("unable to read kthread_t at %p", addr);
+ return (WALK_ERR);
+ }
+ return (wsp->walk_callback(addr, &tti->tti_thread,
+ wsp->walk_cbdata));
+ }
+
+ if (kt->t_taskq == NULL)
+ return (WALK_NEXT);
+
+ if (tq != NULL && kt->t_taskq != tq)
+ return (WALK_NEXT);
+
+ return (wsp->walk_callback(wsp->walk_addr, kt, wsp->walk_cbdata));
+}
+
+void
+taskq_thread_walk_fini(mdb_walk_state_t *wsp)
+{
+ taskq_thread_info_t *tti = wsp->walk_data;
+
+ if (tti->tti_nthreads > 0) {
+ mdb_free(tti->tti_tlist,
+ tti->tti_nthreads * sizeof (*tti->tti_tlist));
+ }
+ mdb_free(tti, sizeof (*tti));
+}
diff --git a/usr/src/cmd/mdb/common/modules/genunix/taskq.h b/usr/src/cmd/mdb/common/modules/genunix/taskq.h
new file mode 100644
index 0000000000..dfa2c9e8bd
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/genunix/taskq.h
@@ -0,0 +1,49 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * 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.
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TASKQ_H
+#define _TASKQ_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int taskq(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern void taskq_help(void);
+
+extern int taskq_ent(uintptr_t, uint_t, int, const mdb_arg_t *);
+
+extern int taskq_ent_walk_init(mdb_walk_state_t *);
+extern int taskq_ent_walk_step(mdb_walk_state_t *);
+
+extern int taskq_thread_walk_init(mdb_walk_state_t *);
+extern int taskq_thread_walk_step(mdb_walk_state_t *);
+extern void taskq_thread_walk_fini(mdb_walk_state_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TASKQ_H */