1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 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> 31 32 #include "taskq.h" 33 34 typedef struct tqarray_ent { 35 uintptr_t tq_addr; 36 char tq_name[TASKQ_NAMELEN + 1]; 37 int tq_instance; 38 uint_t tq_flags; 39 } tqarray_ent_t; 40 41 typedef struct tq_info { 42 tqarray_ent_t *tqi_array; 43 size_t tqi_count; 44 size_t tqi_size; 45 } tq_info_t; 46 47 /* 48 * We sort taskqs as follows: 49 * 50 * DYNAMIC last 51 * NOINSTANCE first 52 * within NOINSTANCE, sort by order of creation (instance #) 53 * within non-NOINSTANCE, sort by name (case-insensitive) then instance # 54 */ 55 int 56 tqcmp(const void *lhs, const void *rhs) 57 { 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; 62 int ret; 63 64 if ((lflags & TASKQ_DYNAMIC) && !(rflags & TASKQ_DYNAMIC)) 65 return (1); 66 if (!(lflags & TASKQ_DYNAMIC) && (rflags & TASKQ_DYNAMIC)) 67 return (-1); 68 69 if ((lflags & TASKQ_NOINSTANCE) && !(rflags & TASKQ_NOINSTANCE)) 70 return (-1); 71 if (!(lflags & TASKQ_NOINSTANCE) && (rflags & TASKQ_NOINSTANCE)) 72 return (1); 73 74 if (!(lflags & TASKQ_NOINSTANCE) && 75 (ret = strcasecmp(l->tq_name, r->tq_name)) != 0) 76 return (ret); 77 78 if (l->tq_instance < r->tq_instance) 79 return (-1); 80 if (l->tq_instance > r->tq_instance) 81 return (1); 82 return (0); 83 } 84 85 /*ARGSUSED*/ 86 int 87 tq_count(uintptr_t addr, const void *ignored, void *arg) 88 { 89 tq_info_t *ti = arg; 90 91 ti->tqi_size++; 92 return (WALK_NEXT); 93 } 94 95 /*ARGSUSED*/ 96 int 97 tq_fill(uintptr_t addr, const void *ignored, tq_info_t *ti) 98 { 99 int idx = ti->tqi_count; 100 taskq_t tq; 101 tqarray_ent_t *tqe = &ti->tqi_array[idx]; 102 103 if (idx == ti->tqi_size) { 104 mdb_warn("taskq: inadequate slop\n"); 105 return (WALK_ERR); 106 } 107 if (mdb_vread(&tq, sizeof (tq), addr) == -1) { 108 mdb_warn("unable to read taskq_t at %p", addr); 109 return (WALK_NEXT); 110 } 111 112 ti->tqi_count++; 113 tqe->tq_addr = 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; 117 118 return (WALK_NEXT); 119 } 120 121 void 122 taskq_help(void) 123 { 124 mdb_printf("%s", 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" 128 " -m min_maxq\n" 129 " Only show Dynamic taskqs and taskqs with a MAXQ of at\n" 130 " least min_maxq.\n" 131 " -n name\n" 132 " Only show taskqs which contain name somewhere in their\n" 133 " name.\n"); 134 } 135 136 int 137 taskq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 138 { 139 taskq_t tq; 140 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; 146 147 size_t tact, tcount, queued, maxq; 148 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, 155 NULL) != argc) 156 return (DCMD_USAGE); 157 158 if (!(flags & DCMD_ADDRSPEC)) { 159 size_t idx; 160 tq_info_t tqi; 161 162 bzero(&tqi, sizeof (tqi)); 163 164 if (mdb_walk("taskq_cache", tq_count, &tqi) == -1) { 165 mdb_warn("unable to walk taskq_cache"); 166 return (DCMD_ERR); 167 } 168 tqi.tqi_size += 10; /* slop */ 169 tqi.tqi_array = mdb_zalloc( 170 sizeof (*tqi.tqi_array) * tqi.tqi_size, UM_SLEEP|UM_GC); 171 172 if (mdb_walk("taskq_cache", (mdb_walk_cb_t)tq_fill, 173 &tqi) == -1) { 174 mdb_warn("unable to walk taskq_cache"); 175 return (DCMD_ERR); 176 } 177 qsort(tqi.tqi_array, tqi.tqi_count, sizeof (*tqi.tqi_array), 178 tqcmp); 179 180 flags &= ~DCMD_PIPE; 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, 184 argc, argv); 185 if (ret != DCMD_OK) 186 return (ret); 187 flags &= ~DCMD_LOOPFIRST; 188 } 189 190 return (DCMD_OK); 191 } 192 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"); 197 } 198 199 if (mdb_vread(&tq, sizeof (tq), addr) == -1) { 200 mdb_warn("failed to read taskq_t at %p", addr); 201 return (DCMD_ERR); 202 } 203 204 /* terminate the name, just in case */ 205 tq.tq_name[sizeof (tq.tq_name) - 1] = 0; 206 207 tact = tq.tq_active; 208 tcount = tq.tq_nthreads; 209 queued = tq.tq_tasks - tq.tq_executed; 210 maxq = tq.tq_maxtasks; 211 212 if (tq.tq_flags & TASKQ_DYNAMIC) { 213 size_t bsize = tq.tq_nbuckets * sizeof (*tq.tq_buckets); 214 size_t idx; 215 taskq_bucket_t *b = mdb_zalloc(bsize, UM_SLEEP | UM_GC); 216 217 if (mdb_vread(b, bsize, (uintptr_t)tq.tq_buckets) == -1) { 218 mdb_warn("unable to read buckets for taskq %p", addr); 219 return (DCMD_ERR); 220 } 221 222 tcount += (tq.tq_tcreates - tq.tq_tdeaths); 223 224 for (idx = 0; idx < tq.tq_nbuckets; idx++) { 225 tact += b[idx].tqbucket_nalloc; 226 } 227 } 228 229 /* filter out taskqs that aren't of interest. */ 230 if (name != NULL && strstr(tq.tq_name, name) == NULL) 231 return (DCMD_OK); 232 if (active && tact == 0 && queued == 0) 233 return (DCMD_OK); 234 if (!(tq.tq_flags & TASKQ_DYNAMIC) && maxq < minmaxq) 235 return (DCMD_OK); 236 237 if (flags & DCMD_PIPE_OUT) { 238 mdb_printf("%#lr\n", addr); 239 return (DCMD_OK); 240 } 241 242 mdb_printf("%?p %-31s %4d/%4d %4d ", 243 addr, tq.tq_name, tact, tcount, queued); 244 245 if (tq.tq_flags & TASKQ_DYNAMIC) 246 mdb_printf("%5s ", "-"); 247 else 248 mdb_printf("%5d ", maxq); 249 250 if (tq.tq_flags & TASKQ_NOINSTANCE) 251 mdb_printf("%4s", "-"); 252 else 253 mdb_printf("%4x", tq.tq_instance); 254 255 mdb_printf("\n"); 256 257 if (print_threads || print_threads_all) { 258 int ret; 259 char strbuf[128]; 260 const char *arg = 261 print_threads_all ? "" : "-C \"taskq_thread_wait\""; 262 263 /* 264 * We can't use mdb_pwalk_dcmd() here, because ::stacks needs 265 * to get the full pipeline. 266 */ 267 mdb_snprintf(strbuf, sizeof (strbuf), 268 "%p::walk taskq_thread | ::stacks -a %s", 269 addr, arg); 270 271 (void) mdb_inc_indent(4); 272 ret = mdb_eval(strbuf); 273 (void) mdb_dec_indent(4); 274 275 /* abort, since they could have control-Ced the eval */ 276 if (ret == -1) 277 return (DCMD_ABORT); 278 } 279 280 return (DCMD_OK); 281 } 282 283 /* 284 * Dump a taskq_ent_t given its address. 285 */ 286 /*ARGSUSED*/ 287 int 288 taskq_ent(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 289 { 290 taskq_ent_t taskq_ent; 291 292 if (!(flags & DCMD_ADDRSPEC)) { 293 return (DCMD_USAGE); 294 } 295 296 if (mdb_vread(&taskq_ent, sizeof (taskq_ent_t), addr) == -1) { 297 mdb_warn("failed to read taskq_ent_t at %p", addr); 298 return (DCMD_ERR); 299 } 300 301 if (DCMD_HDRSPEC(flags)) { 302 mdb_printf("%<u>%-?s %-?s %-s%</u>\n", 303 "ENTRY", "ARG", "FUNCTION"); 304 } 305 306 mdb_printf("%-?p %-?p %a\n", addr, taskq_ent.tqent_arg, 307 taskq_ent.tqent_func); 308 309 return (DCMD_OK); 310 } 311 312 313 /* 314 * Given the address of the (taskq_t) task queue head, walk the queue listing 315 * the address of every taskq_ent_t. 316 */ 317 int 318 taskq_ent_walk_init(mdb_walk_state_t *wsp) 319 { 320 taskq_t tq_head; 321 322 323 if (wsp->walk_addr == NULL) { 324 mdb_warn("start address required\n"); 325 return (WALK_ERR); 326 } 327 328 329 /* 330 * Save the address of the list head entry. This terminates the list. 331 */ 332 wsp->walk_data = (void *) 333 ((size_t)wsp->walk_addr + OFFSETOF(taskq_t, tq_task)); 334 335 336 /* 337 * Read in taskq head, set walk_addr to point to first taskq_ent_t. 338 */ 339 if (mdb_vread((void *)&tq_head, sizeof (taskq_t), wsp->walk_addr) == 340 -1) { 341 mdb_warn("failed to read taskq list head at %p", 342 wsp->walk_addr); 343 } 344 wsp->walk_addr = (uintptr_t)tq_head.tq_task.tqent_next; 345 346 347 /* 348 * Check for null list (next=head) 349 */ 350 if (wsp->walk_addr == (uintptr_t)wsp->walk_data) { 351 return (WALK_DONE); 352 } 353 354 return (WALK_NEXT); 355 } 356 357 358 int 359 taskq_ent_walk_step(mdb_walk_state_t *wsp) 360 { 361 taskq_ent_t tq_ent; 362 int status; 363 364 365 if (mdb_vread((void *)&tq_ent, sizeof (taskq_ent_t), wsp->walk_addr) == 366 -1) { 367 mdb_warn("failed to read taskq_ent_t at %p", wsp->walk_addr); 368 return (DCMD_ERR); 369 } 370 371 status = wsp->walk_callback(wsp->walk_addr, (void *)&tq_ent, 372 wsp->walk_cbdata); 373 374 wsp->walk_addr = (uintptr_t)tq_ent.tqent_next; 375 376 377 /* Check if we're at the last element (next=head) */ 378 if (wsp->walk_addr == (uintptr_t)wsp->walk_data) { 379 return (WALK_DONE); 380 } 381 382 return (status); 383 } 384 385 typedef struct taskq_thread_info { 386 uintptr_t tti_addr; 387 uintptr_t *tti_tlist; 388 size_t tti_nthreads; 389 size_t tti_idx; 390 391 kthread_t tti_thread; 392 } taskq_thread_info_t; 393 394 int 395 taskq_thread_walk_init(mdb_walk_state_t *wsp) 396 { 397 taskq_thread_info_t *tti; 398 taskq_t tq; 399 uintptr_t *tlist; 400 size_t nthreads; 401 402 tti = wsp->walk_data = mdb_zalloc(sizeof (*tti), UM_SLEEP); 403 tti->tti_addr = wsp->walk_addr; 404 405 if (wsp->walk_addr != NULL && 406 mdb_vread(&tq, sizeof (tq), wsp->walk_addr) != -1 && 407 !(tq.tq_flags & TASKQ_DYNAMIC)) { 408 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; 413 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", 417 wsp->walk_addr); 418 mdb_free(tlist, nthreads * sizeof (*tlist)); 419 return (WALK_ERR); 420 } 421 422 tti->tti_tlist = tlist; 423 tti->tti_nthreads = nthreads; 424 return (WALK_NEXT); 425 } 426 427 wsp->walk_addr = 0; 428 if (mdb_layered_walk("thread", wsp) == -1) { 429 mdb_warn("can't walk \"thread\""); 430 return (WALK_ERR); 431 } 432 return (0); 433 } 434 435 int 436 taskq_thread_walk_step(mdb_walk_state_t *wsp) 437 { 438 taskq_thread_info_t *tti = wsp->walk_data; 439 440 const kthread_t *kt = wsp->walk_layer; 441 taskq_t *tq = (taskq_t *)tti->tti_addr; 442 443 if (kt == NULL) { 444 uintptr_t addr; 445 446 if (tti->tti_idx >= tti->tti_nthreads) 447 return (WALK_DONE); 448 449 addr = tti->tti_tlist[tti->tti_idx]; 450 tti->tti_idx++; 451 452 if (addr == NULL) 453 return (WALK_NEXT); 454 455 if (mdb_vread(&tti->tti_thread, sizeof (kthread_t), 456 addr) == -1) { 457 mdb_warn("unable to read kthread_t at %p", addr); 458 return (WALK_ERR); 459 } 460 return (wsp->walk_callback(addr, &tti->tti_thread, 461 wsp->walk_cbdata)); 462 } 463 464 if (kt->t_taskq == NULL) 465 return (WALK_NEXT); 466 467 if (tq != NULL && kt->t_taskq != tq) 468 return (WALK_NEXT); 469 470 return (wsp->walk_callback(wsp->walk_addr, kt, wsp->walk_cbdata)); 471 } 472 473 void 474 taskq_thread_walk_fini(mdb_walk_state_t *wsp) 475 { 476 taskq_thread_info_t *tti = wsp->walk_data; 477 478 if (tti->tti_nthreads > 0) { 479 mdb_free(tti->tti_tlist, 480 tti->tti_nthreads * sizeof (*tti->tti_tlist)); 481 } 482 mdb_free(tti, sizeof (*tti)); 483 } 484