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 2011 Nexenta Systems, Inc. All rights reserved. 23 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2015 Joyent, Inc. 25 * Copyright (c) 2013 by Delphix. All rights reserved. 26 */ 27 28 #include <mdb/mdb_param.h> 29 #include <mdb/mdb_modapi.h> 30 #include <mdb/mdb_ks.h> 31 #include <mdb/mdb_ctf.h> 32 33 #include <sys/types.h> 34 #include <sys/thread.h> 35 #include <sys/session.h> 36 #include <sys/user.h> 37 #include <sys/proc.h> 38 #include <sys/var.h> 39 #include <sys/t_lock.h> 40 #include <sys/callo.h> 41 #include <sys/priocntl.h> 42 #include <sys/class.h> 43 #include <sys/regset.h> 44 #include <sys/stack.h> 45 #include <sys/cpuvar.h> 46 #include <sys/vnode.h> 47 #include <sys/vfs.h> 48 #include <sys/flock_impl.h> 49 #include <sys/kmem_impl.h> 50 #include <sys/vmem_impl.h> 51 #include <sys/kstat.h> 52 #include <sys/dditypes.h> 53 #include <sys/ddi_impldefs.h> 54 #include <sys/sysmacros.h> 55 #include <sys/sysconf.h> 56 #include <sys/task.h> 57 #include <sys/project.h> 58 #include <sys/errorq_impl.h> 59 #include <sys/cred_impl.h> 60 #include <sys/zone.h> 61 #include <sys/panic.h> 62 #include <regex.h> 63 #include <sys/port_impl.h> 64 65 #include "avl.h" 66 #include "bio.h" 67 #include "bitset.h" 68 #include "combined.h" 69 #include "contract.h" 70 #include "cpupart_mdb.h" 71 #include "cred.h" 72 #include "ctxop.h" 73 #include "cyclic.h" 74 #include "damap.h" 75 #include "ddi_periodic.h" 76 #include "devinfo.h" 77 #include "findstack.h" 78 #include "fm.h" 79 #include "gcore.h" 80 #include "group.h" 81 #include "irm.h" 82 #include "kgrep.h" 83 #include "kmem.h" 84 #include "ldi.h" 85 #include "leaky.h" 86 #include "lgrp.h" 87 #include "list.h" 88 #include "log.h" 89 #include "mdi.h" 90 #include "memory.h" 91 #include "mmd.h" 92 #include "modhash.h" 93 #include "ndievents.h" 94 #include "net.h" 95 #include "netstack.h" 96 #include "nvpair.h" 97 #include "pg.h" 98 #include "rctl.h" 99 #include "sobj.h" 100 #include "streams.h" 101 #include "sysevent.h" 102 #include "taskq.h" 103 #include "thread.h" 104 #include "tsd.h" 105 #include "tsol.h" 106 #include "typegraph.h" 107 #include "vfs.h" 108 #include "zone.h" 109 #include "hotplug.h" 110 111 /* 112 * Surely this is defined somewhere... 113 */ 114 #define NINTR 16 115 116 #define KILOS 10 117 #define MEGS 20 118 #define GIGS 30 119 120 #ifndef STACK_BIAS 121 #define STACK_BIAS 0 122 #endif 123 124 static char 125 pstat2ch(uchar_t state) 126 { 127 switch (state) { 128 case SSLEEP: return ('S'); 129 case SRUN: return ('R'); 130 case SZOMB: return ('Z'); 131 case SIDL: return ('I'); 132 case SONPROC: return ('O'); 133 case SSTOP: return ('T'); 134 case SWAIT: return ('W'); 135 default: return ('?'); 136 } 137 } 138 139 #define PS_PRTTHREADS 0x1 140 #define PS_PRTLWPS 0x2 141 #define PS_PSARGS 0x4 142 #define PS_TASKS 0x8 143 #define PS_PROJECTS 0x10 144 #define PS_ZONES 0x20 145 146 static int 147 ps_threadprint(uintptr_t addr, const void *data, void *private) 148 { 149 const kthread_t *t = (const kthread_t *)data; 150 uint_t prt_flags = *((uint_t *)private); 151 152 static const mdb_bitmask_t t_state_bits[] = { 153 { "TS_FREE", UINT_MAX, TS_FREE }, 154 { "TS_SLEEP", TS_SLEEP, TS_SLEEP }, 155 { "TS_RUN", TS_RUN, TS_RUN }, 156 { "TS_ONPROC", TS_ONPROC, TS_ONPROC }, 157 { "TS_ZOMB", TS_ZOMB, TS_ZOMB }, 158 { "TS_STOPPED", TS_STOPPED, TS_STOPPED }, 159 { "TS_WAIT", TS_WAIT, TS_WAIT }, 160 { NULL, 0, 0 } 161 }; 162 163 if (prt_flags & PS_PRTTHREADS) 164 mdb_printf("\tT %?a <%b>\n", addr, t->t_state, t_state_bits); 165 166 if (prt_flags & PS_PRTLWPS) 167 mdb_printf("\tL %?a ID: %u\n", t->t_lwp, t->t_tid); 168 169 return (WALK_NEXT); 170 } 171 172 static int 173 pflags(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 174 { 175 proc_t pr; 176 struct pid pid; 177 178 static const mdb_bitmask_t p_flag_bits[] = { 179 { "SSYS", SSYS, SSYS }, 180 { "SEXITING", SEXITING, SEXITING }, 181 { "SITBUSY", SITBUSY, SITBUSY }, 182 { "SFORKING", SFORKING, SFORKING }, 183 { "SWATCHOK", SWATCHOK, SWATCHOK }, 184 { "SKILLED", SKILLED, SKILLED }, 185 { "SSCONT", SSCONT, SSCONT }, 186 { "SZONETOP", SZONETOP, SZONETOP }, 187 { "SEXTKILLED", SEXTKILLED, SEXTKILLED }, 188 { "SUGID", SUGID, SUGID }, 189 { "SEXECED", SEXECED, SEXECED }, 190 { "SJCTL", SJCTL, SJCTL }, 191 { "SNOWAIT", SNOWAIT, SNOWAIT }, 192 { "SVFORK", SVFORK, SVFORK }, 193 { "SVFWAIT", SVFWAIT, SVFWAIT }, 194 { "SEXITLWPS", SEXITLWPS, SEXITLWPS }, 195 { "SHOLDFORK", SHOLDFORK, SHOLDFORK }, 196 { "SHOLDFORK1", SHOLDFORK1, SHOLDFORK1 }, 197 { "SCOREDUMP", SCOREDUMP, SCOREDUMP }, 198 { "SMSACCT", SMSACCT, SMSACCT }, 199 { "SLWPWRAP", SLWPWRAP, SLWPWRAP }, 200 { "SAUTOLPG", SAUTOLPG, SAUTOLPG }, 201 { "SNOCD", SNOCD, SNOCD }, 202 { "SHOLDWATCH", SHOLDWATCH, SHOLDWATCH }, 203 { "SMSFORK", SMSFORK, SMSFORK }, 204 { "SDOCORE", SDOCORE, SDOCORE }, 205 { NULL, 0, 0 } 206 }; 207 208 static const mdb_bitmask_t p_pidflag_bits[] = { 209 { "CLDPEND", CLDPEND, CLDPEND }, 210 { "CLDCONT", CLDCONT, CLDCONT }, 211 { "CLDNOSIGCHLD", CLDNOSIGCHLD, CLDNOSIGCHLD }, 212 { "CLDWAITPID", CLDWAITPID, CLDWAITPID }, 213 { NULL, 0, 0 } 214 }; 215 216 static const mdb_bitmask_t p_proc_flag_bits[] = { 217 { "P_PR_TRACE", P_PR_TRACE, P_PR_TRACE }, 218 { "P_PR_PTRACE", P_PR_PTRACE, P_PR_PTRACE }, 219 { "P_PR_FORK", P_PR_FORK, P_PR_FORK }, 220 { "P_PR_LOCK", P_PR_LOCK, P_PR_LOCK }, 221 { "P_PR_ASYNC", P_PR_ASYNC, P_PR_ASYNC }, 222 { "P_PR_EXEC", P_PR_EXEC, P_PR_EXEC }, 223 { "P_PR_BPTADJ", P_PR_BPTADJ, P_PR_BPTADJ }, 224 { "P_PR_RUNLCL", P_PR_RUNLCL, P_PR_RUNLCL }, 225 { "P_PR_KILLCL", P_PR_KILLCL, P_PR_KILLCL }, 226 { NULL, 0, 0 } 227 }; 228 229 if (!(flags & DCMD_ADDRSPEC)) { 230 if (mdb_walk_dcmd("proc", "pflags", argc, argv) == -1) { 231 mdb_warn("can't walk 'proc'"); 232 return (DCMD_ERR); 233 } 234 return (DCMD_OK); 235 } 236 237 if (mdb_vread(&pr, sizeof (pr), addr) == -1 || 238 mdb_vread(&pid, sizeof (pid), (uintptr_t)pr.p_pidp) == -1) { 239 mdb_warn("cannot read proc_t or pid"); 240 return (DCMD_ERR); 241 } 242 243 mdb_printf("%p [pid %d]:\n", addr, pid.pid_id); 244 mdb_printf("\tp_flag: %08x <%b>\n", pr.p_flag, pr.p_flag, 245 p_flag_bits); 246 mdb_printf("\tp_pidflag: %08x <%b>\n", pr.p_pidflag, pr.p_pidflag, 247 p_pidflag_bits); 248 mdb_printf("\tp_proc_flag: %08x <%b>\n", pr.p_proc_flag, pr.p_proc_flag, 249 p_proc_flag_bits); 250 251 return (DCMD_OK); 252 } 253 254 int 255 ps(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 256 { 257 uint_t prt_flags = 0; 258 proc_t pr; 259 struct pid pid, pgid, sid; 260 sess_t session; 261 cred_t cred; 262 task_t tk; 263 kproject_t pj; 264 zone_t zn; 265 266 if (!(flags & DCMD_ADDRSPEC)) { 267 if (mdb_walk_dcmd("proc", "ps", argc, argv) == -1) { 268 mdb_warn("can't walk 'proc'"); 269 return (DCMD_ERR); 270 } 271 return (DCMD_OK); 272 } 273 274 if (mdb_getopts(argc, argv, 275 'f', MDB_OPT_SETBITS, PS_PSARGS, &prt_flags, 276 'l', MDB_OPT_SETBITS, PS_PRTLWPS, &prt_flags, 277 'T', MDB_OPT_SETBITS, PS_TASKS, &prt_flags, 278 'P', MDB_OPT_SETBITS, PS_PROJECTS, &prt_flags, 279 'z', MDB_OPT_SETBITS, PS_ZONES, &prt_flags, 280 't', MDB_OPT_SETBITS, PS_PRTTHREADS, &prt_flags, NULL) != argc) 281 return (DCMD_USAGE); 282 283 if (DCMD_HDRSPEC(flags)) { 284 mdb_printf("%<u>%1s %6s %6s %6s %6s ", 285 "S", "PID", "PPID", "PGID", "SID"); 286 if (prt_flags & PS_TASKS) 287 mdb_printf("%5s ", "TASK"); 288 if (prt_flags & PS_PROJECTS) 289 mdb_printf("%5s ", "PROJ"); 290 if (prt_flags & PS_ZONES) 291 mdb_printf("%5s ", "ZONE"); 292 mdb_printf("%6s %10s %?s %s%</u>\n", 293 "UID", "FLAGS", "ADDR", "NAME"); 294 } 295 296 mdb_vread(&pr, sizeof (pr), addr); 297 mdb_vread(&pid, sizeof (pid), (uintptr_t)pr.p_pidp); 298 mdb_vread(&pgid, sizeof (pgid), (uintptr_t)pr.p_pgidp); 299 mdb_vread(&cred, sizeof (cred), (uintptr_t)pr.p_cred); 300 mdb_vread(&session, sizeof (session), (uintptr_t)pr.p_sessp); 301 mdb_vread(&sid, sizeof (sid), (uintptr_t)session.s_sidp); 302 if (prt_flags & (PS_TASKS | PS_PROJECTS)) 303 mdb_vread(&tk, sizeof (tk), (uintptr_t)pr.p_task); 304 if (prt_flags & PS_PROJECTS) 305 mdb_vread(&pj, sizeof (pj), (uintptr_t)tk.tk_proj); 306 if (prt_flags & PS_ZONES) 307 mdb_vread(&zn, sizeof (zone_t), (uintptr_t)pr.p_zone); 308 309 mdb_printf("%c %6d %6d %6d %6d ", 310 pstat2ch(pr.p_stat), pid.pid_id, pr.p_ppid, pgid.pid_id, 311 sid.pid_id); 312 if (prt_flags & PS_TASKS) 313 mdb_printf("%5d ", tk.tk_tkid); 314 if (prt_flags & PS_PROJECTS) 315 mdb_printf("%5d ", pj.kpj_id); 316 if (prt_flags & PS_ZONES) 317 mdb_printf("%5d ", zn.zone_id); 318 mdb_printf("%6d 0x%08x %0?p %s\n", 319 cred.cr_uid, pr.p_flag, addr, 320 (prt_flags & PS_PSARGS) ? pr.p_user.u_psargs : pr.p_user.u_comm); 321 322 if (prt_flags & ~PS_PSARGS) 323 (void) mdb_pwalk("thread", ps_threadprint, &prt_flags, addr); 324 325 return (DCMD_OK); 326 } 327 328 #define PG_NEWEST 0x0001 329 #define PG_OLDEST 0x0002 330 #define PG_PIPE_OUT 0x0004 331 #define PG_EXACT_MATCH 0x0008 332 333 typedef struct pgrep_data { 334 uint_t pg_flags; 335 uint_t pg_psflags; 336 uintptr_t pg_xaddr; 337 hrtime_t pg_xstart; 338 const char *pg_pat; 339 #ifndef _KMDB 340 regex_t pg_reg; 341 #endif 342 } pgrep_data_t; 343 344 /*ARGSUSED*/ 345 static int 346 pgrep_cb(uintptr_t addr, const void *pdata, void *data) 347 { 348 const proc_t *prp = pdata; 349 pgrep_data_t *pgp = data; 350 #ifndef _KMDB 351 regmatch_t pmatch; 352 #endif 353 354 /* 355 * kmdb doesn't have access to the reg* functions, so we fall back 356 * to strstr/strcmp. 357 */ 358 #ifdef _KMDB 359 if ((pgp->pg_flags & PG_EXACT_MATCH) ? 360 (strcmp(prp->p_user.u_comm, pgp->pg_pat) != 0) : 361 (strstr(prp->p_user.u_comm, pgp->pg_pat) == NULL)) 362 return (WALK_NEXT); 363 #else 364 if (regexec(&pgp->pg_reg, prp->p_user.u_comm, 1, &pmatch, 0) != 0) 365 return (WALK_NEXT); 366 367 if ((pgp->pg_flags & PG_EXACT_MATCH) && 368 (pmatch.rm_so != 0 || prp->p_user.u_comm[pmatch.rm_eo] != '\0')) 369 return (WALK_NEXT); 370 #endif 371 372 if (pgp->pg_flags & (PG_NEWEST | PG_OLDEST)) { 373 hrtime_t start; 374 375 start = (hrtime_t)prp->p_user.u_start.tv_sec * NANOSEC + 376 prp->p_user.u_start.tv_nsec; 377 378 if (pgp->pg_flags & PG_NEWEST) { 379 if (pgp->pg_xaddr == NULL || start > pgp->pg_xstart) { 380 pgp->pg_xaddr = addr; 381 pgp->pg_xstart = start; 382 } 383 } else { 384 if (pgp->pg_xaddr == NULL || start < pgp->pg_xstart) { 385 pgp->pg_xaddr = addr; 386 pgp->pg_xstart = start; 387 } 388 } 389 390 } else if (pgp->pg_flags & PG_PIPE_OUT) { 391 mdb_printf("%p\n", addr); 392 393 } else { 394 if (mdb_call_dcmd("ps", addr, pgp->pg_psflags, 0, NULL) != 0) { 395 mdb_warn("can't invoke 'ps'"); 396 return (WALK_DONE); 397 } 398 pgp->pg_psflags &= ~DCMD_LOOPFIRST; 399 } 400 401 return (WALK_NEXT); 402 } 403 404 /*ARGSUSED*/ 405 int 406 pgrep(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 407 { 408 pgrep_data_t pg; 409 int i; 410 #ifndef _KMDB 411 int err; 412 #endif 413 414 if (flags & DCMD_ADDRSPEC) 415 return (DCMD_USAGE); 416 417 pg.pg_flags = 0; 418 pg.pg_xaddr = 0; 419 420 i = mdb_getopts(argc, argv, 421 'n', MDB_OPT_SETBITS, PG_NEWEST, &pg.pg_flags, 422 'o', MDB_OPT_SETBITS, PG_OLDEST, &pg.pg_flags, 423 'x', MDB_OPT_SETBITS, PG_EXACT_MATCH, &pg.pg_flags, 424 NULL); 425 426 argc -= i; 427 argv += i; 428 429 if (argc != 1) 430 return (DCMD_USAGE); 431 432 /* 433 * -n and -o are mutually exclusive. 434 */ 435 if ((pg.pg_flags & PG_NEWEST) && (pg.pg_flags & PG_OLDEST)) 436 return (DCMD_USAGE); 437 438 if (argv->a_type != MDB_TYPE_STRING) 439 return (DCMD_USAGE); 440 441 if (flags & DCMD_PIPE_OUT) 442 pg.pg_flags |= PG_PIPE_OUT; 443 444 pg.pg_pat = argv->a_un.a_str; 445 if (DCMD_HDRSPEC(flags)) 446 pg.pg_psflags = DCMD_ADDRSPEC | DCMD_LOOP | DCMD_LOOPFIRST; 447 else 448 pg.pg_psflags = DCMD_ADDRSPEC | DCMD_LOOP; 449 450 #ifndef _KMDB 451 if ((err = regcomp(&pg.pg_reg, pg.pg_pat, REG_EXTENDED)) != 0) { 452 size_t nbytes; 453 char *buf; 454 455 nbytes = regerror(err, &pg.pg_reg, NULL, 0); 456 buf = mdb_alloc(nbytes + 1, UM_SLEEP | UM_GC); 457 (void) regerror(err, &pg.pg_reg, buf, nbytes); 458 mdb_warn("%s\n", buf); 459 460 return (DCMD_ERR); 461 } 462 #endif 463 464 if (mdb_walk("proc", pgrep_cb, &pg) != 0) { 465 mdb_warn("can't walk 'proc'"); 466 return (DCMD_ERR); 467 } 468 469 if (pg.pg_xaddr != 0 && (pg.pg_flags & (PG_NEWEST | PG_OLDEST))) { 470 if (pg.pg_flags & PG_PIPE_OUT) { 471 mdb_printf("%p\n", pg.pg_xaddr); 472 } else { 473 if (mdb_call_dcmd("ps", pg.pg_xaddr, pg.pg_psflags, 474 0, NULL) != 0) { 475 mdb_warn("can't invoke 'ps'"); 476 return (DCMD_ERR); 477 } 478 } 479 } 480 481 return (DCMD_OK); 482 } 483 484 int 485 task(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 486 { 487 task_t tk; 488 kproject_t pj; 489 490 if (!(flags & DCMD_ADDRSPEC)) { 491 if (mdb_walk_dcmd("task_cache", "task", argc, argv) == -1) { 492 mdb_warn("can't walk task_cache"); 493 return (DCMD_ERR); 494 } 495 return (DCMD_OK); 496 } 497 if (DCMD_HDRSPEC(flags)) { 498 mdb_printf("%<u>%?s %6s %6s %6s %6s %10s%</u>\n", 499 "ADDR", "TASKID", "PROJID", "ZONEID", "REFCNT", "FLAGS"); 500 } 501 if (mdb_vread(&tk, sizeof (task_t), addr) == -1) { 502 mdb_warn("can't read task_t structure at %p", addr); 503 return (DCMD_ERR); 504 } 505 if (mdb_vread(&pj, sizeof (kproject_t), (uintptr_t)tk.tk_proj) == -1) { 506 mdb_warn("can't read project_t structure at %p", addr); 507 return (DCMD_ERR); 508 } 509 mdb_printf("%0?p %6d %6d %6d %6u 0x%08x\n", 510 addr, tk.tk_tkid, pj.kpj_id, pj.kpj_zoneid, tk.tk_hold_count, 511 tk.tk_flags); 512 return (DCMD_OK); 513 } 514 515 int 516 project(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 517 { 518 kproject_t pj; 519 520 if (!(flags & DCMD_ADDRSPEC)) { 521 if (mdb_walk_dcmd("projects", "project", argc, argv) == -1) { 522 mdb_warn("can't walk projects"); 523 return (DCMD_ERR); 524 } 525 return (DCMD_OK); 526 } 527 if (DCMD_HDRSPEC(flags)) { 528 mdb_printf("%<u>%?s %6s %6s %6s%</u>\n", 529 "ADDR", "PROJID", "ZONEID", "REFCNT"); 530 } 531 if (mdb_vread(&pj, sizeof (kproject_t), addr) == -1) { 532 mdb_warn("can't read kproject_t structure at %p", addr); 533 return (DCMD_ERR); 534 } 535 mdb_printf("%0?p %6d %6d %6u\n", addr, pj.kpj_id, pj.kpj_zoneid, 536 pj.kpj_count); 537 return (DCMD_OK); 538 } 539 540 /* walk callouts themselves, either by list or id hash. */ 541 int 542 callout_walk_init(mdb_walk_state_t *wsp) 543 { 544 if (wsp->walk_addr == NULL) { 545 mdb_warn("callout doesn't support global walk"); 546 return (WALK_ERR); 547 } 548 wsp->walk_data = mdb_alloc(sizeof (callout_t), UM_SLEEP); 549 return (WALK_NEXT); 550 } 551 552 #define CALLOUT_WALK_BYLIST 0 553 #define CALLOUT_WALK_BYID 1 554 555 /* the walker arg switches between walking by list (0) and walking by id (1). */ 556 int 557 callout_walk_step(mdb_walk_state_t *wsp) 558 { 559 int retval; 560 561 if (wsp->walk_addr == NULL) { 562 return (WALK_DONE); 563 } 564 if (mdb_vread(wsp->walk_data, sizeof (callout_t), 565 wsp->walk_addr) == -1) { 566 mdb_warn("failed to read callout at %p", wsp->walk_addr); 567 return (WALK_DONE); 568 } 569 retval = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, 570 wsp->walk_cbdata); 571 572 if ((ulong_t)wsp->walk_arg == CALLOUT_WALK_BYID) { 573 wsp->walk_addr = 574 (uintptr_t)(((callout_t *)wsp->walk_data)->c_idnext); 575 } else { 576 wsp->walk_addr = 577 (uintptr_t)(((callout_t *)wsp->walk_data)->c_clnext); 578 } 579 580 return (retval); 581 } 582 583 void 584 callout_walk_fini(mdb_walk_state_t *wsp) 585 { 586 mdb_free(wsp->walk_data, sizeof (callout_t)); 587 } 588 589 /* 590 * walker for callout lists. This is different from hashes and callouts. 591 * Thankfully, it's also simpler. 592 */ 593 int 594 callout_list_walk_init(mdb_walk_state_t *wsp) 595 { 596 if (wsp->walk_addr == NULL) { 597 mdb_warn("callout list doesn't support global walk"); 598 return (WALK_ERR); 599 } 600 wsp->walk_data = mdb_alloc(sizeof (callout_list_t), UM_SLEEP); 601 return (WALK_NEXT); 602 } 603 604 int 605 callout_list_walk_step(mdb_walk_state_t *wsp) 606 { 607 int retval; 608 609 if (wsp->walk_addr == NULL) { 610 return (WALK_DONE); 611 } 612 if (mdb_vread(wsp->walk_data, sizeof (callout_list_t), 613 wsp->walk_addr) != sizeof (callout_list_t)) { 614 mdb_warn("failed to read callout_list at %p", wsp->walk_addr); 615 return (WALK_ERR); 616 } 617 retval = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, 618 wsp->walk_cbdata); 619 620 wsp->walk_addr = (uintptr_t) 621 (((callout_list_t *)wsp->walk_data)->cl_next); 622 623 return (retval); 624 } 625 626 void 627 callout_list_walk_fini(mdb_walk_state_t *wsp) 628 { 629 mdb_free(wsp->walk_data, sizeof (callout_list_t)); 630 } 631 632 /* routines/structs to walk callout table(s) */ 633 typedef struct cot_data { 634 callout_table_t *ct0; 635 callout_table_t ct; 636 callout_hash_t cot_idhash[CALLOUT_BUCKETS]; 637 callout_hash_t cot_clhash[CALLOUT_BUCKETS]; 638 kstat_named_t ct_kstat_data[CALLOUT_NUM_STATS]; 639 int cotndx; 640 int cotsize; 641 } cot_data_t; 642 643 int 644 callout_table_walk_init(mdb_walk_state_t *wsp) 645 { 646 int max_ncpus; 647 cot_data_t *cot_walk_data; 648 649 cot_walk_data = mdb_alloc(sizeof (cot_data_t), UM_SLEEP); 650 651 if (wsp->walk_addr == NULL) { 652 if (mdb_readvar(&cot_walk_data->ct0, "callout_table") == -1) { 653 mdb_warn("failed to read 'callout_table'"); 654 return (WALK_ERR); 655 } 656 if (mdb_readvar(&max_ncpus, "max_ncpus") == -1) { 657 mdb_warn("failed to get callout_table array size"); 658 return (WALK_ERR); 659 } 660 cot_walk_data->cotsize = CALLOUT_NTYPES * max_ncpus; 661 wsp->walk_addr = (uintptr_t)cot_walk_data->ct0; 662 } else { 663 /* not a global walk */ 664 cot_walk_data->cotsize = 1; 665 } 666 667 cot_walk_data->cotndx = 0; 668 wsp->walk_data = cot_walk_data; 669 670 return (WALK_NEXT); 671 } 672 673 int 674 callout_table_walk_step(mdb_walk_state_t *wsp) 675 { 676 int retval; 677 cot_data_t *cotwd = (cot_data_t *)wsp->walk_data; 678 size_t size; 679 680 if (cotwd->cotndx >= cotwd->cotsize) { 681 return (WALK_DONE); 682 } 683 if (mdb_vread(&(cotwd->ct), sizeof (callout_table_t), 684 wsp->walk_addr) != sizeof (callout_table_t)) { 685 mdb_warn("failed to read callout_table at %p", wsp->walk_addr); 686 return (WALK_ERR); 687 } 688 689 size = sizeof (callout_hash_t) * CALLOUT_BUCKETS; 690 if (cotwd->ct.ct_idhash != NULL) { 691 if (mdb_vread(cotwd->cot_idhash, size, 692 (uintptr_t)(cotwd->ct.ct_idhash)) != size) { 693 mdb_warn("failed to read id_hash at %p", 694 cotwd->ct.ct_idhash); 695 return (WALK_ERR); 696 } 697 } 698 if (cotwd->ct.ct_clhash != NULL) { 699 if (mdb_vread(&(cotwd->cot_clhash), size, 700 (uintptr_t)cotwd->ct.ct_clhash) == -1) { 701 mdb_warn("failed to read cl_hash at %p", 702 cotwd->ct.ct_clhash); 703 return (WALK_ERR); 704 } 705 } 706 size = sizeof (kstat_named_t) * CALLOUT_NUM_STATS; 707 if (cotwd->ct.ct_kstat_data != NULL) { 708 if (mdb_vread(&(cotwd->ct_kstat_data), size, 709 (uintptr_t)cotwd->ct.ct_kstat_data) == -1) { 710 mdb_warn("failed to read kstats at %p", 711 cotwd->ct.ct_kstat_data); 712 return (WALK_ERR); 713 } 714 } 715 retval = wsp->walk_callback(wsp->walk_addr, (void *)cotwd, 716 wsp->walk_cbdata); 717 718 cotwd->cotndx++; 719 if (cotwd->cotndx >= cotwd->cotsize) { 720 return (WALK_DONE); 721 } 722 wsp->walk_addr = (uintptr_t)((char *)wsp->walk_addr + 723 sizeof (callout_table_t)); 724 725 return (retval); 726 } 727 728 void 729 callout_table_walk_fini(mdb_walk_state_t *wsp) 730 { 731 mdb_free(wsp->walk_data, sizeof (cot_data_t)); 732 } 733 734 static const char *co_typenames[] = { "R", "N" }; 735 736 #define CO_PLAIN_ID(xid) ((xid) & CALLOUT_ID_MASK) 737 738 #define TABLE_TO_SEQID(x) ((x) >> CALLOUT_TYPE_BITS) 739 740 /* callout flags, in no particular order */ 741 #define COF_REAL 0x00000001 742 #define COF_NORM 0x00000002 743 #define COF_LONG 0x00000004 744 #define COF_SHORT 0x00000008 745 #define COF_EMPTY 0x00000010 746 #define COF_TIME 0x00000020 747 #define COF_BEFORE 0x00000040 748 #define COF_AFTER 0x00000080 749 #define COF_SEQID 0x00000100 750 #define COF_FUNC 0x00000200 751 #define COF_ADDR 0x00000400 752 #define COF_EXEC 0x00000800 753 #define COF_HIRES 0x00001000 754 #define COF_ABS 0x00002000 755 #define COF_TABLE 0x00004000 756 #define COF_BYIDH 0x00008000 757 #define COF_FREE 0x00010000 758 #define COF_LIST 0x00020000 759 #define COF_EXPREL 0x00040000 760 #define COF_HDR 0x00080000 761 #define COF_VERBOSE 0x00100000 762 #define COF_LONGLIST 0x00200000 763 #define COF_THDR 0x00400000 764 #define COF_LHDR 0x00800000 765 #define COF_CHDR 0x01000000 766 #define COF_PARAM 0x02000000 767 #define COF_DECODE 0x04000000 768 #define COF_HEAP 0x08000000 769 #define COF_QUEUE 0x10000000 770 771 /* show real and normal, short and long, expired and unexpired. */ 772 #define COF_DEFAULT (COF_REAL | COF_NORM | COF_LONG | COF_SHORT) 773 774 #define COF_LIST_FLAGS \ 775 (CALLOUT_LIST_FLAG_HRESTIME | CALLOUT_LIST_FLAG_ABSOLUTE) 776 777 /* private callout data for callback functions */ 778 typedef struct callout_data { 779 uint_t flags; /* COF_* */ 780 cpu_t *cpu; /* cpu pointer if given */ 781 int seqid; /* cpu seqid, or -1 */ 782 hrtime_t time; /* expiration time value */ 783 hrtime_t atime; /* expiration before value */ 784 hrtime_t btime; /* expiration after value */ 785 uintptr_t funcaddr; /* function address or NULL */ 786 uintptr_t param; /* parameter to function or NULL */ 787 hrtime_t now; /* current system time */ 788 int nsec_per_tick; /* for conversions */ 789 ulong_t ctbits; /* for decoding xid */ 790 callout_table_t *co_table; /* top of callout table array */ 791 int ndx; /* table index. */ 792 int bucket; /* which list/id bucket are we in */ 793 hrtime_t exp; /* expire time */ 794 int list_flags; /* copy of cl_flags */ 795 } callout_data_t; 796 797 /* this callback does the actual callback itself (finally). */ 798 /*ARGSUSED*/ 799 static int 800 callouts_cb(uintptr_t addr, const void *data, void *priv) 801 { 802 callout_data_t *coargs = (callout_data_t *)priv; 803 callout_t *co = (callout_t *)data; 804 int tableid, list_flags; 805 callout_id_t coid; 806 807 if ((coargs == NULL) || (co == NULL)) { 808 return (WALK_ERR); 809 } 810 811 if ((coargs->flags & COF_FREE) && !(co->c_xid & CALLOUT_ID_FREE)) { 812 /* 813 * The callout must have been reallocated. No point in 814 * walking any more. 815 */ 816 return (WALK_DONE); 817 } 818 if (!(coargs->flags & COF_FREE) && (co->c_xid & CALLOUT_ID_FREE)) { 819 /* 820 * The callout must have been freed. No point in 821 * walking any more. 822 */ 823 return (WALK_DONE); 824 } 825 if ((coargs->flags & COF_FUNC) && 826 (coargs->funcaddr != (uintptr_t)co->c_func)) { 827 return (WALK_NEXT); 828 } 829 if ((coargs->flags & COF_PARAM) && 830 (coargs->param != (uintptr_t)co->c_arg)) { 831 return (WALK_NEXT); 832 } 833 if (!(coargs->flags & COF_LONG) && (co->c_xid & CALLOUT_LONGTERM)) { 834 return (WALK_NEXT); 835 } 836 if (!(coargs->flags & COF_SHORT) && !(co->c_xid & CALLOUT_LONGTERM)) { 837 return (WALK_NEXT); 838 } 839 if ((coargs->flags & COF_EXEC) && !(co->c_xid & CALLOUT_EXECUTING)) { 840 return (WALK_NEXT); 841 } 842 /* it is possible we don't have the exp time or flags */ 843 if (coargs->flags & COF_BYIDH) { 844 if (!(coargs->flags & COF_FREE)) { 845 /* we have to fetch the expire time ourselves. */ 846 if (mdb_vread(&coargs->exp, sizeof (hrtime_t), 847 (uintptr_t)co->c_list + offsetof(callout_list_t, 848 cl_expiration)) == -1) { 849 mdb_warn("failed to read expiration " 850 "time from %p", co->c_list); 851 coargs->exp = 0; 852 } 853 /* and flags. */ 854 if (mdb_vread(&coargs->list_flags, sizeof (int), 855 (uintptr_t)co->c_list + offsetof(callout_list_t, 856 cl_flags)) == -1) { 857 mdb_warn("failed to read list flags" 858 "from %p", co->c_list); 859 coargs->list_flags = 0; 860 } 861 } else { 862 /* free callouts can't use list pointer. */ 863 coargs->exp = 0; 864 coargs->list_flags = 0; 865 } 866 if (coargs->exp != 0) { 867 if ((coargs->flags & COF_TIME) && 868 (coargs->exp != coargs->time)) { 869 return (WALK_NEXT); 870 } 871 if ((coargs->flags & COF_BEFORE) && 872 (coargs->exp > coargs->btime)) { 873 return (WALK_NEXT); 874 } 875 if ((coargs->flags & COF_AFTER) && 876 (coargs->exp < coargs->atime)) { 877 return (WALK_NEXT); 878 } 879 } 880 /* tricky part, since both HIRES and ABS can be set */ 881 list_flags = coargs->list_flags; 882 if ((coargs->flags & COF_HIRES) && (coargs->flags & COF_ABS)) { 883 /* both flags are set, only skip "regular" ones */ 884 if (! (list_flags & COF_LIST_FLAGS)) { 885 return (WALK_NEXT); 886 } 887 } else { 888 /* individual flags, or no flags */ 889 if ((coargs->flags & COF_HIRES) && 890 !(list_flags & CALLOUT_LIST_FLAG_HRESTIME)) { 891 return (WALK_NEXT); 892 } 893 if ((coargs->flags & COF_ABS) && 894 !(list_flags & CALLOUT_LIST_FLAG_ABSOLUTE)) { 895 return (WALK_NEXT); 896 } 897 } 898 /* 899 * We do the checks for COF_HEAP and COF_QUEUE here only if we 900 * are traversing BYIDH. If the traversal is by callout list, 901 * we do this check in callout_list_cb() to be more 902 * efficient. 903 */ 904 if ((coargs->flags & COF_HEAP) && 905 !(list_flags & CALLOUT_LIST_FLAG_HEAPED)) { 906 return (WALK_NEXT); 907 } 908 909 if ((coargs->flags & COF_QUEUE) && 910 !(list_flags & CALLOUT_LIST_FLAG_QUEUED)) { 911 return (WALK_NEXT); 912 } 913 } 914 915 #define callout_table_mask ((1 << coargs->ctbits) - 1) 916 tableid = CALLOUT_ID_TO_TABLE(co->c_xid); 917 #undef callout_table_mask 918 coid = CO_PLAIN_ID(co->c_xid); 919 920 if ((coargs->flags & COF_CHDR) && !(coargs->flags & COF_ADDR)) { 921 /* 922 * We need to print the headers. If walking by id, then 923 * the list header isn't printed, so we must include 924 * that info here. 925 */ 926 if (!(coargs->flags & COF_VERBOSE)) { 927 mdb_printf("%<u>%3s %-1s %-14s %</u>", 928 "SEQ", "T", "EXP"); 929 } else if (coargs->flags & COF_BYIDH) { 930 mdb_printf("%<u>%-14s %</u>", "EXP"); 931 } 932 mdb_printf("%<u>%-4s %-?s %-20s%</u>", 933 "XHAL", "XID", "FUNC(ARG)"); 934 if (coargs->flags & COF_LONGLIST) { 935 mdb_printf("%<u> %-?s %-?s %-?s %-?s%</u>", 936 "PREVID", "NEXTID", "PREVL", "NEXTL"); 937 mdb_printf("%<u> %-?s %-4s %-?s%</u>", 938 "DONE", "UTOS", "THREAD"); 939 } 940 mdb_printf("\n"); 941 coargs->flags &= ~COF_CHDR; 942 coargs->flags |= (COF_THDR | COF_LHDR); 943 } 944 945 if (!(coargs->flags & COF_ADDR)) { 946 if (!(coargs->flags & COF_VERBOSE)) { 947 mdb_printf("%-3d %1s %-14llx ", 948 TABLE_TO_SEQID(tableid), 949 co_typenames[tableid & CALLOUT_TYPE_MASK], 950 (coargs->flags & COF_EXPREL) ? 951 coargs->exp - coargs->now : coargs->exp); 952 } else if (coargs->flags & COF_BYIDH) { 953 mdb_printf("%-14x ", 954 (coargs->flags & COF_EXPREL) ? 955 coargs->exp - coargs->now : coargs->exp); 956 } 957 list_flags = coargs->list_flags; 958 mdb_printf("%1s%1s%1s%1s %-?llx %a(%p)", 959 (co->c_xid & CALLOUT_EXECUTING) ? "X" : " ", 960 (list_flags & CALLOUT_LIST_FLAG_HRESTIME) ? "H" : " ", 961 (list_flags & CALLOUT_LIST_FLAG_ABSOLUTE) ? "A" : " ", 962 (co->c_xid & CALLOUT_LONGTERM) ? "L" : " ", 963 (long long)coid, co->c_func, co->c_arg); 964 if (coargs->flags & COF_LONGLIST) { 965 mdb_printf(" %-?p %-?p %-?p %-?p", 966 co->c_idprev, co->c_idnext, co->c_clprev, 967 co->c_clnext); 968 mdb_printf(" %-?p %-4d %-0?p", 969 co->c_done, co->c_waiting, co->c_executor); 970 } 971 } else { 972 /* address only */ 973 mdb_printf("%-0p", addr); 974 } 975 mdb_printf("\n"); 976 return (WALK_NEXT); 977 } 978 979 /* this callback is for callout list handling. idhash is done by callout_t_cb */ 980 /*ARGSUSED*/ 981 static int 982 callout_list_cb(uintptr_t addr, const void *data, void *priv) 983 { 984 callout_data_t *coargs = (callout_data_t *)priv; 985 callout_list_t *cl = (callout_list_t *)data; 986 callout_t *coptr; 987 int list_flags; 988 989 if ((coargs == NULL) || (cl == NULL)) { 990 return (WALK_ERR); 991 } 992 993 coargs->exp = cl->cl_expiration; 994 coargs->list_flags = cl->cl_flags; 995 if ((coargs->flags & COF_FREE) && 996 !(cl->cl_flags & CALLOUT_LIST_FLAG_FREE)) { 997 /* 998 * The callout list must have been reallocated. No point in 999 * walking any more. 1000 */ 1001 return (WALK_DONE); 1002 } 1003 if (!(coargs->flags & COF_FREE) && 1004 (cl->cl_flags & CALLOUT_LIST_FLAG_FREE)) { 1005 /* 1006 * The callout list must have been freed. No point in 1007 * walking any more. 1008 */ 1009 return (WALK_DONE); 1010 } 1011 if ((coargs->flags & COF_TIME) && 1012 (cl->cl_expiration != coargs->time)) { 1013 return (WALK_NEXT); 1014 } 1015 if ((coargs->flags & COF_BEFORE) && 1016 (cl->cl_expiration > coargs->btime)) { 1017 return (WALK_NEXT); 1018 } 1019 if ((coargs->flags & COF_AFTER) && 1020 (cl->cl_expiration < coargs->atime)) { 1021 return (WALK_NEXT); 1022 } 1023 if (!(coargs->flags & COF_EMPTY) && 1024 (cl->cl_callouts.ch_head == NULL)) { 1025 return (WALK_NEXT); 1026 } 1027 /* FOUR cases, each different, !A!B, !AB, A!B, AB */ 1028 if ((coargs->flags & COF_HIRES) && (coargs->flags & COF_ABS)) { 1029 /* both flags are set, only skip "regular" ones */ 1030 if (! (cl->cl_flags & COF_LIST_FLAGS)) { 1031 return (WALK_NEXT); 1032 } 1033 } else { 1034 if ((coargs->flags & COF_HIRES) && 1035 !(cl->cl_flags & CALLOUT_LIST_FLAG_HRESTIME)) { 1036 return (WALK_NEXT); 1037 } 1038 if ((coargs->flags & COF_ABS) && 1039 !(cl->cl_flags & CALLOUT_LIST_FLAG_ABSOLUTE)) { 1040 return (WALK_NEXT); 1041 } 1042 } 1043 1044 if ((coargs->flags & COF_HEAP) && 1045 !(coargs->list_flags & CALLOUT_LIST_FLAG_HEAPED)) { 1046 return (WALK_NEXT); 1047 } 1048 1049 if ((coargs->flags & COF_QUEUE) && 1050 !(coargs->list_flags & CALLOUT_LIST_FLAG_QUEUED)) { 1051 return (WALK_NEXT); 1052 } 1053 1054 if ((coargs->flags & COF_LHDR) && !(coargs->flags & COF_ADDR) && 1055 (coargs->flags & (COF_LIST | COF_VERBOSE))) { 1056 if (!(coargs->flags & COF_VERBOSE)) { 1057 /* don't be redundant again */ 1058 mdb_printf("%<u>SEQ T %</u>"); 1059 } 1060 mdb_printf("%<u>EXP HA BUCKET " 1061 "CALLOUTS %</u>"); 1062 1063 if (coargs->flags & COF_LONGLIST) { 1064 mdb_printf("%<u> %-?s %-?s%</u>", 1065 "PREV", "NEXT"); 1066 } 1067 mdb_printf("\n"); 1068 coargs->flags &= ~COF_LHDR; 1069 coargs->flags |= (COF_THDR | COF_CHDR); 1070 } 1071 if (coargs->flags & (COF_LIST | COF_VERBOSE)) { 1072 if (!(coargs->flags & COF_ADDR)) { 1073 if (!(coargs->flags & COF_VERBOSE)) { 1074 mdb_printf("%3d %1s ", 1075 TABLE_TO_SEQID(coargs->ndx), 1076 co_typenames[coargs->ndx & 1077 CALLOUT_TYPE_MASK]); 1078 } 1079 1080 list_flags = coargs->list_flags; 1081 mdb_printf("%-14llx %1s%1s %-6d %-0?p ", 1082 (coargs->flags & COF_EXPREL) ? 1083 coargs->exp - coargs->now : coargs->exp, 1084 (list_flags & CALLOUT_LIST_FLAG_HRESTIME) ? 1085 "H" : " ", 1086 (list_flags & CALLOUT_LIST_FLAG_ABSOLUTE) ? 1087 "A" : " ", 1088 coargs->bucket, cl->cl_callouts.ch_head); 1089 1090 if (coargs->flags & COF_LONGLIST) { 1091 mdb_printf(" %-?p %-?p", 1092 cl->cl_prev, cl->cl_next); 1093 } 1094 } else { 1095 /* address only */ 1096 mdb_printf("%-0p", addr); 1097 } 1098 mdb_printf("\n"); 1099 if (coargs->flags & COF_LIST) { 1100 return (WALK_NEXT); 1101 } 1102 } 1103 /* yet another layer as we walk the actual callouts via list. */ 1104 if (cl->cl_callouts.ch_head == NULL) { 1105 return (WALK_NEXT); 1106 } 1107 /* free list structures do not have valid callouts off of them. */ 1108 if (coargs->flags & COF_FREE) { 1109 return (WALK_NEXT); 1110 } 1111 coptr = (callout_t *)cl->cl_callouts.ch_head; 1112 1113 if (coargs->flags & COF_VERBOSE) { 1114 mdb_inc_indent(4); 1115 } 1116 /* 1117 * walk callouts using yet another callback routine. 1118 * we use callouts_bytime because id hash is handled via 1119 * the callout_t_cb callback. 1120 */ 1121 if (mdb_pwalk("callouts_bytime", callouts_cb, coargs, 1122 (uintptr_t)coptr) == -1) { 1123 mdb_warn("cannot walk callouts at %p", coptr); 1124 return (WALK_ERR); 1125 } 1126 if (coargs->flags & COF_VERBOSE) { 1127 mdb_dec_indent(4); 1128 } 1129 1130 return (WALK_NEXT); 1131 } 1132 1133 /* this callback handles the details of callout table walking. */ 1134 static int 1135 callout_t_cb(uintptr_t addr, const void *data, void *priv) 1136 { 1137 callout_data_t *coargs = (callout_data_t *)priv; 1138 cot_data_t *cotwd = (cot_data_t *)data; 1139 callout_table_t *ct = &(cotwd->ct); 1140 int index, seqid, cotype; 1141 int i; 1142 callout_list_t *clptr; 1143 callout_t *coptr; 1144 1145 if ((coargs == NULL) || (ct == NULL) || (coargs->co_table == NULL)) { 1146 return (WALK_ERR); 1147 } 1148 1149 index = ((char *)addr - (char *)coargs->co_table) / 1150 sizeof (callout_table_t); 1151 cotype = index & CALLOUT_TYPE_MASK; 1152 seqid = TABLE_TO_SEQID(index); 1153 1154 if ((coargs->flags & COF_SEQID) && (coargs->seqid != seqid)) { 1155 return (WALK_NEXT); 1156 } 1157 1158 if (!(coargs->flags & COF_REAL) && (cotype == CALLOUT_REALTIME)) { 1159 return (WALK_NEXT); 1160 } 1161 1162 if (!(coargs->flags & COF_NORM) && (cotype == CALLOUT_NORMAL)) { 1163 return (WALK_NEXT); 1164 } 1165 1166 if (!(coargs->flags & COF_EMPTY) && ( 1167 (ct->ct_heap == NULL) || (ct->ct_cyclic == NULL))) { 1168 return (WALK_NEXT); 1169 } 1170 1171 if ((coargs->flags & COF_THDR) && !(coargs->flags & COF_ADDR) && 1172 (coargs->flags & (COF_TABLE | COF_VERBOSE))) { 1173 /* print table hdr */ 1174 mdb_printf("%<u>%-3s %-1s %-?s %-?s %-?s %-?s%</u>", 1175 "SEQ", "T", "FREE", "LFREE", "CYCLIC", "HEAP"); 1176 coargs->flags &= ~COF_THDR; 1177 coargs->flags |= (COF_LHDR | COF_CHDR); 1178 if (coargs->flags & COF_LONGLIST) { 1179 /* more info! */ 1180 mdb_printf("%<u> %-T%-7s %-7s %-?s %-?s %-?s" 1181 " %-?s %-?s %-?s%</u>", 1182 "HEAPNUM", "HEAPMAX", "TASKQ", "EXPQ", "QUE", 1183 "PEND", "FREE", "LOCK"); 1184 } 1185 mdb_printf("\n"); 1186 } 1187 if (coargs->flags & (COF_TABLE | COF_VERBOSE)) { 1188 if (!(coargs->flags & COF_ADDR)) { 1189 mdb_printf("%-3d %-1s %-0?p %-0?p %-0?p %-?p", 1190 seqid, co_typenames[cotype], 1191 ct->ct_free, ct->ct_lfree, ct->ct_cyclic, 1192 ct->ct_heap); 1193 if (coargs->flags & COF_LONGLIST) { 1194 /* more info! */ 1195 mdb_printf(" %-7d %-7d %-?p %-?p %-?p" 1196 " %-?lld %-?lld %-?p", 1197 ct->ct_heap_num, ct->ct_heap_max, 1198 ct->ct_taskq, ct->ct_expired.ch_head, 1199 ct->ct_queue.ch_head, 1200 cotwd->ct_timeouts_pending, 1201 cotwd->ct_allocations - 1202 cotwd->ct_timeouts_pending, 1203 ct->ct_mutex); 1204 } 1205 } else { 1206 /* address only */ 1207 mdb_printf("%-0?p", addr); 1208 } 1209 mdb_printf("\n"); 1210 if (coargs->flags & COF_TABLE) { 1211 return (WALK_NEXT); 1212 } 1213 } 1214 1215 coargs->ndx = index; 1216 if (coargs->flags & COF_VERBOSE) { 1217 mdb_inc_indent(4); 1218 } 1219 /* keep digging. */ 1220 if (!(coargs->flags & COF_BYIDH)) { 1221 /* walk the list hash table */ 1222 if (coargs->flags & COF_FREE) { 1223 clptr = ct->ct_lfree; 1224 coargs->bucket = 0; 1225 if (clptr == NULL) { 1226 return (WALK_NEXT); 1227 } 1228 if (mdb_pwalk("callout_list", callout_list_cb, coargs, 1229 (uintptr_t)clptr) == -1) { 1230 mdb_warn("cannot walk callout free list at %p", 1231 clptr); 1232 return (WALK_ERR); 1233 } 1234 } else { 1235 /* first print the expired list. */ 1236 clptr = (callout_list_t *)ct->ct_expired.ch_head; 1237 if (clptr != NULL) { 1238 coargs->bucket = -1; 1239 if (mdb_pwalk("callout_list", callout_list_cb, 1240 coargs, (uintptr_t)clptr) == -1) { 1241 mdb_warn("cannot walk callout_list" 1242 " at %p", clptr); 1243 return (WALK_ERR); 1244 } 1245 } 1246 /* then, print the callout queue */ 1247 clptr = (callout_list_t *)ct->ct_queue.ch_head; 1248 if (clptr != NULL) { 1249 coargs->bucket = -1; 1250 if (mdb_pwalk("callout_list", callout_list_cb, 1251 coargs, (uintptr_t)clptr) == -1) { 1252 mdb_warn("cannot walk callout_list" 1253 " at %p", clptr); 1254 return (WALK_ERR); 1255 } 1256 } 1257 for (i = 0; i < CALLOUT_BUCKETS; i++) { 1258 if (ct->ct_clhash == NULL) { 1259 /* nothing to do */ 1260 break; 1261 } 1262 if (cotwd->cot_clhash[i].ch_head == NULL) { 1263 continue; 1264 } 1265 clptr = (callout_list_t *) 1266 cotwd->cot_clhash[i].ch_head; 1267 coargs->bucket = i; 1268 /* walk list with callback routine. */ 1269 if (mdb_pwalk("callout_list", callout_list_cb, 1270 coargs, (uintptr_t)clptr) == -1) { 1271 mdb_warn("cannot walk callout_list" 1272 " at %p", clptr); 1273 return (WALK_ERR); 1274 } 1275 } 1276 } 1277 } else { 1278 /* walk the id hash table. */ 1279 if (coargs->flags & COF_FREE) { 1280 coptr = ct->ct_free; 1281 coargs->bucket = 0; 1282 if (coptr == NULL) { 1283 return (WALK_NEXT); 1284 } 1285 if (mdb_pwalk("callouts_byid", callouts_cb, coargs, 1286 (uintptr_t)coptr) == -1) { 1287 mdb_warn("cannot walk callout id free list" 1288 " at %p", coptr); 1289 return (WALK_ERR); 1290 } 1291 } else { 1292 for (i = 0; i < CALLOUT_BUCKETS; i++) { 1293 if (ct->ct_idhash == NULL) { 1294 break; 1295 } 1296 coptr = (callout_t *) 1297 cotwd->cot_idhash[i].ch_head; 1298 if (coptr == NULL) { 1299 continue; 1300 } 1301 coargs->bucket = i; 1302 1303 /* 1304 * walk callouts directly by id. For id 1305 * chain, the callout list is just a header, 1306 * so there's no need to walk it. 1307 */ 1308 if (mdb_pwalk("callouts_byid", callouts_cb, 1309 coargs, (uintptr_t)coptr) == -1) { 1310 mdb_warn("cannot walk callouts at %p", 1311 coptr); 1312 return (WALK_ERR); 1313 } 1314 } 1315 } 1316 } 1317 if (coargs->flags & COF_VERBOSE) { 1318 mdb_dec_indent(4); 1319 } 1320 return (WALK_NEXT); 1321 } 1322 1323 /* 1324 * initialize some common info for both callout dcmds. 1325 */ 1326 int 1327 callout_common_init(callout_data_t *coargs) 1328 { 1329 /* we need a couple of things */ 1330 if (mdb_readvar(&(coargs->co_table), "callout_table") == -1) { 1331 mdb_warn("failed to read 'callout_table'"); 1332 return (DCMD_ERR); 1333 } 1334 /* need to get now in nsecs. Approximate with hrtime vars */ 1335 if (mdb_readsym(&(coargs->now), sizeof (hrtime_t), "hrtime_last") != 1336 sizeof (hrtime_t)) { 1337 if (mdb_readsym(&(coargs->now), sizeof (hrtime_t), 1338 "hrtime_base") != sizeof (hrtime_t)) { 1339 mdb_warn("Could not determine current system time"); 1340 return (DCMD_ERR); 1341 } 1342 } 1343 1344 if (mdb_readvar(&(coargs->ctbits), "callout_table_bits") == -1) { 1345 mdb_warn("failed to read 'callout_table_bits'"); 1346 return (DCMD_ERR); 1347 } 1348 if (mdb_readvar(&(coargs->nsec_per_tick), "nsec_per_tick") == -1) { 1349 mdb_warn("failed to read 'nsec_per_tick'"); 1350 return (DCMD_ERR); 1351 } 1352 return (DCMD_OK); 1353 } 1354 1355 /* 1356 * dcmd to print callouts. Optional addr limits to specific table. 1357 * Parses lots of options that get passed to callbacks for walkers. 1358 * Has it's own help function. 1359 */ 1360 /*ARGSUSED*/ 1361 int 1362 callout(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1363 { 1364 callout_data_t coargs; 1365 /* getopts doesn't help much with stuff like this */ 1366 boolean_t Sflag, Cflag, tflag, aflag, bflag, dflag, kflag; 1367 char *funcname = NULL; 1368 char *paramstr = NULL; 1369 uintptr_t Stmp, Ctmp; /* for getopt. */ 1370 int retval; 1371 1372 coargs.flags = COF_DEFAULT; 1373 Sflag = Cflag = tflag = bflag = aflag = dflag = kflag = FALSE; 1374 coargs.seqid = -1; 1375 1376 if (mdb_getopts(argc, argv, 1377 'r', MDB_OPT_CLRBITS, COF_NORM, &coargs.flags, 1378 'n', MDB_OPT_CLRBITS, COF_REAL, &coargs.flags, 1379 'l', MDB_OPT_CLRBITS, COF_SHORT, &coargs.flags, 1380 's', MDB_OPT_CLRBITS, COF_LONG, &coargs.flags, 1381 'x', MDB_OPT_SETBITS, COF_EXEC, &coargs.flags, 1382 'h', MDB_OPT_SETBITS, COF_HIRES, &coargs.flags, 1383 'B', MDB_OPT_SETBITS, COF_ABS, &coargs.flags, 1384 'E', MDB_OPT_SETBITS, COF_EMPTY, &coargs.flags, 1385 'd', MDB_OPT_SETBITS, 1, &dflag, 1386 'C', MDB_OPT_UINTPTR_SET, &Cflag, &Ctmp, 1387 'S', MDB_OPT_UINTPTR_SET, &Sflag, &Stmp, 1388 't', MDB_OPT_UINTPTR_SET, &tflag, (uintptr_t *)&coargs.time, 1389 'a', MDB_OPT_UINTPTR_SET, &aflag, (uintptr_t *)&coargs.atime, 1390 'b', MDB_OPT_UINTPTR_SET, &bflag, (uintptr_t *)&coargs.btime, 1391 'k', MDB_OPT_SETBITS, 1, &kflag, 1392 'f', MDB_OPT_STR, &funcname, 1393 'p', MDB_OPT_STR, ¶mstr, 1394 'T', MDB_OPT_SETBITS, COF_TABLE, &coargs.flags, 1395 'D', MDB_OPT_SETBITS, COF_EXPREL, &coargs.flags, 1396 'L', MDB_OPT_SETBITS, COF_LIST, &coargs.flags, 1397 'V', MDB_OPT_SETBITS, COF_VERBOSE, &coargs.flags, 1398 'v', MDB_OPT_SETBITS, COF_LONGLIST, &coargs.flags, 1399 'i', MDB_OPT_SETBITS, COF_BYIDH, &coargs.flags, 1400 'F', MDB_OPT_SETBITS, COF_FREE, &coargs.flags, 1401 'H', MDB_OPT_SETBITS, COF_HEAP, &coargs.flags, 1402 'Q', MDB_OPT_SETBITS, COF_QUEUE, &coargs.flags, 1403 'A', MDB_OPT_SETBITS, COF_ADDR, &coargs.flags, 1404 NULL) != argc) { 1405 return (DCMD_USAGE); 1406 } 1407 1408 /* initialize from kernel variables */ 1409 if ((retval = callout_common_init(&coargs)) != DCMD_OK) { 1410 return (retval); 1411 } 1412 1413 /* do some option post-processing */ 1414 if (kflag) { 1415 coargs.time *= coargs.nsec_per_tick; 1416 coargs.atime *= coargs.nsec_per_tick; 1417 coargs.btime *= coargs.nsec_per_tick; 1418 } 1419 1420 if (dflag) { 1421 coargs.time += coargs.now; 1422 coargs.atime += coargs.now; 1423 coargs.btime += coargs.now; 1424 } 1425 if (Sflag) { 1426 if (flags & DCMD_ADDRSPEC) { 1427 mdb_printf("-S option conflicts with explicit" 1428 " address\n"); 1429 return (DCMD_USAGE); 1430 } 1431 coargs.flags |= COF_SEQID; 1432 coargs.seqid = (int)Stmp; 1433 } 1434 if (Cflag) { 1435 if (flags & DCMD_ADDRSPEC) { 1436 mdb_printf("-C option conflicts with explicit" 1437 " address\n"); 1438 return (DCMD_USAGE); 1439 } 1440 if (coargs.flags & COF_SEQID) { 1441 mdb_printf("-C and -S are mutually exclusive\n"); 1442 return (DCMD_USAGE); 1443 } 1444 coargs.cpu = (cpu_t *)Ctmp; 1445 if (mdb_vread(&coargs.seqid, sizeof (processorid_t), 1446 (uintptr_t)&(coargs.cpu->cpu_seqid)) == -1) { 1447 mdb_warn("failed to read cpu_t at %p", Ctmp); 1448 return (DCMD_ERR); 1449 } 1450 coargs.flags |= COF_SEQID; 1451 } 1452 /* avoid null outputs. */ 1453 if (!(coargs.flags & (COF_REAL | COF_NORM))) { 1454 coargs.flags |= COF_REAL | COF_NORM; 1455 } 1456 if (!(coargs.flags & (COF_LONG | COF_SHORT))) { 1457 coargs.flags |= COF_LONG | COF_SHORT; 1458 } 1459 if (tflag) { 1460 if (aflag || bflag) { 1461 mdb_printf("-t and -a|b are mutually exclusive\n"); 1462 return (DCMD_USAGE); 1463 } 1464 coargs.flags |= COF_TIME; 1465 } 1466 if (aflag) { 1467 coargs.flags |= COF_AFTER; 1468 } 1469 if (bflag) { 1470 coargs.flags |= COF_BEFORE; 1471 } 1472 if ((aflag && bflag) && (coargs.btime <= coargs.atime)) { 1473 mdb_printf("value for -a must be earlier than the value" 1474 " for -b.\n"); 1475 return (DCMD_USAGE); 1476 } 1477 1478 if ((coargs.flags & COF_HEAP) && (coargs.flags & COF_QUEUE)) { 1479 mdb_printf("-H and -Q are mutually exclusive\n"); 1480 return (DCMD_USAGE); 1481 } 1482 1483 if (funcname != NULL) { 1484 GElf_Sym sym; 1485 1486 if (mdb_lookup_by_name(funcname, &sym) != 0) { 1487 coargs.funcaddr = mdb_strtoull(funcname); 1488 } else { 1489 coargs.funcaddr = sym.st_value; 1490 } 1491 coargs.flags |= COF_FUNC; 1492 } 1493 1494 if (paramstr != NULL) { 1495 GElf_Sym sym; 1496 1497 if (mdb_lookup_by_name(paramstr, &sym) != 0) { 1498 coargs.param = mdb_strtoull(paramstr); 1499 } else { 1500 coargs.param = sym.st_value; 1501 } 1502 coargs.flags |= COF_PARAM; 1503 } 1504 1505 if (!(flags & DCMD_ADDRSPEC)) { 1506 /* don't pass "dot" if no addr. */ 1507 addr = NULL; 1508 } 1509 if (addr != NULL) { 1510 /* 1511 * a callout table was specified. Ignore -r|n option 1512 * to avoid null output. 1513 */ 1514 coargs.flags |= (COF_REAL | COF_NORM); 1515 } 1516 1517 if (DCMD_HDRSPEC(flags) || (coargs.flags & COF_VERBOSE)) { 1518 coargs.flags |= COF_THDR | COF_LHDR | COF_CHDR; 1519 } 1520 if (coargs.flags & COF_FREE) { 1521 coargs.flags |= COF_EMPTY; 1522 /* -F = free callouts, -FL = free lists */ 1523 if (!(coargs.flags & COF_LIST)) { 1524 coargs.flags |= COF_BYIDH; 1525 } 1526 } 1527 1528 /* walk table, using specialized callback routine. */ 1529 if (mdb_pwalk("callout_table", callout_t_cb, &coargs, addr) == -1) { 1530 mdb_warn("cannot walk callout_table"); 1531 return (DCMD_ERR); 1532 } 1533 return (DCMD_OK); 1534 } 1535 1536 1537 /* 1538 * Given an extended callout id, dump its information. 1539 */ 1540 /*ARGSUSED*/ 1541 int 1542 calloutid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1543 { 1544 callout_data_t coargs; 1545 callout_table_t *ctptr; 1546 callout_table_t ct; 1547 callout_id_t coid; 1548 callout_t *coptr; 1549 int tableid; 1550 callout_id_t xid; 1551 ulong_t idhash; 1552 int i, retval; 1553 const mdb_arg_t *arg; 1554 size_t size; 1555 callout_hash_t cot_idhash[CALLOUT_BUCKETS]; 1556 1557 coargs.flags = COF_DEFAULT | COF_BYIDH; 1558 i = mdb_getopts(argc, argv, 1559 'd', MDB_OPT_SETBITS, COF_DECODE, &coargs.flags, 1560 'v', MDB_OPT_SETBITS, COF_LONGLIST, &coargs.flags, 1561 NULL); 1562 argc -= i; 1563 argv += i; 1564 1565 if (argc != 1) { 1566 return (DCMD_USAGE); 1567 } 1568 arg = &argv[0]; 1569 1570 if (arg->a_type == MDB_TYPE_IMMEDIATE) { 1571 xid = arg->a_un.a_val; 1572 } else { 1573 xid = (callout_id_t)mdb_strtoull(arg->a_un.a_str); 1574 } 1575 1576 if (DCMD_HDRSPEC(flags)) { 1577 coargs.flags |= COF_CHDR; 1578 } 1579 1580 1581 /* initialize from kernel variables */ 1582 if ((retval = callout_common_init(&coargs)) != DCMD_OK) { 1583 return (retval); 1584 } 1585 1586 /* we must massage the environment so that the macros will play nice */ 1587 #define callout_table_mask ((1 << coargs.ctbits) - 1) 1588 #define callout_table_bits coargs.ctbits 1589 #define nsec_per_tick coargs.nsec_per_tick 1590 tableid = CALLOUT_ID_TO_TABLE(xid); 1591 idhash = CALLOUT_IDHASH(xid); 1592 #undef callouts_table_bits 1593 #undef callout_table_mask 1594 #undef nsec_per_tick 1595 coid = CO_PLAIN_ID(xid); 1596 1597 if (flags & DCMD_ADDRSPEC) { 1598 mdb_printf("calloutid does not accept explicit address.\n"); 1599 return (DCMD_USAGE); 1600 } 1601 1602 if (coargs.flags & COF_DECODE) { 1603 if (DCMD_HDRSPEC(flags)) { 1604 mdb_printf("%<u>%3s %1s %2s %-?s %-6s %</u>\n", 1605 "SEQ", "T", "XL", "XID", "IDHASH"); 1606 } 1607 mdb_printf("%-3d %1s %1s%1s %-?llx %-6d\n", 1608 TABLE_TO_SEQID(tableid), 1609 co_typenames[tableid & CALLOUT_TYPE_MASK], 1610 (xid & CALLOUT_EXECUTING) ? "X" : " ", 1611 (xid & CALLOUT_LONGTERM) ? "L" : " ", 1612 (long long)coid, idhash); 1613 return (DCMD_OK); 1614 } 1615 1616 /* get our table. Note this relies on the types being correct */ 1617 ctptr = coargs.co_table + tableid; 1618 if (mdb_vread(&ct, sizeof (callout_table_t), (uintptr_t)ctptr) == -1) { 1619 mdb_warn("failed to read callout_table at %p", ctptr); 1620 return (DCMD_ERR); 1621 } 1622 size = sizeof (callout_hash_t) * CALLOUT_BUCKETS; 1623 if (ct.ct_idhash != NULL) { 1624 if (mdb_vread(&(cot_idhash), size, 1625 (uintptr_t)ct.ct_idhash) == -1) { 1626 mdb_warn("failed to read id_hash at %p", 1627 ct.ct_idhash); 1628 return (WALK_ERR); 1629 } 1630 } 1631 1632 /* callout at beginning of hash chain */ 1633 if (ct.ct_idhash == NULL) { 1634 mdb_printf("id hash chain for this xid is empty\n"); 1635 return (DCMD_ERR); 1636 } 1637 coptr = (callout_t *)cot_idhash[idhash].ch_head; 1638 if (coptr == NULL) { 1639 mdb_printf("id hash chain for this xid is empty\n"); 1640 return (DCMD_ERR); 1641 } 1642 1643 coargs.ndx = tableid; 1644 coargs.bucket = idhash; 1645 1646 /* use the walker, luke */ 1647 if (mdb_pwalk("callouts_byid", callouts_cb, &coargs, 1648 (uintptr_t)coptr) == -1) { 1649 mdb_warn("cannot walk callouts at %p", coptr); 1650 return (WALK_ERR); 1651 } 1652 1653 return (DCMD_OK); 1654 } 1655 1656 void 1657 callout_help(void) 1658 { 1659 mdb_printf("callout: display callouts.\n" 1660 "Given a callout table address, display callouts from table.\n" 1661 "Without an address, display callouts from all tables.\n" 1662 "options:\n" 1663 " -r|n : limit display to (r)ealtime or (n)ormal type callouts\n" 1664 " -s|l : limit display to (s)hort-term ids or (l)ong-term ids\n" 1665 " -x : limit display to callouts which are executing\n" 1666 " -h : limit display to callouts based on hrestime\n" 1667 " -B : limit display to callouts based on absolute time\n" 1668 " -t|a|b nsec: limit display to callouts that expire a(t) time," 1669 " (a)fter time,\n or (b)efore time. Use -a and -b together " 1670 " to specify a range.\n For \"now\", use -d[t|a|b] 0.\n" 1671 " -d : interpret time option to -t|a|b as delta from current time\n" 1672 " -k : use ticks instead of nanoseconds as arguments to" 1673 " -t|a|b. Note that\n ticks are less accurate and may not" 1674 " match other tick times (ie: lbolt).\n" 1675 " -D : display exiration time as delta from current time\n" 1676 " -S seqid : limit display to callouts for this cpu sequence id\n" 1677 " -C addr : limit display to callouts for this cpu pointer\n" 1678 " -f name|addr : limit display to callouts with this function\n" 1679 " -p name|addr : limit display to callouts functions with this" 1680 " parameter\n" 1681 " -T : display the callout table itself, instead of callouts\n" 1682 " -L : display callout lists instead of callouts\n" 1683 " -E : with -T or L, display empty data structures.\n" 1684 " -i : traverse callouts by id hash instead of list hash\n" 1685 " -F : walk free callout list (free list with -i) instead\n" 1686 " -v : display more info for each item\n" 1687 " -V : show details of each level of info as it is traversed\n" 1688 " -H : limit display to callouts in the callout heap\n" 1689 " -Q : limit display to callouts in the callout queue\n" 1690 " -A : show only addresses. Useful for pipelines.\n"); 1691 } 1692 1693 void 1694 calloutid_help(void) 1695 { 1696 mdb_printf("calloutid: display callout by id.\n" 1697 "Given an extended callout id, display the callout infomation.\n" 1698 "options:\n" 1699 " -d : do not dereference callout, just decode the id.\n" 1700 " -v : verbose display more info about the callout\n"); 1701 } 1702 1703 /*ARGSUSED*/ 1704 int 1705 class(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1706 { 1707 long num_classes, i; 1708 sclass_t *class_tbl; 1709 GElf_Sym g_sclass; 1710 char class_name[PC_CLNMSZ]; 1711 size_t tbl_size; 1712 1713 if (mdb_lookup_by_name("sclass", &g_sclass) == -1) { 1714 mdb_warn("failed to find symbol sclass\n"); 1715 return (DCMD_ERR); 1716 } 1717 1718 tbl_size = (size_t)g_sclass.st_size; 1719 num_classes = tbl_size / (sizeof (sclass_t)); 1720 class_tbl = mdb_alloc(tbl_size, UM_SLEEP | UM_GC); 1721 1722 if (mdb_readsym(class_tbl, tbl_size, "sclass") == -1) { 1723 mdb_warn("failed to read sclass"); 1724 return (DCMD_ERR); 1725 } 1726 1727 mdb_printf("%<u>%4s %-10s %-24s %-24s%</u>\n", "SLOT", "NAME", 1728 "INIT FCN", "CLASS FCN"); 1729 1730 for (i = 0; i < num_classes; i++) { 1731 if (mdb_vread(class_name, sizeof (class_name), 1732 (uintptr_t)class_tbl[i].cl_name) == -1) 1733 (void) strcpy(class_name, "???"); 1734 1735 mdb_printf("%4ld %-10s %-24a %-24a\n", i, class_name, 1736 class_tbl[i].cl_init, class_tbl[i].cl_funcs); 1737 } 1738 1739 return (DCMD_OK); 1740 } 1741 1742 #define FSNAMELEN 32 /* Max len of FS name we read from vnodeops */ 1743 1744 int 1745 vnode2path(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1746 { 1747 uintptr_t rootdir; 1748 vnode_t vn; 1749 char buf[MAXPATHLEN]; 1750 1751 uint_t opt_F = FALSE; 1752 1753 if (mdb_getopts(argc, argv, 1754 'F', MDB_OPT_SETBITS, TRUE, &opt_F, NULL) != argc) 1755 return (DCMD_USAGE); 1756 1757 if (!(flags & DCMD_ADDRSPEC)) { 1758 mdb_warn("expected explicit vnode_t address before ::\n"); 1759 return (DCMD_USAGE); 1760 } 1761 1762 if (mdb_readvar(&rootdir, "rootdir") == -1) { 1763 mdb_warn("failed to read rootdir"); 1764 return (DCMD_ERR); 1765 } 1766 1767 if (mdb_vnode2path(addr, buf, sizeof (buf)) == -1) 1768 return (DCMD_ERR); 1769 1770 if (*buf == '\0') { 1771 mdb_printf("??\n"); 1772 return (DCMD_OK); 1773 } 1774 1775 mdb_printf("%s", buf); 1776 if (opt_F && buf[strlen(buf)-1] != '/' && 1777 mdb_vread(&vn, sizeof (vn), addr) == sizeof (vn)) 1778 mdb_printf("%c", mdb_vtype2chr(vn.v_type, 0)); 1779 mdb_printf("\n"); 1780 1781 return (DCMD_OK); 1782 } 1783 1784 int 1785 ld_walk_init(mdb_walk_state_t *wsp) 1786 { 1787 wsp->walk_data = (void *)wsp->walk_addr; 1788 return (WALK_NEXT); 1789 } 1790 1791 int 1792 ld_walk_step(mdb_walk_state_t *wsp) 1793 { 1794 int status; 1795 lock_descriptor_t ld; 1796 1797 if (mdb_vread(&ld, sizeof (lock_descriptor_t), wsp->walk_addr) == -1) { 1798 mdb_warn("couldn't read lock_descriptor_t at %p\n", 1799 wsp->walk_addr); 1800 return (WALK_ERR); 1801 } 1802 1803 status = wsp->walk_callback(wsp->walk_addr, &ld, wsp->walk_cbdata); 1804 if (status == WALK_ERR) 1805 return (WALK_ERR); 1806 1807 wsp->walk_addr = (uintptr_t)ld.l_next; 1808 if (wsp->walk_addr == (uintptr_t)wsp->walk_data) 1809 return (WALK_DONE); 1810 1811 return (status); 1812 } 1813 1814 int 1815 lg_walk_init(mdb_walk_state_t *wsp) 1816 { 1817 GElf_Sym sym; 1818 1819 if (mdb_lookup_by_name("lock_graph", &sym) == -1) { 1820 mdb_warn("failed to find symbol 'lock_graph'\n"); 1821 return (WALK_ERR); 1822 } 1823 1824 wsp->walk_addr = (uintptr_t)sym.st_value; 1825 wsp->walk_data = (void *)(uintptr_t)(sym.st_value + sym.st_size); 1826 1827 return (WALK_NEXT); 1828 } 1829 1830 typedef struct lg_walk_data { 1831 uintptr_t startaddr; 1832 mdb_walk_cb_t callback; 1833 void *data; 1834 } lg_walk_data_t; 1835 1836 /* 1837 * We can't use ::walk lock_descriptor directly, because the head of each graph 1838 * is really a dummy lock. Rather than trying to dynamically determine if this 1839 * is a dummy node or not, we just filter out the initial element of the 1840 * list. 1841 */ 1842 static int 1843 lg_walk_cb(uintptr_t addr, const void *data, void *priv) 1844 { 1845 lg_walk_data_t *lw = priv; 1846 1847 if (addr != lw->startaddr) 1848 return (lw->callback(addr, data, lw->data)); 1849 1850 return (WALK_NEXT); 1851 } 1852 1853 int 1854 lg_walk_step(mdb_walk_state_t *wsp) 1855 { 1856 graph_t *graph; 1857 lg_walk_data_t lw; 1858 1859 if (wsp->walk_addr >= (uintptr_t)wsp->walk_data) 1860 return (WALK_DONE); 1861 1862 if (mdb_vread(&graph, sizeof (graph), wsp->walk_addr) == -1) { 1863 mdb_warn("failed to read graph_t at %p", wsp->walk_addr); 1864 return (WALK_ERR); 1865 } 1866 1867 wsp->walk_addr += sizeof (graph); 1868 1869 if (graph == NULL) 1870 return (WALK_NEXT); 1871 1872 lw.callback = wsp->walk_callback; 1873 lw.data = wsp->walk_cbdata; 1874 1875 lw.startaddr = (uintptr_t)&(graph->active_locks); 1876 if (mdb_pwalk("lock_descriptor", lg_walk_cb, &lw, lw.startaddr)) { 1877 mdb_warn("couldn't walk lock_descriptor at %p\n", lw.startaddr); 1878 return (WALK_ERR); 1879 } 1880 1881 lw.startaddr = (uintptr_t)&(graph->sleeping_locks); 1882 if (mdb_pwalk("lock_descriptor", lg_walk_cb, &lw, lw.startaddr)) { 1883 mdb_warn("couldn't walk lock_descriptor at %p\n", lw.startaddr); 1884 return (WALK_ERR); 1885 } 1886 1887 return (WALK_NEXT); 1888 } 1889 1890 /* 1891 * The space available for the path corresponding to the locked vnode depends 1892 * on whether we are printing 32- or 64-bit addresses. 1893 */ 1894 #ifdef _LP64 1895 #define LM_VNPATHLEN 20 1896 #else 1897 #define LM_VNPATHLEN 30 1898 #endif 1899 1900 /*ARGSUSED*/ 1901 static int 1902 lminfo_cb(uintptr_t addr, const void *data, void *priv) 1903 { 1904 const lock_descriptor_t *ld = data; 1905 char buf[LM_VNPATHLEN]; 1906 proc_t p; 1907 1908 mdb_printf("%-?p %2s %04x %6d %-16s %-?p ", 1909 addr, ld->l_type == F_RDLCK ? "RD" : 1910 ld->l_type == F_WRLCK ? "WR" : "??", 1911 ld->l_state, ld->l_flock.l_pid, 1912 ld->l_flock.l_pid == 0 ? "<kernel>" : 1913 mdb_pid2proc(ld->l_flock.l_pid, &p) == NULL ? 1914 "<defunct>" : p.p_user.u_comm, 1915 ld->l_vnode); 1916 1917 mdb_vnode2path((uintptr_t)ld->l_vnode, buf, 1918 sizeof (buf)); 1919 mdb_printf("%s\n", buf); 1920 1921 return (WALK_NEXT); 1922 } 1923 1924 /*ARGSUSED*/ 1925 int 1926 lminfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1927 { 1928 if (DCMD_HDRSPEC(flags)) 1929 mdb_printf("%<u>%-?s %2s %4s %6s %-16s %-?s %s%</u>\n", 1930 "ADDR", "TP", "FLAG", "PID", "COMM", "VNODE", "PATH"); 1931 1932 return (mdb_pwalk("lock_graph", lminfo_cb, NULL, NULL)); 1933 } 1934 1935 /*ARGSUSED*/ 1936 int 1937 whereopen_fwalk(uintptr_t addr, struct file *f, uintptr_t *target) 1938 { 1939 if ((uintptr_t)f->f_vnode == *target) { 1940 mdb_printf("file %p\n", addr); 1941 *target = NULL; 1942 } 1943 1944 return (WALK_NEXT); 1945 } 1946 1947 /*ARGSUSED*/ 1948 int 1949 whereopen_pwalk(uintptr_t addr, void *ignored, uintptr_t *target) 1950 { 1951 uintptr_t t = *target; 1952 1953 if (mdb_pwalk("file", (mdb_walk_cb_t)whereopen_fwalk, &t, addr) == -1) { 1954 mdb_warn("couldn't file walk proc %p", addr); 1955 return (WALK_ERR); 1956 } 1957 1958 if (t == NULL) 1959 mdb_printf("%p\n", addr); 1960 1961 return (WALK_NEXT); 1962 } 1963 1964 /*ARGSUSED*/ 1965 int 1966 whereopen(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1967 { 1968 uintptr_t target = addr; 1969 1970 if (!(flags & DCMD_ADDRSPEC) || addr == NULL) 1971 return (DCMD_USAGE); 1972 1973 if (mdb_walk("proc", (mdb_walk_cb_t)whereopen_pwalk, &target) == -1) { 1974 mdb_warn("can't proc walk"); 1975 return (DCMD_ERR); 1976 } 1977 1978 return (DCMD_OK); 1979 } 1980 1981 typedef struct datafmt { 1982 char *hdr1; 1983 char *hdr2; 1984 char *dashes; 1985 char *fmt; 1986 } datafmt_t; 1987 1988 static datafmt_t kmemfmt[] = { 1989 { "cache ", "name ", 1990 "-------------------------", "%-25s " }, 1991 { " buf", " size", "------", "%6u " }, 1992 { " buf", "in use", "------", "%6u " }, 1993 { " buf", " total", "------", "%6u " }, 1994 { " memory", " in use", "----------", "%10lu%c " }, 1995 { " alloc", " succeed", "---------", "%9u " }, 1996 { "alloc", " fail", "-----", "%5u " }, 1997 { NULL, NULL, NULL, NULL } 1998 }; 1999 2000 static datafmt_t vmemfmt[] = { 2001 { "vmem ", "name ", 2002 "-------------------------", "%-*s " }, 2003 { " memory", " in use", "----------", "%9llu%c " }, 2004 { " memory", " total", "-----------", "%10llu%c " }, 2005 { " memory", " import", "----------", "%9llu%c " }, 2006 { " alloc", " succeed", "---------", "%9llu " }, 2007 { "alloc", " fail", "-----", "%5llu " }, 2008 { NULL, NULL, NULL, NULL } 2009 }; 2010 2011 /*ARGSUSED*/ 2012 static int 2013 kmastat_cpu_avail(uintptr_t addr, const kmem_cpu_cache_t *ccp, int *avail) 2014 { 2015 short rounds, prounds; 2016 2017 if (KMEM_DUMPCC(ccp)) { 2018 rounds = ccp->cc_dump_rounds; 2019 prounds = ccp->cc_dump_prounds; 2020 } else { 2021 rounds = ccp->cc_rounds; 2022 prounds = ccp->cc_prounds; 2023 } 2024 if (rounds > 0) 2025 *avail += rounds; 2026 if (prounds > 0) 2027 *avail += prounds; 2028 2029 return (WALK_NEXT); 2030 } 2031 2032 /*ARGSUSED*/ 2033 static int 2034 kmastat_cpu_alloc(uintptr_t addr, const kmem_cpu_cache_t *ccp, int *alloc) 2035 { 2036 *alloc += ccp->cc_alloc; 2037 2038 return (WALK_NEXT); 2039 } 2040 2041 /*ARGSUSED*/ 2042 static int 2043 kmastat_slab_avail(uintptr_t addr, const kmem_slab_t *sp, int *avail) 2044 { 2045 *avail += sp->slab_chunks - sp->slab_refcnt; 2046 2047 return (WALK_NEXT); 2048 } 2049 2050 typedef struct kmastat_vmem { 2051 uintptr_t kv_addr; 2052 struct kmastat_vmem *kv_next; 2053 size_t kv_meminuse; 2054 int kv_alloc; 2055 int kv_fail; 2056 } kmastat_vmem_t; 2057 2058 typedef struct kmastat_args { 2059 kmastat_vmem_t **ka_kvpp; 2060 uint_t ka_shift; 2061 } kmastat_args_t; 2062 2063 static int 2064 kmastat_cache(uintptr_t addr, const kmem_cache_t *cp, kmastat_args_t *kap) 2065 { 2066 kmastat_vmem_t **kvpp = kap->ka_kvpp; 2067 kmastat_vmem_t *kv; 2068 datafmt_t *dfp = kmemfmt; 2069 int magsize; 2070 2071 int avail, alloc, total; 2072 size_t meminuse = (cp->cache_slab_create - cp->cache_slab_destroy) * 2073 cp->cache_slabsize; 2074 2075 mdb_walk_cb_t cpu_avail = (mdb_walk_cb_t)kmastat_cpu_avail; 2076 mdb_walk_cb_t cpu_alloc = (mdb_walk_cb_t)kmastat_cpu_alloc; 2077 mdb_walk_cb_t slab_avail = (mdb_walk_cb_t)kmastat_slab_avail; 2078 2079 magsize = kmem_get_magsize(cp); 2080 2081 alloc = cp->cache_slab_alloc + cp->cache_full.ml_alloc; 2082 avail = cp->cache_full.ml_total * magsize; 2083 total = cp->cache_buftotal; 2084 2085 (void) mdb_pwalk("kmem_cpu_cache", cpu_alloc, &alloc, addr); 2086 (void) mdb_pwalk("kmem_cpu_cache", cpu_avail, &avail, addr); 2087 (void) mdb_pwalk("kmem_slab_partial", slab_avail, &avail, addr); 2088 2089 for (kv = *kvpp; kv != NULL; kv = kv->kv_next) { 2090 if (kv->kv_addr == (uintptr_t)cp->cache_arena) 2091 goto out; 2092 } 2093 2094 kv = mdb_zalloc(sizeof (kmastat_vmem_t), UM_SLEEP | UM_GC); 2095 kv->kv_next = *kvpp; 2096 kv->kv_addr = (uintptr_t)cp->cache_arena; 2097 *kvpp = kv; 2098 out: 2099 kv->kv_meminuse += meminuse; 2100 kv->kv_alloc += alloc; 2101 kv->kv_fail += cp->cache_alloc_fail; 2102 2103 mdb_printf((dfp++)->fmt, cp->cache_name); 2104 mdb_printf((dfp++)->fmt, cp->cache_bufsize); 2105 mdb_printf((dfp++)->fmt, total - avail); 2106 mdb_printf((dfp++)->fmt, total); 2107 mdb_printf((dfp++)->fmt, meminuse >> kap->ka_shift, 2108 kap->ka_shift == GIGS ? 'G' : kap->ka_shift == MEGS ? 'M' : 2109 kap->ka_shift == KILOS ? 'K' : 'B'); 2110 mdb_printf((dfp++)->fmt, alloc); 2111 mdb_printf((dfp++)->fmt, cp->cache_alloc_fail); 2112 mdb_printf("\n"); 2113 2114 return (WALK_NEXT); 2115 } 2116 2117 static int 2118 kmastat_vmem_totals(uintptr_t addr, const vmem_t *v, kmastat_args_t *kap) 2119 { 2120 kmastat_vmem_t *kv = *kap->ka_kvpp; 2121 size_t len; 2122 2123 while (kv != NULL && kv->kv_addr != addr) 2124 kv = kv->kv_next; 2125 2126 if (kv == NULL || kv->kv_alloc == 0) 2127 return (WALK_NEXT); 2128 2129 len = MIN(17, strlen(v->vm_name)); 2130 2131 mdb_printf("Total [%s]%*s %6s %6s %6s %10lu%c %9u %5u\n", v->vm_name, 2132 17 - len, "", "", "", "", 2133 kv->kv_meminuse >> kap->ka_shift, 2134 kap->ka_shift == GIGS ? 'G' : kap->ka_shift == MEGS ? 'M' : 2135 kap->ka_shift == KILOS ? 'K' : 'B', kv->kv_alloc, kv->kv_fail); 2136 2137 return (WALK_NEXT); 2138 } 2139 2140 /*ARGSUSED*/ 2141 static int 2142 kmastat_vmem(uintptr_t addr, const vmem_t *v, const uint_t *shiftp) 2143 { 2144 datafmt_t *dfp = vmemfmt; 2145 const vmem_kstat_t *vkp = &v->vm_kstat; 2146 uintptr_t paddr; 2147 vmem_t parent; 2148 int ident = 0; 2149 2150 for (paddr = (uintptr_t)v->vm_source; paddr != NULL; ident += 4) { 2151 if (mdb_vread(&parent, sizeof (parent), paddr) == -1) { 2152 mdb_warn("couldn't trace %p's ancestry", addr); 2153 ident = 0; 2154 break; 2155 } 2156 paddr = (uintptr_t)parent.vm_source; 2157 } 2158 2159 mdb_printf("%*s", ident, ""); 2160 mdb_printf((dfp++)->fmt, 25 - ident, v->vm_name); 2161 mdb_printf((dfp++)->fmt, vkp->vk_mem_inuse.value.ui64 >> *shiftp, 2162 *shiftp == GIGS ? 'G' : *shiftp == MEGS ? 'M' : 2163 *shiftp == KILOS ? 'K' : 'B'); 2164 mdb_printf((dfp++)->fmt, vkp->vk_mem_total.value.ui64 >> *shiftp, 2165 *shiftp == GIGS ? 'G' : *shiftp == MEGS ? 'M' : 2166 *shiftp == KILOS ? 'K' : 'B'); 2167 mdb_printf((dfp++)->fmt, vkp->vk_mem_import.value.ui64 >> *shiftp, 2168 *shiftp == GIGS ? 'G' : *shiftp == MEGS ? 'M' : 2169 *shiftp == KILOS ? 'K' : 'B'); 2170 mdb_printf((dfp++)->fmt, vkp->vk_alloc.value.ui64); 2171 mdb_printf((dfp++)->fmt, vkp->vk_fail.value.ui64); 2172 2173 mdb_printf("\n"); 2174 2175 return (WALK_NEXT); 2176 } 2177 2178 /*ARGSUSED*/ 2179 int 2180 kmastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 2181 { 2182 kmastat_vmem_t *kv = NULL; 2183 datafmt_t *dfp; 2184 kmastat_args_t ka; 2185 2186 ka.ka_shift = 0; 2187 if (mdb_getopts(argc, argv, 2188 'k', MDB_OPT_SETBITS, KILOS, &ka.ka_shift, 2189 'm', MDB_OPT_SETBITS, MEGS, &ka.ka_shift, 2190 'g', MDB_OPT_SETBITS, GIGS, &ka.ka_shift, NULL) != argc) 2191 return (DCMD_USAGE); 2192 2193 for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) 2194 mdb_printf("%s ", dfp->hdr1); 2195 mdb_printf("\n"); 2196 2197 for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) 2198 mdb_printf("%s ", dfp->hdr2); 2199 mdb_printf("\n"); 2200 2201 for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) 2202 mdb_printf("%s ", dfp->dashes); 2203 mdb_printf("\n"); 2204 2205 ka.ka_kvpp = &kv; 2206 if (mdb_walk("kmem_cache", (mdb_walk_cb_t)kmastat_cache, &ka) == -1) { 2207 mdb_warn("can't walk 'kmem_cache'"); 2208 return (DCMD_ERR); 2209 } 2210 2211 for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) 2212 mdb_printf("%s ", dfp->dashes); 2213 mdb_printf("\n"); 2214 2215 if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem_totals, &ka) == -1) { 2216 mdb_warn("can't walk 'vmem'"); 2217 return (DCMD_ERR); 2218 } 2219 2220 for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) 2221 mdb_printf("%s ", dfp->dashes); 2222 mdb_printf("\n"); 2223 2224 mdb_printf("\n"); 2225 2226 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++) 2227 mdb_printf("%s ", dfp->hdr1); 2228 mdb_printf("\n"); 2229 2230 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++) 2231 mdb_printf("%s ", dfp->hdr2); 2232 mdb_printf("\n"); 2233 2234 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++) 2235 mdb_printf("%s ", dfp->dashes); 2236 mdb_printf("\n"); 2237 2238 if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem, &ka.ka_shift) == -1) { 2239 mdb_warn("can't walk 'vmem'"); 2240 return (DCMD_ERR); 2241 } 2242 2243 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++) 2244 mdb_printf("%s ", dfp->dashes); 2245 mdb_printf("\n"); 2246 return (DCMD_OK); 2247 } 2248 2249 /* 2250 * Our ::kgrep callback scans the entire kernel VA space (kas). kas is made 2251 * up of a set of 'struct seg's. We could just scan each seg en masse, but 2252 * unfortunately, a few of the segs are both large and sparse, so we could 2253 * spend quite a bit of time scanning VAs which have no backing pages. 2254 * 2255 * So for the few very sparse segs, we skip the segment itself, and scan 2256 * the allocated vmem_segs in the vmem arena which manages that part of kas. 2257 * Currently, we do this for: 2258 * 2259 * SEG VMEM ARENA 2260 * kvseg heap_arena 2261 * kvseg32 heap32_arena 2262 * kvseg_core heap_core_arena 2263 * 2264 * In addition, we skip the segkpm segment in its entirety, since it is very 2265 * sparse, and contains no new kernel data. 2266 */ 2267 typedef struct kgrep_walk_data { 2268 kgrep_cb_func *kg_cb; 2269 void *kg_cbdata; 2270 uintptr_t kg_kvseg; 2271 uintptr_t kg_kvseg32; 2272 uintptr_t kg_kvseg_core; 2273 uintptr_t kg_segkpm; 2274 uintptr_t kg_heap_lp_base; 2275 uintptr_t kg_heap_lp_end; 2276 } kgrep_walk_data_t; 2277 2278 static int 2279 kgrep_walk_seg(uintptr_t addr, const struct seg *seg, kgrep_walk_data_t *kg) 2280 { 2281 uintptr_t base = (uintptr_t)seg->s_base; 2282 2283 if (addr == kg->kg_kvseg || addr == kg->kg_kvseg32 || 2284 addr == kg->kg_kvseg_core) 2285 return (WALK_NEXT); 2286 2287 if ((uintptr_t)seg->s_ops == kg->kg_segkpm) 2288 return (WALK_NEXT); 2289 2290 return (kg->kg_cb(base, base + seg->s_size, kg->kg_cbdata)); 2291 } 2292 2293 /*ARGSUSED*/ 2294 static int 2295 kgrep_walk_vseg(uintptr_t addr, const vmem_seg_t *seg, kgrep_walk_data_t *kg) 2296 { 2297 /* 2298 * skip large page heap address range - it is scanned by walking 2299 * allocated vmem_segs in the heap_lp_arena 2300 */ 2301 if (seg->vs_start == kg->kg_heap_lp_base && 2302 seg->vs_end == kg->kg_heap_lp_end) 2303 return (WALK_NEXT); 2304 2305 return (kg->kg_cb(seg->vs_start, seg->vs_end, kg->kg_cbdata)); 2306 } 2307 2308 /*ARGSUSED*/ 2309 static int 2310 kgrep_xwalk_vseg(uintptr_t addr, const vmem_seg_t *seg, kgrep_walk_data_t *kg) 2311 { 2312 return (kg->kg_cb(seg->vs_start, seg->vs_end, kg->kg_cbdata)); 2313 } 2314 2315 static int 2316 kgrep_walk_vmem(uintptr_t addr, const vmem_t *vmem, kgrep_walk_data_t *kg) 2317 { 2318 mdb_walk_cb_t walk_vseg = (mdb_walk_cb_t)kgrep_walk_vseg; 2319 2320 if (strcmp(vmem->vm_name, "heap") != 0 && 2321 strcmp(vmem->vm_name, "heap32") != 0 && 2322 strcmp(vmem->vm_name, "heap_core") != 0 && 2323 strcmp(vmem->vm_name, "heap_lp") != 0) 2324 return (WALK_NEXT); 2325 2326 if (strcmp(vmem->vm_name, "heap_lp") == 0) 2327 walk_vseg = (mdb_walk_cb_t)kgrep_xwalk_vseg; 2328 2329 if (mdb_pwalk("vmem_alloc", walk_vseg, kg, addr) == -1) { 2330 mdb_warn("couldn't walk vmem_alloc for vmem %p", addr); 2331 return (WALK_ERR); 2332 } 2333 2334 return (WALK_NEXT); 2335 } 2336 2337 int 2338 kgrep_subr(kgrep_cb_func *cb, void *cbdata) 2339 { 2340 GElf_Sym kas, kvseg, kvseg32, kvseg_core, segkpm; 2341 kgrep_walk_data_t kg; 2342 2343 if (mdb_get_state() == MDB_STATE_RUNNING) { 2344 mdb_warn("kgrep can only be run on a system " 2345 "dump or under kmdb; see dumpadm(1M)\n"); 2346 return (DCMD_ERR); 2347 } 2348 2349 if (mdb_lookup_by_name("kas", &kas) == -1) { 2350 mdb_warn("failed to locate 'kas' symbol\n"); 2351 return (DCMD_ERR); 2352 } 2353 2354 if (mdb_lookup_by_name("kvseg", &kvseg) == -1) { 2355 mdb_warn("failed to locate 'kvseg' symbol\n"); 2356 return (DCMD_ERR); 2357 } 2358 2359 if (mdb_lookup_by_name("kvseg32", &kvseg32) == -1) { 2360 mdb_warn("failed to locate 'kvseg32' symbol\n"); 2361 return (DCMD_ERR); 2362 } 2363 2364 if (mdb_lookup_by_name("kvseg_core", &kvseg_core) == -1) { 2365 mdb_warn("failed to locate 'kvseg_core' symbol\n"); 2366 return (DCMD_ERR); 2367 } 2368 2369 if (mdb_lookup_by_name("segkpm_ops", &segkpm) == -1) { 2370 mdb_warn("failed to locate 'segkpm_ops' symbol\n"); 2371 return (DCMD_ERR); 2372 } 2373 2374 if (mdb_readvar(&kg.kg_heap_lp_base, "heap_lp_base") == -1) { 2375 mdb_warn("failed to read 'heap_lp_base'\n"); 2376 return (DCMD_ERR); 2377 } 2378 2379 if (mdb_readvar(&kg.kg_heap_lp_end, "heap_lp_end") == -1) { 2380 mdb_warn("failed to read 'heap_lp_end'\n"); 2381 return (DCMD_ERR); 2382 } 2383 2384 kg.kg_cb = cb; 2385 kg.kg_cbdata = cbdata; 2386 kg.kg_kvseg = (uintptr_t)kvseg.st_value; 2387 kg.kg_kvseg32 = (uintptr_t)kvseg32.st_value; 2388 kg.kg_kvseg_core = (uintptr_t)kvseg_core.st_value; 2389 kg.kg_segkpm = (uintptr_t)segkpm.st_value; 2390 2391 if (mdb_pwalk("seg", (mdb_walk_cb_t)kgrep_walk_seg, 2392 &kg, kas.st_value) == -1) { 2393 mdb_warn("failed to walk kas segments"); 2394 return (DCMD_ERR); 2395 } 2396 2397 if (mdb_walk("vmem", (mdb_walk_cb_t)kgrep_walk_vmem, &kg) == -1) { 2398 mdb_warn("failed to walk heap/heap32 vmem arenas"); 2399 return (DCMD_ERR); 2400 } 2401 2402 return (DCMD_OK); 2403 } 2404 2405 size_t 2406 kgrep_subr_pagesize(void) 2407 { 2408 return (PAGESIZE); 2409 } 2410 2411 typedef struct file_walk_data { 2412 struct uf_entry *fw_flist; 2413 int fw_flistsz; 2414 int fw_ndx; 2415 int fw_nofiles; 2416 } file_walk_data_t; 2417 2418 int 2419 file_walk_init(mdb_walk_state_t *wsp) 2420 { 2421 file_walk_data_t *fw; 2422 proc_t p; 2423 2424 if (wsp->walk_addr == NULL) { 2425 mdb_warn("file walk doesn't support global walks\n"); 2426 return (WALK_ERR); 2427 } 2428 2429 fw = mdb_alloc(sizeof (file_walk_data_t), UM_SLEEP); 2430 2431 if (mdb_vread(&p, sizeof (p), wsp->walk_addr) == -1) { 2432 mdb_free(fw, sizeof (file_walk_data_t)); 2433 mdb_warn("failed to read proc structure at %p", wsp->walk_addr); 2434 return (WALK_ERR); 2435 } 2436 2437 if (p.p_user.u_finfo.fi_nfiles == 0) { 2438 mdb_free(fw, sizeof (file_walk_data_t)); 2439 return (WALK_DONE); 2440 } 2441 2442 fw->fw_nofiles = p.p_user.u_finfo.fi_nfiles; 2443 fw->fw_flistsz = sizeof (struct uf_entry) * fw->fw_nofiles; 2444 fw->fw_flist = mdb_alloc(fw->fw_flistsz, UM_SLEEP); 2445 2446 if (mdb_vread(fw->fw_flist, fw->fw_flistsz, 2447 (uintptr_t)p.p_user.u_finfo.fi_list) == -1) { 2448 mdb_warn("failed to read file array at %p", 2449 p.p_user.u_finfo.fi_list); 2450 mdb_free(fw->fw_flist, fw->fw_flistsz); 2451 mdb_free(fw, sizeof (file_walk_data_t)); 2452 return (WALK_ERR); 2453 } 2454 2455 fw->fw_ndx = 0; 2456 wsp->walk_data = fw; 2457 2458 return (WALK_NEXT); 2459 } 2460 2461 int 2462 file_walk_step(mdb_walk_state_t *wsp) 2463 { 2464 file_walk_data_t *fw = (file_walk_data_t *)wsp->walk_data; 2465 struct file file; 2466 uintptr_t fp; 2467 2468 again: 2469 if (fw->fw_ndx == fw->fw_nofiles) 2470 return (WALK_DONE); 2471 2472 if ((fp = (uintptr_t)fw->fw_flist[fw->fw_ndx++].uf_file) == NULL) 2473 goto again; 2474 2475 (void) mdb_vread(&file, sizeof (file), (uintptr_t)fp); 2476 return (wsp->walk_callback(fp, &file, wsp->walk_cbdata)); 2477 } 2478 2479 int 2480 allfile_walk_step(mdb_walk_state_t *wsp) 2481 { 2482 file_walk_data_t *fw = (file_walk_data_t *)wsp->walk_data; 2483 struct file file; 2484 uintptr_t fp; 2485 2486 if (fw->fw_ndx == fw->fw_nofiles) 2487 return (WALK_DONE); 2488 2489 if ((fp = (uintptr_t)fw->fw_flist[fw->fw_ndx++].uf_file) != NULL) 2490 (void) mdb_vread(&file, sizeof (file), (uintptr_t)fp); 2491 else 2492 bzero(&file, sizeof (file)); 2493 2494 return (wsp->walk_callback(fp, &file, wsp->walk_cbdata)); 2495 } 2496 2497 void 2498 file_walk_fini(mdb_walk_state_t *wsp) 2499 { 2500 file_walk_data_t *fw = (file_walk_data_t *)wsp->walk_data; 2501 2502 mdb_free(fw->fw_flist, fw->fw_flistsz); 2503 mdb_free(fw, sizeof (file_walk_data_t)); 2504 } 2505 2506 int 2507 port_walk_init(mdb_walk_state_t *wsp) 2508 { 2509 if (wsp->walk_addr == NULL) { 2510 mdb_warn("port walk doesn't support global walks\n"); 2511 return (WALK_ERR); 2512 } 2513 2514 if (mdb_layered_walk("file", wsp) == -1) { 2515 mdb_warn("couldn't walk 'file'"); 2516 return (WALK_ERR); 2517 } 2518 return (WALK_NEXT); 2519 } 2520 2521 int 2522 port_walk_step(mdb_walk_state_t *wsp) 2523 { 2524 struct vnode vn; 2525 uintptr_t vp; 2526 uintptr_t pp; 2527 struct port port; 2528 2529 vp = (uintptr_t)((struct file *)wsp->walk_layer)->f_vnode; 2530 if (mdb_vread(&vn, sizeof (vn), vp) == -1) { 2531 mdb_warn("failed to read vnode_t at %p", vp); 2532 return (WALK_ERR); 2533 } 2534 if (vn.v_type != VPORT) 2535 return (WALK_NEXT); 2536 2537 pp = (uintptr_t)vn.v_data; 2538 if (mdb_vread(&port, sizeof (port), pp) == -1) { 2539 mdb_warn("failed to read port_t at %p", pp); 2540 return (WALK_ERR); 2541 } 2542 return (wsp->walk_callback(pp, &port, wsp->walk_cbdata)); 2543 } 2544 2545 typedef struct portev_walk_data { 2546 list_node_t *pev_node; 2547 list_node_t *pev_last; 2548 size_t pev_offset; 2549 } portev_walk_data_t; 2550 2551 int 2552 portev_walk_init(mdb_walk_state_t *wsp) 2553 { 2554 portev_walk_data_t *pevd; 2555 struct port port; 2556 struct vnode vn; 2557 struct list *list; 2558 uintptr_t vp; 2559 2560 if (wsp->walk_addr == NULL) { 2561 mdb_warn("portev walk doesn't support global walks\n"); 2562 return (WALK_ERR); 2563 } 2564 2565 pevd = mdb_alloc(sizeof (portev_walk_data_t), UM_SLEEP); 2566 2567 if (mdb_vread(&port, sizeof (port), wsp->walk_addr) == -1) { 2568 mdb_free(pevd, sizeof (portev_walk_data_t)); 2569 mdb_warn("failed to read port structure at %p", wsp->walk_addr); 2570 return (WALK_ERR); 2571 } 2572 2573 vp = (uintptr_t)port.port_vnode; 2574 if (mdb_vread(&vn, sizeof (vn), vp) == -1) { 2575 mdb_free(pevd, sizeof (portev_walk_data_t)); 2576 mdb_warn("failed to read vnode_t at %p", vp); 2577 return (WALK_ERR); 2578 } 2579 2580 if (vn.v_type != VPORT) { 2581 mdb_free(pevd, sizeof (portev_walk_data_t)); 2582 mdb_warn("input address (%p) does not point to an event port", 2583 wsp->walk_addr); 2584 return (WALK_ERR); 2585 } 2586 2587 if (port.port_queue.portq_nent == 0) { 2588 mdb_free(pevd, sizeof (portev_walk_data_t)); 2589 return (WALK_DONE); 2590 } 2591 list = &port.port_queue.portq_list; 2592 pevd->pev_offset = list->list_offset; 2593 pevd->pev_last = list->list_head.list_prev; 2594 pevd->pev_node = list->list_head.list_next; 2595 wsp->walk_data = pevd; 2596 return (WALK_NEXT); 2597 } 2598 2599 int 2600 portev_walk_step(mdb_walk_state_t *wsp) 2601 { 2602 portev_walk_data_t *pevd; 2603 struct port_kevent ev; 2604 uintptr_t evp; 2605 2606 pevd = (portev_walk_data_t *)wsp->walk_data; 2607 2608 if (pevd->pev_last == NULL) 2609 return (WALK_DONE); 2610 if (pevd->pev_node == pevd->pev_last) 2611 pevd->pev_last = NULL; /* last round */ 2612 2613 evp = ((uintptr_t)(((char *)pevd->pev_node) - pevd->pev_offset)); 2614 if (mdb_vread(&ev, sizeof (ev), evp) == -1) { 2615 mdb_warn("failed to read port_kevent at %p", evp); 2616 return (WALK_DONE); 2617 } 2618 pevd->pev_node = ev.portkev_node.list_next; 2619 return (wsp->walk_callback(evp, &ev, wsp->walk_cbdata)); 2620 } 2621 2622 void 2623 portev_walk_fini(mdb_walk_state_t *wsp) 2624 { 2625 portev_walk_data_t *pevd = (portev_walk_data_t *)wsp->walk_data; 2626 2627 if (pevd != NULL) 2628 mdb_free(pevd, sizeof (portev_walk_data_t)); 2629 } 2630 2631 typedef struct proc_walk_data { 2632 uintptr_t *pw_stack; 2633 int pw_depth; 2634 int pw_max; 2635 } proc_walk_data_t; 2636 2637 int 2638 proc_walk_init(mdb_walk_state_t *wsp) 2639 { 2640 GElf_Sym sym; 2641 proc_walk_data_t *pw; 2642 2643 if (wsp->walk_addr == NULL) { 2644 if (mdb_lookup_by_name("p0", &sym) == -1) { 2645 mdb_warn("failed to read 'practive'"); 2646 return (WALK_ERR); 2647 } 2648 wsp->walk_addr = (uintptr_t)sym.st_value; 2649 } 2650 2651 pw = mdb_zalloc(sizeof (proc_walk_data_t), UM_SLEEP); 2652 2653 if (mdb_readvar(&pw->pw_max, "nproc") == -1) { 2654 mdb_warn("failed to read 'nproc'"); 2655 mdb_free(pw, sizeof (pw)); 2656 return (WALK_ERR); 2657 } 2658 2659 pw->pw_stack = mdb_alloc(pw->pw_max * sizeof (uintptr_t), UM_SLEEP); 2660 wsp->walk_data = pw; 2661 2662 return (WALK_NEXT); 2663 } 2664 2665 int 2666 proc_walk_step(mdb_walk_state_t *wsp) 2667 { 2668 proc_walk_data_t *pw = wsp->walk_data; 2669 uintptr_t addr = wsp->walk_addr; 2670 uintptr_t cld, sib; 2671 2672 int status; 2673 proc_t pr; 2674 2675 if (mdb_vread(&pr, sizeof (proc_t), addr) == -1) { 2676 mdb_warn("failed to read proc at %p", addr); 2677 return (WALK_DONE); 2678 } 2679 2680 cld = (uintptr_t)pr.p_child; 2681 sib = (uintptr_t)pr.p_sibling; 2682 2683 if (pw->pw_depth > 0 && addr == pw->pw_stack[pw->pw_depth - 1]) { 2684 pw->pw_depth--; 2685 goto sib; 2686 } 2687 2688 status = wsp->walk_callback(addr, &pr, wsp->walk_cbdata); 2689 2690 if (status != WALK_NEXT) 2691 return (status); 2692 2693 if ((wsp->walk_addr = cld) != NULL) { 2694 if (mdb_vread(&pr, sizeof (proc_t), cld) == -1) { 2695 mdb_warn("proc %p has invalid p_child %p; skipping\n", 2696 addr, cld); 2697 goto sib; 2698 } 2699 2700 pw->pw_stack[pw->pw_depth++] = addr; 2701 2702 if (pw->pw_depth == pw->pw_max) { 2703 mdb_warn("depth %d exceeds max depth; try again\n", 2704 pw->pw_depth); 2705 return (WALK_DONE); 2706 } 2707 return (WALK_NEXT); 2708 } 2709 2710 sib: 2711 /* 2712 * We know that p0 has no siblings, and if another starting proc 2713 * was given, we don't want to walk its siblings anyway. 2714 */ 2715 if (pw->pw_depth == 0) 2716 return (WALK_DONE); 2717 2718 if (sib != NULL && mdb_vread(&pr, sizeof (proc_t), sib) == -1) { 2719 mdb_warn("proc %p has invalid p_sibling %p; skipping\n", 2720 addr, sib); 2721 sib = NULL; 2722 } 2723 2724 if ((wsp->walk_addr = sib) == NULL) { 2725 if (pw->pw_depth > 0) { 2726 wsp->walk_addr = pw->pw_stack[pw->pw_depth - 1]; 2727 return (WALK_NEXT); 2728 } 2729 return (WALK_DONE); 2730 } 2731 2732 return (WALK_NEXT); 2733 } 2734 2735 void 2736 proc_walk_fini(mdb_walk_state_t *wsp) 2737 { 2738 proc_walk_data_t *pw = wsp->walk_data; 2739 2740 mdb_free(pw->pw_stack, pw->pw_max * sizeof (uintptr_t)); 2741 mdb_free(pw, sizeof (proc_walk_data_t)); 2742 } 2743 2744 int 2745 task_walk_init(mdb_walk_state_t *wsp) 2746 { 2747 task_t task; 2748 2749 if (mdb_vread(&task, sizeof (task_t), wsp->walk_addr) == -1) { 2750 mdb_warn("failed to read task at %p", wsp->walk_addr); 2751 return (WALK_ERR); 2752 } 2753 wsp->walk_addr = (uintptr_t)task.tk_memb_list; 2754 wsp->walk_data = task.tk_memb_list; 2755 return (WALK_NEXT); 2756 } 2757 2758 int 2759 task_walk_step(mdb_walk_state_t *wsp) 2760 { 2761 proc_t proc; 2762 int status; 2763 2764 if (mdb_vread(&proc, sizeof (proc_t), wsp->walk_addr) == -1) { 2765 mdb_warn("failed to read proc at %p", wsp->walk_addr); 2766 return (WALK_DONE); 2767 } 2768 2769 status = wsp->walk_callback(wsp->walk_addr, NULL, wsp->walk_cbdata); 2770 2771 if (proc.p_tasknext == wsp->walk_data) 2772 return (WALK_DONE); 2773 2774 wsp->walk_addr = (uintptr_t)proc.p_tasknext; 2775 return (status); 2776 } 2777 2778 int 2779 project_walk_init(mdb_walk_state_t *wsp) 2780 { 2781 if (wsp->walk_addr == NULL) { 2782 if (mdb_readvar(&wsp->walk_addr, "proj0p") == -1) { 2783 mdb_warn("failed to read 'proj0p'"); 2784 return (WALK_ERR); 2785 } 2786 } 2787 wsp->walk_data = (void *)wsp->walk_addr; 2788 return (WALK_NEXT); 2789 } 2790 2791 int 2792 project_walk_step(mdb_walk_state_t *wsp) 2793 { 2794 uintptr_t addr = wsp->walk_addr; 2795 kproject_t pj; 2796 int status; 2797 2798 if (mdb_vread(&pj, sizeof (kproject_t), addr) == -1) { 2799 mdb_warn("failed to read project at %p", addr); 2800 return (WALK_DONE); 2801 } 2802 status = wsp->walk_callback(addr, &pj, wsp->walk_cbdata); 2803 if (status != WALK_NEXT) 2804 return (status); 2805 wsp->walk_addr = (uintptr_t)pj.kpj_next; 2806 if ((void *)wsp->walk_addr == wsp->walk_data) 2807 return (WALK_DONE); 2808 return (WALK_NEXT); 2809 } 2810 2811 static int 2812 generic_walk_step(mdb_walk_state_t *wsp) 2813 { 2814 return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer, 2815 wsp->walk_cbdata)); 2816 } 2817 2818 static int 2819 cpu_walk_cmp(const void *l, const void *r) 2820 { 2821 uintptr_t lhs = *((uintptr_t *)l); 2822 uintptr_t rhs = *((uintptr_t *)r); 2823 cpu_t lcpu, rcpu; 2824 2825 (void) mdb_vread(&lcpu, sizeof (lcpu), lhs); 2826 (void) mdb_vread(&rcpu, sizeof (rcpu), rhs); 2827 2828 if (lcpu.cpu_id < rcpu.cpu_id) 2829 return (-1); 2830 2831 if (lcpu.cpu_id > rcpu.cpu_id) 2832 return (1); 2833 2834 return (0); 2835 } 2836 2837 typedef struct cpu_walk { 2838 uintptr_t *cw_array; 2839 int cw_ndx; 2840 } cpu_walk_t; 2841 2842 int 2843 cpu_walk_init(mdb_walk_state_t *wsp) 2844 { 2845 cpu_walk_t *cw; 2846 int max_ncpus, i = 0; 2847 uintptr_t current, first; 2848 cpu_t cpu, panic_cpu; 2849 uintptr_t panicstr, addr; 2850 GElf_Sym sym; 2851 2852 cw = mdb_zalloc(sizeof (cpu_walk_t), UM_SLEEP | UM_GC); 2853 2854 if (mdb_readvar(&max_ncpus, "max_ncpus") == -1) { 2855 mdb_warn("failed to read 'max_ncpus'"); 2856 return (WALK_ERR); 2857 } 2858 2859 if (mdb_readvar(&panicstr, "panicstr") == -1) { 2860 mdb_warn("failed to read 'panicstr'"); 2861 return (WALK_ERR); 2862 } 2863 2864 if (panicstr != NULL) { 2865 if (mdb_lookup_by_name("panic_cpu", &sym) == -1) { 2866 mdb_warn("failed to find 'panic_cpu'"); 2867 return (WALK_ERR); 2868 } 2869 2870 addr = (uintptr_t)sym.st_value; 2871 2872 if (mdb_vread(&panic_cpu, sizeof (cpu_t), addr) == -1) { 2873 mdb_warn("failed to read 'panic_cpu'"); 2874 return (WALK_ERR); 2875 } 2876 } 2877 2878 /* 2879 * Unfortunately, there is no platform-independent way to walk 2880 * CPUs in ID order. We therefore loop through in cpu_next order, 2881 * building an array of CPU pointers which will subsequently be 2882 * sorted. 2883 */ 2884 cw->cw_array = 2885 mdb_zalloc((max_ncpus + 1) * sizeof (uintptr_t), UM_SLEEP | UM_GC); 2886 2887 if (mdb_readvar(&first, "cpu_list") == -1) { 2888 mdb_warn("failed to read 'cpu_list'"); 2889 return (WALK_ERR); 2890 } 2891 2892 current = first; 2893 do { 2894 if (mdb_vread(&cpu, sizeof (cpu), current) == -1) { 2895 mdb_warn("failed to read cpu at %p", current); 2896 return (WALK_ERR); 2897 } 2898 2899 if (panicstr != NULL && panic_cpu.cpu_id == cpu.cpu_id) { 2900 cw->cw_array[i++] = addr; 2901 } else { 2902 cw->cw_array[i++] = current; 2903 } 2904 } while ((current = (uintptr_t)cpu.cpu_next) != first); 2905 2906 qsort(cw->cw_array, i, sizeof (uintptr_t), cpu_walk_cmp); 2907 wsp->walk_data = cw; 2908 2909 return (WALK_NEXT); 2910 } 2911 2912 int 2913 cpu_walk_step(mdb_walk_state_t *wsp) 2914 { 2915 cpu_walk_t *cw = wsp->walk_data; 2916 cpu_t cpu; 2917 uintptr_t addr = cw->cw_array[cw->cw_ndx++]; 2918 2919 if (addr == NULL) 2920 return (WALK_DONE); 2921 2922 if (mdb_vread(&cpu, sizeof (cpu), addr) == -1) { 2923 mdb_warn("failed to read cpu at %p", addr); 2924 return (WALK_DONE); 2925 } 2926 2927 return (wsp->walk_callback(addr, &cpu, wsp->walk_cbdata)); 2928 } 2929 2930 typedef struct cpuinfo_data { 2931 intptr_t cid_cpu; 2932 uintptr_t **cid_ithr; 2933 char cid_print_head; 2934 char cid_print_thr; 2935 char cid_print_ithr; 2936 char cid_print_flags; 2937 } cpuinfo_data_t; 2938 2939 int 2940 cpuinfo_walk_ithread(uintptr_t addr, const kthread_t *thr, cpuinfo_data_t *cid) 2941 { 2942 cpu_t c; 2943 int id; 2944 uint8_t pil; 2945 2946 if (!(thr->t_flag & T_INTR_THREAD) || thr->t_state == TS_FREE) 2947 return (WALK_NEXT); 2948 2949 if (thr->t_bound_cpu == NULL) { 2950 mdb_warn("thr %p is intr thread w/out a CPU\n", addr); 2951 return (WALK_NEXT); 2952 } 2953 2954 (void) mdb_vread(&c, sizeof (c), (uintptr_t)thr->t_bound_cpu); 2955 2956 if ((id = c.cpu_id) >= NCPU) { 2957 mdb_warn("CPU %p has id (%d) greater than NCPU (%d)\n", 2958 thr->t_bound_cpu, id, NCPU); 2959 return (WALK_NEXT); 2960 } 2961 2962 if ((pil = thr->t_pil) >= NINTR) { 2963 mdb_warn("thread %p has pil (%d) greater than %d\n", 2964 addr, pil, NINTR); 2965 return (WALK_NEXT); 2966 } 2967 2968 if (cid->cid_ithr[id][pil] != NULL) { 2969 mdb_warn("CPU %d has multiple threads at pil %d (at least " 2970 "%p and %p)\n", id, pil, addr, cid->cid_ithr[id][pil]); 2971 return (WALK_NEXT); 2972 } 2973 2974 cid->cid_ithr[id][pil] = addr; 2975 2976 return (WALK_NEXT); 2977 } 2978 2979 #define CPUINFO_IDWIDTH 3 2980 #define CPUINFO_FLAGWIDTH 9 2981 2982 #ifdef _LP64 2983 #if defined(__amd64) 2984 #define CPUINFO_TWIDTH 16 2985 #define CPUINFO_CPUWIDTH 16 2986 #else 2987 #define CPUINFO_CPUWIDTH 11 2988 #define CPUINFO_TWIDTH 11 2989 #endif 2990 #else 2991 #define CPUINFO_CPUWIDTH 8 2992 #define CPUINFO_TWIDTH 8 2993 #endif 2994 2995 #define CPUINFO_THRDELT (CPUINFO_IDWIDTH + CPUINFO_CPUWIDTH + 9) 2996 #define CPUINFO_FLAGDELT (CPUINFO_IDWIDTH + CPUINFO_CPUWIDTH + 4) 2997 #define CPUINFO_ITHRDELT 4 2998 2999 #define CPUINFO_INDENT mdb_printf("%*s", CPUINFO_THRDELT, \ 3000 flagline < nflaglines ? flagbuf[flagline++] : "") 3001 3002 int 3003 cpuinfo_walk_cpu(uintptr_t addr, const cpu_t *cpu, cpuinfo_data_t *cid) 3004 { 3005 kthread_t t; 3006 disp_t disp; 3007 proc_t p; 3008 uintptr_t pinned; 3009 char **flagbuf; 3010 int nflaglines = 0, flagline = 0, bspl, rval = WALK_NEXT; 3011 3012 const char *flags[] = { 3013 "RUNNING", "READY", "QUIESCED", "EXISTS", 3014 "ENABLE", "OFFLINE", "POWEROFF", "FROZEN", 3015 "SPARE", "FAULTED", NULL 3016 }; 3017 3018 if (cid->cid_cpu != -1) { 3019 if (addr != cid->cid_cpu && cpu->cpu_id != cid->cid_cpu) 3020 return (WALK_NEXT); 3021 3022 /* 3023 * Set cid_cpu to -1 to indicate that we found a matching CPU. 3024 */ 3025 cid->cid_cpu = -1; 3026 rval = WALK_DONE; 3027 } 3028 3029 if (cid->cid_print_head) { 3030 mdb_printf("%3s %-*s %3s %4s %4s %3s %4s %5s %-6s %-*s %s\n", 3031 "ID", CPUINFO_CPUWIDTH, "ADDR", "FLG", "NRUN", "BSPL", 3032 "PRI", "RNRN", "KRNRN", "SWITCH", CPUINFO_TWIDTH, "THREAD", 3033 "PROC"); 3034 cid->cid_print_head = FALSE; 3035 } 3036 3037 bspl = cpu->cpu_base_spl; 3038 3039 if (mdb_vread(&disp, sizeof (disp_t), (uintptr_t)cpu->cpu_disp) == -1) { 3040 mdb_warn("failed to read disp_t at %p", cpu->cpu_disp); 3041 return (WALK_ERR); 3042 } 3043 3044 mdb_printf("%3d %0*p %3x %4d %4d ", 3045 cpu->cpu_id, CPUINFO_CPUWIDTH, addr, cpu->cpu_flags, 3046 disp.disp_nrunnable, bspl); 3047 3048 if (mdb_vread(&t, sizeof (t), (uintptr_t)cpu->cpu_thread) != -1) { 3049 mdb_printf("%3d ", t.t_pri); 3050 } else { 3051 mdb_printf("%3s ", "-"); 3052 } 3053 3054 mdb_printf("%4s %5s ", cpu->cpu_runrun ? "yes" : "no", 3055 cpu->cpu_kprunrun ? "yes" : "no"); 3056 3057 if (cpu->cpu_last_swtch) { 3058 mdb_printf("t-%-4d ", 3059 (clock_t)mdb_get_lbolt() - cpu->cpu_last_swtch); 3060 } else { 3061 mdb_printf("%-6s ", "-"); 3062 } 3063 3064 mdb_printf("%0*p", CPUINFO_TWIDTH, cpu->cpu_thread); 3065 3066 if (cpu->cpu_thread == cpu->cpu_idle_thread) 3067 mdb_printf(" (idle)\n"); 3068 else if (cpu->cpu_thread == NULL) 3069 mdb_printf(" -\n"); 3070 else { 3071 if (mdb_vread(&p, sizeof (p), (uintptr_t)t.t_procp) != -1) { 3072 mdb_printf(" %s\n", p.p_user.u_comm); 3073 } else { 3074 mdb_printf(" ?\n"); 3075 } 3076 } 3077 3078 flagbuf = mdb_zalloc(sizeof (flags), UM_SLEEP | UM_GC); 3079 3080 if (cid->cid_print_flags) { 3081 int first = 1, i, j, k; 3082 char *s; 3083 3084 cid->cid_print_head = TRUE; 3085 3086 for (i = 1, j = 0; flags[j] != NULL; i <<= 1, j++) { 3087 if (!(cpu->cpu_flags & i)) 3088 continue; 3089 3090 if (first) { 3091 s = mdb_alloc(CPUINFO_THRDELT + 1, 3092 UM_GC | UM_SLEEP); 3093 3094 (void) mdb_snprintf(s, CPUINFO_THRDELT + 1, 3095 "%*s|%*s", CPUINFO_FLAGDELT, "", 3096 CPUINFO_THRDELT - 1 - CPUINFO_FLAGDELT, ""); 3097 flagbuf[nflaglines++] = s; 3098 } 3099 3100 s = mdb_alloc(CPUINFO_THRDELT + 1, UM_GC | UM_SLEEP); 3101 (void) mdb_snprintf(s, CPUINFO_THRDELT + 1, "%*s%*s %s", 3102 CPUINFO_IDWIDTH + CPUINFO_CPUWIDTH - 3103 CPUINFO_FLAGWIDTH, "", CPUINFO_FLAGWIDTH, flags[j], 3104 first ? "<--+" : ""); 3105 3106 for (k = strlen(s); k < CPUINFO_THRDELT; k++) 3107 s[k] = ' '; 3108 s[k] = '\0'; 3109 3110 flagbuf[nflaglines++] = s; 3111 first = 0; 3112 } 3113 } 3114 3115 if (cid->cid_print_ithr) { 3116 int i, found_one = FALSE; 3117 int print_thr = disp.disp_nrunnable && cid->cid_print_thr; 3118 3119 for (i = NINTR - 1; i >= 0; i--) { 3120 uintptr_t iaddr = cid->cid_ithr[cpu->cpu_id][i]; 3121 3122 if (iaddr == NULL) 3123 continue; 3124 3125 if (!found_one) { 3126 found_one = TRUE; 3127 3128 CPUINFO_INDENT; 3129 mdb_printf("%c%*s|\n", print_thr ? '|' : ' ', 3130 CPUINFO_ITHRDELT, ""); 3131 3132 CPUINFO_INDENT; 3133 mdb_printf("%c%*s+--> %3s %s\n", 3134 print_thr ? '|' : ' ', CPUINFO_ITHRDELT, 3135 "", "PIL", "THREAD"); 3136 } 3137 3138 if (mdb_vread(&t, sizeof (t), iaddr) == -1) { 3139 mdb_warn("failed to read kthread_t at %p", 3140 iaddr); 3141 return (WALK_ERR); 3142 } 3143 3144 CPUINFO_INDENT; 3145 mdb_printf("%c%*s %3d %0*p\n", 3146 print_thr ? '|' : ' ', CPUINFO_ITHRDELT, "", 3147 t.t_pil, CPUINFO_TWIDTH, iaddr); 3148 3149 pinned = (uintptr_t)t.t_intr; 3150 } 3151 3152 if (found_one && pinned != NULL) { 3153 cid->cid_print_head = TRUE; 3154 (void) strcpy(p.p_user.u_comm, "?"); 3155 3156 if (mdb_vread(&t, sizeof (t), 3157 (uintptr_t)pinned) == -1) { 3158 mdb_warn("failed to read kthread_t at %p", 3159 pinned); 3160 return (WALK_ERR); 3161 } 3162 if (mdb_vread(&p, sizeof (p), 3163 (uintptr_t)t.t_procp) == -1) { 3164 mdb_warn("failed to read proc_t at %p", 3165 t.t_procp); 3166 return (WALK_ERR); 3167 } 3168 3169 CPUINFO_INDENT; 3170 mdb_printf("%c%*s %3s %0*p %s\n", 3171 print_thr ? '|' : ' ', CPUINFO_ITHRDELT, "", "-", 3172 CPUINFO_TWIDTH, pinned, 3173 pinned == (uintptr_t)cpu->cpu_idle_thread ? 3174 "(idle)" : p.p_user.u_comm); 3175 } 3176 } 3177 3178 if (disp.disp_nrunnable && cid->cid_print_thr) { 3179 dispq_t *dq; 3180 3181 int i, npri = disp.disp_npri; 3182 3183 dq = mdb_alloc(sizeof (dispq_t) * npri, UM_SLEEP | UM_GC); 3184 3185 if (mdb_vread(dq, sizeof (dispq_t) * npri, 3186 (uintptr_t)disp.disp_q) == -1) { 3187 mdb_warn("failed to read dispq_t at %p", disp.disp_q); 3188 return (WALK_ERR); 3189 } 3190 3191 CPUINFO_INDENT; 3192 mdb_printf("|\n"); 3193 3194 CPUINFO_INDENT; 3195 mdb_printf("+--> %3s %-*s %s\n", "PRI", 3196 CPUINFO_TWIDTH, "THREAD", "PROC"); 3197 3198 for (i = npri - 1; i >= 0; i--) { 3199 uintptr_t taddr = (uintptr_t)dq[i].dq_first; 3200 3201 while (taddr != NULL) { 3202 if (mdb_vread(&t, sizeof (t), taddr) == -1) { 3203 mdb_warn("failed to read kthread_t " 3204 "at %p", taddr); 3205 return (WALK_ERR); 3206 } 3207 if (mdb_vread(&p, sizeof (p), 3208 (uintptr_t)t.t_procp) == -1) { 3209 mdb_warn("failed to read proc_t at %p", 3210 t.t_procp); 3211 return (WALK_ERR); 3212 } 3213 3214 CPUINFO_INDENT; 3215 mdb_printf(" %3d %0*p %s\n", t.t_pri, 3216 CPUINFO_TWIDTH, taddr, p.p_user.u_comm); 3217 3218 taddr = (uintptr_t)t.t_link; 3219 } 3220 } 3221 cid->cid_print_head = TRUE; 3222 } 3223 3224 while (flagline < nflaglines) 3225 mdb_printf("%s\n", flagbuf[flagline++]); 3226 3227 if (cid->cid_print_head) 3228 mdb_printf("\n"); 3229 3230 return (rval); 3231 } 3232 3233 int 3234 cpuinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3235 { 3236 uint_t verbose = FALSE; 3237 cpuinfo_data_t cid; 3238 3239 cid.cid_print_ithr = FALSE; 3240 cid.cid_print_thr = FALSE; 3241 cid.cid_print_flags = FALSE; 3242 cid.cid_print_head = DCMD_HDRSPEC(flags) ? TRUE : FALSE; 3243 cid.cid_cpu = -1; 3244 3245 if (flags & DCMD_ADDRSPEC) 3246 cid.cid_cpu = addr; 3247 3248 if (mdb_getopts(argc, argv, 3249 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL) != argc) 3250 return (DCMD_USAGE); 3251 3252 if (verbose) { 3253 cid.cid_print_ithr = TRUE; 3254 cid.cid_print_thr = TRUE; 3255 cid.cid_print_flags = TRUE; 3256 cid.cid_print_head = TRUE; 3257 } 3258 3259 if (cid.cid_print_ithr) { 3260 int i; 3261 3262 cid.cid_ithr = mdb_alloc(sizeof (uintptr_t **) 3263 * NCPU, UM_SLEEP | UM_GC); 3264 3265 for (i = 0; i < NCPU; i++) 3266 cid.cid_ithr[i] = mdb_zalloc(sizeof (uintptr_t *) * 3267 NINTR, UM_SLEEP | UM_GC); 3268 3269 if (mdb_walk("thread", (mdb_walk_cb_t)cpuinfo_walk_ithread, 3270 &cid) == -1) { 3271 mdb_warn("couldn't walk thread"); 3272 return (DCMD_ERR); 3273 } 3274 } 3275 3276 if (mdb_walk("cpu", (mdb_walk_cb_t)cpuinfo_walk_cpu, &cid) == -1) { 3277 mdb_warn("can't walk cpus"); 3278 return (DCMD_ERR); 3279 } 3280 3281 if (cid.cid_cpu != -1) { 3282 /* 3283 * We didn't find this CPU when we walked through the CPUs 3284 * (i.e. the address specified doesn't show up in the "cpu" 3285 * walk). However, the specified address may still correspond 3286 * to a valid cpu_t (for example, if the specified address is 3287 * the actual panicking cpu_t and not the cached panic_cpu). 3288 * Point is: even if we didn't find it, we still want to try 3289 * to print the specified address as a cpu_t. 3290 */ 3291 cpu_t cpu; 3292 3293 if (mdb_vread(&cpu, sizeof (cpu), cid.cid_cpu) == -1) { 3294 mdb_warn("%p is neither a valid CPU ID nor a " 3295 "valid cpu_t address\n", cid.cid_cpu); 3296 return (DCMD_ERR); 3297 } 3298 3299 (void) cpuinfo_walk_cpu(cid.cid_cpu, &cpu, &cid); 3300 } 3301 3302 return (DCMD_OK); 3303 } 3304 3305 /*ARGSUSED*/ 3306 int 3307 flipone(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3308 { 3309 int i; 3310 3311 if (!(flags & DCMD_ADDRSPEC)) 3312 return (DCMD_USAGE); 3313 3314 for (i = 0; i < sizeof (addr) * NBBY; i++) 3315 mdb_printf("%p\n", addr ^ (1UL << i)); 3316 3317 return (DCMD_OK); 3318 } 3319 3320 int 3321 as2proc_walk(uintptr_t addr, const proc_t *p, struct as **asp) 3322 { 3323 if (p->p_as == *asp) 3324 mdb_printf("%p\n", addr); 3325 return (WALK_NEXT); 3326 } 3327 3328 /*ARGSUSED*/ 3329 int 3330 as2proc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3331 { 3332 if (!(flags & DCMD_ADDRSPEC) || argc != 0) 3333 return (DCMD_USAGE); 3334 3335 if (mdb_walk("proc", (mdb_walk_cb_t)as2proc_walk, &addr) == -1) { 3336 mdb_warn("failed to walk proc"); 3337 return (DCMD_ERR); 3338 } 3339 3340 return (DCMD_OK); 3341 } 3342 3343 /*ARGSUSED*/ 3344 int 3345 ptree_walk(uintptr_t addr, const proc_t *p, void *ignored) 3346 { 3347 proc_t parent; 3348 int ident = 0; 3349 uintptr_t paddr; 3350 3351 for (paddr = (uintptr_t)p->p_parent; paddr != NULL; ident += 5) { 3352 mdb_vread(&parent, sizeof (parent), paddr); 3353 paddr = (uintptr_t)parent.p_parent; 3354 } 3355 3356 mdb_inc_indent(ident); 3357 mdb_printf("%0?p %s\n", addr, p->p_user.u_comm); 3358 mdb_dec_indent(ident); 3359 3360 return (WALK_NEXT); 3361 } 3362 3363 void 3364 ptree_ancestors(uintptr_t addr, uintptr_t start) 3365 { 3366 proc_t p; 3367 3368 if (mdb_vread(&p, sizeof (p), addr) == -1) { 3369 mdb_warn("couldn't read ancestor at %p", addr); 3370 return; 3371 } 3372 3373 if (p.p_parent != NULL) 3374 ptree_ancestors((uintptr_t)p.p_parent, start); 3375 3376 if (addr != start) 3377 (void) ptree_walk(addr, &p, NULL); 3378 } 3379 3380 /*ARGSUSED*/ 3381 int 3382 ptree(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3383 { 3384 if (!(flags & DCMD_ADDRSPEC)) 3385 addr = NULL; 3386 else 3387 ptree_ancestors(addr, addr); 3388 3389 if (mdb_pwalk("proc", (mdb_walk_cb_t)ptree_walk, NULL, addr) == -1) { 3390 mdb_warn("couldn't walk 'proc'"); 3391 return (DCMD_ERR); 3392 } 3393 3394 return (DCMD_OK); 3395 } 3396 3397 /*ARGSUSED*/ 3398 static int 3399 fd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3400 { 3401 int fdnum; 3402 const mdb_arg_t *argp = &argv[0]; 3403 proc_t p; 3404 uf_entry_t uf; 3405 3406 if ((flags & DCMD_ADDRSPEC) == 0) { 3407 mdb_warn("fd doesn't give global information\n"); 3408 return (DCMD_ERR); 3409 } 3410 if (argc != 1) 3411 return (DCMD_USAGE); 3412 3413 if (argp->a_type == MDB_TYPE_IMMEDIATE) 3414 fdnum = argp->a_un.a_val; 3415 else 3416 fdnum = mdb_strtoull(argp->a_un.a_str); 3417 3418 if (mdb_vread(&p, sizeof (struct proc), addr) == -1) { 3419 mdb_warn("couldn't read proc_t at %p", addr); 3420 return (DCMD_ERR); 3421 } 3422 if (fdnum > p.p_user.u_finfo.fi_nfiles) { 3423 mdb_warn("process %p only has %d files open.\n", 3424 addr, p.p_user.u_finfo.fi_nfiles); 3425 return (DCMD_ERR); 3426 } 3427 if (mdb_vread(&uf, sizeof (uf_entry_t), 3428 (uintptr_t)&p.p_user.u_finfo.fi_list[fdnum]) == -1) { 3429 mdb_warn("couldn't read uf_entry_t at %p", 3430 &p.p_user.u_finfo.fi_list[fdnum]); 3431 return (DCMD_ERR); 3432 } 3433 3434 mdb_printf("%p\n", uf.uf_file); 3435 return (DCMD_OK); 3436 } 3437 3438 /*ARGSUSED*/ 3439 static int 3440 pid2proc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3441 { 3442 pid_t pid = (pid_t)addr; 3443 3444 if (argc != 0) 3445 return (DCMD_USAGE); 3446 3447 if ((addr = mdb_pid2proc(pid, NULL)) == NULL) { 3448 mdb_warn("PID 0t%d not found\n", pid); 3449 return (DCMD_ERR); 3450 } 3451 3452 mdb_printf("%p\n", addr); 3453 return (DCMD_OK); 3454 } 3455 3456 static char *sysfile_cmd[] = { 3457 "exclude:", 3458 "include:", 3459 "forceload:", 3460 "rootdev:", 3461 "rootfs:", 3462 "swapdev:", 3463 "swapfs:", 3464 "moddir:", 3465 "set", 3466 "unknown", 3467 }; 3468 3469 static char *sysfile_ops[] = { "", "=", "&", "|" }; 3470 3471 /*ARGSUSED*/ 3472 static int 3473 sysfile_vmem_seg(uintptr_t addr, const vmem_seg_t *vsp, void **target) 3474 { 3475 if (vsp->vs_type == VMEM_ALLOC && (void *)vsp->vs_start == *target) { 3476 *target = NULL; 3477 return (WALK_DONE); 3478 } 3479 return (WALK_NEXT); 3480 } 3481 3482 /*ARGSUSED*/ 3483 static int 3484 sysfile(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3485 { 3486 struct sysparam *sysp, sys; 3487 char var[256]; 3488 char modname[256]; 3489 char val[256]; 3490 char strval[256]; 3491 vmem_t *mod_sysfile_arena; 3492 void *straddr; 3493 3494 if (mdb_readvar(&sysp, "sysparam_hd") == -1) { 3495 mdb_warn("failed to read sysparam_hd"); 3496 return (DCMD_ERR); 3497 } 3498 3499 if (mdb_readvar(&mod_sysfile_arena, "mod_sysfile_arena") == -1) { 3500 mdb_warn("failed to read mod_sysfile_arena"); 3501 return (DCMD_ERR); 3502 } 3503 3504 while (sysp != NULL) { 3505 var[0] = '\0'; 3506 val[0] = '\0'; 3507 modname[0] = '\0'; 3508 if (mdb_vread(&sys, sizeof (sys), (uintptr_t)sysp) == -1) { 3509 mdb_warn("couldn't read sysparam %p", sysp); 3510 return (DCMD_ERR); 3511 } 3512 if (sys.sys_modnam != NULL && 3513 mdb_readstr(modname, 256, 3514 (uintptr_t)sys.sys_modnam) == -1) { 3515 mdb_warn("couldn't read modname in %p", sysp); 3516 return (DCMD_ERR); 3517 } 3518 if (sys.sys_ptr != NULL && 3519 mdb_readstr(var, 256, (uintptr_t)sys.sys_ptr) == -1) { 3520 mdb_warn("couldn't read ptr in %p", sysp); 3521 return (DCMD_ERR); 3522 } 3523 if (sys.sys_op != SETOP_NONE) { 3524 /* 3525 * Is this an int or a string? We determine this 3526 * by checking whether straddr is contained in 3527 * mod_sysfile_arena. If so, the walker will set 3528 * straddr to NULL. 3529 */ 3530 straddr = (void *)(uintptr_t)sys.sys_info; 3531 if (sys.sys_op == SETOP_ASSIGN && 3532 sys.sys_info != 0 && 3533 mdb_pwalk("vmem_seg", 3534 (mdb_walk_cb_t)sysfile_vmem_seg, &straddr, 3535 (uintptr_t)mod_sysfile_arena) == 0 && 3536 straddr == NULL && 3537 mdb_readstr(strval, 256, 3538 (uintptr_t)sys.sys_info) != -1) { 3539 (void) mdb_snprintf(val, sizeof (val), "\"%s\"", 3540 strval); 3541 } else { 3542 (void) mdb_snprintf(val, sizeof (val), 3543 "0x%llx [0t%llu]", sys.sys_info, 3544 sys.sys_info); 3545 } 3546 } 3547 mdb_printf("%s %s%s%s%s%s\n", sysfile_cmd[sys.sys_type], 3548 modname, modname[0] == '\0' ? "" : ":", 3549 var, sysfile_ops[sys.sys_op], val); 3550 3551 sysp = sys.sys_next; 3552 } 3553 3554 return (DCMD_OK); 3555 } 3556 3557 int 3558 didmatch(uintptr_t addr, const kthread_t *thr, kt_did_t *didp) 3559 { 3560 3561 if (*didp == thr->t_did) { 3562 mdb_printf("%p\n", addr); 3563 return (WALK_DONE); 3564 } else 3565 return (WALK_NEXT); 3566 } 3567 3568 /*ARGSUSED*/ 3569 int 3570 did2thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3571 { 3572 const mdb_arg_t *argp = &argv[0]; 3573 kt_did_t did; 3574 3575 if (argc != 1) 3576 return (DCMD_USAGE); 3577 3578 did = (kt_did_t)mdb_strtoull(argp->a_un.a_str); 3579 3580 if (mdb_walk("thread", (mdb_walk_cb_t)didmatch, (void *)&did) == -1) { 3581 mdb_warn("failed to walk thread"); 3582 return (DCMD_ERR); 3583 3584 } 3585 return (DCMD_OK); 3586 3587 } 3588 3589 static int 3590 errorq_walk_init(mdb_walk_state_t *wsp) 3591 { 3592 if (wsp->walk_addr == NULL && 3593 mdb_readvar(&wsp->walk_addr, "errorq_list") == -1) { 3594 mdb_warn("failed to read errorq_list"); 3595 return (WALK_ERR); 3596 } 3597 3598 return (WALK_NEXT); 3599 } 3600 3601 static int 3602 errorq_walk_step(mdb_walk_state_t *wsp) 3603 { 3604 uintptr_t addr = wsp->walk_addr; 3605 errorq_t eq; 3606 3607 if (addr == NULL) 3608 return (WALK_DONE); 3609 3610 if (mdb_vread(&eq, sizeof (eq), addr) == -1) { 3611 mdb_warn("failed to read errorq at %p", addr); 3612 return (WALK_ERR); 3613 } 3614 3615 wsp->walk_addr = (uintptr_t)eq.eq_next; 3616 return (wsp->walk_callback(addr, &eq, wsp->walk_cbdata)); 3617 } 3618 3619 typedef struct eqd_walk_data { 3620 uintptr_t *eqd_stack; 3621 void *eqd_buf; 3622 ulong_t eqd_qpos; 3623 ulong_t eqd_qlen; 3624 size_t eqd_size; 3625 } eqd_walk_data_t; 3626 3627 /* 3628 * In order to walk the list of pending error queue elements, we push the 3629 * addresses of the corresponding data buffers in to the eqd_stack array. 3630 * The error lists are in reverse chronological order when iterating using 3631 * eqe_prev, so we then pop things off the top in eqd_walk_step so that the 3632 * walker client gets addresses in order from oldest error to newest error. 3633 */ 3634 static void 3635 eqd_push_list(eqd_walk_data_t *eqdp, uintptr_t addr) 3636 { 3637 errorq_elem_t eqe; 3638 3639 while (addr != NULL) { 3640 if (mdb_vread(&eqe, sizeof (eqe), addr) != sizeof (eqe)) { 3641 mdb_warn("failed to read errorq element at %p", addr); 3642 break; 3643 } 3644 3645 if (eqdp->eqd_qpos == eqdp->eqd_qlen) { 3646 mdb_warn("errorq is overfull -- more than %lu " 3647 "elems found\n", eqdp->eqd_qlen); 3648 break; 3649 } 3650 3651 eqdp->eqd_stack[eqdp->eqd_qpos++] = (uintptr_t)eqe.eqe_data; 3652 addr = (uintptr_t)eqe.eqe_prev; 3653 } 3654 } 3655 3656 static int 3657 eqd_walk_init(mdb_walk_state_t *wsp) 3658 { 3659 eqd_walk_data_t *eqdp; 3660 errorq_elem_t eqe, *addr; 3661 errorq_t eq; 3662 ulong_t i; 3663 3664 if (mdb_vread(&eq, sizeof (eq), wsp->walk_addr) == -1) { 3665 mdb_warn("failed to read errorq at %p", wsp->walk_addr); 3666 return (WALK_ERR); 3667 } 3668 3669 if (eq.eq_ptail != NULL && 3670 mdb_vread(&eqe, sizeof (eqe), (uintptr_t)eq.eq_ptail) == -1) { 3671 mdb_warn("failed to read errorq element at %p", eq.eq_ptail); 3672 return (WALK_ERR); 3673 } 3674 3675 eqdp = mdb_alloc(sizeof (eqd_walk_data_t), UM_SLEEP); 3676 wsp->walk_data = eqdp; 3677 3678 eqdp->eqd_stack = mdb_zalloc(sizeof (uintptr_t) * eq.eq_qlen, UM_SLEEP); 3679 eqdp->eqd_buf = mdb_alloc(eq.eq_size, UM_SLEEP); 3680 eqdp->eqd_qlen = eq.eq_qlen; 3681 eqdp->eqd_qpos = 0; 3682 eqdp->eqd_size = eq.eq_size; 3683 3684 /* 3685 * The newest elements in the queue are on the pending list, so we 3686 * push those on to our stack first. 3687 */ 3688 eqd_push_list(eqdp, (uintptr_t)eq.eq_pend); 3689 3690 /* 3691 * If eq_ptail is set, it may point to a subset of the errors on the 3692 * pending list in the event a atomic_cas_ptr() failed; if ptail's 3693 * data is already in our stack, NULL out eq_ptail and ignore it. 3694 */ 3695 if (eq.eq_ptail != NULL) { 3696 for (i = 0; i < eqdp->eqd_qpos; i++) { 3697 if (eqdp->eqd_stack[i] == (uintptr_t)eqe.eqe_data) { 3698 eq.eq_ptail = NULL; 3699 break; 3700 } 3701 } 3702 } 3703 3704 /* 3705 * If eq_phead is set, it has the processing list in order from oldest 3706 * to newest. Use this to recompute eq_ptail as best we can and then 3707 * we nicely fall into eqd_push_list() of eq_ptail below. 3708 */ 3709 for (addr = eq.eq_phead; addr != NULL && mdb_vread(&eqe, sizeof (eqe), 3710 (uintptr_t)addr) == sizeof (eqe); addr = eqe.eqe_next) 3711 eq.eq_ptail = addr; 3712 3713 /* 3714 * The oldest elements in the queue are on the processing list, subject 3715 * to machinations in the if-clauses above. Push any such elements. 3716 */ 3717 eqd_push_list(eqdp, (uintptr_t)eq.eq_ptail); 3718 return (WALK_NEXT); 3719 } 3720 3721 static int 3722 eqd_walk_step(mdb_walk_state_t *wsp) 3723 { 3724 eqd_walk_data_t *eqdp = wsp->walk_data; 3725 uintptr_t addr; 3726 3727 if (eqdp->eqd_qpos == 0) 3728 return (WALK_DONE); 3729 3730 addr = eqdp->eqd_stack[--eqdp->eqd_qpos]; 3731 3732 if (mdb_vread(eqdp->eqd_buf, eqdp->eqd_size, addr) != eqdp->eqd_size) { 3733 mdb_warn("failed to read errorq data at %p", addr); 3734 return (WALK_ERR); 3735 } 3736 3737 return (wsp->walk_callback(addr, eqdp->eqd_buf, wsp->walk_cbdata)); 3738 } 3739 3740 static void 3741 eqd_walk_fini(mdb_walk_state_t *wsp) 3742 { 3743 eqd_walk_data_t *eqdp = wsp->walk_data; 3744 3745 mdb_free(eqdp->eqd_stack, sizeof (uintptr_t) * eqdp->eqd_qlen); 3746 mdb_free(eqdp->eqd_buf, eqdp->eqd_size); 3747 mdb_free(eqdp, sizeof (eqd_walk_data_t)); 3748 } 3749 3750 #define EQKSVAL(eqv, what) (eqv.eq_kstat.what.value.ui64) 3751 3752 static int 3753 errorq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3754 { 3755 int i; 3756 errorq_t eq; 3757 uint_t opt_v = FALSE; 3758 3759 if (!(flags & DCMD_ADDRSPEC)) { 3760 if (mdb_walk_dcmd("errorq", "errorq", argc, argv) == -1) { 3761 mdb_warn("can't walk 'errorq'"); 3762 return (DCMD_ERR); 3763 } 3764 return (DCMD_OK); 3765 } 3766 3767 i = mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL); 3768 argc -= i; 3769 argv += i; 3770 3771 if (argc != 0) 3772 return (DCMD_USAGE); 3773 3774 if (opt_v || DCMD_HDRSPEC(flags)) { 3775 mdb_printf("%<u>%-11s %-16s %1s %1s %1s ", 3776 "ADDR", "NAME", "S", "V", "N"); 3777 if (!opt_v) { 3778 mdb_printf("%7s %7s %7s%</u>\n", 3779 "ACCEPT", "DROP", "LOG"); 3780 } else { 3781 mdb_printf("%5s %6s %6s %3s %16s%</u>\n", 3782 "KSTAT", "QLEN", "SIZE", "IPL", "FUNC"); 3783 } 3784 } 3785 3786 if (mdb_vread(&eq, sizeof (eq), addr) != sizeof (eq)) { 3787 mdb_warn("failed to read errorq at %p", addr); 3788 return (DCMD_ERR); 3789 } 3790 3791 mdb_printf("%-11p %-16s %c %c %c ", addr, eq.eq_name, 3792 (eq.eq_flags & ERRORQ_ACTIVE) ? '+' : '-', 3793 (eq.eq_flags & ERRORQ_VITAL) ? '!' : ' ', 3794 (eq.eq_flags & ERRORQ_NVLIST) ? '*' : ' '); 3795 3796 if (!opt_v) { 3797 mdb_printf("%7llu %7llu %7llu\n", 3798 EQKSVAL(eq, eqk_dispatched) + EQKSVAL(eq, eqk_committed), 3799 EQKSVAL(eq, eqk_dropped) + EQKSVAL(eq, eqk_reserve_fail) + 3800 EQKSVAL(eq, eqk_commit_fail), EQKSVAL(eq, eqk_logged)); 3801 } else { 3802 mdb_printf("%5s %6lu %6lu %3u %a\n", 3803 " | ", eq.eq_qlen, eq.eq_size, eq.eq_ipl, eq.eq_func); 3804 mdb_printf("%38s\n%41s" 3805 "%12s %llu\n" 3806 "%53s %llu\n" 3807 "%53s %llu\n" 3808 "%53s %llu\n" 3809 "%53s %llu\n" 3810 "%53s %llu\n" 3811 "%53s %llu\n" 3812 "%53s %llu\n\n", 3813 "|", "+-> ", 3814 "DISPATCHED", EQKSVAL(eq, eqk_dispatched), 3815 "DROPPED", EQKSVAL(eq, eqk_dropped), 3816 "LOGGED", EQKSVAL(eq, eqk_logged), 3817 "RESERVED", EQKSVAL(eq, eqk_reserved), 3818 "RESERVE FAIL", EQKSVAL(eq, eqk_reserve_fail), 3819 "COMMITTED", EQKSVAL(eq, eqk_committed), 3820 "COMMIT FAIL", EQKSVAL(eq, eqk_commit_fail), 3821 "CANCELLED", EQKSVAL(eq, eqk_cancelled)); 3822 } 3823 3824 return (DCMD_OK); 3825 } 3826 3827 /*ARGSUSED*/ 3828 static int 3829 panicinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3830 { 3831 cpu_t panic_cpu; 3832 kthread_t *panic_thread; 3833 void *buf; 3834 panic_data_t *pd; 3835 int i, n; 3836 3837 if (!mdb_prop_postmortem) { 3838 mdb_warn("panicinfo can only be run on a system " 3839 "dump; see dumpadm(1M)\n"); 3840 return (DCMD_ERR); 3841 } 3842 3843 if (flags & DCMD_ADDRSPEC || argc != 0) 3844 return (DCMD_USAGE); 3845 3846 if (mdb_readsym(&panic_cpu, sizeof (cpu_t), "panic_cpu") == -1) 3847 mdb_warn("failed to read 'panic_cpu'"); 3848 else 3849 mdb_printf("%16s %?d\n", "cpu", panic_cpu.cpu_id); 3850 3851 if (mdb_readvar(&panic_thread, "panic_thread") == -1) 3852 mdb_warn("failed to read 'panic_thread'"); 3853 else 3854 mdb_printf("%16s %?p\n", "thread", panic_thread); 3855 3856 buf = mdb_alloc(PANICBUFSIZE, UM_SLEEP); 3857 pd = (panic_data_t *)buf; 3858 3859 if (mdb_readsym(buf, PANICBUFSIZE, "panicbuf") == -1 || 3860 pd->pd_version != PANICBUFVERS) { 3861 mdb_warn("failed to read 'panicbuf'"); 3862 mdb_free(buf, PANICBUFSIZE); 3863 return (DCMD_ERR); 3864 } 3865 3866 mdb_printf("%16s %s\n", "message", (char *)buf + pd->pd_msgoff); 3867 3868 n = (pd->pd_msgoff - (sizeof (panic_data_t) - 3869 sizeof (panic_nv_t))) / sizeof (panic_nv_t); 3870 3871 for (i = 0; i < n; i++) 3872 mdb_printf("%16s %?llx\n", 3873 pd->pd_nvdata[i].pnv_name, pd->pd_nvdata[i].pnv_value); 3874 3875 mdb_free(buf, PANICBUFSIZE); 3876 return (DCMD_OK); 3877 } 3878 3879 /* 3880 * ::time dcmd, which will print a hires timestamp of when we entered the 3881 * debugger, or the lbolt value if used with the -l option. 3882 * 3883 */ 3884 /*ARGSUSED*/ 3885 static int 3886 time(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3887 { 3888 uint_t opt_dec = FALSE; 3889 uint_t opt_lbolt = FALSE; 3890 uint_t opt_hex = FALSE; 3891 const char *fmt; 3892 hrtime_t result; 3893 3894 if (mdb_getopts(argc, argv, 3895 'd', MDB_OPT_SETBITS, TRUE, &opt_dec, 3896 'l', MDB_OPT_SETBITS, TRUE, &opt_lbolt, 3897 'x', MDB_OPT_SETBITS, TRUE, &opt_hex, 3898 NULL) != argc) 3899 return (DCMD_USAGE); 3900 3901 if (opt_dec && opt_hex) 3902 return (DCMD_USAGE); 3903 3904 result = opt_lbolt ? mdb_get_lbolt() : mdb_gethrtime(); 3905 fmt = 3906 opt_hex ? "0x%llx\n" : 3907 opt_dec ? "0t%lld\n" : "%#llr\n"; 3908 3909 mdb_printf(fmt, result); 3910 return (DCMD_OK); 3911 } 3912 3913 void 3914 time_help(void) 3915 { 3916 mdb_printf("Prints the system time in nanoseconds.\n\n" 3917 "::time will return the timestamp at which we dropped into, \n" 3918 "if called from, kmdb(1); the core dump's high resolution \n" 3919 "time if inspecting one; or the running hires time if we're \n" 3920 "looking at a live system.\n\n" 3921 "Switches:\n" 3922 " -d report times in decimal\n" 3923 " -l prints the number of clock ticks since system boot\n" 3924 " -x report times in hexadecimal\n"); 3925 } 3926 3927 static const mdb_dcmd_t dcmds[] = { 3928 3929 /* from genunix.c */ 3930 { "as2proc", ":", "convert as to proc_t address", as2proc }, 3931 { "binding_hash_entry", ":", "print driver names hash table entry", 3932 binding_hash_entry }, 3933 { "callout", "?[-r|n] [-s|l] [-xhB] [-t | -ab nsec [-dkD]]" 3934 " [-C addr | -S seqid] [-f name|addr] [-p name| addr] [-T|L [-E]]" 3935 " [-FivVA]", 3936 "display callouts", callout, callout_help }, 3937 { "calloutid", "[-d|v] xid", "print callout by extended id", 3938 calloutid, calloutid_help }, 3939 { "class", NULL, "print process scheduler classes", class }, 3940 { "cpuinfo", "?[-v]", "print CPUs and runnable threads", cpuinfo }, 3941 { "did2thread", "? kt_did", "find kernel thread for this id", 3942 did2thread }, 3943 { "errorq", "?[-v]", "display kernel error queues", errorq }, 3944 { "fd", ":[fd num]", "get a file pointer from an fd", fd }, 3945 { "flipone", ":", "the vik_rev_level 2 special", flipone }, 3946 { "lminfo", NULL, "print lock manager information", lminfo }, 3947 { "ndi_event_hdl", "?", "print ndi_event_hdl", ndi_event_hdl }, 3948 { "panicinfo", NULL, "print panic information", panicinfo }, 3949 { "pid2proc", "?", "convert PID to proc_t address", pid2proc }, 3950 { "project", NULL, "display kernel project(s)", project }, 3951 { "ps", "[-fltzTP]", "list processes (and associated thr,lwp)", ps }, 3952 { "pflags", NULL, "display various proc_t flags", pflags }, 3953 { "pgrep", "[-x] [-n | -o] pattern", 3954 "pattern match against all processes", pgrep }, 3955 { "ptree", NULL, "print process tree", ptree }, 3956 { "sysevent", "?[-sv]", "print sysevent pending or sent queue", 3957 sysevent}, 3958 { "sysevent_channel", "?", "print sysevent channel database", 3959 sysevent_channel}, 3960 { "sysevent_class_list", ":", "print sysevent class list", 3961 sysevent_class_list}, 3962 { "sysevent_subclass_list", ":", 3963 "print sysevent subclass list", sysevent_subclass_list}, 3964 { "system", NULL, "print contents of /etc/system file", sysfile }, 3965 { "task", NULL, "display kernel task(s)", task }, 3966 { "time", "[-dlx]", "display system time", time, time_help }, 3967 { "vnode2path", ":[-F]", "vnode address to pathname", vnode2path }, 3968 { "whereopen", ":", "given a vnode, dumps procs which have it open", 3969 whereopen }, 3970 3971 /* from bio.c */ 3972 { "bufpagefind", ":addr", "find page_t on buf_t list", bufpagefind }, 3973 3974 /* from bitset.c */ 3975 { "bitset", ":", "display a bitset", bitset, bitset_help }, 3976 3977 /* from contract.c */ 3978 { "contract", "?", "display a contract", cmd_contract }, 3979 { "ctevent", ":", "display a contract event", cmd_ctevent }, 3980 { "ctid", ":", "convert id to a contract pointer", cmd_ctid }, 3981 3982 /* from cpupart.c */ 3983 { "cpupart", "?[-v]", "print cpu partition info", cpupart }, 3984 3985 /* from cred.c */ 3986 { "cred", ":[-v]", "display a credential", cmd_cred }, 3987 { "credgrp", ":[-v]", "display cred_t groups", cmd_credgrp }, 3988 { "credsid", ":[-v]", "display a credsid_t", cmd_credsid }, 3989 { "ksidlist", ":[-v]", "display a ksidlist_t", cmd_ksidlist }, 3990 3991 /* from cyclic.c */ 3992 { "cyccover", NULL, "dump cyclic coverage information", cyccover }, 3993 { "cycid", "?", "dump a cyclic id", cycid }, 3994 { "cycinfo", "?", "dump cyc_cpu info", cycinfo }, 3995 { "cyclic", ":", "developer information", cyclic }, 3996 { "cyctrace", "?", "dump cyclic trace buffer", cyctrace }, 3997 3998 /* from damap.c */ 3999 { "damap", ":", "display a damap_t", damap, damap_help }, 4000 4001 /* from ddi_periodic.c */ 4002 { "ddi_periodic", "?[-v]", "dump ddi_periodic_impl_t info", dprinfo }, 4003 4004 /* from devinfo.c */ 4005 { "devbindings", "?[-qs] [device-name | major-num]", 4006 "print devinfo nodes bound to device-name or major-num", 4007 devbindings, devinfo_help }, 4008 { "devinfo", ":[-qs]", "detailed devinfo of one node", devinfo, 4009 devinfo_help }, 4010 { "devinfo_audit", ":[-v]", "devinfo configuration audit record", 4011 devinfo_audit }, 4012 { "devinfo_audit_log", "?[-v]", "system wide devinfo configuration log", 4013 devinfo_audit_log }, 4014 { "devinfo_audit_node", ":[-v]", "devinfo node configuration history", 4015 devinfo_audit_node }, 4016 { "devinfo2driver", ":", "find driver name for this devinfo node", 4017 devinfo2driver }, 4018 { "devnames", "?[-vm] [num]", "print devnames array", devnames }, 4019 { "dev2major", "?<dev_t>", "convert dev_t to a major number", 4020 dev2major }, 4021 { "dev2minor", "?<dev_t>", "convert dev_t to a minor number", 4022 dev2minor }, 4023 { "devt", "?<dev_t>", "display a dev_t's major and minor numbers", 4024 devt }, 4025 { "major2name", "?<major-num>", "convert major number to dev name", 4026 major2name }, 4027 { "minornodes", ":", "given a devinfo node, print its minor nodes", 4028 minornodes }, 4029 { "modctl2devinfo", ":", "given a modctl, list its devinfos", 4030 modctl2devinfo }, 4031 { "name2major", "<dev-name>", "convert dev name to major number", 4032 name2major }, 4033 { "prtconf", "?[-vpc]", "print devinfo tree", prtconf, prtconf_help }, 4034 { "softstate", ":<instance>", "retrieve soft-state pointer", 4035 softstate }, 4036 { "devinfo_fm", ":", "devinfo fault managment configuration", 4037 devinfo_fm }, 4038 { "devinfo_fmce", ":", "devinfo fault managment cache entry", 4039 devinfo_fmce}, 4040 4041 /* from findstack.c */ 4042 { "findstack", ":[-v]", "find kernel thread stack", findstack }, 4043 { "findstack_debug", NULL, "toggle findstack debugging", 4044 findstack_debug }, 4045 { "stacks", "?[-afiv] [-c func] [-C func] [-m module] [-M module] " 4046 "[-s sobj | -S sobj] [-t tstate | -T tstate]", 4047 "print unique kernel thread stacks", 4048 stacks, stacks_help }, 4049 4050 /* from fm.c */ 4051 { "ereport", "[-v]", "print ereports logged in dump", 4052 ereport }, 4053 4054 /* from group.c */ 4055 { "group", "?[-q]", "display a group", group}, 4056 4057 /* from hotplug.c */ 4058 { "hotplug", "?[-p]", "display a registered hotplug attachment", 4059 hotplug, hotplug_help }, 4060 4061 /* from irm.c */ 4062 { "irmpools", NULL, "display interrupt pools", irmpools_dcmd }, 4063 { "irmreqs", NULL, "display interrupt requests in an interrupt pool", 4064 irmreqs_dcmd }, 4065 { "irmreq", NULL, "display an interrupt request", irmreq_dcmd }, 4066 4067 /* from kgrep.c + genunix.c */ 4068 { "kgrep", KGREP_USAGE, "search kernel as for a pointer", kgrep, 4069 kgrep_help }, 4070 4071 /* from kmem.c */ 4072 { "allocdby", ":", "given a thread, print its allocated buffers", 4073 allocdby }, 4074 { "bufctl", ":[-vh] [-a addr] [-c caller] [-e earliest] [-l latest] " 4075 "[-t thd]", "print or filter a bufctl", bufctl, bufctl_help }, 4076 { "freedby", ":", "given a thread, print its freed buffers", freedby }, 4077 { "kmalog", "?[ fail | slab ]", 4078 "display kmem transaction log and stack traces", kmalog }, 4079 { "kmastat", "[-kmg]", "kernel memory allocator stats", 4080 kmastat }, 4081 { "kmausers", "?[-ef] [cache ...]", "current medium and large users " 4082 "of the kmem allocator", kmausers, kmausers_help }, 4083 { "kmem_cache", "?[-n name]", 4084 "print kernel memory caches", kmem_cache, kmem_cache_help}, 4085 { "kmem_slabs", "?[-v] [-n cache] [-N cache] [-b maxbins] " 4086 "[-B minbinsize]", "display slab usage per kmem cache", 4087 kmem_slabs, kmem_slabs_help }, 4088 { "kmem_debug", NULL, "toggle kmem dcmd/walk debugging", kmem_debug }, 4089 { "kmem_log", "?[-b]", "dump kmem transaction log", kmem_log }, 4090 { "kmem_verify", "?", "check integrity of kmem-managed memory", 4091 kmem_verify }, 4092 { "vmem", "?", "print a vmem_t", vmem }, 4093 { "vmem_seg", ":[-sv] [-c caller] [-e earliest] [-l latest] " 4094 "[-m minsize] [-M maxsize] [-t thread] [-T type]", 4095 "print or filter a vmem_seg", vmem_seg, vmem_seg_help }, 4096 { "whatthread", ":[-v]", "print threads whose stack contains the " 4097 "given address", whatthread }, 4098 4099 /* from ldi.c */ 4100 { "ldi_handle", "?[-i]", "display a layered driver handle", 4101 ldi_handle, ldi_handle_help }, 4102 { "ldi_ident", NULL, "display a layered driver identifier", 4103 ldi_ident, ldi_ident_help }, 4104 4105 /* from leaky.c + leaky_subr.c */ 4106 { "findleaks", FINDLEAKS_USAGE, 4107 "search for potential kernel memory leaks", findleaks, 4108 findleaks_help }, 4109 4110 /* from lgrp.c */ 4111 { "lgrp", "?[-q] [-p | -Pih]", "display an lgrp", lgrp}, 4112 { "lgrp_set", "", "display bitmask of lgroups as a list", lgrp_set}, 4113 4114 /* from log.c */ 4115 { "msgbuf", "?[-v]", "print most recent console messages", msgbuf }, 4116 4117 /* from mdi.c */ 4118 { "mdipi", NULL, "given a path, dump mdi_pathinfo " 4119 "and detailed pi_prop list", mdipi }, 4120 { "mdiprops", NULL, "given a pi_prop, dump the pi_prop list", 4121 mdiprops }, 4122 { "mdiphci", NULL, "given a phci, dump mdi_phci and " 4123 "list all paths", mdiphci }, 4124 { "mdivhci", NULL, "given a vhci, dump mdi_vhci and list " 4125 "all phcis", mdivhci }, 4126 { "mdiclient_paths", NULL, "given a path, walk mdi_pathinfo " 4127 "client links", mdiclient_paths }, 4128 { "mdiphci_paths", NULL, "given a path, walk through mdi_pathinfo " 4129 "phci links", mdiphci_paths }, 4130 { "mdiphcis", NULL, "given a phci, walk through mdi_phci ph_next links", 4131 mdiphcis }, 4132 4133 /* from memory.c */ 4134 { "addr2smap", ":[offset]", "translate address to smap", addr2smap }, 4135 { "memlist", "?[-iav]", "display a struct memlist", memlist }, 4136 { "memstat", NULL, "display memory usage summary", memstat }, 4137 { "page", "?", "display a summarized page_t", page }, 4138 { "pagelookup", "?[-v vp] [-o offset]", 4139 "find the page_t with the name {vp, offset}", 4140 pagelookup, pagelookup_help }, 4141 { "page_num2pp", ":", "find the page_t for a given page frame number", 4142 page_num2pp }, 4143 { "pmap", ":[-q]", "print process memory map", pmap }, 4144 { "seg", ":", "print address space segment", seg }, 4145 { "swapinfo", "?", "display a struct swapinfo", swapinfof }, 4146 { "vnode2smap", ":[offset]", "translate vnode to smap", vnode2smap }, 4147 4148 /* from mmd.c */ 4149 { "multidata", ":[-sv]", "display a summarized multidata_t", 4150 multidata }, 4151 { "pattbl", ":", "display a summarized multidata attribute table", 4152 pattbl }, 4153 { "pattr2multidata", ":", "print multidata pointer from pattr_t", 4154 pattr2multidata }, 4155 { "pdesc2slab", ":", "print pdesc slab pointer from pdesc_t", 4156 pdesc2slab }, 4157 { "pdesc_verify", ":", "verify integrity of a pdesc_t", pdesc_verify }, 4158 { "slab2multidata", ":", "print multidata pointer from pdesc_slab_t", 4159 slab2multidata }, 4160 4161 /* from modhash.c */ 4162 { "modhash", "?[-ceht] [-k key] [-v val] [-i index]", 4163 "display information about one or all mod_hash structures", 4164 modhash, modhash_help }, 4165 { "modent", ":[-k | -v | -t type]", 4166 "display information about a mod_hash_entry", modent, 4167 modent_help }, 4168 4169 /* from net.c */ 4170 { "dladm", "?<sub-command> [flags]", "show data link information", 4171 dladm, dladm_help }, 4172 { "mi", ":[-p] [-d | -m]", "filter and display MI object or payload", 4173 mi }, 4174 { "netstat", "[-arv] [-f inet | inet6 | unix] [-P tcp | udp | icmp]", 4175 "show network statistics", netstat }, 4176 { "sonode", "?[-f inet | inet6 | unix | #] " 4177 "[-t stream | dgram | raw | #] [-p #]", 4178 "filter and display sonode", sonode }, 4179 4180 /* from netstack.c */ 4181 { "netstack", "", "show stack instances", netstack }, 4182 { "netstackid2netstack", ":", 4183 "translate a netstack id to its netstack_t", 4184 netstackid2netstack }, 4185 4186 /* from nvpair.c */ 4187 { NVPAIR_DCMD_NAME, NVPAIR_DCMD_USAGE, NVPAIR_DCMD_DESCR, 4188 nvpair_print }, 4189 { NVLIST_DCMD_NAME, NVLIST_DCMD_USAGE, NVLIST_DCMD_DESCR, 4190 print_nvlist }, 4191 4192 /* from pg.c */ 4193 { "pg", "?[-q]", "display a pg", pg}, 4194 4195 /* from rctl.c */ 4196 { "rctl_dict", "?", "print systemwide default rctl definitions", 4197 rctl_dict }, 4198 { "rctl_list", ":[handle]", "print rctls for the given proc", 4199 rctl_list }, 4200 { "rctl", ":[handle]", "print a rctl_t, only if it matches the handle", 4201 rctl }, 4202 { "rctl_validate", ":[-v] [-n #]", "test resource control value " 4203 "sequence", rctl_validate }, 4204 4205 /* from sobj.c */ 4206 { "rwlock", ":", "dump out a readers/writer lock", rwlock }, 4207 { "mutex", ":[-f]", "dump out an adaptive or spin mutex", mutex, 4208 mutex_help }, 4209 { "sobj2ts", ":", "perform turnstile lookup on synch object", sobj2ts }, 4210 { "wchaninfo", "?[-v]", "dump condition variable", wchaninfo }, 4211 { "turnstile", "?", "display a turnstile", turnstile }, 4212 4213 /* from stream.c */ 4214 { "mblk", ":[-q|v] [-f|F flag] [-t|T type] [-l|L|B len] [-d dbaddr]", 4215 "print an mblk", mblk_prt, mblk_help }, 4216 { "mblk_verify", "?", "verify integrity of an mblk", mblk_verify }, 4217 { "mblk2dblk", ":", "convert mblk_t address to dblk_t address", 4218 mblk2dblk }, 4219 { "q2otherq", ":", "print peer queue for a given queue", q2otherq }, 4220 { "q2rdq", ":", "print read queue for a given queue", q2rdq }, 4221 { "q2syncq", ":", "print syncq for a given queue", q2syncq }, 4222 { "q2stream", ":", "print stream pointer for a given queue", q2stream }, 4223 { "q2wrq", ":", "print write queue for a given queue", q2wrq }, 4224 { "queue", ":[-q|v] [-m mod] [-f flag] [-F flag] [-s syncq_addr]", 4225 "filter and display STREAM queue", queue, queue_help }, 4226 { "stdata", ":[-q|v] [-f flag] [-F flag]", 4227 "filter and display STREAM head", stdata, stdata_help }, 4228 { "str2mate", ":", "print mate of this stream", str2mate }, 4229 { "str2wrq", ":", "print write queue of this stream", str2wrq }, 4230 { "stream", ":", "display STREAM", stream }, 4231 { "strftevent", ":", "print STREAMS flow trace event", strftevent }, 4232 { "syncq", ":[-q|v] [-f flag] [-F flag] [-t type] [-T type]", 4233 "filter and display STREAM sync queue", syncq, syncq_help }, 4234 { "syncq2q", ":", "print queue for a given syncq", syncq2q }, 4235 4236 /* from taskq.c */ 4237 { "taskq", ":[-atT] [-m min_maxq] [-n name]", 4238 "display a taskq", taskq, taskq_help }, 4239 { "taskq_entry", ":", "display a taskq_ent_t", taskq_ent }, 4240 4241 /* from thread.c */ 4242 { "thread", "?[-bdfimps]", "display a summarized kthread_t", thread, 4243 thread_help }, 4244 { "threadlist", "?[-t] [-v [count]]", 4245 "display threads and associated C stack traces", threadlist, 4246 threadlist_help }, 4247 { "stackinfo", "?[-h|-a]", "display kthread_t stack usage", stackinfo, 4248 stackinfo_help }, 4249 4250 /* from tsd.c */ 4251 { "tsd", ":-k key", "print tsd[key-1] for this thread", ttotsd }, 4252 { "tsdtot", ":", "find thread with this tsd", tsdtot }, 4253 4254 /* 4255 * typegraph does not work under kmdb, as it requires too much memory 4256 * for its internal data structures. 4257 */ 4258 #ifndef _KMDB 4259 /* from typegraph.c */ 4260 { "findlocks", ":", "find locks held by specified thread", findlocks }, 4261 { "findfalse", "?[-v]", "find potentially falsely shared structures", 4262 findfalse }, 4263 { "typegraph", NULL, "build type graph", typegraph }, 4264 { "istype", ":type", "manually set object type", istype }, 4265 { "notype", ":", "manually clear object type", notype }, 4266 { "whattype", ":", "determine object type", whattype }, 4267 #endif 4268 4269 /* from vfs.c */ 4270 { "fsinfo", "?[-v]", "print mounted filesystems", fsinfo }, 4271 { "pfiles", ":[-fp]", "print process file information", pfiles, 4272 pfiles_help }, 4273 4274 /* from zone.c */ 4275 { "zid2zone", ":", "find the zone_t with the given zone id", 4276 zid2zone }, 4277 { "zone", "?[-r [-v]]", "display kernel zone(s)", zoneprt }, 4278 { "zsd", ":[-v] [zsd_key]", "display zone-specific-data entries for " 4279 "selected zones", zsd }, 4280 4281 #ifndef _KMDB 4282 { "gcore", NULL, "generate a user core for the given process", 4283 gcore_dcmd }, 4284 #endif 4285 4286 { NULL } 4287 }; 4288 4289 static const mdb_walker_t walkers[] = { 4290 4291 /* from genunix.c */ 4292 { "callouts_bytime", "walk callouts by list chain (expiration time)", 4293 callout_walk_init, callout_walk_step, callout_walk_fini, 4294 (void *)CALLOUT_WALK_BYLIST }, 4295 { "callouts_byid", "walk callouts by id hash chain", 4296 callout_walk_init, callout_walk_step, callout_walk_fini, 4297 (void *)CALLOUT_WALK_BYID }, 4298 { "callout_list", "walk a callout list", callout_list_walk_init, 4299 callout_list_walk_step, callout_list_walk_fini }, 4300 { "callout_table", "walk callout table array", callout_table_walk_init, 4301 callout_table_walk_step, callout_table_walk_fini }, 4302 { "cpu", "walk cpu structures", cpu_walk_init, cpu_walk_step }, 4303 { "ereportq_dump", "walk list of ereports in dump error queue", 4304 ereportq_dump_walk_init, ereportq_dump_walk_step, NULL }, 4305 { "ereportq_pend", "walk list of ereports in pending error queue", 4306 ereportq_pend_walk_init, ereportq_pend_walk_step, NULL }, 4307 { "errorq", "walk list of system error queues", 4308 errorq_walk_init, errorq_walk_step, NULL }, 4309 { "errorq_data", "walk pending error queue data buffers", 4310 eqd_walk_init, eqd_walk_step, eqd_walk_fini }, 4311 { "allfile", "given a proc pointer, list all file pointers", 4312 file_walk_init, allfile_walk_step, file_walk_fini }, 4313 { "file", "given a proc pointer, list of open file pointers", 4314 file_walk_init, file_walk_step, file_walk_fini }, 4315 { "lock_descriptor", "walk lock_descriptor_t structures", 4316 ld_walk_init, ld_walk_step, NULL }, 4317 { "lock_graph", "walk lock graph", 4318 lg_walk_init, lg_walk_step, NULL }, 4319 { "port", "given a proc pointer, list of created event ports", 4320 port_walk_init, port_walk_step, NULL }, 4321 { "portev", "given a port pointer, list of events in the queue", 4322 portev_walk_init, portev_walk_step, portev_walk_fini }, 4323 { "proc", "list of active proc_t structures", 4324 proc_walk_init, proc_walk_step, proc_walk_fini }, 4325 { "projects", "walk a list of kernel projects", 4326 project_walk_init, project_walk_step, NULL }, 4327 { "sysevent_pend", "walk sysevent pending queue", 4328 sysevent_pend_walk_init, sysevent_walk_step, 4329 sysevent_walk_fini}, 4330 { "sysevent_sent", "walk sysevent sent queue", sysevent_sent_walk_init, 4331 sysevent_walk_step, sysevent_walk_fini}, 4332 { "sysevent_channel", "walk sysevent channel subscriptions", 4333 sysevent_channel_walk_init, sysevent_channel_walk_step, 4334 sysevent_channel_walk_fini}, 4335 { "sysevent_class_list", "walk sysevent subscription's class list", 4336 sysevent_class_list_walk_init, sysevent_class_list_walk_step, 4337 sysevent_class_list_walk_fini}, 4338 { "sysevent_subclass_list", 4339 "walk sysevent subscription's subclass list", 4340 sysevent_subclass_list_walk_init, 4341 sysevent_subclass_list_walk_step, 4342 sysevent_subclass_list_walk_fini}, 4343 { "task", "given a task pointer, walk its processes", 4344 task_walk_init, task_walk_step, NULL }, 4345 4346 /* from avl.c */ 4347 { AVL_WALK_NAME, AVL_WALK_DESC, 4348 avl_walk_init, avl_walk_step, avl_walk_fini }, 4349 4350 /* from bio.c */ 4351 { "buf", "walk the bio buf hash", 4352 buf_walk_init, buf_walk_step, buf_walk_fini }, 4353 4354 /* from contract.c */ 4355 { "contract", "walk all contracts, or those of the specified type", 4356 ct_walk_init, generic_walk_step, NULL }, 4357 { "ct_event", "walk events on a contract event queue", 4358 ct_event_walk_init, generic_walk_step, NULL }, 4359 { "ct_listener", "walk contract event queue listeners", 4360 ct_listener_walk_init, generic_walk_step, NULL }, 4361 4362 /* from cpupart.c */ 4363 { "cpupart_cpulist", "given an cpupart_t, walk cpus in partition", 4364 cpupart_cpulist_walk_init, cpupart_cpulist_walk_step, 4365 NULL }, 4366 { "cpupart_walk", "walk the set of cpu partitions", 4367 cpupart_walk_init, cpupart_walk_step, NULL }, 4368 4369 /* from ctxop.c */ 4370 { "ctxop", "walk list of context ops on a thread", 4371 ctxop_walk_init, ctxop_walk_step, ctxop_walk_fini }, 4372 4373 /* from cyclic.c */ 4374 { "cyccpu", "walk per-CPU cyc_cpu structures", 4375 cyccpu_walk_init, cyccpu_walk_step, NULL }, 4376 { "cycomni", "for an omnipresent cyclic, walk cyc_omni_cpu list", 4377 cycomni_walk_init, cycomni_walk_step, NULL }, 4378 { "cyctrace", "walk cyclic trace buffer", 4379 cyctrace_walk_init, cyctrace_walk_step, cyctrace_walk_fini }, 4380 4381 /* from devinfo.c */ 4382 { "binding_hash", "walk all entries in binding hash table", 4383 binding_hash_walk_init, binding_hash_walk_step, NULL }, 4384 { "devinfo", "walk devinfo tree or subtree", 4385 devinfo_walk_init, devinfo_walk_step, devinfo_walk_fini }, 4386 { "devinfo_audit_log", "walk devinfo audit system-wide log", 4387 devinfo_audit_log_walk_init, devinfo_audit_log_walk_step, 4388 devinfo_audit_log_walk_fini}, 4389 { "devinfo_audit_node", "walk per-devinfo audit history", 4390 devinfo_audit_node_walk_init, devinfo_audit_node_walk_step, 4391 devinfo_audit_node_walk_fini}, 4392 { "devinfo_children", "walk children of devinfo node", 4393 devinfo_children_walk_init, devinfo_children_walk_step, 4394 devinfo_children_walk_fini }, 4395 { "devinfo_parents", "walk ancestors of devinfo node", 4396 devinfo_parents_walk_init, devinfo_parents_walk_step, 4397 devinfo_parents_walk_fini }, 4398 { "devinfo_siblings", "walk siblings of devinfo node", 4399 devinfo_siblings_walk_init, devinfo_siblings_walk_step, NULL }, 4400 { "devi_next", "walk devinfo list", 4401 NULL, devi_next_walk_step, NULL }, 4402 { "devnames", "walk devnames array", 4403 devnames_walk_init, devnames_walk_step, devnames_walk_fini }, 4404 { "minornode", "given a devinfo node, walk minor nodes", 4405 minornode_walk_init, minornode_walk_step, NULL }, 4406 { "softstate", 4407 "given an i_ddi_soft_state*, list all in-use driver stateps", 4408 soft_state_walk_init, soft_state_walk_step, 4409 NULL, NULL }, 4410 { "softstate_all", 4411 "given an i_ddi_soft_state*, list all driver stateps", 4412 soft_state_walk_init, soft_state_all_walk_step, 4413 NULL, NULL }, 4414 { "devinfo_fmc", 4415 "walk a fault management handle cache active list", 4416 devinfo_fmc_walk_init, devinfo_fmc_walk_step, NULL }, 4417 4418 /* from group.c */ 4419 { "group", "walk all elements of a group", 4420 group_walk_init, group_walk_step, NULL }, 4421 4422 /* from irm.c */ 4423 { "irmpools", "walk global list of interrupt pools", 4424 irmpools_walk_init, list_walk_step, list_walk_fini }, 4425 { "irmreqs", "walk list of interrupt requests in an interrupt pool", 4426 irmreqs_walk_init, list_walk_step, list_walk_fini }, 4427 4428 /* from kmem.c */ 4429 { "allocdby", "given a thread, walk its allocated bufctls", 4430 allocdby_walk_init, allocdby_walk_step, allocdby_walk_fini }, 4431 { "bufctl", "walk a kmem cache's bufctls", 4432 bufctl_walk_init, kmem_walk_step, kmem_walk_fini }, 4433 { "bufctl_history", "walk the available history of a bufctl", 4434 bufctl_history_walk_init, bufctl_history_walk_step, 4435 bufctl_history_walk_fini }, 4436 { "freedby", "given a thread, walk its freed bufctls", 4437 freedby_walk_init, allocdby_walk_step, allocdby_walk_fini }, 4438 { "freectl", "walk a kmem cache's free bufctls", 4439 freectl_walk_init, kmem_walk_step, kmem_walk_fini }, 4440 { "freectl_constructed", "walk a kmem cache's constructed free bufctls", 4441 freectl_constructed_walk_init, kmem_walk_step, kmem_walk_fini }, 4442 { "freemem", "walk a kmem cache's free memory", 4443 freemem_walk_init, kmem_walk_step, kmem_walk_fini }, 4444 { "freemem_constructed", "walk a kmem cache's constructed free memory", 4445 freemem_constructed_walk_init, kmem_walk_step, kmem_walk_fini }, 4446 { "kmem", "walk a kmem cache", 4447 kmem_walk_init, kmem_walk_step, kmem_walk_fini }, 4448 { "kmem_cpu_cache", "given a kmem cache, walk its per-CPU caches", 4449 kmem_cpu_cache_walk_init, kmem_cpu_cache_walk_step, NULL }, 4450 { "kmem_hash", "given a kmem cache, walk its allocated hash table", 4451 kmem_hash_walk_init, kmem_hash_walk_step, kmem_hash_walk_fini }, 4452 { "kmem_log", "walk the kmem transaction log", 4453 kmem_log_walk_init, kmem_log_walk_step, kmem_log_walk_fini }, 4454 { "kmem_slab", "given a kmem cache, walk its slabs", 4455 kmem_slab_walk_init, combined_walk_step, combined_walk_fini }, 4456 { "kmem_slab_partial", 4457 "given a kmem cache, walk its partially allocated slabs (min 1)", 4458 kmem_slab_walk_partial_init, combined_walk_step, 4459 combined_walk_fini }, 4460 { "vmem", "walk vmem structures in pre-fix, depth-first order", 4461 vmem_walk_init, vmem_walk_step, vmem_walk_fini }, 4462 { "vmem_alloc", "given a vmem_t, walk its allocated vmem_segs", 4463 vmem_alloc_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini }, 4464 { "vmem_free", "given a vmem_t, walk its free vmem_segs", 4465 vmem_free_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini }, 4466 { "vmem_postfix", "walk vmem structures in post-fix, depth-first order", 4467 vmem_walk_init, vmem_postfix_walk_step, vmem_walk_fini }, 4468 { "vmem_seg", "given a vmem_t, walk all of its vmem_segs", 4469 vmem_seg_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini }, 4470 { "vmem_span", "given a vmem_t, walk its spanning vmem_segs", 4471 vmem_span_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini }, 4472 4473 /* from ldi.c */ 4474 { "ldi_handle", "walk the layered driver handle hash", 4475 ldi_handle_walk_init, ldi_handle_walk_step, NULL }, 4476 { "ldi_ident", "walk the layered driver identifier hash", 4477 ldi_ident_walk_init, ldi_ident_walk_step, NULL }, 4478 4479 /* from leaky.c + leaky_subr.c */ 4480 { "leak", "given a leaked bufctl or vmem_seg, find leaks w/ same " 4481 "stack trace", 4482 leaky_walk_init, leaky_walk_step, leaky_walk_fini }, 4483 { "leakbuf", "given a leaked bufctl or vmem_seg, walk buffers for " 4484 "leaks w/ same stack trace", 4485 leaky_walk_init, leaky_buf_walk_step, leaky_walk_fini }, 4486 4487 /* from lgrp.c */ 4488 { "lgrp_cpulist", "walk CPUs in a given lgroup", 4489 lgrp_cpulist_walk_init, lgrp_cpulist_walk_step, NULL }, 4490 { "lgrptbl", "walk lgroup table", 4491 lgrp_walk_init, lgrp_walk_step, NULL }, 4492 { "lgrp_parents", "walk up lgroup lineage from given lgroup", 4493 lgrp_parents_walk_init, lgrp_parents_walk_step, NULL }, 4494 { "lgrp_rsrc_mem", "walk lgroup memory resources of given lgroup", 4495 lgrp_rsrc_mem_walk_init, lgrp_set_walk_step, NULL }, 4496 { "lgrp_rsrc_cpu", "walk lgroup CPU resources of given lgroup", 4497 lgrp_rsrc_cpu_walk_init, lgrp_set_walk_step, NULL }, 4498 4499 /* from list.c */ 4500 { LIST_WALK_NAME, LIST_WALK_DESC, 4501 list_walk_init, list_walk_step, list_walk_fini }, 4502 4503 /* from mdi.c */ 4504 { "mdipi_client_list", "Walker for mdi_pathinfo pi_client_link", 4505 mdi_pi_client_link_walk_init, 4506 mdi_pi_client_link_walk_step, 4507 mdi_pi_client_link_walk_fini }, 4508 { "mdipi_phci_list", "Walker for mdi_pathinfo pi_phci_link", 4509 mdi_pi_phci_link_walk_init, 4510 mdi_pi_phci_link_walk_step, 4511 mdi_pi_phci_link_walk_fini }, 4512 { "mdiphci_list", "Walker for mdi_phci ph_next link", 4513 mdi_phci_ph_next_walk_init, 4514 mdi_phci_ph_next_walk_step, 4515 mdi_phci_ph_next_walk_fini }, 4516 4517 /* from memory.c */ 4518 { "allpages", "walk all pages, including free pages", 4519 allpages_walk_init, allpages_walk_step, allpages_walk_fini }, 4520 { "anon", "given an amp, list allocated anon structures", 4521 anon_walk_init, anon_walk_step, anon_walk_fini, 4522 ANON_WALK_ALLOC }, 4523 { "anon_all", "given an amp, list contents of all anon slots", 4524 anon_walk_init, anon_walk_step, anon_walk_fini, 4525 ANON_WALK_ALL }, 4526 { "memlist", "walk specified memlist", 4527 NULL, memlist_walk_step, NULL }, 4528 { "page", "walk all pages, or those from the specified vnode", 4529 page_walk_init, page_walk_step, page_walk_fini }, 4530 { "seg", "given an as, list of segments", 4531 seg_walk_init, avl_walk_step, avl_walk_fini }, 4532 { "segvn_anon", 4533 "given a struct segvn_data, list allocated anon structures", 4534 segvn_anon_walk_init, anon_walk_step, anon_walk_fini, 4535 ANON_WALK_ALLOC }, 4536 { "segvn_anon_all", 4537 "given a struct segvn_data, list contents of all anon slots", 4538 segvn_anon_walk_init, anon_walk_step, anon_walk_fini, 4539 ANON_WALK_ALL }, 4540 { "segvn_pages", 4541 "given a struct segvn_data, list resident pages in " 4542 "offset order", 4543 segvn_pages_walk_init, segvn_pages_walk_step, 4544 segvn_pages_walk_fini, SEGVN_PAGES_RESIDENT }, 4545 { "segvn_pages_all", 4546 "for each offset in a struct segvn_data, give page_t pointer " 4547 "(if resident), or NULL.", 4548 segvn_pages_walk_init, segvn_pages_walk_step, 4549 segvn_pages_walk_fini, SEGVN_PAGES_ALL }, 4550 { "swapinfo", "walk swapinfo structures", 4551 swap_walk_init, swap_walk_step, NULL }, 4552 4553 /* from mmd.c */ 4554 { "pattr", "walk pattr_t structures", pattr_walk_init, 4555 mmdq_walk_step, mmdq_walk_fini }, 4556 { "pdesc", "walk pdesc_t structures", 4557 pdesc_walk_init, mmdq_walk_step, mmdq_walk_fini }, 4558 { "pdesc_slab", "walk pdesc_slab_t structures", 4559 pdesc_slab_walk_init, mmdq_walk_step, mmdq_walk_fini }, 4560 4561 /* from modhash.c */ 4562 { "modhash", "walk list of mod_hash structures", modhash_walk_init, 4563 modhash_walk_step, NULL }, 4564 { "modent", "walk list of entries in a given mod_hash", 4565 modent_walk_init, modent_walk_step, modent_walk_fini }, 4566 { "modchain", "walk list of entries in a given mod_hash_entry", 4567 NULL, modchain_walk_step, NULL }, 4568 4569 /* from net.c */ 4570 { "icmp", "walk ICMP control structures using MI for all stacks", 4571 mi_payload_walk_init, mi_payload_walk_step, NULL, 4572 &mi_icmp_arg }, 4573 { "mi", "given a MI_O, walk the MI", 4574 mi_walk_init, mi_walk_step, mi_walk_fini, NULL }, 4575 { "sonode", "given a sonode, walk its children", 4576 sonode_walk_init, sonode_walk_step, sonode_walk_fini, NULL }, 4577 { "icmp_stacks", "walk all the icmp_stack_t", 4578 icmp_stacks_walk_init, icmp_stacks_walk_step, NULL }, 4579 { "tcp_stacks", "walk all the tcp_stack_t", 4580 tcp_stacks_walk_init, tcp_stacks_walk_step, NULL }, 4581 { "udp_stacks", "walk all the udp_stack_t", 4582 udp_stacks_walk_init, udp_stacks_walk_step, NULL }, 4583 4584 /* from netstack.c */ 4585 { "netstack", "walk a list of kernel netstacks", 4586 netstack_walk_init, netstack_walk_step, NULL }, 4587 4588 /* from nvpair.c */ 4589 { NVPAIR_WALKER_NAME, NVPAIR_WALKER_DESCR, 4590 nvpair_walk_init, nvpair_walk_step, NULL }, 4591 4592 /* from rctl.c */ 4593 { "rctl_dict_list", "walk all rctl_dict_entry_t's from rctl_lists", 4594 rctl_dict_walk_init, rctl_dict_walk_step, NULL }, 4595 { "rctl_set", "given a rctl_set, walk all rctls", rctl_set_walk_init, 4596 rctl_set_walk_step, NULL }, 4597 { "rctl_val", "given a rctl_t, walk all rctl_val entries associated", 4598 rctl_val_walk_init, rctl_val_walk_step }, 4599 4600 /* from sobj.c */ 4601 { "blocked", "walk threads blocked on a given sobj", 4602 blocked_walk_init, blocked_walk_step, NULL }, 4603 { "wchan", "given a wchan, list of blocked threads", 4604 wchan_walk_init, wchan_walk_step, wchan_walk_fini }, 4605 4606 /* from stream.c */ 4607 { "b_cont", "walk mblk_t list using b_cont", 4608 mblk_walk_init, b_cont_step, mblk_walk_fini }, 4609 { "b_next", "walk mblk_t list using b_next", 4610 mblk_walk_init, b_next_step, mblk_walk_fini }, 4611 { "qlink", "walk queue_t list using q_link", 4612 queue_walk_init, queue_link_step, queue_walk_fini }, 4613 { "qnext", "walk queue_t list using q_next", 4614 queue_walk_init, queue_next_step, queue_walk_fini }, 4615 { "strftblk", "given a dblk_t, walk STREAMS flow trace event list", 4616 strftblk_walk_init, strftblk_step, strftblk_walk_fini }, 4617 { "readq", "walk read queue side of stdata", 4618 str_walk_init, strr_walk_step, str_walk_fini }, 4619 { "writeq", "walk write queue side of stdata", 4620 str_walk_init, strw_walk_step, str_walk_fini }, 4621 4622 /* from taskq.c */ 4623 { "taskq_thread", "given a taskq_t, list all of its threads", 4624 taskq_thread_walk_init, 4625 taskq_thread_walk_step, 4626 taskq_thread_walk_fini }, 4627 { "taskq_entry", "given a taskq_t*, list all taskq_ent_t in the list", 4628 taskq_ent_walk_init, taskq_ent_walk_step, NULL }, 4629 4630 /* from thread.c */ 4631 { "deathrow", "walk threads on both lwp_ and thread_deathrow", 4632 deathrow_walk_init, deathrow_walk_step, NULL }, 4633 { "cpu_dispq", "given a cpu_t, walk threads in dispatcher queues", 4634 cpu_dispq_walk_init, dispq_walk_step, dispq_walk_fini }, 4635 { "cpupart_dispq", 4636 "given a cpupart_t, walk threads in dispatcher queues", 4637 cpupart_dispq_walk_init, dispq_walk_step, dispq_walk_fini }, 4638 { "lwp_deathrow", "walk lwp_deathrow", 4639 lwp_deathrow_walk_init, deathrow_walk_step, NULL }, 4640 { "thread", "global or per-process kthread_t structures", 4641 thread_walk_init, thread_walk_step, thread_walk_fini }, 4642 { "thread_deathrow", "walk threads on thread_deathrow", 4643 thread_deathrow_walk_init, deathrow_walk_step, NULL }, 4644 4645 /* from tsd.c */ 4646 { "tsd", "walk list of thread-specific data", 4647 tsd_walk_init, tsd_walk_step, tsd_walk_fini }, 4648 4649 /* from tsol.c */ 4650 { "tnrh", "walk remote host cache structures", 4651 tnrh_walk_init, tnrh_walk_step, tnrh_walk_fini }, 4652 { "tnrhtp", "walk remote host template structures", 4653 tnrhtp_walk_init, tnrhtp_walk_step, tnrhtp_walk_fini }, 4654 4655 /* 4656 * typegraph does not work under kmdb, as it requires too much memory 4657 * for its internal data structures. 4658 */ 4659 #ifndef _KMDB 4660 /* from typegraph.c */ 4661 { "typeconflict", "walk buffers with conflicting type inferences", 4662 typegraph_walk_init, typeconflict_walk_step }, 4663 { "typeunknown", "walk buffers with unknown types", 4664 typegraph_walk_init, typeunknown_walk_step }, 4665 #endif 4666 4667 /* from vfs.c */ 4668 { "vfs", "walk file system list", 4669 vfs_walk_init, vfs_walk_step }, 4670 4671 /* from zone.c */ 4672 { "zone", "walk a list of kernel zones", 4673 zone_walk_init, zone_walk_step, NULL }, 4674 { "zsd", "walk list of zsd entries for a zone", 4675 zsd_walk_init, zsd_walk_step, NULL }, 4676 4677 { NULL } 4678 }; 4679 4680 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers }; 4681 4682 /*ARGSUSED*/ 4683 static void 4684 genunix_statechange_cb(void *ignored) 4685 { 4686 /* 4687 * Force ::findleaks and ::stacks to let go any cached state. 4688 */ 4689 leaky_cleanup(1); 4690 stacks_cleanup(1); 4691 4692 kmem_statechange(); /* notify kmem */ 4693 } 4694 4695 const mdb_modinfo_t * 4696 _mdb_init(void) 4697 { 4698 kmem_init(); 4699 4700 (void) mdb_callback_add(MDB_CALLBACK_STCHG, 4701 genunix_statechange_cb, NULL); 4702 4703 #ifndef _KMDB 4704 gcore_init(); 4705 #endif 4706 4707 return (&modinfo); 4708 } 4709 4710 void 4711 _mdb_fini(void) 4712 { 4713 leaky_cleanup(1); 4714 stacks_cleanup(1); 4715 } 4716