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