diff options
| author | Jonathan Adams <Jonathan.Adams@Sun.COM> | 2009-10-27 13:44:45 -0700 |
|---|---|---|
| committer | Jonathan Adams <Jonathan.Adams@Sun.COM> | 2009-10-27 13:44:45 -0700 |
| commit | e0ad97e30ea0a9af63c42d71690b5f387c763420 (patch) | |
| tree | 506c19fe9e8d888deada1b6b17b8be1af1cd200c /usr/src/cmd/mdb/common/modules | |
| parent | f717cf302ad33ac8dcb06cab149e63a1f07618c5 (diff) | |
| download | illumos-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.files | 1 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/findstack.c | 88 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/genunix.c | 128 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/taskq.c | 483 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/taskq.h | 49 |
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 */ |
