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 /* 23 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2012 by Delphix. All rights reserved. 25 * Copyright (c) 2019 Carlos Neira <cneirabustos@gmail.com> 26 * Copyright 2019 OmniOS Community Edition (OmniOSce) Association. 27 * Copyright 2019 Joyent, Inc. 28 * Copyright 2024 Oxide Computer Company 29 */ 30 31 #include <sys/mdb_modapi.h> 32 #include <mdb/mdb_whatis.h> 33 #include <mdb/mdb_ctf.h> 34 #include <procfs.h> 35 #include <ucontext.h> 36 #include <siginfo.h> 37 #include <signal.h> 38 #include <setjmp.h> 39 #include <string.h> 40 #include <thr_uberdata.h> 41 #include "findstack.h" 42 #include <libproc.h> 43 44 static const char * 45 stack_flags(const stack_t *sp) 46 { 47 static char buf[32]; 48 49 if (sp->ss_flags == 0) 50 (void) strcpy(buf, " 0"); 51 else if (sp->ss_flags & ~(SS_ONSTACK | SS_DISABLE)) 52 (void) mdb_snprintf(buf, sizeof (buf), " 0x%x", sp->ss_flags); 53 else { 54 buf[0] = '\0'; 55 if (sp->ss_flags & SS_ONSTACK) 56 (void) strcat(buf, "|ONSTACK"); 57 if (sp->ss_flags & SS_DISABLE) 58 (void) strcat(buf, "|DISABLE"); 59 } 60 61 return (buf + 1); 62 } 63 64 /*ARGSUSED*/ 65 static int 66 d_jmp_buf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 67 { 68 jmp_buf jb; 69 const ulong_t *b = (const ulong_t *)jb; 70 71 if (argc != 0) 72 return (DCMD_USAGE); 73 74 if (mdb_vread(&jb, sizeof (jb), addr) != sizeof (jb)) { 75 mdb_warn("failed to read jmp_buf at %p", addr); 76 return (DCMD_ERR); 77 } 78 79 #if defined(__sparc) 80 mdb_printf(" %%sp = 0x%lx\n", b[1]); 81 mdb_printf(" %%pc = 0x%lx %lA\n", b[2], b[2]); 82 mdb_printf(" %%fp = 0x%lx\n", b[3]); 83 mdb_printf(" %%i7 = 0x%lx %lA\n", b[4], b[4]); 84 #elif defined(__amd64) 85 mdb_printf(" %%rbx = 0x%lx\n", b[0]); 86 mdb_printf(" %%r12 = 0x%lx\n", b[1]); 87 mdb_printf(" %%r13 = 0x%lx\n", b[2]); 88 mdb_printf(" %%r14 = 0x%lx\n", b[3]); 89 mdb_printf(" %%r15 = 0x%lx\n", b[4]); 90 mdb_printf(" %%rbp = 0x%lx\n", b[5]); 91 mdb_printf(" %%rsp = 0x%lx\n", b[6]); 92 mdb_printf(" %%rip = 0x%lx %lA\n", b[7], b[7]); 93 #elif defined(__i386) 94 mdb_printf(" %%ebx = 0x%lx\n", b[0]); 95 mdb_printf(" %%esi = 0x%lx\n", b[1]); 96 mdb_printf(" %%edi = 0x%lx\n", b[2]); 97 mdb_printf(" %%ebp = 0x%lx\n", b[3]); 98 mdb_printf(" %%esp = 0x%lx\n", b[4]); 99 mdb_printf(" %%eip = 0x%lx %lA\n", b[5], b[5]); 100 #endif 101 return (DCMD_OK); 102 } 103 104 const mdb_bitmask_t uc_flags_bits[] = { 105 { "UC_SIGMASK", UC_SIGMASK, UC_SIGMASK }, 106 { "UC_STACK", UC_STACK, UC_STACK }, 107 { "UC_CPU", UC_CPU, UC_CPU }, 108 { "UC_FPU", UC_FPU, UC_FPU }, 109 #if defined(UC_INTR) 110 { "UC_INTR", UC_INTR, UC_INTR }, 111 #endif 112 #if defined(UC_ASR) 113 { "UC_ASR", UC_ASR, UC_ASR }, 114 #endif 115 { NULL, 0, 0 } 116 }; 117 118 /*ARGSUSED*/ 119 static int 120 d_ucontext(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 121 { 122 ucontext_t uc; 123 124 if (argc != 0) 125 return (DCMD_USAGE); 126 127 if (mdb_vread(&uc, sizeof (uc), addr) != sizeof (uc)) { 128 mdb_warn("failed to read ucontext at %p", addr); 129 return (DCMD_ERR); 130 } 131 132 mdb_printf(" flags = 0x%lx <%b>\n", uc.uc_flags, 133 (uint_t)uc.uc_flags, uc_flags_bits); 134 mdb_printf(" link = 0x%p\n", uc.uc_link); 135 mdb_printf(" sigmask = 0x%08x 0x%08x 0x%08x 0x%08x\n", 136 uc.uc_sigmask.__sigbits[0], uc.uc_sigmask.__sigbits[1], 137 uc.uc_sigmask.__sigbits[2], uc.uc_sigmask.__sigbits[3]); 138 mdb_printf(" stack = sp 0x%p size 0x%lx flags %s\n", 139 uc.uc_stack.ss_sp, uc.uc_stack.ss_size, stack_flags(&uc.uc_stack)); 140 mdb_printf(" mcontext = 0x%p\n", 141 addr + OFFSETOF(ucontext_t, uc_mcontext)); 142 143 return (DCMD_OK); 144 } 145 146 /*ARGSUSED*/ 147 static int 148 d_sigjmp_buf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 149 { 150 #if defined(__sparc) 151 struct { 152 int sjs_flags; 153 greg_t sjs_sp; 154 greg_t sjs_pc; 155 greg_t sjs_fp; 156 greg_t sjs_i7; 157 ucontext_t *sjs_uclink; 158 ulong_t sjs_pad[_JBLEN - 6]; 159 sigset_t sjs_sigmask; 160 #if defined(_LP64) 161 greg_t sjs_asi; 162 greg_t sjs_fprs; 163 #endif 164 stack_t sjs_stack; 165 } s; 166 167 if (argc != 0) 168 return (DCMD_USAGE); 169 170 if (mdb_vread(&s, sizeof (s), addr) != sizeof (s)) { 171 mdb_warn("failed to read sigjmp_buf at %p", addr); 172 return (DCMD_ERR); 173 } 174 175 mdb_printf(" flags = 0x%x\n", s.sjs_flags); 176 mdb_printf(" %%sp = 0x%lx %lA\n", s.sjs_sp, s.sjs_sp); 177 mdb_printf(" %%pc = 0x%lx %lA\n", s.sjs_pc, s.sjs_pc); 178 mdb_printf(" %%fp = 0x%lx %lA\n", s.sjs_fp, s.sjs_fp); 179 mdb_printf(" %%i7 = 0x%lx %lA\n", s.sjs_i7, s.sjs_i7); 180 mdb_printf(" uclink = %p\n", s.sjs_uclink); 181 mdb_printf(" sigset = 0x%08x 0x%08x 0x%08x 0x%08x\n", 182 s.sjs_sigmask.__sigbits[0], s.sjs_sigmask.__sigbits[1], 183 s.sjs_sigmask.__sigbits[2], s.sjs_sigmask.__sigbits[3]); 184 #if defined(_LP64) 185 mdb_printf(" %%asi = 0x%lx\n", s.sjs_asi); 186 mdb_printf(" %%fprs = 0x%lx\n", s.sjs_fprs); 187 #endif 188 mdb_printf(" stack = sp 0x%p size 0x%lx flags %s\n", 189 s.sjs_stack.ss_sp, s.sjs_stack.ss_size, stack_flags(&s.sjs_stack)); 190 191 return (DCMD_OK); 192 193 #elif defined(__i386) || defined(__amd64) 194 return (d_ucontext(addr, flags, argc, argv)); 195 #endif 196 } 197 198 /*ARGSUSED*/ 199 static int 200 d_siginfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 201 { 202 static const char *const msname[] = { 203 "USER", "SYSTEM", "TRAP", "TFAULT", "DFAULT", "KFAULT", 204 "USER_LOCK", "SLEEP", "WAIT_CPU", "STOPPED" 205 }; 206 207 char signame[SIG2STR_MAX]; 208 siginfo_t si; 209 int i; 210 211 if (argc != 0) 212 return (DCMD_USAGE); 213 214 if (mdb_vread(&si, sizeof (si), addr) != sizeof (si)) { 215 mdb_warn("failed to read siginfo at %p", addr); 216 return (DCMD_ERR); 217 } 218 219 if (sig2str(si.si_signo, signame) == -1) 220 (void) strcpy(signame, "unknown"); 221 222 mdb_printf(" signal %5d (%s)\n", si.si_signo, signame); 223 mdb_printf(" code %5d (", si.si_code); 224 225 switch (si.si_code) { 226 case SI_NOINFO: 227 mdb_printf("no info"); 228 break; 229 case SI_DTRACE: 230 mdb_printf("from DTrace raise() action"); 231 break; 232 case SI_RCTL: 233 mdb_printf("from rctl action"); 234 break; 235 case SI_USER: 236 mdb_printf("user generated via kill"); 237 break; 238 case SI_LWP: 239 mdb_printf("user generated via lwp_kill"); 240 break; 241 case SI_QUEUE: 242 mdb_printf("user generated via sigqueue"); 243 break; 244 case SI_TIMER: 245 mdb_printf("from timer expiration"); 246 break; 247 case SI_ASYNCIO: 248 mdb_printf("from async i/o completion"); 249 break; 250 case SI_MESGQ: 251 mdb_printf("from message arrival"); 252 break; 253 default: 254 if (SI_FROMUSER(&si)) 255 mdb_printf("from user process"); 256 else 257 mdb_printf("from kernel"); 258 } 259 260 mdb_printf(")\n errno %5d (%s)\n", 261 si.si_errno, strerror(si.si_errno)); 262 263 if (si.si_code == SI_USER || si.si_code == SI_QUEUE) { 264 mdb_printf(" signal sent from PID %d (uid %d)\n", 265 si.si_pid, si.si_uid); 266 } 267 268 if (si.si_code == SI_QUEUE) { 269 mdb_printf(" signal value = 0t%d / %p\n", 270 si.si_value.sival_int, si.si_value.sival_ptr); 271 } 272 273 switch (si.si_signo) { 274 case SIGCLD: 275 mdb_printf(" signal sent from child PID %d (uid %d)\n", 276 si.si_pid, si.si_uid); 277 mdb_printf(" usr time = 0t%ld ticks, sys time = 0t%ld ticks\n", 278 si.si_utime, si.si_stime); 279 mdb_printf(" wait status = 0x%x\n", si.si_status); 280 break; 281 282 case SIGSEGV: 283 case SIGBUS: 284 case SIGILL: 285 case SIGTRAP: 286 case SIGFPE: 287 mdb_printf(" fault address = 0x%p\n trapno = %d\n", 288 si.si_addr, si.si_trapno); 289 mdb_printf(" instruction address = 0x%p %lA\n", 290 si.si_pc, si.si_pc); 291 break; 292 293 case SIGPOLL: 294 case SIGXFSZ: 295 mdb_printf(" fd = %d band = 0x%lx\n", 296 si.si_fd, si.si_band); 297 break; 298 299 case SIGPROF: 300 mdb_printf(" last fault address = 0x%p fault type = %d\n", 301 si.si_faddr, si.si_fault); 302 mdb_printf(" timestamp = 0t%ld sec 0t%ld nsec\n", 303 si.si_tstamp.tv_sec, si.si_tstamp.tv_nsec); 304 305 if (si.__data.__prof.__syscall != 0) { 306 mdb_printf(" system call %d (", si.si_syscall); 307 if (si.si_nsysarg > 0) { 308 mdb_printf("%lx", si.si_sysarg[0]); 309 for (i = 1; i < si.si_nsysarg; i++) 310 mdb_printf(", %lx", si.si_sysarg[i]); 311 } 312 mdb_printf(" )\n"); 313 } 314 315 for (i = 0; i < sizeof (msname) / sizeof (msname[0]); i++) { 316 mdb_printf(" mstate[\"%s\"] = %d\n", 317 msname[i], si.si_mstate[i]); 318 } 319 break; 320 } 321 322 return (DCMD_OK); 323 } 324 325 static int 326 uc_walk_step(mdb_walk_state_t *wsp) 327 { 328 uintptr_t addr = wsp->walk_addr; 329 ucontext_t uc; 330 331 if (addr == 0) 332 return (WALK_DONE); 333 334 if (mdb_vread(&uc, sizeof (uc), addr) != sizeof (uc)) { 335 mdb_warn("failed to read ucontext at %p", addr); 336 return (WALK_ERR); 337 } 338 339 wsp->walk_addr = (uintptr_t)uc.uc_link; 340 return (wsp->walk_callback(addr, &uc, wsp->walk_cbdata)); 341 } 342 343 static int 344 oldc_walk_init(mdb_walk_state_t *wsp) 345 { 346 ssize_t nbytes = mdb_get_xdata("lwpstatus", NULL, 0); 347 348 if (nbytes <= 0) { 349 mdb_warn("lwpstatus information not available"); 350 return (WALK_ERR); 351 } 352 353 if (wsp->walk_addr != 0) { 354 mdb_warn("walker only supports global walk\n"); 355 return (WALK_ERR); 356 } 357 358 wsp->walk_addr = nbytes; /* Use walk_addr to track size */ 359 wsp->walk_data = mdb_alloc(nbytes, UM_SLEEP); 360 361 if (mdb_get_xdata("lwpstatus", wsp->walk_data, nbytes) != nbytes) { 362 mdb_warn("failed to read lwpstatus information"); 363 mdb_free(wsp->walk_data, nbytes); 364 return (WALK_ERR); 365 } 366 367 wsp->walk_arg = wsp->walk_data; /* Use walk_arg to track pointer */ 368 return (WALK_NEXT); 369 } 370 371 static int 372 oldc_walk_step(mdb_walk_state_t *wsp) 373 { 374 const lwpstatus_t *lsp, *end; 375 376 end = (const lwpstatus_t *)((uintptr_t)wsp->walk_data + wsp->walk_addr); 377 lsp = wsp->walk_arg; 378 379 wsp->walk_arg = (void *)(lsp + 1); 380 381 if (lsp < end) { 382 uintptr_t addr = lsp->pr_oldcontext; 383 ucontext_t uc; 384 385 if (addr == 0) 386 return (WALK_NEXT); 387 388 if (mdb_vread(&uc, sizeof (uc), addr) != sizeof (uc)) { 389 mdb_warn("failed to read ucontext at %p", addr); 390 return (WALK_NEXT); 391 } 392 393 return (wsp->walk_callback(addr, &uc, wsp->walk_cbdata)); 394 } 395 396 return (WALK_DONE); 397 } 398 399 static void 400 oldc_walk_fini(mdb_walk_state_t *wsp) 401 { 402 mdb_free(wsp->walk_data, wsp->walk_addr); /* walk_addr has size */ 403 } 404 405 /* 406 * ==================== threads ========================== 407 * These are the interfaces that used to require libthread. 408 * Now, libthread has been folded into libc. 409 * ======================================================= 410 */ 411 412 /* 413 * prt_addr() is called up to three times to generate arguments for 414 * one call to mdb_printf(). We must return at least three different 415 * pointers to static storage for consecutive calls to prt_addr(). 416 */ 417 static const char * 418 prt_addr(void *addr, int pad) 419 { 420 static char buffer[4][24]; 421 static int ix = 0; 422 char *buf; 423 424 if (ix == 4) /* use buffers in sequence: 0, 1, 2, 3 */ 425 ix = 0; 426 buf = buffer[ix++]; 427 if (addr == NULL) 428 return (pad? "<NULL> " : "<NULL>"); 429 else { 430 #ifdef _LP64 431 (void) mdb_snprintf(buf, sizeof (buffer[0]), "0x%016lx", addr); 432 if (pad) 433 (void) strcpy(buf + 18, " "); 434 #else 435 (void) mdb_snprintf(buf, sizeof (buffer[0]), "0x%08lx", addr); 436 if (pad) 437 (void) strcpy(buf + 10, " "); 438 #endif /* _LP64 */ 439 return (buf); 440 } 441 } 442 443 #define HD(str) mdb_printf(" " str "\n") 444 #define OFFSTR "+0x%-7lx " 445 #define OFFSET(member) ((size_t)OFFSETOF(ulwp_t, member)) 446 447 /*ARGSUSED*/ 448 static int 449 d_ulwp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 450 { 451 ulwp_t ulwp; 452 453 if (argc != 0 || !(flags & DCMD_ADDRSPEC)) 454 return (DCMD_USAGE); 455 456 if (mdb_vread(&ulwp, sizeof (ulwp), addr) != sizeof (ulwp) && 457 (bzero(&ulwp, sizeof (ulwp)), 458 mdb_vread(&ulwp, REPLACEMENT_SIZE, addr)) != REPLACEMENT_SIZE) { 459 mdb_warn("failed to read ulwp at 0x%p", addr); 460 return (DCMD_ERR); 461 } 462 463 mdb_printf("%#a\n", addr); 464 465 HD("self uberdata"); 466 mdb_printf(OFFSTR "%s %s\n", 467 OFFSET(ul_self), 468 prt_addr(ulwp.ul_self, 1), 469 prt_addr(ulwp.ul_uberdata, 0)); 470 471 HD("tlsent ntlsent"); 472 mdb_printf(OFFSTR "%s %ld\n", 473 OFFSET(ul_tlsent), 474 prt_addr(ulwp.ul_tlsent, 1), 475 ulwp.ul_ntlsent); 476 477 HD("forw back next"); 478 mdb_printf(OFFSTR "%s %s %s\n", 479 OFFSET(ul_forw), 480 prt_addr(ulwp.ul_forw, 1), 481 prt_addr(ulwp.ul_back, 1), 482 prt_addr(ulwp.ul_next, 0)); 483 484 HD("hash rval stk"); 485 mdb_printf(OFFSTR "%s %s %s\n", 486 OFFSET(ul_hash), 487 prt_addr(ulwp.ul_hash, 1), 488 prt_addr(ulwp.ul_rval, 1), 489 prt_addr(ulwp.ul_stk, 0)); 490 491 HD("mapsiz guardsize stktop stksiz"); 492 mdb_printf(OFFSTR "%-10ld %-10ld %s %ld\n", 493 OFFSET(ul_mapsiz), 494 ulwp.ul_mapsiz, 495 ulwp.ul_guardsize, 496 prt_addr((void *)ulwp.ul_stktop, 1), 497 ulwp.ul_stksiz); 498 499 HD("ustack.ss_sp ustack.ss_size ustack.ss_flags"); 500 mdb_printf(OFFSTR "%s %-21ld %s\n", 501 OFFSET(ul_ustack.ss_sp), 502 prt_addr(ulwp.ul_ustack.ss_sp, 1), 503 ulwp.ul_ustack.ss_size, 504 stack_flags(&ulwp.ul_ustack)); 505 506 HD("ix lwpid pri epri policy cid"); 507 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 508 OFFSET(ul_ix), 509 ulwp.ul_ix, 510 ulwp.ul_lwpid, 511 ulwp.ul_pri, 512 ulwp.ul_epri, 513 ulwp.ul_policy, 514 ulwp.ul_cid); 515 516 HD("cursig pleasestop stop signalled dead unwind"); 517 mdb_printf(OFFSTR "%-10d ", 518 OFFSET(ul_cursig), 519 ulwp.ul_cursig); 520 mdb_printf(ulwp.ul_pleasestop? "0x%-8x " : "%-10d ", 521 ulwp.ul_pleasestop); 522 mdb_printf(ulwp.ul_stop? "0x%-8x " : "%-10d ", 523 ulwp.ul_stop); 524 mdb_printf("%-10d %-10d %d\n", 525 ulwp.ul_signalled, 526 ulwp.ul_dead, 527 ulwp.ul_unwind); 528 529 HD("detached writer stopping can'prolog preempt savpreempt"); 530 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 531 OFFSET(ul_detached), 532 ulwp.ul_detached, 533 ulwp.ul_writer, 534 ulwp.ul_stopping, 535 ulwp.ul_cancel_prologue, 536 ulwp.ul_preempt, 537 ulwp.ul_savpreempt); 538 539 HD("sigsuspend main fork primarymap m'spinners d'noreserv"); 540 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 541 OFFSET(ul_sigsuspend), 542 ulwp.ul_sigsuspend, 543 ulwp.ul_main, 544 ulwp.ul_fork, 545 ulwp.ul_primarymap, 546 ulwp.ul_max_spinners, 547 ulwp.ul_door_noreserve); 548 549 HD("queue_fifo c'w'defer e'detect' async_safe rt rtqueued"); 550 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 551 OFFSET(ul_queue_fifo), 552 ulwp.ul_queue_fifo, 553 ulwp.ul_cond_wait_defer, 554 ulwp.ul_error_detection, 555 ulwp.ul_async_safe, 556 ulwp.ul_rt, 557 ulwp.ul_rtqueued); 558 559 HD("misaligned adapt'spin queue_spin critical sigdefer vfork"); 560 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 561 OFFSET(ul_misaligned), 562 ulwp.ul_misaligned, 563 ulwp.ul_adaptive_spin, 564 ulwp.ul_queue_spin, 565 ulwp.ul_critical, 566 ulwp.ul_sigdefer, 567 ulwp.ul_vfork); 568 569 HD("cancelable c'pending c'disabled c'async save_async mutator"); 570 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 571 OFFSET(ul_cancelable), 572 ulwp.ul_cancelable, 573 ulwp.ul_cancel_pending, 574 ulwp.ul_cancel_disabled, 575 ulwp.ul_cancel_async, 576 ulwp.ul_save_async, 577 ulwp.ul_mutator); 578 579 HD("created replace nocancel errno errnop"); 580 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %s\n", 581 OFFSET(ul_created), 582 ulwp.ul_created, 583 ulwp.ul_replace, 584 ulwp.ul_nocancel, 585 ulwp.ul_errno, 586 prt_addr(ulwp.ul_errnop, 0)); 587 588 HD("clnup_hdr schedctl_called schedctl"); 589 mdb_printf(OFFSTR "%s %s %s\n", 590 OFFSET(ul_clnup_hdr), 591 prt_addr(ulwp.ul_clnup_hdr, 1), 592 prt_addr(ulwp.ul_schedctl_called, 1), 593 prt_addr((void *)ulwp.ul_schedctl, 0)); 594 595 HD("bindflags libc_locks stsd &ftsd"); 596 mdb_printf(OFFSTR, 597 OFFSET(ul_bindflags)); 598 mdb_printf(ulwp.ul_bindflags? "0x%-8x " : "%-10d ", 599 ulwp.ul_bindflags); 600 mdb_printf("%-10d ", ulwp.ul_libc_locks); 601 mdb_printf("%s %s\n", 602 prt_addr(ulwp.ul_stsd, 1), 603 prt_addr((void *)(addr + OFFSET(ul_ftsd[0])), 0)); 604 605 HD("eventmask[0..1] eventnum eventdata"); 606 mdb_printf(OFFSTR "0x%08x 0x%08x %-21d %s\n", 607 OFFSET(ul_td_evbuf.eventmask.event_bits[0]), 608 ulwp.ul_td_evbuf.eventmask.event_bits[0], 609 ulwp.ul_td_evbuf.eventmask.event_bits[1], 610 ulwp.ul_td_evbuf.eventnum, 611 prt_addr(ulwp.ul_td_evbuf.eventdata, 0)); 612 613 HD("td'enable sync'reg qtype cv_wake rtld usropts"); 614 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d ", 615 OFFSET(ul_td_events_enable), 616 ulwp.ul_td_events_enable, 617 ulwp.ul_sync_obj_reg, 618 ulwp.ul_qtype, 619 ulwp.ul_cv_wake, 620 ulwp.ul_rtld); 621 mdb_printf(ulwp.ul_usropts? "0x%x\n" : "%d\n", 622 ulwp.ul_usropts); 623 624 HD("startpc startarg wchan"); 625 mdb_printf(OFFSTR "%s %s %s\n", 626 OFFSET(ul_startpc), 627 prt_addr((void *)ulwp.ul_startpc, 1), 628 prt_addr(ulwp.ul_startarg, 1), 629 prt_addr(ulwp.ul_wchan, 0)); 630 631 HD("link sleepq cvmutex"); 632 mdb_printf(OFFSTR "%s %s %s\n", 633 OFFSET(ul_link), 634 prt_addr(ulwp.ul_link, 1), 635 prt_addr(ulwp.ul_sleepq, 1), 636 prt_addr(ulwp.ul_cvmutex, 0)); 637 638 HD("mxchain save_state"); 639 mdb_printf(OFFSTR "%s %d\n", 640 OFFSET(ul_mxchain), 641 prt_addr(ulwp.ul_mxchain, 1), 642 ulwp.ul_save_state); 643 644 HD("rdlockcnt rd_rwlock rd_count"); 645 mdb_printf(OFFSTR "%-21d %s %d\n", 646 OFFSET(ul_rdlockcnt), 647 ulwp.ul_rdlockcnt, 648 prt_addr(ulwp.ul_readlock.single.rd_rwlock, 1), 649 ulwp.ul_readlock.single.rd_count); 650 651 HD("heldlockcnt heldlocks tpdp"); 652 mdb_printf(OFFSTR "%-21d %s %s\n", 653 OFFSET(ul_heldlockcnt), 654 ulwp.ul_heldlockcnt, 655 prt_addr(ulwp.ul_heldlocks.single, 1), 656 prt_addr(ulwp.ul_tpdp, 0)); 657 658 HD("siglink s'l'spin s'l'spin2 s'l'sleep s'l'wakeup"); 659 mdb_printf(OFFSTR "%s %-10d %-10d %-10d %d\n", 660 OFFSET(ul_siglink), 661 prt_addr(ulwp.ul_siglink, 1), 662 ulwp.ul_spin_lock_spin, 663 ulwp.ul_spin_lock_spin2, 664 ulwp.ul_spin_lock_sleep, 665 ulwp.ul_spin_lock_wakeup); 666 667 HD("&queue_root rtclassid pilocks"); 668 mdb_printf(OFFSTR "%s %-10d %d\n", 669 OFFSET(ul_queue_root), 670 prt_addr((void *)(addr + OFFSET(ul_queue_root)), 1), 671 ulwp.ul_rtclassid, 672 ulwp.ul_pilocks); 673 674 /* 675 * The remainder of the ulwp_t structure 676 * is invalid if this is a replacement. 677 */ 678 if (ulwp.ul_replace) 679 return (DCMD_OK); 680 681 HD("sigmask[0..3]"); 682 mdb_printf(OFFSTR "0x%08x 0x%08x 0x%08x 0x%08x\n", 683 OFFSET(ul_sigmask.__sigbits[0]), 684 ulwp.ul_sigmask.__sigbits[0], 685 ulwp.ul_sigmask.__sigbits[1], 686 ulwp.ul_sigmask.__sigbits[2], 687 ulwp.ul_sigmask.__sigbits[3]); 688 689 HD("tmpmask[0..3]"); 690 mdb_printf(OFFSTR "0x%08x 0x%08x 0x%08x 0x%08x\n", 691 OFFSET(ul_tmpmask.__sigbits[0]), 692 ulwp.ul_tmpmask.__sigbits[0], 693 ulwp.ul_tmpmask.__sigbits[1], 694 ulwp.ul_tmpmask.__sigbits[2], 695 ulwp.ul_tmpmask.__sigbits[3]); 696 697 HD("&siginfo &spinlock &fpuenv"); 698 mdb_printf(OFFSTR "%s %s %s\n", 699 OFFSET(ul_siginfo), 700 prt_addr((void *)(addr + OFFSET(ul_siginfo)), 1), 701 prt_addr((void *)(addr + OFFSET(ul_spinlock)), 1), 702 prt_addr((void *)(addr + OFFSET(ul_fpuenv)), 0)); 703 704 HD("tmem.size &tmem.roots"); 705 mdb_printf(OFFSTR "%-21H %s\n", 706 OFFSET(ul_tmem), 707 ulwp.ul_tmem.tm_size, 708 prt_addr((void *)(addr + OFFSET(ul_tmem) + sizeof (size_t)), 0)); 709 710 return (DCMD_OK); 711 } 712 713 /* 714 * Get the address of the unique uberdata_t structure. 715 */ 716 static uintptr_t 717 uberdata_addr(void) 718 { 719 uintptr_t uaddr; 720 uintptr_t addr; 721 GElf_Sym sym; 722 723 if (mdb_lookup_by_obj("libc.so.1", "_tdb_bootstrap", &sym) != 0) { 724 mdb_warn("cannot find libc.so.1`_tdb_bootstrap"); 725 return (0); 726 } 727 if (mdb_vread(&addr, sizeof (addr), sym.st_value) == sizeof (addr) && 728 addr != 0 && 729 mdb_vread(&uaddr, sizeof (uaddr), addr) == sizeof (uaddr) && 730 uaddr != 0) { 731 return (uaddr); 732 } 733 if (mdb_lookup_by_obj("libc.so.1", "_uberdata", &sym) != 0) { 734 mdb_warn("cannot find libc.so.1`_uberdata"); 735 return (0); 736 } 737 return ((uintptr_t)sym.st_value); 738 } 739 740 #undef OFFSET 741 #define OFFSET(member) ((size_t)OFFSETOF(uberdata_t, member)) 742 743 /*ARGSUSED*/ 744 static int 745 d_uberdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 746 { 747 uberdata_t uberdata; 748 int i; 749 750 if (argc != 0) 751 return (DCMD_USAGE); 752 if (!(flags & DCMD_ADDRSPEC) && (addr = uberdata_addr()) == 0) 753 return (DCMD_ERR); 754 755 if (mdb_vread(&uberdata, sizeof (uberdata), addr) != 756 sizeof (uberdata)) { 757 mdb_warn("failed to read uberdata at 0x%p", addr); 758 return (DCMD_ERR); 759 } 760 761 mdb_printf("%#a\n", addr); 762 763 HD("&link_lock &ld_lock &fork_lock"); 764 mdb_printf(OFFSTR "%s %s %s\n", 765 OFFSET(link_lock), 766 prt_addr((void *)(addr + OFFSET(link_lock)), 1), 767 prt_addr((void *)(addr + OFFSET(ld_lock)), 1), 768 prt_addr((void *)(addr + OFFSET(fork_lock)), 0)); 769 770 HD("&atfork_lock &callout_lock &tdb_hash_lock"); 771 mdb_printf(OFFSTR "%s %s %s\n", 772 OFFSET(atfork_lock), 773 prt_addr((void *)(addr + OFFSET(atfork_lock)), 1), 774 prt_addr((void *)(addr + OFFSET(callout_lock)), 1), 775 prt_addr((void *)(addr + OFFSET(tdb_hash_lock)), 0)); 776 777 HD("&tdb_hash_lock_stats &siguaction[0]"); 778 mdb_printf(OFFSTR "%s %s\n", 779 OFFSET(tdb_hash_lock_stats), 780 prt_addr((void *)(addr + OFFSET(tdb_hash_lock_stats)), 1), 781 prt_addr((void *)(addr + OFFSET(siguaction)), 0)); 782 783 HD("&bucket free_list chunks"); 784 for (i = 0; i < NBUCKETS; i++) { 785 mdb_printf(OFFSTR "%s %s %ld\n", 786 OFFSET(bucket[i]), 787 prt_addr((void *)(addr + OFFSET(bucket[i])), 1), 788 prt_addr(uberdata.bucket[i].free_list, 1), 789 uberdata.bucket[i].chunks); 790 } 791 792 HD("&atexit_root head exit_frame_monitor"); 793 mdb_printf(OFFSTR "%s %s %s\n", 794 OFFSET(atexit_root), 795 prt_addr((void *)(addr + OFFSET(atexit_root.exitfns_lock)), 1), 796 prt_addr(uberdata.atexit_root.head, 1), 797 prt_addr(uberdata.atexit_root.exit_frame_monitor, 0)); 798 799 HD("&quickexit_root head"); 800 mdb_printf(OFFSTR "%s %s\n", 801 OFFSET(quickexit_root), 802 prt_addr((void *)(addr + OFFSET(quickexit_root.exitfns_lock)), 1), 803 prt_addr(uberdata.quickexit_root.head, 0)); 804 805 806 HD("&tsd_metadata tsdm_nkeys tsdm_nused tsdm_destro"); 807 mdb_printf(OFFSTR "%s %-10d %-10d %s\n", 808 OFFSET(tsd_metadata), 809 prt_addr((void *)(addr + OFFSET(tsd_metadata.tsdm_lock)), 1), 810 uberdata.tsd_metadata.tsdm_nkeys, 811 uberdata.tsd_metadata.tsdm_nused, 812 prt_addr((void *)uberdata.tsd_metadata.tsdm_destro, 0)); 813 814 HD("&tls_metadata tls_modinfo.data tls_modinfo.size"); 815 mdb_printf(OFFSTR "%s %s %ld\n", 816 OFFSET(tls_metadata), 817 prt_addr((void *)(addr + OFFSET(tls_metadata.tls_lock)), 1), 818 prt_addr(uberdata.tls_metadata.tls_modinfo.tls_data, 1), 819 uberdata.tls_metadata.tls_modinfo.tls_size); 820 821 HD(" static_tls.data static_tls.size"); 822 mdb_printf(OFFSTR "%s %s %ld\n", 823 OFFSET(tls_metadata.static_tls), 824 " ", 825 prt_addr(uberdata.tls_metadata.static_tls.tls_data, 1), 826 uberdata.tls_metadata.static_tls.tls_size); 827 828 HD("primary_ma bucket_ini uflags.mt uflags.pad uflags.trs uflags.ted"); 829 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 830 OFFSET(primary_map), 831 uberdata.primary_map, 832 uberdata.bucket_init, 833 uberdata.uberflags.uf_x.x_mt, 834 uberdata.uberflags.uf_x.x_pad, 835 uberdata.uberflags.uf_x.x_tdb_register_sync, 836 uberdata.uberflags.uf_x.x_thread_error_detection); 837 838 HD("queue_head thr_hash_table hash_size hash_mask"); 839 mdb_printf(OFFSTR "%s %s %-10d 0x%x\n", 840 OFFSET(queue_head), 841 prt_addr(uberdata.queue_head, 1), 842 prt_addr(uberdata.thr_hash_table, 1), 843 uberdata.hash_size, 844 uberdata.hash_mask); 845 846 HD("ulwp_one all_lwps all_zombies"); 847 mdb_printf(OFFSTR "%s %s %s\n", 848 OFFSET(ulwp_one), 849 prt_addr(uberdata.ulwp_one, 1), 850 prt_addr(uberdata.all_lwps, 1), 851 prt_addr(uberdata.all_zombies, 0)); 852 853 HD("nthreads nzombies ndaemons pid sigacthandler"); 854 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %s\n", 855 OFFSET(nthreads), 856 uberdata.nthreads, 857 uberdata.nzombies, 858 uberdata.ndaemons, 859 (int)uberdata.pid, 860 prt_addr((void *)uberdata.sigacthandler, 0)); 861 862 HD("lwp_stacks lwp_laststack nfreestack stk_cache"); 863 mdb_printf(OFFSTR "%s %s %-10d %d\n", 864 OFFSET(lwp_stacks), 865 prt_addr(uberdata.lwp_stacks, 1), 866 prt_addr(uberdata.lwp_laststack, 1), 867 uberdata.nfreestack, 868 uberdata.thread_stack_cache); 869 870 HD("ulwp_freelist ulwp_lastfree ulwp_replace_free"); 871 mdb_printf(OFFSTR "%s %s %s\n", 872 OFFSET(ulwp_freelist), 873 prt_addr(uberdata.ulwp_freelist, 1), 874 prt_addr(uberdata.ulwp_lastfree, 1), 875 prt_addr(uberdata.ulwp_replace_free, 0)); 876 877 HD("ulwp_replace_last atforklist"); 878 mdb_printf(OFFSTR "%s %s\n", 879 OFFSET(ulwp_replace_last), 880 prt_addr(uberdata.ulwp_replace_last, 1), 881 prt_addr(uberdata.atforklist, 0)); 882 883 HD("robustlocks robustlist progname"); 884 mdb_printf(OFFSTR "%s %s %s\n", 885 OFFSET(robustlocks), 886 prt_addr(uberdata.robustlocks, 1), 887 prt_addr(uberdata.robustlist, 1), 888 prt_addr(uberdata.progname, 0)); 889 890 HD("tdb_bootstrap tdb_sync_addr_hash tdb_'count tdb_'fail"); 891 mdb_printf(OFFSTR "%s %s %-10d %d\n", 892 OFFSET(tdb_bootstrap), 893 prt_addr(uberdata.tdb_bootstrap, 1), 894 prt_addr(uberdata.tdb.tdb_sync_addr_hash, 1), 895 uberdata.tdb.tdb_register_count, 896 uberdata.tdb.tdb_hash_alloc_failed); 897 898 HD("tdb_sync_addr_free tdb_sync_addr_last tdb_sync_alloc"); 899 mdb_printf(OFFSTR "%s %s %ld\n", 900 OFFSET(tdb.tdb_sync_addr_free), 901 prt_addr(uberdata.tdb.tdb_sync_addr_free, 1), 902 prt_addr(uberdata.tdb.tdb_sync_addr_last, 1), 903 uberdata.tdb.tdb_sync_alloc); 904 905 HD("tdb_ev_global_mask tdb_events"); 906 mdb_printf(OFFSTR "0x%08x 0x%08x %s\n", 907 OFFSET(tdb.tdb_ev_global_mask), 908 uberdata.tdb.tdb_ev_global_mask.event_bits[0], 909 uberdata.tdb.tdb_ev_global_mask.event_bits[1], 910 prt_addr((void *)uberdata.tdb.tdb_events, 0)); 911 912 return (DCMD_OK); 913 } 914 915 static int 916 ulwp_walk_init(mdb_walk_state_t *wsp) 917 { 918 uintptr_t addr = wsp->walk_addr; 919 uintptr_t uber_addr; 920 int offset; 921 922 offset = mdb_ctf_offsetof_by_name("uberdata_t", "all_lwps"); 923 if (offset == -1) { 924 offset = OFFSETOF(uberdata_t, all_lwps); 925 mdb_warn("CTF data is missing for uberdata_t; using current " 926 "platform's offset for uberdata.all_lwps"); 927 } 928 929 if (addr == 0 && 930 ((uber_addr = uberdata_addr()) == 0 || 931 mdb_vread(&addr, sizeof (addr), uber_addr + offset) 932 != sizeof (addr))) { 933 mdb_warn("cannot find 'uberdata.all_lwps'"); 934 return (WALK_ERR); 935 } 936 if (addr == 0) 937 return (WALK_DONE); 938 wsp->walk_addr = addr; 939 wsp->walk_data = (void *)addr; 940 return (WALK_NEXT); 941 } 942 943 static int 944 ulwp_walk_step(mdb_walk_state_t *wsp) 945 { 946 uintptr_t addr = wsp->walk_addr; 947 ulwp_t ulwp; 948 949 if (addr == 0) 950 return (WALK_DONE); 951 if (mdb_vread(&ulwp, sizeof (ulwp), addr) != sizeof (ulwp) && 952 (bzero(&ulwp, sizeof (ulwp)), 953 mdb_vread(&ulwp, REPLACEMENT_SIZE, addr)) != REPLACEMENT_SIZE) { 954 mdb_warn("failed to read ulwp at 0x%p", addr); 955 return (WALK_ERR); 956 } 957 /* 958 * If we have looped around to the beginning 959 * of the circular linked list, we are done. 960 */ 961 if ((wsp->walk_addr = (uintptr_t)ulwp.ul_forw) 962 == (uintptr_t)wsp->walk_data) 963 wsp->walk_addr = 0; 964 return (wsp->walk_callback(addr, &ulwp, wsp->walk_cbdata)); 965 } 966 967 typedef struct lwp_wchan { 968 uint8_t wchan_flag[4]; 969 uint16_t wchan_type; 970 uint16_t wchan_magic; 971 } lwp_wchan_t; 972 973 static int 974 wchan_walk_init(mdb_walk_state_t *wsp) 975 { 976 if (wsp->walk_addr != 0) { 977 mdb_warn("wchan walk only supports global walks"); 978 return (WALK_ERR); 979 } 980 981 if (mdb_layered_walk("ulwp", wsp) == -1) { 982 mdb_warn("couldn't walk ulwp"); 983 return (WALK_ERR); 984 } 985 986 return (WALK_NEXT); 987 } 988 989 static int 990 wchan_walk_step(mdb_walk_state_t *wsp) 991 { 992 uintptr_t addr = (uintptr_t)(((ulwp_t *)wsp->walk_layer)->ul_wchan); 993 lwp_wchan_t wchan; 994 995 if (addr == (uintptr_t)NULL) { 996 return (WALK_NEXT); 997 } 998 999 if (mdb_vread(&wchan, sizeof (wchan), addr) != sizeof (wchan)) { 1000 mdb_warn("failed to read wchan at 0x%p", addr); 1001 return (WALK_ERR); 1002 } 1003 1004 return (wsp->walk_callback(addr, &wchan, wsp->walk_cbdata)); 1005 } 1006 1007 /* Avoid classifying NULL pointers as part of the main stack on x86 */ 1008 #define MIN_STACK_ADDR (0x10000ul) 1009 1010 static int 1011 whatis_walk_ulwp(uintptr_t addr, const ulwp_t *ulwp, mdb_whatis_t *w) 1012 { 1013 uintptr_t cur; 1014 lwpid_t id = ulwp->ul_lwpid; 1015 uintptr_t top, base, size; 1016 1017 while (mdb_whatis_match(w, addr, sizeof (ulwp_t), &cur)) 1018 mdb_whatis_report_object(w, cur, addr, 1019 "allocated as thread %#r's ulwp_t\n", id); 1020 1021 top = (uintptr_t)ulwp->ul_stktop; 1022 size = ulwp->ul_stksiz; 1023 1024 /* 1025 * The main stack ends up being a little weird, especially if 1026 * the stack ulimit is unlimited. This tries to take that into 1027 * account. 1028 */ 1029 if (size > top) 1030 size = top; 1031 if (top > MIN_STACK_ADDR && top - size < MIN_STACK_ADDR) 1032 size = top - MIN_STACK_ADDR; 1033 1034 base = top - size; 1035 1036 while (mdb_whatis_match(w, base, size, &cur)) 1037 mdb_whatis_report_address(w, cur, "in [ stack tid=%#r ]\n", id); 1038 1039 if (ulwp->ul_ustack.ss_flags & SS_ONSTACK) { 1040 base = (uintptr_t)ulwp->ul_ustack.ss_sp; 1041 size = ulwp->ul_ustack.ss_size; 1042 1043 while (mdb_whatis_match(w, base, size, &cur)) 1044 mdb_whatis_report_address(w, cur, 1045 "in [ altstack tid=%#r ]\n", id); 1046 } 1047 1048 return (WHATIS_WALKRET(w)); 1049 } 1050 1051 /*ARGSUSED*/ 1052 static int 1053 whatis_run_ulwps(mdb_whatis_t *w, void *arg) 1054 { 1055 if (mdb_walk("ulwps", (mdb_walk_cb_t)whatis_walk_ulwp, w) == -1) { 1056 mdb_warn("couldn't find ulwps walker"); 1057 return (1); 1058 } 1059 return (0); 1060 } 1061 1062 /* 1063 * ======================================================= 1064 * End of thread (previously libthread) interfaces. 1065 * ==================== threads ========================== 1066 */ 1067 1068 int 1069 stacks_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1070 { 1071 int rval = stacks(addr, flags, argc, argv); 1072 1073 /* 1074 * For the user-level variant of ::stacks, we don't bother caching 1075 * state, as even a very large program is unlikely to compare to the 1076 * kernel in terms of number of threads. (And if you find yourself 1077 * here in anger, frustrated about how long ::stacks is running on 1078 * your galactically complicated zillion-thread program, hopefully 1079 * you will find some solace in the irony. Okay, probably not...) 1080 */ 1081 stacks_cleanup(B_TRUE); 1082 return (rval); 1083 } 1084 1085 typedef struct tid2ulwp_walk { 1086 lwpid_t t2u_tid; 1087 uintptr_t t2u_lwp; 1088 boolean_t t2u_found; 1089 } tid2ulwp_walk_t; 1090 1091 /*ARGSUSED*/ 1092 static int 1093 tid2ulwp_walk(uintptr_t addr, ulwp_t *ulwp, tid2ulwp_walk_t *t2u) 1094 { 1095 if (ulwp->ul_lwpid == t2u->t2u_tid) { 1096 t2u->t2u_lwp = addr; 1097 t2u->t2u_found = B_TRUE; 1098 return (WALK_DONE); 1099 } 1100 1101 return (WALK_NEXT); 1102 } 1103 1104 static int 1105 tid2ulwp_impl(uintptr_t tid_addr, uintptr_t *ulwp_addrp) 1106 { 1107 tid2ulwp_walk_t t2u; 1108 1109 bzero(&t2u, sizeof (t2u)); 1110 t2u.t2u_tid = (lwpid_t)tid_addr; 1111 1112 if (mdb_walk("ulwp", (mdb_walk_cb_t)tid2ulwp_walk, &t2u) != 0) { 1113 mdb_warn("can't walk 'ulwp'"); 1114 return (DCMD_ERR); 1115 } 1116 1117 if (!t2u.t2u_found) { 1118 mdb_warn("thread ID %d not found", t2u.t2u_tid); 1119 return (DCMD_ERR); 1120 } 1121 *ulwp_addrp = t2u.t2u_lwp; 1122 return (DCMD_OK); 1123 } 1124 1125 /*ARGSUSED*/ 1126 static int 1127 tid2ulwp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1128 { 1129 uintptr_t ulwp_addr; 1130 int error; 1131 1132 if (argc != 0) 1133 return (DCMD_USAGE); 1134 1135 error = tid2ulwp_impl(addr, &ulwp_addr); 1136 if (error == DCMD_OK) 1137 mdb_printf("%p\n", ulwp_addr); 1138 return (error); 1139 } 1140 1141 /* 1142 * This is used by both d_tsd and d_errno, and contains the sum of all 1143 * members used by both commands. 1144 */ 1145 typedef struct mdb_libc_ulwp { 1146 void *ul_ftsd[TSD_NFAST]; 1147 tsd_t *ul_stsd; 1148 int *ul_errnop; 1149 } mdb_libc_ulwp_t; 1150 1151 /* 1152 * Map from thread pointer to tsd for given key 1153 */ 1154 static int 1155 d_tsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1156 { 1157 mdb_libc_ulwp_t u; 1158 uintptr_t ulwp_addr; 1159 uintptr_t key = 0; 1160 void *element = NULL; 1161 1162 if (mdb_getopts(argc, argv, 'k', MDB_OPT_UINTPTR, &key, NULL) != argc) 1163 return (DCMD_USAGE); 1164 1165 if (!(flags & DCMD_ADDRSPEC) || key == 0) 1166 return (DCMD_USAGE); 1167 1168 if (tid2ulwp_impl(addr, &ulwp_addr) != DCMD_OK) 1169 return (DCMD_ERR); 1170 1171 if (mdb_ctf_vread(&u, "ulwp_t", "mdb_libc_ulwp_t", ulwp_addr, 0) == -1) 1172 return (DCMD_ERR); 1173 1174 if (key < TSD_NFAST) { 1175 element = u.ul_ftsd[key]; 1176 } else if (u.ul_stsd != NULL) { 1177 uint_t nalloc; 1178 /* tsd_t is a union, so we can't use ctf_vread() on it. */ 1179 if (mdb_vread(&nalloc, sizeof (nalloc), 1180 (uintptr_t)&u.ul_stsd->tsd_nalloc) == -1) { 1181 mdb_warn("failed to read tsd_t at %p", u.ul_stsd); 1182 return (DCMD_ERR); 1183 } 1184 if (key < nalloc) { 1185 if (mdb_vread(&element, sizeof (element), 1186 (uintptr_t)&u.ul_stsd->tsd_data[key]) == -1) { 1187 mdb_warn("failed to read tsd_t at %p", 1188 u.ul_stsd); 1189 return (DCMD_ERR); 1190 } 1191 } 1192 } 1193 1194 if (element == NULL && (flags & DCMD_PIPE)) 1195 return (DCMD_OK); 1196 1197 mdb_printf("%p\n", element); 1198 return (DCMD_OK); 1199 } 1200 1201 /* 1202 * Print percent from 16-bit binary fraction [0 .. 1] 1203 * Round up .01 to .1 to indicate some small percentage (the 0x7000 below). 1204 * 1205 * Note: This routine was copied from elfdump/common/corenote.c and modified. 1206 * 1207 */ 1208 static uint_t 1209 pct_value(ushort_t pct) 1210 { 1211 uint_t value = pct; 1212 1213 value = ((value * 1000) + 0x7000) >> 15; /* [0 .. 1000] */ 1214 if (value >= 1000) 1215 value = 999; 1216 1217 return (value); 1218 } 1219 1220 static void 1221 psinfo_raw(psinfo_t psinfo) 1222 { 1223 const int minspaces = 2; 1224 const int spbcols = 20; 1225 char sysname[SYS2STR_MAX]; 1226 uint_t cpu, mem; 1227 char buff[32]; 1228 int bufflen; 1229 1230 mdb_printf("[ NT_PRPSINFO ]\n"); 1231 1232 mdb_printf("\tpr_state: %d\t\t\tpr_sname: %c\n", 1233 psinfo.pr_lwp.pr_state, psinfo.pr_lwp.pr_sname); 1234 1235 mdb_printf("\tpr_zomb: %d\t\t\tpr_nice: %d\n", 1236 psinfo.pr_nzomb, psinfo.pr_lwp.pr_nice); 1237 1238 mdb_printf("\tpr_uid: %u\t\t\tpr_gid: %u\n", 1239 psinfo.pr_uid, psinfo.pr_gid); 1240 1241 mdb_snprintf(buff, sizeof (buff), 1242 "%d", psinfo.pr_pid); 1243 1244 bufflen = strlen(buff); 1245 mdb_printf("\tpr_pid: %s%*spr_ppid: %d\n", 1246 buff, strlen(buff) > spbcols ? minspaces : (spbcols - bufflen), " ", 1247 psinfo.pr_ppid); 1248 1249 mdb_printf("\tpr_pgid: %u\t\t\tpr_sid: %d\n", 1250 psinfo.pr_gid, psinfo.pr_sid); 1251 1252 mdb_snprintf(buff, sizeof (buff), 1253 "0x%lx", (ulong_t)psinfo.pr_addr); 1254 1255 bufflen = strlen(buff); 1256 1257 mdb_printf("\tpr_addr: %s%*spr_size: %#x\n", 1258 buff, strlen(buff) > spbcols ? minspaces : (spbcols - bufflen), " ", 1259 (ulong_t)psinfo.pr_size); 1260 1261 mdb_printf("\tpr_rssize: %#lx\t\tpr_wchan: %#lx\n", 1262 (ulong_t)psinfo.pr_rssize, (ulong_t)psinfo.pr_lwp.pr_wchan); 1263 1264 mdb_printf("\tpr_start:\n\t tv_sec: %ld\t\ttv_nsec: %ld\n", 1265 psinfo.pr_start.tv_sec, psinfo.pr_start.tv_nsec); 1266 1267 mdb_printf("\tpr_time:\n\t tv_sec: %ld\t\t\ttv_nsec: %ld\n", 1268 psinfo.pr_time.tv_sec, psinfo.pr_time.tv_nsec); 1269 1270 mdb_printf("\tpr_pri: %d\t\t\tpr_oldpri: %d\n", 1271 psinfo.pr_lwp.pr_pri, psinfo.pr_lwp.pr_oldpri); 1272 1273 mdb_printf("\tpr_cpu: %d\n", psinfo.pr_lwp.pr_cpu); 1274 1275 mdb_printf("\tpr_clname: %s\n", psinfo.pr_lwp.pr_clname); 1276 1277 mdb_printf("\tpr_fname: %s\n", psinfo.pr_fname); 1278 1279 mdb_printf("\tpr_psargs: %s\n", psinfo.pr_psargs); 1280 1281 1282 mdb_printf("\tpr_syscall: [ %s ]\n", 1283 proc_sysname(psinfo.pr_lwp.pr_syscall, sysname, 1284 sizeof (sysname))); 1285 1286 mdb_printf("\tpr_ctime:\n\t tv_sec: %ld\t\t\ttv_nsec: %ld\n", 1287 psinfo.pr_ctime.tv_sec, psinfo.pr_ctime.tv_nsec); 1288 1289 mdb_printf("\tpr_argc: %d\t\t\tpr_argv: 0x%lx\n", 1290 psinfo.pr_argc, (ulong_t)psinfo.pr_argv); 1291 1292 mdb_snprintf(buff, sizeof (buff), "0x%lx", (ulong_t)psinfo.pr_envp); 1293 1294 bufflen = strlen(buff); 1295 1296 mdb_printf("\tpr_envp: %s%*spr_wstat: %d\n", 1297 buff, strlen(buff) > spbcols ? minspaces : (spbcols - bufflen), " ", 1298 psinfo.pr_wstat); 1299 1300 cpu = pct_value(psinfo.pr_pctcpu); 1301 mem = pct_value(psinfo.pr_pctmem); 1302 1303 mdb_printf("\tpr_pctcpu: %u.%u%%\t\tpr_pctmem: %u.%u%%\n", 1304 cpu / 10, cpu % 10, mem / 10, mem % 10); 1305 1306 mdb_printf("\tpr_euid: %u\t\t\tpr_egid: %u\n", 1307 psinfo.pr_euid, psinfo.pr_egid); 1308 1309 mdb_printf("\tpr_dmodel: [%s]\n", 1310 proc_dmodelname(psinfo.pr_dmodel, buff, sizeof (buff))); 1311 } 1312 1313 static void 1314 psinfo_sum(psinfo_t psinfo) 1315 { 1316 const int minspaces = 2; 1317 const int spbcols = 23; 1318 char buff[64]; 1319 int bufflen; 1320 int ms; 1321 1322 mdb_printf("PID: %6d (process id)\t\t" 1323 "UID: %4u (real user id)\n", 1324 psinfo.pr_pid, psinfo.pr_uid); 1325 1326 mdb_printf("PPID: %6d (parent process id)\tEUID: %4d" 1327 " (effective user id)\n", psinfo.pr_ppid, psinfo.pr_euid); 1328 1329 mdb_printf("PGID: %6d (process group id)\tGID: %4u" 1330 " (real group id)\n", psinfo.pr_pgid, psinfo.pr_gid); 1331 1332 mdb_printf("SID: %6d (session id)\t\tEGID: %4u" 1333 " (effective group id)\n", 1334 psinfo.pr_sid, psinfo.pr_egid); 1335 1336 mdb_printf("ZONEID: %6d\t\t\t\tCONTRACT:%4d\n", 1337 psinfo.pr_zoneid, psinfo.pr_contract); 1338 1339 mdb_printf("PROJECT:%6d \t\t\t\tTASK: %4d\n\n", 1340 psinfo.pr_projid, psinfo.pr_taskid); 1341 1342 mdb_printf("START: %Y (wall timestamp when the process started)\n", 1343 psinfo.pr_start); 1344 1345 ms = NSEC2MSEC(psinfo.pr_time.tv_nsec); 1346 1347 mdb_snprintf(buff, sizeof (buff), "%ld.%d seconds", 1348 psinfo.pr_time.tv_sec, ms); 1349 1350 bufflen = strlen(buff); 1351 1352 mdb_printf("TIME: %s%*s" 1353 "(CPU time used by this process)\n", 1354 buff, bufflen > spbcols ? minspaces : (spbcols - bufflen), " "); 1355 1356 ms = NSEC2MSEC(psinfo.pr_ctime.tv_nsec); 1357 1358 mdb_snprintf(buff, sizeof (buff), "%ld.%d seconds", 1359 psinfo.pr_ctime.tv_sec, ms); 1360 1361 mdb_printf("CTIME: %s%*s" 1362 "(CPU time used by child processes)\n", 1363 buff, bufflen > spbcols ? minspaces : (spbcols - bufflen), " "); 1364 1365 mdb_snprintf(buff, sizeof (buff), "%s", psinfo.pr_fname); 1366 bufflen = strlen(buff); 1367 1368 mdb_printf("FNAME: %s%*s(name of the program executed)\n", 1369 buff, bufflen > spbcols ? minspaces : (spbcols - bufflen), " "); 1370 1371 mdb_printf("PSARGS: \"%s\"\n", psinfo.pr_psargs); 1372 } 1373 1374 void 1375 d_psinfo_dcmd_help(void) 1376 { 1377 mdb_printf( 1378 "Prints relevant fields from psinfo_t data and\n" 1379 "most fields from NT_PRPSINFO note section\n\n" 1380 "Usage: ::psinfo [-v]\n" 1381 "Options:\n" 1382 " -v verbose output\n"); 1383 } 1384 1385 static int 1386 d_psinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1387 { 1388 psinfo_t psinfo; 1389 uint_t opt_v = FALSE; 1390 ssize_t nbytes; 1391 1392 if (mdb_getopts(argc, argv, 'v', 1393 MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc) 1394 return (DCMD_USAGE); 1395 1396 nbytes = mdb_get_xdata("psinfo", NULL, 0); 1397 1398 if (nbytes <= 0) { 1399 mdb_warn("information not available for analysis"); 1400 return (DCMD_ERR); 1401 } 1402 1403 if (mdb_get_xdata("psinfo", &psinfo, nbytes) != nbytes) { 1404 mdb_warn("failed to read psinfo information"); 1405 return (DCMD_ERR); 1406 } 1407 1408 if (opt_v) { 1409 psinfo_raw(psinfo); 1410 } else { 1411 psinfo_sum(psinfo); 1412 } 1413 1414 return (DCMD_OK); 1415 } 1416 1417 typedef struct d_mutex_output { 1418 char *mo_output; 1419 struct d_mutex_output *mo_next; 1420 } d_mutex_output_t; 1421 1422 static void 1423 d_mutex_output_push(d_mutex_output_t **head, const char *out) 1424 { 1425 d_mutex_output_t *new; 1426 size_t len = strlen(out) + 1; 1427 1428 new = mdb_alloc(sizeof (d_mutex_output_t), UM_SLEEP | UM_GC); 1429 new->mo_next = *head; 1430 new->mo_output = mdb_alloc(len, UM_SLEEP | UM_GC); 1431 bcopy(out, new->mo_output, len); 1432 1433 *head = new; 1434 } 1435 1436 void 1437 d_mutex_output_reverse(d_mutex_output_t **head) 1438 { 1439 d_mutex_output_t *current, *next, *last = NULL; 1440 1441 for (current = *head; current != NULL; current = next) { 1442 next = current->mo_next; 1443 current->mo_next = last; 1444 last = current; 1445 1446 if (next == NULL) { 1447 break; 1448 } 1449 } 1450 1451 *head = current; 1452 } 1453 1454 typedef struct d_mutex_walkdata { 1455 uintptr_t mow_target; 1456 d_mutex_output_t *mow_output; 1457 } d_mutex_walkdata_t; 1458 1459 int 1460 d_mutex_walk(uintptr_t addr, const ulwp_t *ulwp, d_mutex_walkdata_t *wd) 1461 { 1462 char buf[256]; 1463 1464 if ((uintptr_t)ulwp->ul_wchan != wd->mow_target) 1465 return (WALK_NEXT); 1466 1467 if (mdb_thread_name(ulwp->ul_lwpid, buf, sizeof (buf)) != 0) { 1468 mdb_snprintf(buf, sizeof (buf), "0x%p", addr); 1469 } 1470 1471 d_mutex_output_push(&wd->mow_output, buf); 1472 return (WALK_NEXT); 1473 } 1474 1475 static void 1476 d_mutex_help(void) 1477 { 1478 mdb_printf("%s\n", 1479 "Dump a mutex, optionally decoding flags and displaying waiters.\n"); 1480 mdb_dec_indent(2); 1481 mdb_printf("%<b>OPTIONS%</b>\n"); 1482 mdb_inc_indent(2); 1483 mdb_printf("%s", 1484 " -v Dump verbosely, decoding type and flags and showing waiters\n" 1485 " -f force printing as a mutex, even if it doesn't appear to be one\n"); 1486 } 1487 1488 static int 1489 d_mutex(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1490 { 1491 lwp_mutex_t mutex; 1492 uintptr_t owner; 1493 char buf[256]; 1494 uint_t opt_v = FALSE, opt_f = FALSE, warn; 1495 d_mutex_walkdata_t wd; 1496 d_mutex_output_t *toutput = NULL, *foutput = NULL; 1497 size_t i; 1498 1499 1500 if (!(flags & DCMD_ADDRSPEC)) 1501 return (DCMD_USAGE); 1502 1503 if (mdb_getopts(argc, argv, 1504 'v', MDB_OPT_SETBITS, TRUE, &opt_v, 1505 'f', MDB_OPT_SETBITS, TRUE, &opt_f, 1506 NULL) != argc) { 1507 return (DCMD_USAGE); 1508 } 1509 1510 if (DCMD_HDRSPEC(flags)) { 1511 mdb_printf("%-16s %4s %4s %4s %s\n", 1512 "ADDR", "TYPE", "FLAG", "WTRS", "OWNER"); 1513 } 1514 1515 /* 1516 * If we aren't in a loop or a pipe, we will warn explicitly when 1517 * we can't make sense of a mutex. 1518 */ 1519 warn = (flags & (DCMD_LOOP | DCMD_PIPE)) ? FALSE : TRUE; 1520 1521 if (mdb_vread(&mutex, sizeof (mutex), addr) != sizeof (mutex)) { 1522 if (warn) 1523 mdb_warn("failed to read mutex at 0x%p", addr); 1524 1525 return (DCMD_ERR); 1526 } 1527 1528 /* 1529 * It's legal to have a zero'd mutex_t in BSS -- so we can only 1530 * rely on the magic to disambiguate a mutex if it is non-zero. 1531 */ 1532 if (!opt_f && mutex.flags.magic != 0 && 1533 mutex.flags.magic != MUTEX_MAGIC) { 1534 if (!warn) 1535 return (DCMD_ERR); 1536 1537 if (mutex.flags.magic == COND_MAGIC) { 1538 mdb_warn("0x%p is not a mutex (appears to be a " 1539 "condition variable)\n", addr); 1540 } else if (mutex.flags.magic == SEMA_MAGIC) { 1541 mdb_warn("0x%p is not a mutex (appears to be a " 1542 "semaphore)\n", addr); 1543 } else if (mutex.flags.magic == RWL_MAGIC) { 1544 mdb_warn("0x%p is not a mutex (appears to be a " 1545 "readers/writer lock)\n", addr); 1546 } else { 1547 mdb_warn("0x%p does not appear to be a mutex (expected " 1548 "0x%x, found 0x%x)\n", addr, MUTEX_MAGIC, 1549 mutex.flags.magic); 1550 } 1551 1552 return (DCMD_ERR); 1553 } 1554 1555 if (!opt_f) { 1556 /* 1557 * Sanity check that if we have an owner, it at least isn't 1558 * obviously not a ulwp_t. 1559 */ 1560 uintptr_t owner = mutex.mutex_owner; 1561 ulwp_t u; 1562 1563 if (owner == (uintptr_t)NULL) { 1564 if (mutex.mutex_waiters) { 1565 if (!warn) 1566 return (DCMD_ERR); 1567 1568 mdb_warn("0x%p does not appear to be a mutex " 1569 "(waiters, but no owner?)\n", addr); 1570 1571 return (DCMD_ERR); 1572 } 1573 } else if (mdb_vread(&u, sizeof (u), owner) != sizeof (u) || 1574 (uintptr_t)u.ul_self != owner) { 1575 if (!warn) 1576 return (DCMD_ERR); 1577 1578 mdb_warn("0x%p does not appear to be mutex " 1579 "(owner 0x%p does not appear to be a ulwp_t)\n", 1580 addr, owner); 1581 1582 return (DCMD_ERR); 1583 } 1584 } 1585 1586 mdb_printf("%-16p %4x %4x %4s ", addr, mutex.mutex_type, 1587 mutex.mutex_flag, 1588 mutex.mutex_waiters ? "yes" : "no"); 1589 1590 if ((owner = mutex.mutex_owner) == (uintptr_t)NULL) { 1591 mdb_printf("-\n"); 1592 } else { 1593 ulwp_t u; 1594 1595 if (mdb_vread(&u, sizeof (u), owner) != sizeof (u) || 1596 mdb_thread_name(u.ul_lwpid, buf, sizeof (buf)) != 0) { 1597 mdb_snprintf(buf, sizeof (buf), "%d", u.ul_lwpid); 1598 } 1599 1600 mdb_printf("%p %s\n", owner, buf); 1601 } 1602 1603 if (!opt_v) 1604 return (DCMD_OK); 1605 1606 static struct { 1607 int val; 1608 char *str; 1609 } tvals[] = { 1610 { 0x01, "LOCK_SHARED" }, 1611 { 0x02, "LOCK_ERRORCHECK" }, 1612 { 0x04, "LOCK_RECURSIVE" }, 1613 { 0x10, "LOCK_PRIO_INHERIT" }, 1614 { 0x20, "LOCK_PRIO_PROTECT" }, 1615 { 0x40, "LOCK_ROBUST" }, 1616 /* 1617 * This is a defunct type, but an ancient (or corrupt) mutex 1618 * might have it set; indicate it if we see it. 1619 */ 1620 { 0x08, "PROCESS_ROBUST" }, 1621 { 0, "" } 1622 }; 1623 1624 if (!(mutex.mutex_type & LOCK_SHARED)) { 1625 d_mutex_output_push(&toutput, "LOCK_NORMAL"); 1626 } 1627 1628 for (i = 0; tvals[i].val != 0; i++) { 1629 if ((mutex.mutex_type & tvals[i].val) != 0) { 1630 d_mutex_output_push(&toutput, tvals[i].str); 1631 } 1632 } 1633 1634 static struct { 1635 int val; 1636 char *str; 1637 } fvals[] = { 1638 { 0x1, "LOCK_OWNERDEAD" }, 1639 { 0x2, "LOCK_NOTRECOVERABLE" }, 1640 { 0x4, "LOCK_INITED" }, 1641 { 0x8, "LOCK_UNMAPPED" }, 1642 { 0, "" }, 1643 }; 1644 1645 for (i = 0; fvals[i].val != 0; i++) { 1646 if ((mutex.mutex_flag & fvals[i].val) != 0) { 1647 d_mutex_output_push(&foutput, fvals[i].str); 1648 } 1649 } 1650 1651 wd.mow_target = addr; 1652 wd.mow_output = NULL; 1653 1654 if (mdb_walk("ulwp", (mdb_walk_cb_t)d_mutex_walk, &wd) != 0) { 1655 mdb_warn("can't walk \"ulwp\""); 1656 return (DCMD_ERR); 1657 } 1658 1659 d_mutex_output_t *ooutput = wd.mow_output; 1660 d_mutex_output_reverse(&toutput); 1661 d_mutex_output_reverse(&foutput); 1662 d_mutex_output_reverse(&ooutput); 1663 1664 d_mutex_output_t *thead = toutput; 1665 d_mutex_output_t *fhead = foutput; 1666 d_mutex_output_t *ohead = ooutput; 1667 1668 mdb_printf("%21s", toutput != NULL ? "|" : ""); 1669 mdb_printf("%5s", foutput != NULL ? "|" : ""); 1670 mdb_printf("%3s\n", ooutput != NULL ? "|" : ""); 1671 1672 boolean_t needblank = ooutput != NULL; 1673 1674 while (toutput != NULL || foutput != NULL || ooutput != NULL) { 1675 if (toutput != NULL) { 1676 mdb_printf("%17s %s", toutput->mo_output, 1677 toutput == thead ? "<-+" : " "); 1678 toutput = toutput->mo_next; 1679 } else { 1680 mdb_printf("%21s", ""); 1681 } 1682 1683 if (foutput != NULL) { 1684 if (ooutput != NULL) { 1685 mdb_printf("%5s", "|"); 1686 } else { 1687 if (needblank) { 1688 mdb_printf("%5s", "|"); 1689 needblank = B_FALSE; 1690 } else { 1691 mdb_printf("%7s %s", 1692 foutput == fhead ? "+->" : "", 1693 foutput->mo_output); 1694 foutput = foutput->mo_next; 1695 } 1696 } 1697 } else { 1698 mdb_printf("%5s", ""); 1699 } 1700 1701 if (ooutput != NULL) { 1702 mdb_printf("%5s %s", 1703 ooutput == ohead ? "+->" : "", ooutput->mo_output); 1704 ooutput = ooutput->mo_next; 1705 } 1706 1707 mdb_printf("\n"); 1708 } 1709 1710 return (DCMD_OK); 1711 } 1712 1713 static int 1714 d_errno(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1715 { 1716 mdb_libc_ulwp_t u; 1717 uintptr_t ulwp_addr; 1718 int error, errval; 1719 1720 if (argc != 0 || (flags & DCMD_ADDRSPEC) == 0) 1721 return (DCMD_USAGE); 1722 1723 error = tid2ulwp_impl(addr, &ulwp_addr); 1724 if (error != DCMD_OK) 1725 return (error); 1726 1727 /* 1728 * For historical compatibility, thread 1's errno value is stored in 1729 * a libc global variable 'errno', while each additional thread's 1730 * errno value is stored in ulwp_t->ul_errno. In addition, 1731 * ulwp_t->ul_errnop is set to the address of the thread's errno value, 1732 * (i.e. for tid 1, curthead->ul_errnop = &errno, for tid > 1, 1733 * curthread->ul_errnop = &curthread->ul_errno). 1734 * 1735 * Since errno itself uses *curthread->ul_errnop (see ___errno()) to 1736 * return the thread's current errno value, we do the same. 1737 */ 1738 if (mdb_ctf_vread(&u, "ulwp_t", "mdb_libc_ulwp_t", ulwp_addr, 0) == -1) 1739 return (DCMD_ERR); 1740 1741 if (mdb_vread(&errval, sizeof (errval), (uintptr_t)u.ul_errnop) == -1) { 1742 mdb_warn("cannot read error value at 0x%p", u.ul_errnop); 1743 return (DCMD_ERR); 1744 } 1745 1746 mdb_printf("%d\n", errval); 1747 return (DCMD_OK); 1748 } 1749 1750 static const mdb_dcmd_t dcmds[] = { 1751 { "errno", "?", "print errno of a given TID", d_errno, NULL }, 1752 { "jmp_buf", ":", "print jmp_buf contents", d_jmp_buf, NULL }, 1753 { "mutex", ":[-f|v]", "dump out a mutex", d_mutex, d_mutex_help }, 1754 { "psinfo", "[-v]", "prints relevant psinfo_t data", d_psinfo, 1755 d_psinfo_dcmd_help }, 1756 { "siginfo", ":", "print siginfo_t structure", d_siginfo, NULL }, 1757 { "sigjmp_buf", ":", "print sigjmp_buf contents", d_sigjmp_buf, NULL }, 1758 { "stacks", "?[-afiv] [-c func] [-C func] [-m module] [-M module] ", 1759 "print unique thread stacks", stacks_dcmd, stacks_help }, 1760 { "tid2ulwp", "?", "convert TID to ulwp_t address", tid2ulwp }, 1761 { "tsd", ":-k key", "print tsd for this thread", d_tsd, NULL }, 1762 { "ucontext", ":", "print ucontext_t structure", d_ucontext, NULL }, 1763 { "ulwp", ":", "print ulwp_t structure", d_ulwp, NULL }, 1764 { "uberdata", ":", "print uberdata_t structure", d_uberdata, NULL }, 1765 { NULL } 1766 }; 1767 1768 static const mdb_walker_t walkers[] = { 1769 { "ucontext", "walk ucontext_t uc_link list", 1770 NULL, uc_walk_step, NULL, NULL }, 1771 { "oldcontext", "walk per-lwp oldcontext pointers", 1772 oldc_walk_init, oldc_walk_step, oldc_walk_fini, NULL }, 1773 { "ulwps", "walk list of ulwp_t pointers", 1774 ulwp_walk_init, ulwp_walk_step, NULL, NULL }, 1775 { "ulwp", "walk list of ulwp_t pointers", 1776 ulwp_walk_init, ulwp_walk_step, NULL, NULL }, 1777 { "wchan", "walk wait channels", 1778 wchan_walk_init, wchan_walk_step, NULL, NULL }, 1779 { NULL } 1780 }; 1781 1782 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers }; 1783 1784 const mdb_modinfo_t * 1785 _mdb_init(void) 1786 { 1787 mdb_whatis_register("threads", whatis_run_ulwps, NULL, 1788 WHATIS_PRIO_EARLY, WHATIS_REG_NO_ID); 1789 1790 return (&modinfo); 1791 } 1792