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