4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <mdb/mdb_param.h>
27 #include <mdb/mdb_modapi.h>
28 #include <mdb/mdb_ks.h>
29 #include <sys/taskq.h>
30 #include <sys/taskq_impl.h>
34 typedef struct tqarray_ent
{
36 char tq_name
[TASKQ_NAMELEN
+ 1];
41 typedef struct tq_info
{
42 tqarray_ent_t
*tqi_array
;
48 * We sort taskqs as follows:
52 * within NOINSTANCE, sort by order of creation (instance #)
53 * within non-NOINSTANCE, sort by name (case-insensitive) then instance #
56 tqcmp(const void *lhs
, const void *rhs
)
58 const tqarray_ent_t
*l
= lhs
;
59 const tqarray_ent_t
*r
= rhs
;
60 uint_t lflags
= l
->tq_flags
;
61 uint_t rflags
= r
->tq_flags
;
64 if ((lflags
& TASKQ_DYNAMIC
) && !(rflags
& TASKQ_DYNAMIC
))
66 if (!(lflags
& TASKQ_DYNAMIC
) && (rflags
& TASKQ_DYNAMIC
))
69 if ((lflags
& TASKQ_NOINSTANCE
) && !(rflags
& TASKQ_NOINSTANCE
))
71 if (!(lflags
& TASKQ_NOINSTANCE
) && (rflags
& TASKQ_NOINSTANCE
))
74 if (!(lflags
& TASKQ_NOINSTANCE
) &&
75 (ret
= strcasecmp(l
->tq_name
, r
->tq_name
)) != 0)
78 if (l
->tq_instance
< r
->tq_instance
)
80 if (l
->tq_instance
> r
->tq_instance
)
87 tq_count(uintptr_t addr
, const void *ignored
, void *arg
)
97 tq_fill(uintptr_t addr
, const void *ignored
, tq_info_t
*ti
)
99 int idx
= ti
->tqi_count
;
101 tqarray_ent_t
*tqe
= &ti
->tqi_array
[idx
];
103 if (idx
== ti
->tqi_size
) {
104 mdb_warn("taskq: inadequate slop\n");
107 if (mdb_vread(&tq
, sizeof (tq
), addr
) == -1) {
108 mdb_warn("unable to read taskq_t at %p", addr
);
114 strncpy(tqe
->tq_name
, tq
.tq_name
, TASKQ_NAMELEN
);
115 tqe
->tq_instance
= tq
.tq_instance
;
116 tqe
->tq_flags
= tq
.tq_flags
;
125 " -a Only show taskqs with active threads.\n"
126 " -t Display active thread stacks in each taskq.\n"
127 " -T Display all thread stacks in each taskq.\n"
129 " Only show Dynamic taskqs and taskqs with a MAXQ of at\n"
132 " Only show taskqs which contain name somewhere in their\n"
137 taskq(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
141 const char *name
= NULL
;
142 uintptr_t minmaxq
= 0;
143 uint_t active
= FALSE
;
144 uint_t print_threads
= FALSE
;
145 uint_t print_threads_all
= FALSE
;
147 size_t tact
, tcount
, queued
, maxq
;
149 if (mdb_getopts(argc
, argv
,
150 'a', MDB_OPT_SETBITS
, TRUE
, &active
,
151 'm', MDB_OPT_UINTPTR
, &minmaxq
,
152 'n', MDB_OPT_STR
, &name
,
153 't', MDB_OPT_SETBITS
, TRUE
, &print_threads
,
154 'T', MDB_OPT_SETBITS
, TRUE
, &print_threads_all
,
158 if (!(flags
& DCMD_ADDRSPEC
)) {
162 bzero(&tqi
, sizeof (tqi
));
164 if (mdb_walk("taskq_cache", tq_count
, &tqi
) == -1) {
165 mdb_warn("unable to walk taskq_cache");
168 tqi
.tqi_size
+= 10; /* slop */
169 tqi
.tqi_array
= mdb_zalloc(
170 sizeof (*tqi
.tqi_array
) * tqi
.tqi_size
, UM_SLEEP
|UM_GC
);
172 if (mdb_walk("taskq_cache", (mdb_walk_cb_t
)tq_fill
,
174 mdb_warn("unable to walk taskq_cache");
177 qsort(tqi
.tqi_array
, tqi
.tqi_count
, sizeof (*tqi
.tqi_array
),
181 flags
|= DCMD_LOOP
| DCMD_LOOPFIRST
| DCMD_ADDRSPEC
;
182 for (idx
= 0; idx
< tqi
.tqi_count
; idx
++) {
183 int ret
= taskq(tqi
.tqi_array
[idx
].tq_addr
, flags
,
187 flags
&= ~DCMD_LOOPFIRST
;
193 if (DCMD_HDRSPEC(flags
) && !(flags
& DCMD_PIPE_OUT
)) {
194 mdb_printf("%<u>%-?s %-31s %4s/%4s %4s %5s %4s%</u>\n",
195 "ADDR", "NAME", "ACT", "THDS",
196 "Q'ED", "MAXQ", "INST");
199 if (mdb_vread(&tq
, sizeof (tq
), addr
) == -1) {
200 mdb_warn("failed to read taskq_t at %p", addr
);
204 /* terminate the name, just in case */
205 tq
.tq_name
[sizeof (tq
.tq_name
) - 1] = 0;
208 tcount
= tq
.tq_nthreads
;
209 queued
= tq
.tq_tasks
- tq
.tq_executed
;
210 maxq
= tq
.tq_maxtasks
;
212 if (tq
.tq_flags
& TASKQ_DYNAMIC
) {
213 size_t bsize
= tq
.tq_nbuckets
* sizeof (*tq
.tq_buckets
);
215 taskq_bucket_t
*b
= mdb_zalloc(bsize
, UM_SLEEP
| UM_GC
);
217 if (mdb_vread(b
, bsize
, (uintptr_t)tq
.tq_buckets
) == -1) {
218 mdb_warn("unable to read buckets for taskq %p", addr
);
222 tcount
+= (tq
.tq_tcreates
- tq
.tq_tdeaths
);
224 for (idx
= 0; idx
< tq
.tq_nbuckets
; idx
++) {
225 tact
+= b
[idx
].tqbucket_nalloc
;
229 /* filter out taskqs that aren't of interest. */
230 if (name
!= NULL
&& strstr(tq
.tq_name
, name
) == NULL
)
232 if (active
&& tact
== 0 && queued
== 0)
234 if (!(tq
.tq_flags
& TASKQ_DYNAMIC
) && maxq
< minmaxq
)
237 if (flags
& DCMD_PIPE_OUT
) {
238 mdb_printf("%#lr\n", addr
);
242 mdb_printf("%?p %-31s %4d/%4d %4d ",
243 addr
, tq
.tq_name
, tact
, tcount
, queued
);
245 if (tq
.tq_flags
& TASKQ_DYNAMIC
)
246 mdb_printf("%5s ", "-");
248 mdb_printf("%5d ", maxq
);
250 if (tq
.tq_flags
& TASKQ_NOINSTANCE
)
251 mdb_printf("%4s", "-");
253 mdb_printf("%4x", tq
.tq_instance
);
257 if (print_threads
|| print_threads_all
) {
261 print_threads_all
? "" : "-C \"taskq_thread_wait\"";
264 * We can't use mdb_pwalk_dcmd() here, because ::stacks needs
265 * to get the full pipeline.
267 mdb_snprintf(strbuf
, sizeof (strbuf
),
268 "%p::walk taskq_thread | ::stacks -a %s",
271 (void) mdb_inc_indent(4);
272 ret
= mdb_eval(strbuf
);
273 (void) mdb_dec_indent(4);
275 /* abort, since they could have control-Ced the eval */
284 * Dump a taskq_ent_t given its address.
288 taskq_ent(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
290 taskq_ent_t taskq_ent
;
292 if (!(flags
& DCMD_ADDRSPEC
)) {
296 if (mdb_vread(&taskq_ent
, sizeof (taskq_ent_t
), addr
) == -1) {
297 mdb_warn("failed to read taskq_ent_t at %p", addr
);
301 if (DCMD_HDRSPEC(flags
)) {
302 mdb_printf("%<u>%-?s %-?s %-s%</u>\n",
303 "ENTRY", "ARG", "FUNCTION");
306 mdb_printf("%-?p %-?p %a\n", addr
, taskq_ent
.tqent_arg
,
307 taskq_ent
.tqent_func
);
314 * Given the address of the (taskq_t) task queue head, walk the queue listing
315 * the address of every taskq_ent_t.
318 taskq_ent_walk_init(mdb_walk_state_t
*wsp
)
323 if (wsp
->walk_addr
== (uintptr_t)NULL
) {
324 mdb_warn("start address required\n");
330 * Save the address of the list head entry. This terminates the list.
332 wsp
->walk_data
= (void *)
333 ((size_t)wsp
->walk_addr
+ OFFSETOF(taskq_t
, tq_task
));
337 * Read in taskq head, set walk_addr to point to first taskq_ent_t.
339 if (mdb_vread((void *)&tq_head
, sizeof (taskq_t
), wsp
->walk_addr
) ==
341 mdb_warn("failed to read taskq list head at %p",
344 wsp
->walk_addr
= (uintptr_t)tq_head
.tq_task
.tqent_next
;
348 * Check for null list (next=head)
350 if (wsp
->walk_addr
== (uintptr_t)wsp
->walk_data
) {
359 taskq_ent_walk_step(mdb_walk_state_t
*wsp
)
365 if (mdb_vread((void *)&tq_ent
, sizeof (taskq_ent_t
), wsp
->walk_addr
) ==
367 mdb_warn("failed to read taskq_ent_t at %p", wsp
->walk_addr
);
371 status
= wsp
->walk_callback(wsp
->walk_addr
, (void *)&tq_ent
,
374 wsp
->walk_addr
= (uintptr_t)tq_ent
.tqent_next
;
377 /* Check if we're at the last element (next=head) */
378 if (wsp
->walk_addr
== (uintptr_t)wsp
->walk_data
) {
385 typedef struct taskq_thread_info
{
387 uintptr_t *tti_tlist
;
391 kthread_t tti_thread
;
392 } taskq_thread_info_t
;
395 taskq_thread_walk_init(mdb_walk_state_t
*wsp
)
397 taskq_thread_info_t
*tti
;
402 tti
= wsp
->walk_data
= mdb_zalloc(sizeof (*tti
), UM_SLEEP
);
403 tti
->tti_addr
= wsp
->walk_addr
;
405 if (wsp
->walk_addr
!= (uintptr_t)NULL
&&
406 mdb_vread(&tq
, sizeof (tq
), wsp
->walk_addr
) != -1 &&
407 !(tq
.tq_flags
& TASKQ_DYNAMIC
)) {
409 nthreads
= tq
.tq_nthreads
;
410 tlist
= mdb_alloc(nthreads
* sizeof (*tlist
), UM_SLEEP
);
411 if (tq
.tq_nthreads_max
== 1) {
412 tlist
[0] = (uintptr_t)tq
.tq_thread
;
414 } else if (mdb_vread(tlist
, nthreads
* sizeof (*tlist
),
415 (uintptr_t)tq
.tq_threadlist
) == -1) {
416 mdb_warn("unable to read threadlist for taskq_t %p",
418 mdb_free(tlist
, nthreads
* sizeof (*tlist
));
422 tti
->tti_tlist
= tlist
;
423 tti
->tti_nthreads
= nthreads
;
428 if (mdb_layered_walk("thread", wsp
) == -1) {
429 mdb_warn("can't walk \"thread\"");
436 taskq_thread_walk_step(mdb_walk_state_t
*wsp
)
438 taskq_thread_info_t
*tti
= wsp
->walk_data
;
440 const kthread_t
*kt
= wsp
->walk_layer
;
441 taskq_t
*tq
= (taskq_t
*)tti
->tti_addr
;
446 if (tti
->tti_idx
>= tti
->tti_nthreads
)
449 addr
= tti
->tti_tlist
[tti
->tti_idx
];
452 if (addr
== (uintptr_t)NULL
)
455 if (mdb_vread(&tti
->tti_thread
, sizeof (kthread_t
),
457 mdb_warn("unable to read kthread_t at %p", addr
);
460 return (wsp
->walk_callback(addr
, &tti
->tti_thread
,
464 if (kt
->t_taskq
== NULL
)
467 if (tq
!= NULL
&& kt
->t_taskq
!= tq
)
470 return (wsp
->walk_callback(wsp
->walk_addr
, kt
, wsp
->walk_cbdata
));
474 taskq_thread_walk_fini(mdb_walk_state_t
*wsp
)
476 taskq_thread_info_t
*tti
= wsp
->walk_data
;
478 if (tti
->tti_nthreads
> 0) {
479 mdb_free(tti
->tti_tlist
,
480 tti
->tti_nthreads
* sizeof (*tti
->tti_tlist
));
482 mdb_free(tti
, sizeof (*tti
));