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 */ 26 /* 27 * Copyright (c) 2015, Joyent, Inc. 28 */ 29 30 #include <sys/mdb_modapi.h> 31 #include <mdb/mdb_whatis.h> 32 #include <mdb/mdb_ctf.h> 33 #include <procfs.h> 34 #include <ucontext.h> 35 #include <siginfo.h> 36 #include <signal.h> 37 #include <setjmp.h> 38 #include <string.h> 39 #include <thr_uberdata.h> 40 #include "findstack.h" 41 42 static const char * 43 stack_flags(const stack_t *sp) 44 { 45 static char buf[32]; 46 47 if (sp->ss_flags == 0) 48 (void) strcpy(buf, " 0"); 49 else if (sp->ss_flags & ~(SS_ONSTACK | SS_DISABLE)) 50 (void) mdb_snprintf(buf, sizeof (buf), " 0x%x", sp->ss_flags); 51 else { 52 buf[0] = '\0'; 53 if (sp->ss_flags & SS_ONSTACK) 54 (void) strcat(buf, "|ONSTACK"); 55 if (sp->ss_flags & SS_DISABLE) 56 (void) strcat(buf, "|DISABLE"); 57 } 58 59 return (buf + 1); 60 } 61 62 /*ARGSUSED*/ 63 static int 64 d_jmp_buf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 65 { 66 jmp_buf jb; 67 const ulong_t *b = (const ulong_t *)jb; 68 69 if (argc != 0) 70 return (DCMD_USAGE); 71 72 if (mdb_vread(&jb, sizeof (jb), addr) != sizeof (jb)) { 73 mdb_warn("failed to read jmp_buf at %p", addr); 74 return (DCMD_ERR); 75 } 76 77 #if defined(__sparc) 78 mdb_printf(" %%sp = 0x%lx\n", b[1]); 79 mdb_printf(" %%pc = 0x%lx %lA\n", b[2], b[2]); 80 mdb_printf(" %%fp = 0x%lx\n", b[3]); 81 mdb_printf(" %%i7 = 0x%lx %lA\n", b[4], b[4]); 82 #elif defined(__amd64) 83 mdb_printf(" %%rbx = 0x%lx\n", b[0]); 84 mdb_printf(" %%r12 = 0x%lx\n", b[1]); 85 mdb_printf(" %%r13 = 0x%lx\n", b[2]); 86 mdb_printf(" %%r14 = 0x%lx\n", b[3]); 87 mdb_printf(" %%r15 = 0x%lx\n", b[4]); 88 mdb_printf(" %%rbp = 0x%lx\n", b[5]); 89 mdb_printf(" %%rsp = 0x%lx\n", b[6]); 90 mdb_printf(" %%rip = 0x%lx %lA\n", b[7], b[7]); 91 #elif defined(__i386) 92 mdb_printf(" %%ebx = 0x%lx\n", b[0]); 93 mdb_printf(" %%esi = 0x%lx\n", b[1]); 94 mdb_printf(" %%edi = 0x%lx\n", b[2]); 95 mdb_printf(" %%ebp = 0x%lx\n", b[3]); 96 mdb_printf(" %%esp = 0x%lx\n", b[4]); 97 mdb_printf(" %%eip = 0x%lx %lA\n", b[5], b[5]); 98 #endif 99 return (DCMD_OK); 100 } 101 102 const mdb_bitmask_t uc_flags_bits[] = { 103 { "UC_SIGMASK", UC_SIGMASK, UC_SIGMASK }, 104 { "UC_STACK", UC_STACK, UC_STACK }, 105 { "UC_CPU", UC_CPU, UC_CPU }, 106 { "UC_FPU", UC_FPU, UC_FPU }, 107 #if defined(UC_INTR) 108 { "UC_INTR", UC_INTR, UC_INTR }, 109 #endif 110 #if defined(UC_ASR) 111 { "UC_ASR", UC_ASR, UC_ASR }, 112 #endif 113 { NULL, 0, 0 } 114 }; 115 116 /*ARGSUSED*/ 117 static int 118 d_ucontext(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 119 { 120 ucontext_t uc; 121 122 if (argc != 0) 123 return (DCMD_USAGE); 124 125 if (mdb_vread(&uc, sizeof (uc), addr) != sizeof (uc)) { 126 mdb_warn("failed to read ucontext at %p", addr); 127 return (DCMD_ERR); 128 } 129 130 mdb_printf(" flags = 0x%lx <%b>\n", uc.uc_flags, 131 (uint_t)uc.uc_flags, uc_flags_bits); 132 mdb_printf(" link = 0x%p\n", uc.uc_link); 133 mdb_printf(" sigmask = 0x%08x 0x%08x 0x%08x 0x%08x\n", 134 uc.uc_sigmask.__sigbits[0], uc.uc_sigmask.__sigbits[1], 135 uc.uc_sigmask.__sigbits[2], uc.uc_sigmask.__sigbits[3]); 136 mdb_printf(" stack = sp 0x%p size 0x%lx flags %s\n", 137 uc.uc_stack.ss_sp, uc.uc_stack.ss_size, stack_flags(&uc.uc_stack)); 138 mdb_printf(" mcontext = 0x%p\n", 139 addr + OFFSETOF(ucontext_t, uc_mcontext)); 140 141 return (DCMD_OK); 142 } 143 144 /*ARGSUSED*/ 145 static int 146 d_sigjmp_buf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 147 { 148 #if defined(__sparc) 149 struct { 150 int sjs_flags; 151 greg_t sjs_sp; 152 greg_t sjs_pc; 153 greg_t sjs_fp; 154 greg_t sjs_i7; 155 ucontext_t *sjs_uclink; 156 ulong_t sjs_pad[_JBLEN - 6]; 157 sigset_t sjs_sigmask; 158 #if defined(_LP64) 159 greg_t sjs_asi; 160 greg_t sjs_fprs; 161 #endif 162 stack_t sjs_stack; 163 } s; 164 165 if (argc != 0) 166 return (DCMD_USAGE); 167 168 if (mdb_vread(&s, sizeof (s), addr) != sizeof (s)) { 169 mdb_warn("failed to read sigjmp_buf at %p", addr); 170 return (DCMD_ERR); 171 } 172 173 mdb_printf(" flags = 0x%x\n", s.sjs_flags); 174 mdb_printf(" %%sp = 0x%lx %lA\n", s.sjs_sp, s.sjs_sp); 175 mdb_printf(" %%pc = 0x%lx %lA\n", s.sjs_pc, s.sjs_pc); 176 mdb_printf(" %%fp = 0x%lx %lA\n", s.sjs_fp, s.sjs_fp); 177 mdb_printf(" %%i7 = 0x%lx %lA\n", s.sjs_i7, s.sjs_i7); 178 mdb_printf(" uclink = %p\n", s.sjs_uclink); 179 mdb_printf(" sigset = 0x%08x 0x%08x 0x%08x 0x%08x\n", 180 s.sjs_sigmask.__sigbits[0], s.sjs_sigmask.__sigbits[1], 181 s.sjs_sigmask.__sigbits[2], s.sjs_sigmask.__sigbits[3]); 182 #if defined(_LP64) 183 mdb_printf(" %%asi = 0x%lx\n", s.sjs_asi); 184 mdb_printf(" %%fprs = 0x%lx\n", s.sjs_fprs); 185 #endif 186 mdb_printf(" stack = sp 0x%p size 0x%lx flags %s\n", 187 s.sjs_stack.ss_sp, s.sjs_stack.ss_size, stack_flags(&s.sjs_stack)); 188 189 return (DCMD_OK); 190 191 #elif defined(__i386) || defined(__amd64) 192 return (d_ucontext(addr, flags, argc, argv)); 193 #endif 194 } 195 196 /*ARGSUSED*/ 197 static int 198 d_siginfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 199 { 200 static const char *const msname[] = { 201 "USER", "SYSTEM", "TRAP", "TFAULT", "DFAULT", "KFAULT", 202 "USER_LOCK", "SLEEP", "WAIT_CPU", "STOPPED" 203 }; 204 205 char signame[SIG2STR_MAX]; 206 siginfo_t si; 207 int i; 208 209 if (argc != 0) 210 return (DCMD_USAGE); 211 212 if (mdb_vread(&si, sizeof (si), addr) != sizeof (si)) { 213 mdb_warn("failed to read siginfo at %p", addr); 214 return (DCMD_ERR); 215 } 216 217 if (sig2str(si.si_signo, signame) == -1) 218 (void) strcpy(signame, "unknown"); 219 220 mdb_printf(" signal %5d (%s)\n", si.si_signo, signame); 221 mdb_printf(" code %5d (", si.si_code); 222 223 switch (si.si_code) { 224 case SI_NOINFO: 225 mdb_printf("no info"); 226 break; 227 case SI_DTRACE: 228 mdb_printf("from DTrace raise() action"); 229 break; 230 case SI_RCTL: 231 mdb_printf("from rctl action"); 232 break; 233 case SI_USER: 234 mdb_printf("user generated via kill"); 235 break; 236 case SI_LWP: 237 mdb_printf("user generated via lwp_kill"); 238 break; 239 case SI_QUEUE: 240 mdb_printf("user generated via sigqueue"); 241 break; 242 case SI_TIMER: 243 mdb_printf("from timer expiration"); 244 break; 245 case SI_ASYNCIO: 246 mdb_printf("from async i/o completion"); 247 break; 248 case SI_MESGQ: 249 mdb_printf("from message arrival"); 250 break; 251 default: 252 if (SI_FROMUSER(&si)) 253 mdb_printf("from user process"); 254 else 255 mdb_printf("from kernel"); 256 } 257 258 mdb_printf(")\n errno %5d (%s)\n", 259 si.si_errno, strerror(si.si_errno)); 260 261 if (si.si_code == SI_USER || si.si_code == SI_QUEUE) { 262 mdb_printf(" signal sent from PID %d (uid %d)\n", 263 si.si_pid, si.si_uid); 264 } 265 266 if (si.si_code == SI_QUEUE) { 267 mdb_printf(" signal value = 0t%d / %p\n", 268 si.si_value.sival_int, si.si_value.sival_ptr); 269 } 270 271 switch (si.si_signo) { 272 case SIGCLD: 273 mdb_printf(" signal sent from child PID %d (uid %d)\n", 274 si.si_pid, si.si_uid); 275 mdb_printf(" usr time = 0t%ld ticks, sys time = 0t%ld ticks\n", 276 si.si_utime, si.si_stime); 277 mdb_printf(" wait status = 0x%x\n", si.si_status); 278 break; 279 280 case SIGSEGV: 281 case SIGBUS: 282 case SIGILL: 283 case SIGTRAP: 284 case SIGFPE: 285 mdb_printf(" fault address = 0x%p\n trapno = %d\n", 286 si.si_addr, si.si_trapno); 287 mdb_printf(" instruction address = 0x%p %lA\n", 288 si.si_pc, si.si_pc); 289 break; 290 291 case SIGPOLL: 292 case SIGXFSZ: 293 mdb_printf(" fd = %d band = 0x%lx\n", 294 si.si_fd, si.si_band); 295 break; 296 297 case SIGPROF: 298 mdb_printf(" last fault address = 0x%p fault type = %d\n", 299 si.si_faddr, si.si_fault); 300 mdb_printf(" timestamp = 0t%ld sec 0t%ld nsec\n", 301 si.si_tstamp.tv_sec, si.si_tstamp.tv_nsec); 302 303 if (si.__data.__prof.__syscall != 0) { 304 mdb_printf(" system call %d (", si.si_syscall); 305 if (si.si_nsysarg > 0) { 306 mdb_printf("%lx", si.si_sysarg[0]); 307 for (i = 1; i < si.si_nsysarg; i++) 308 mdb_printf(", %lx", si.si_sysarg[i]); 309 } 310 mdb_printf(" )\n"); 311 } 312 313 for (i = 0; i < sizeof (msname) / sizeof (msname[0]); i++) { 314 mdb_printf(" mstate[\"%s\"] = %d\n", 315 msname[i], si.si_mstate[i]); 316 } 317 break; 318 } 319 320 return (DCMD_OK); 321 } 322 323 static int 324 uc_walk_step(mdb_walk_state_t *wsp) 325 { 326 uintptr_t addr = wsp->walk_addr; 327 ucontext_t uc; 328 329 if (addr == NULL) 330 return (WALK_DONE); 331 332 if (mdb_vread(&uc, sizeof (uc), addr) != sizeof (uc)) { 333 mdb_warn("failed to read ucontext at %p", addr); 334 return (WALK_ERR); 335 } 336 337 wsp->walk_addr = (uintptr_t)uc.uc_link; 338 return (wsp->walk_callback(addr, &uc, wsp->walk_cbdata)); 339 } 340 341 static int 342 oldc_walk_init(mdb_walk_state_t *wsp) 343 { 344 ssize_t nbytes = mdb_get_xdata("lwpstatus", NULL, 0); 345 346 if (nbytes <= 0) { 347 mdb_warn("lwpstatus information not available"); 348 return (WALK_ERR); 349 } 350 351 if (wsp->walk_addr != NULL) { 352 mdb_warn("walker only supports global walk\n"); 353 return (WALK_ERR); 354 } 355 356 wsp->walk_addr = nbytes; /* Use walk_addr to track size */ 357 wsp->walk_data = mdb_alloc(nbytes, UM_SLEEP); 358 359 if (mdb_get_xdata("lwpstatus", wsp->walk_data, nbytes) != nbytes) { 360 mdb_warn("failed to read lwpstatus information"); 361 mdb_free(wsp->walk_data, nbytes); 362 return (WALK_ERR); 363 } 364 365 wsp->walk_arg = wsp->walk_data; /* Use walk_arg to track pointer */ 366 return (WALK_NEXT); 367 } 368 369 static int 370 oldc_walk_step(mdb_walk_state_t *wsp) 371 { 372 const lwpstatus_t *lsp, *end; 373 374 end = (const lwpstatus_t *)((uintptr_t)wsp->walk_data + wsp->walk_addr); 375 lsp = wsp->walk_arg; 376 377 wsp->walk_arg = (void *)(lsp + 1); 378 379 if (lsp < end) { 380 uintptr_t addr = lsp->pr_oldcontext; 381 ucontext_t uc; 382 383 if (addr == NULL) 384 return (WALK_NEXT); 385 386 if (mdb_vread(&uc, sizeof (uc), addr) != sizeof (uc)) { 387 mdb_warn("failed to read ucontext at %p", addr); 388 return (WALK_NEXT); 389 } 390 391 return (wsp->walk_callback(addr, &uc, wsp->walk_cbdata)); 392 } 393 394 return (WALK_DONE); 395 } 396 397 static void 398 oldc_walk_fini(mdb_walk_state_t *wsp) 399 { 400 mdb_free(wsp->walk_data, wsp->walk_addr); /* walk_addr has size */ 401 } 402 403 /* 404 * ==================== threads ========================== 405 * These are the interfaces that used to require libthread. 406 * Now, libthread has been folded into libc. 407 * ======================================================= 408 */ 409 410 /* 411 * prt_addr() is called up to three times to generate arguments for 412 * one call to mdb_printf(). We must return at least three different 413 * pointers to static storage for consecutive calls to prt_addr(). 414 */ 415 static const char * 416 prt_addr(void *addr, int pad) 417 { 418 static char buffer[4][24]; 419 static int ix = 0; 420 char *buf; 421 422 if (ix == 4) /* use buffers in sequence: 0, 1, 2, 3 */ 423 ix = 0; 424 buf = buffer[ix++]; 425 if (addr == NULL) 426 return (pad? "<NULL> " : "<NULL>"); 427 else { 428 #ifdef _LP64 429 (void) mdb_snprintf(buf, sizeof (buffer[0]), "0x%016lx", addr); 430 if (pad) 431 (void) strcpy(buf + 18, " "); 432 #else 433 (void) mdb_snprintf(buf, sizeof (buffer[0]), "0x%08lx", addr); 434 if (pad) 435 (void) strcpy(buf + 10, " "); 436 #endif /* _LP64 */ 437 return (buf); 438 } 439 } 440 441 #define HD(str) mdb_printf(" " str "\n") 442 #define OFFSTR "+0x%-7lx " 443 #define OFFSET(member) ((size_t)OFFSETOF(ulwp_t, member)) 444 445 /*ARGSUSED*/ 446 static int 447 d_ulwp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 448 { 449 ulwp_t ulwp; 450 451 if (argc != 0 || !(flags & DCMD_ADDRSPEC)) 452 return (DCMD_USAGE); 453 454 if (mdb_vread(&ulwp, sizeof (ulwp), addr) != sizeof (ulwp) && 455 (bzero(&ulwp, sizeof (ulwp)), 456 mdb_vread(&ulwp, REPLACEMENT_SIZE, addr)) != REPLACEMENT_SIZE) { 457 mdb_warn("failed to read ulwp at 0x%p", addr); 458 return (DCMD_ERR); 459 } 460 461 mdb_printf("%#a\n", addr); 462 463 HD("self uberdata"); 464 mdb_printf(OFFSTR "%s %s\n", 465 OFFSET(ul_self), 466 prt_addr(ulwp.ul_self, 1), 467 prt_addr(ulwp.ul_uberdata, 0)); 468 469 HD("tlsent ntlsent"); 470 mdb_printf(OFFSTR "%s %ld\n", 471 OFFSET(ul_tlsent), 472 prt_addr(ulwp.ul_tlsent, 1), 473 ulwp.ul_ntlsent); 474 475 HD("forw back next"); 476 mdb_printf(OFFSTR "%s %s %s\n", 477 OFFSET(ul_forw), 478 prt_addr(ulwp.ul_forw, 1), 479 prt_addr(ulwp.ul_back, 1), 480 prt_addr(ulwp.ul_next, 0)); 481 482 HD("hash rval stk"); 483 mdb_printf(OFFSTR "%s %s %s\n", 484 OFFSET(ul_hash), 485 prt_addr(ulwp.ul_hash, 1), 486 prt_addr(ulwp.ul_rval, 1), 487 prt_addr(ulwp.ul_stk, 0)); 488 489 HD("mapsiz guardsize stktop stksiz"); 490 mdb_printf(OFFSTR "%-10ld %-10ld %s %ld\n", 491 OFFSET(ul_mapsiz), 492 ulwp.ul_mapsiz, 493 ulwp.ul_guardsize, 494 prt_addr((void *)ulwp.ul_stktop, 1), 495 ulwp.ul_stksiz); 496 497 HD("ustack.ss_sp ustack.ss_size ustack.ss_flags"); 498 mdb_printf(OFFSTR "%s %-21ld %s\n", 499 OFFSET(ul_ustack.ss_sp), 500 prt_addr(ulwp.ul_ustack.ss_sp, 1), 501 ulwp.ul_ustack.ss_size, 502 stack_flags(&ulwp.ul_ustack)); 503 504 HD("ix lwpid pri epri policy cid"); 505 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 506 OFFSET(ul_ix), 507 ulwp.ul_ix, 508 ulwp.ul_lwpid, 509 ulwp.ul_pri, 510 ulwp.ul_epri, 511 ulwp.ul_policy, 512 ulwp.ul_cid); 513 514 HD("cursig pleasestop stop signalled dead unwind"); 515 mdb_printf(OFFSTR "%-10d ", 516 OFFSET(ul_cursig), 517 ulwp.ul_cursig); 518 mdb_printf(ulwp.ul_pleasestop? "0x%-8x " : "%-10d ", 519 ulwp.ul_pleasestop); 520 mdb_printf(ulwp.ul_stop? "0x%-8x " : "%-10d ", 521 ulwp.ul_stop); 522 mdb_printf("%-10d %-10d %d\n", 523 ulwp.ul_signalled, 524 ulwp.ul_dead, 525 ulwp.ul_unwind); 526 527 HD("detached writer stopping can'prolog preempt savpreempt"); 528 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 529 OFFSET(ul_detached), 530 ulwp.ul_detached, 531 ulwp.ul_writer, 532 ulwp.ul_stopping, 533 ulwp.ul_cancel_prologue, 534 ulwp.ul_preempt, 535 ulwp.ul_savpreempt); 536 537 HD("sigsuspend main fork primarymap m'spinners d'noreserv"); 538 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 539 OFFSET(ul_sigsuspend), 540 ulwp.ul_sigsuspend, 541 ulwp.ul_main, 542 ulwp.ul_fork, 543 ulwp.ul_primarymap, 544 ulwp.ul_max_spinners, 545 ulwp.ul_door_noreserve); 546 547 HD("queue_fifo c'w'defer e'detect' async_safe rt rtqueued"); 548 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 549 OFFSET(ul_queue_fifo), 550 ulwp.ul_queue_fifo, 551 ulwp.ul_cond_wait_defer, 552 ulwp.ul_error_detection, 553 ulwp.ul_async_safe, 554 ulwp.ul_rt, 555 ulwp.ul_rtqueued); 556 557 HD("misaligned adapt'spin queue_spin critical sigdefer vfork"); 558 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 559 OFFSET(ul_misaligned), 560 ulwp.ul_misaligned, 561 ulwp.ul_adaptive_spin, 562 ulwp.ul_queue_spin, 563 ulwp.ul_critical, 564 ulwp.ul_sigdefer, 565 ulwp.ul_vfork); 566 567 HD("cancelable c'pending c'disabled c'async save_async mutator"); 568 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 569 OFFSET(ul_cancelable), 570 ulwp.ul_cancelable, 571 ulwp.ul_cancel_pending, 572 ulwp.ul_cancel_disabled, 573 ulwp.ul_cancel_async, 574 ulwp.ul_save_async, 575 ulwp.ul_mutator); 576 577 HD("created replace nocancel errno errnop"); 578 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %s\n", 579 OFFSET(ul_created), 580 ulwp.ul_created, 581 ulwp.ul_replace, 582 ulwp.ul_nocancel, 583 ulwp.ul_errno, 584 prt_addr(ulwp.ul_errnop, 0)); 585 586 HD("clnup_hdr schedctl_called schedctl"); 587 mdb_printf(OFFSTR "%s %s %s\n", 588 OFFSET(ul_clnup_hdr), 589 prt_addr(ulwp.ul_clnup_hdr, 1), 590 prt_addr(ulwp.ul_schedctl_called, 1), 591 prt_addr((void *)ulwp.ul_schedctl, 0)); 592 593 HD("bindflags libc_locks stsd &ftsd"); 594 mdb_printf(OFFSTR, 595 OFFSET(ul_bindflags)); 596 mdb_printf(ulwp.ul_bindflags? "0x%-8x " : "%-10d ", 597 ulwp.ul_bindflags); 598 mdb_printf("%-10d ", ulwp.ul_libc_locks); 599 mdb_printf("%s %s\n", 600 prt_addr(ulwp.ul_stsd, 1), 601 prt_addr((void *)(addr + OFFSET(ul_ftsd[0])), 0)); 602 603 HD("eventmask[0..1] eventnum eventdata"); 604 mdb_printf(OFFSTR "0x%08x 0x%08x %-21d %s\n", 605 OFFSET(ul_td_evbuf.eventmask.event_bits[0]), 606 ulwp.ul_td_evbuf.eventmask.event_bits[0], 607 ulwp.ul_td_evbuf.eventmask.event_bits[1], 608 ulwp.ul_td_evbuf.eventnum, 609 prt_addr(ulwp.ul_td_evbuf.eventdata, 0)); 610 611 HD("td'enable sync'reg qtype cv_wake rtld usropts"); 612 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d ", 613 OFFSET(ul_td_events_enable), 614 ulwp.ul_td_events_enable, 615 ulwp.ul_sync_obj_reg, 616 ulwp.ul_qtype, 617 ulwp.ul_cv_wake, 618 ulwp.ul_rtld); 619 mdb_printf(ulwp.ul_usropts? "0x%x\n" : "%d\n", 620 ulwp.ul_usropts); 621 622 HD("startpc startarg wchan"); 623 mdb_printf(OFFSTR "%s %s %s\n", 624 OFFSET(ul_startpc), 625 prt_addr((void *)ulwp.ul_startpc, 1), 626 prt_addr(ulwp.ul_startarg, 1), 627 prt_addr(ulwp.ul_wchan, 0)); 628 629 HD("link sleepq cvmutex"); 630 mdb_printf(OFFSTR "%s %s %s\n", 631 OFFSET(ul_link), 632 prt_addr(ulwp.ul_link, 1), 633 prt_addr(ulwp.ul_sleepq, 1), 634 prt_addr(ulwp.ul_cvmutex, 0)); 635 636 HD("mxchain save_state"); 637 mdb_printf(OFFSTR "%s %d\n", 638 OFFSET(ul_mxchain), 639 prt_addr(ulwp.ul_mxchain, 1), 640 ulwp.ul_save_state); 641 642 HD("rdlockcnt rd_rwlock rd_count"); 643 mdb_printf(OFFSTR "%-21d %s %d\n", 644 OFFSET(ul_rdlockcnt), 645 ulwp.ul_rdlockcnt, 646 prt_addr(ulwp.ul_readlock.single.rd_rwlock, 1), 647 ulwp.ul_readlock.single.rd_count); 648 649 HD("heldlockcnt heldlocks tpdp"); 650 mdb_printf(OFFSTR "%-21d %s %s\n", 651 OFFSET(ul_heldlockcnt), 652 ulwp.ul_heldlockcnt, 653 prt_addr(ulwp.ul_heldlocks.single, 1), 654 prt_addr(ulwp.ul_tpdp, 0)); 655 656 HD("siglink s'l'spin s'l'spin2 s'l'sleep s'l'wakeup"); 657 mdb_printf(OFFSTR "%s %-10d %-10d %-10d %d\n", 658 OFFSET(ul_siglink), 659 prt_addr(ulwp.ul_siglink, 1), 660 ulwp.ul_spin_lock_spin, 661 ulwp.ul_spin_lock_spin2, 662 ulwp.ul_spin_lock_sleep, 663 ulwp.ul_spin_lock_wakeup); 664 665 HD("&queue_root rtclassid pilocks"); 666 mdb_printf(OFFSTR "%s %-10d %d\n", 667 OFFSET(ul_queue_root), 668 prt_addr((void *)(addr + OFFSET(ul_queue_root)), 1), 669 ulwp.ul_rtclassid, 670 ulwp.ul_pilocks); 671 672 /* 673 * The remainder of the ulwp_t structure 674 * is invalid if this is a replacement. 675 */ 676 if (ulwp.ul_replace) 677 return (DCMD_OK); 678 679 HD("sigmask[0..3]"); 680 mdb_printf(OFFSTR "0x%08x 0x%08x 0x%08x 0x%08x\n", 681 OFFSET(ul_sigmask.__sigbits[0]), 682 ulwp.ul_sigmask.__sigbits[0], 683 ulwp.ul_sigmask.__sigbits[1], 684 ulwp.ul_sigmask.__sigbits[2], 685 ulwp.ul_sigmask.__sigbits[3]); 686 687 HD("tmpmask[0..3]"); 688 mdb_printf(OFFSTR "0x%08x 0x%08x 0x%08x 0x%08x\n", 689 OFFSET(ul_tmpmask.__sigbits[0]), 690 ulwp.ul_tmpmask.__sigbits[0], 691 ulwp.ul_tmpmask.__sigbits[1], 692 ulwp.ul_tmpmask.__sigbits[2], 693 ulwp.ul_tmpmask.__sigbits[3]); 694 695 HD("&siginfo &spinlock &fpuenv"); 696 mdb_printf(OFFSTR "%s %s %s\n", 697 OFFSET(ul_siginfo), 698 prt_addr((void *)(addr + OFFSET(ul_siginfo)), 1), 699 prt_addr((void *)(addr + OFFSET(ul_spinlock)), 1), 700 prt_addr((void *)(addr + OFFSET(ul_fpuenv)), 0)); 701 702 HD("tmem.size &tmem.roots"); 703 mdb_printf(OFFSTR "%-21H %s\n", 704 OFFSET(ul_tmem), 705 ulwp.ul_tmem.tm_size, 706 prt_addr((void *)(addr + OFFSET(ul_tmem) + sizeof (size_t)), 0)); 707 708 return (DCMD_OK); 709 } 710 711 /* 712 * Get the address of the unique uberdata_t structure. 713 */ 714 static uintptr_t 715 uberdata_addr(void) 716 { 717 uintptr_t uaddr; 718 uintptr_t addr; 719 GElf_Sym sym; 720 721 if (mdb_lookup_by_obj("libc.so.1", "_tdb_bootstrap", &sym) != 0) { 722 mdb_warn("cannot find libc.so.1`_tdb_bootstrap"); 723 return (NULL); 724 } 725 if (mdb_vread(&addr, sizeof (addr), sym.st_value) == sizeof (addr) && 726 addr != NULL && 727 mdb_vread(&uaddr, sizeof (uaddr), addr) == sizeof (uaddr) && 728 uaddr != NULL) { 729 return (uaddr); 730 } 731 if (mdb_lookup_by_obj("libc.so.1", "_uberdata", &sym) != 0) { 732 mdb_warn("cannot find libc.so.1`_uberdata"); 733 return (NULL); 734 } 735 return ((uintptr_t)sym.st_value); 736 } 737 738 #undef OFFSET 739 #define OFFSET(member) ((size_t)OFFSETOF(uberdata_t, member)) 740 741 /*ARGSUSED*/ 742 static int 743 d_uberdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 744 { 745 uberdata_t uberdata; 746 int i; 747 748 if (argc != 0) 749 return (DCMD_USAGE); 750 if (!(flags & DCMD_ADDRSPEC) && (addr = uberdata_addr()) == NULL) 751 return (DCMD_ERR); 752 753 if (mdb_vread(&uberdata, sizeof (uberdata), addr) != 754 sizeof (uberdata)) { 755 mdb_warn("failed to read uberdata at 0x%p", addr); 756 return (DCMD_ERR); 757 } 758 759 mdb_printf("%#a\n", addr); 760 761 HD("&link_lock &ld_lock &fork_lock"); 762 mdb_printf(OFFSTR "%s %s %s\n", 763 OFFSET(link_lock), 764 prt_addr((void *)(addr + OFFSET(link_lock)), 1), 765 prt_addr((void *)(addr + OFFSET(ld_lock)), 1), 766 prt_addr((void *)(addr + OFFSET(fork_lock)), 0)); 767 768 HD("&atfork_lock &callout_lock &tdb_hash_lock"); 769 mdb_printf(OFFSTR "%s %s %s\n", 770 OFFSET(atfork_lock), 771 prt_addr((void *)(addr + OFFSET(atfork_lock)), 1), 772 prt_addr((void *)(addr + OFFSET(callout_lock)), 1), 773 prt_addr((void *)(addr + OFFSET(tdb_hash_lock)), 0)); 774 775 HD("&tdb_hash_lock_stats &siguaction[0]"); 776 mdb_printf(OFFSTR "%s %s\n", 777 OFFSET(tdb_hash_lock_stats), 778 prt_addr((void *)(addr + OFFSET(tdb_hash_lock_stats)), 1), 779 prt_addr((void *)(addr + OFFSET(siguaction)), 0)); 780 781 HD("&bucket free_list chunks"); 782 for (i = 0; i < NBUCKETS; i++) { 783 mdb_printf(OFFSTR "%s %s %ld\n", 784 OFFSET(bucket[i]), 785 prt_addr((void *)(addr + OFFSET(bucket[i])), 1), 786 prt_addr(uberdata.bucket[i].free_list, 1), 787 uberdata.bucket[i].chunks); 788 } 789 790 HD("&atexit_root head exit_frame_monitor"); 791 mdb_printf(OFFSTR "%s %s %s\n", 792 OFFSET(atexit_root), 793 prt_addr((void *)(addr + OFFSET(atexit_root.exitfns_lock)), 1), 794 prt_addr(uberdata.atexit_root.head, 1), 795 prt_addr(uberdata.atexit_root.exit_frame_monitor, 0)); 796 797 HD("&tsd_metadata tsdm_nkeys tsdm_nused tsdm_destro"); 798 mdb_printf(OFFSTR "%s %-10d %-10d %s\n", 799 OFFSET(tsd_metadata), 800 prt_addr((void *)(addr + OFFSET(tsd_metadata.tsdm_lock)), 1), 801 uberdata.tsd_metadata.tsdm_nkeys, 802 uberdata.tsd_metadata.tsdm_nused, 803 prt_addr((void *)uberdata.tsd_metadata.tsdm_destro, 0)); 804 805 HD("&tls_metadata tls_modinfo.data tls_modinfo.size"); 806 mdb_printf(OFFSTR "%s %s %ld\n", 807 OFFSET(tls_metadata), 808 prt_addr((void *)(addr + OFFSET(tls_metadata.tls_lock)), 1), 809 prt_addr(uberdata.tls_metadata.tls_modinfo.tls_data, 1), 810 uberdata.tls_metadata.tls_modinfo.tls_size); 811 812 HD(" static_tls.data static_tls.size"); 813 mdb_printf(OFFSTR "%s %s %ld\n", 814 OFFSET(tls_metadata.static_tls), 815 " ", 816 prt_addr(uberdata.tls_metadata.static_tls.tls_data, 1), 817 uberdata.tls_metadata.static_tls.tls_size); 818 819 HD("primary_ma bucket_ini uflags.mt uflags.pad uflags.trs uflags.ted"); 820 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %-10d %d\n", 821 OFFSET(primary_map), 822 uberdata.primary_map, 823 uberdata.bucket_init, 824 uberdata.uberflags.uf_x.x_mt, 825 uberdata.uberflags.uf_x.x_pad, 826 uberdata.uberflags.uf_x.x_tdb_register_sync, 827 uberdata.uberflags.uf_x.x_thread_error_detection); 828 829 HD("queue_head thr_hash_table hash_size hash_mask"); 830 mdb_printf(OFFSTR "%s %s %-10d 0x%x\n", 831 OFFSET(queue_head), 832 prt_addr(uberdata.queue_head, 1), 833 prt_addr(uberdata.thr_hash_table, 1), 834 uberdata.hash_size, 835 uberdata.hash_mask); 836 837 HD("ulwp_one all_lwps all_zombies"); 838 mdb_printf(OFFSTR "%s %s %s\n", 839 OFFSET(ulwp_one), 840 prt_addr(uberdata.ulwp_one, 1), 841 prt_addr(uberdata.all_lwps, 1), 842 prt_addr(uberdata.all_zombies, 0)); 843 844 HD("nthreads nzombies ndaemons pid sigacthandler"); 845 mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %s\n", 846 OFFSET(nthreads), 847 uberdata.nthreads, 848 uberdata.nzombies, 849 uberdata.ndaemons, 850 (int)uberdata.pid, 851 prt_addr((void *)uberdata.sigacthandler, 0)); 852 853 HD("lwp_stacks lwp_laststack nfreestack stk_cache"); 854 mdb_printf(OFFSTR "%s %s %-10d %d\n", 855 OFFSET(lwp_stacks), 856 prt_addr(uberdata.lwp_stacks, 1), 857 prt_addr(uberdata.lwp_laststack, 1), 858 uberdata.nfreestack, 859 uberdata.thread_stack_cache); 860 861 HD("ulwp_freelist ulwp_lastfree ulwp_replace_free"); 862 mdb_printf(OFFSTR "%s %s %s\n", 863 OFFSET(ulwp_freelist), 864 prt_addr(uberdata.ulwp_freelist, 1), 865 prt_addr(uberdata.ulwp_lastfree, 1), 866 prt_addr(uberdata.ulwp_replace_free, 0)); 867 868 HD("ulwp_replace_last atforklist"); 869 mdb_printf(OFFSTR "%s %s\n", 870 OFFSET(ulwp_replace_last), 871 prt_addr(uberdata.ulwp_replace_last, 1), 872 prt_addr(uberdata.atforklist, 0)); 873 874 HD("robustlocks robustlist progname"); 875 mdb_printf(OFFSTR "%s %s %s\n", 876 OFFSET(robustlocks), 877 prt_addr(uberdata.robustlocks, 1), 878 prt_addr(uberdata.robustlist, 1), 879 prt_addr(uberdata.progname, 0)); 880 881 HD("tdb_bootstrap tdb_sync_addr_hash tdb_'count tdb_'fail"); 882 mdb_printf(OFFSTR "%s %s %-10d %d\n", 883 OFFSET(tdb_bootstrap), 884 prt_addr(uberdata.tdb_bootstrap, 1), 885 prt_addr(uberdata.tdb.tdb_sync_addr_hash, 1), 886 uberdata.tdb.tdb_register_count, 887 uberdata.tdb.tdb_hash_alloc_failed); 888 889 HD("tdb_sync_addr_free tdb_sync_addr_last tdb_sync_alloc"); 890 mdb_printf(OFFSTR "%s %s %ld\n", 891 OFFSET(tdb.tdb_sync_addr_free), 892 prt_addr(uberdata.tdb.tdb_sync_addr_free, 1), 893 prt_addr(uberdata.tdb.tdb_sync_addr_last, 1), 894 uberdata.tdb.tdb_sync_alloc); 895 896 HD("tdb_ev_global_mask tdb_events"); 897 mdb_printf(OFFSTR "0x%08x 0x%08x %s\n", 898 OFFSET(tdb.tdb_ev_global_mask), 899 uberdata.tdb.tdb_ev_global_mask.event_bits[0], 900 uberdata.tdb.tdb_ev_global_mask.event_bits[1], 901 prt_addr((void *)uberdata.tdb.tdb_events, 0)); 902 903 return (DCMD_OK); 904 } 905 906 static int 907 ulwp_walk_init(mdb_walk_state_t *wsp) 908 { 909 uintptr_t addr = wsp->walk_addr; 910 uintptr_t uber_addr; 911 912 if (addr == NULL && 913 ((uber_addr = uberdata_addr()) == NULL || 914 mdb_vread(&addr, sizeof (addr), 915 uber_addr + OFFSETOF(uberdata_t, all_lwps)) 916 != sizeof (addr))) { 917 mdb_warn("cannot find 'uberdata.all_lwps'"); 918 return (WALK_ERR); 919 } 920 if (addr == NULL) 921 return (WALK_DONE); 922 wsp->walk_addr = addr; 923 wsp->walk_data = (void *)addr; 924 return (WALK_NEXT); 925 } 926 927 static int 928 ulwp_walk_step(mdb_walk_state_t *wsp) 929 { 930 uintptr_t addr = wsp->walk_addr; 931 ulwp_t ulwp; 932 933 if (addr == NULL) 934 return (WALK_DONE); 935 if (mdb_vread(&ulwp, sizeof (ulwp), addr) != sizeof (ulwp) && 936 (bzero(&ulwp, sizeof (ulwp)), 937 mdb_vread(&ulwp, REPLACEMENT_SIZE, addr)) != REPLACEMENT_SIZE) { 938 mdb_warn("failed to read ulwp at 0x%p", addr); 939 return (WALK_ERR); 940 } 941 /* 942 * If we have looped around to the beginning 943 * of the circular linked list, we are done. 944 */ 945 if ((wsp->walk_addr = (uintptr_t)ulwp.ul_forw) 946 == (uintptr_t)wsp->walk_data) 947 wsp->walk_addr = NULL; 948 return (wsp->walk_callback(addr, &ulwp, wsp->walk_cbdata)); 949 } 950 951 /* Avoid classifying NULL pointers as part of the main stack on x86 */ 952 #define MIN_STACK_ADDR (0x10000ul) 953 954 static int 955 whatis_walk_ulwp(uintptr_t addr, const ulwp_t *ulwp, mdb_whatis_t *w) 956 { 957 uintptr_t cur; 958 lwpid_t id = ulwp->ul_lwpid; 959 uintptr_t top, base, size; 960 961 while (mdb_whatis_match(w, addr, sizeof (ulwp_t), &cur)) 962 mdb_whatis_report_object(w, cur, addr, 963 "allocated as thread %#r's ulwp_t\n", id); 964 965 top = (uintptr_t)ulwp->ul_stktop; 966 size = ulwp->ul_stksiz; 967 968 /* 969 * The main stack ends up being a little weird, especially if 970 * the stack ulimit is unlimited. This tries to take that into 971 * account. 972 */ 973 if (size > top) 974 size = top; 975 if (top > MIN_STACK_ADDR && top - size < MIN_STACK_ADDR) 976 size = top - MIN_STACK_ADDR; 977 978 base = top - size; 979 980 while (mdb_whatis_match(w, base, size, &cur)) 981 mdb_whatis_report_address(w, cur, "in [ stack tid=%#r ]\n", id); 982 983 if (ulwp->ul_ustack.ss_flags & SS_ONSTACK) { 984 base = (uintptr_t)ulwp->ul_ustack.ss_sp; 985 size = ulwp->ul_ustack.ss_size; 986 987 while (mdb_whatis_match(w, base, size, &cur)) 988 mdb_whatis_report_address(w, cur, 989 "in [ altstack tid=%#r ]\n", id); 990 } 991 992 return (WHATIS_WALKRET(w)); 993 } 994 995 /*ARGSUSED*/ 996 static int 997 whatis_run_ulwps(mdb_whatis_t *w, void *arg) 998 { 999 if (mdb_walk("ulwps", (mdb_walk_cb_t)whatis_walk_ulwp, w) == -1) { 1000 mdb_warn("couldn't find ulwps walker"); 1001 return (1); 1002 } 1003 return (0); 1004 } 1005 1006 /* 1007 * ======================================================= 1008 * End of thread (previously libthread) interfaces. 1009 * ==================== threads ========================== 1010 */ 1011 1012 int 1013 stacks_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1014 { 1015 int rval = stacks(addr, flags, argc, argv); 1016 1017 /* 1018 * For the user-level variant of ::stacks, we don't bother caching 1019 * state, as even a very large program is unlikely to compare to the 1020 * kernel in terms of number of threads. (And if you find yourself 1021 * here in anger, frustrated about how long ::stacks is running on 1022 * your galactically complicated zillion-thread program, hopefully 1023 * you will find some solace in the irony. Okay, probably not...) 1024 */ 1025 stacks_cleanup(B_TRUE); 1026 return (rval); 1027 } 1028 1029 typedef struct tid2ulwp_walk { 1030 lwpid_t t2u_tid; 1031 uintptr_t t2u_lwp; 1032 boolean_t t2u_found; 1033 } tid2ulwp_walk_t; 1034 1035 /*ARGSUSED*/ 1036 static int 1037 tid2ulwp_walk(uintptr_t addr, ulwp_t *ulwp, tid2ulwp_walk_t *t2u) 1038 { 1039 if (ulwp->ul_lwpid == t2u->t2u_tid) { 1040 t2u->t2u_lwp = addr; 1041 t2u->t2u_found = B_TRUE; 1042 return (WALK_DONE); 1043 } 1044 1045 return (WALK_NEXT); 1046 } 1047 1048 static int 1049 tid2ulwp_impl(uintptr_t tid_addr, uintptr_t *ulwp_addrp) 1050 { 1051 tid2ulwp_walk_t t2u; 1052 1053 bzero(&t2u, sizeof (t2u)); 1054 t2u.t2u_tid = (lwpid_t)tid_addr; 1055 1056 if (mdb_walk("ulwp", (mdb_walk_cb_t)tid2ulwp_walk, &t2u) != 0) { 1057 mdb_warn("can't walk 'ulwp'"); 1058 return (DCMD_ERR); 1059 } 1060 1061 if (!t2u.t2u_found) { 1062 mdb_warn("thread ID %d not found", t2u.t2u_tid); 1063 return (DCMD_ERR); 1064 } 1065 *ulwp_addrp = t2u.t2u_lwp; 1066 return (DCMD_OK); 1067 } 1068 1069 /*ARGSUSED*/ 1070 static int 1071 tid2ulwp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1072 { 1073 uintptr_t ulwp_addr; 1074 int error; 1075 1076 if (argc != 0) 1077 return (DCMD_USAGE); 1078 1079 error = tid2ulwp_impl(addr, &ulwp_addr); 1080 if (error == DCMD_OK) 1081 mdb_printf("%p\n", ulwp_addr); 1082 return (error); 1083 } 1084 1085 typedef struct mdb_libc_ulwp { 1086 void *ul_ftsd[TSD_NFAST]; 1087 tsd_t *ul_stsd; 1088 } mdb_libc_ulwp_t; 1089 1090 /* 1091 * Map from thread pointer to tsd for given key 1092 */ 1093 static int 1094 d_tsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1095 { 1096 mdb_libc_ulwp_t u; 1097 uintptr_t ulwp_addr; 1098 uintptr_t key = NULL; 1099 void *element = NULL; 1100 1101 if (mdb_getopts(argc, argv, 'k', MDB_OPT_UINTPTR, &key, NULL) != argc) 1102 return (DCMD_USAGE); 1103 1104 if (!(flags & DCMD_ADDRSPEC) || key == NULL) 1105 return (DCMD_USAGE); 1106 1107 if (tid2ulwp_impl(addr, &ulwp_addr) != DCMD_OK) 1108 return (DCMD_ERR); 1109 1110 if (mdb_ctf_vread(&u, "ulwp_t", "mdb_libc_ulwp_t", ulwp_addr, 0) == -1) 1111 return (DCMD_ERR); 1112 1113 if (key < TSD_NFAST) { 1114 element = u.ul_ftsd[key]; 1115 } else if (u.ul_stsd != NULL) { 1116 uint_t nalloc; 1117 /* tsd_t is a union, so we can't use ctf_vread() on it. */ 1118 if (mdb_vread(&nalloc, sizeof (nalloc), 1119 (uintptr_t)&u.ul_stsd->tsd_nalloc) == -1) { 1120 mdb_warn("failed to read tsd_t at %p", u.ul_stsd); 1121 return (DCMD_ERR); 1122 } 1123 if (key < nalloc) { 1124 if (mdb_vread(&element, sizeof (element), 1125 (uintptr_t)&u.ul_stsd->tsd_data[key]) == -1) { 1126 mdb_warn("failed to read tsd_t at %p", 1127 u.ul_stsd); 1128 return (DCMD_ERR); 1129 } 1130 } 1131 } 1132 1133 if (element == NULL && (flags & DCMD_PIPE)) 1134 return (DCMD_OK); 1135 1136 mdb_printf("%p\n", element); 1137 return (DCMD_OK); 1138 } 1139 1140 static const mdb_dcmd_t dcmds[] = { 1141 { "jmp_buf", ":", "print jmp_buf contents", d_jmp_buf, NULL }, 1142 { "sigjmp_buf", ":", "print sigjmp_buf contents", d_sigjmp_buf, NULL }, 1143 { "siginfo", ":", "print siginfo_t structure", d_siginfo, NULL }, 1144 { "stacks", "?[-afiv] [-c func] [-C func] [-m module] [-M module] ", 1145 "print unique thread stacks", stacks_dcmd, stacks_help }, 1146 { "tid2ulwp", "?", "convert TID to ulwp_t address", tid2ulwp }, 1147 { "ucontext", ":", "print ucontext_t structure", d_ucontext, NULL }, 1148 { "ulwp", ":", "print ulwp_t structure", d_ulwp, NULL }, 1149 { "uberdata", ":", "print uberdata_t structure", d_uberdata, NULL }, 1150 { "tsd", ":-k key", "print tsd for this thread", d_tsd, NULL }, 1151 { NULL } 1152 }; 1153 1154 static const mdb_walker_t walkers[] = { 1155 { "ucontext", "walk ucontext_t uc_link list", 1156 NULL, uc_walk_step, NULL, NULL }, 1157 { "oldcontext", "walk per-lwp oldcontext pointers", 1158 oldc_walk_init, oldc_walk_step, oldc_walk_fini, NULL }, 1159 { "ulwps", "walk list of ulwp_t pointers", 1160 ulwp_walk_init, ulwp_walk_step, NULL, NULL }, 1161 { "ulwp", "walk list of ulwp_t pointers", 1162 ulwp_walk_init, ulwp_walk_step, NULL, NULL }, 1163 { NULL } 1164 }; 1165 1166 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers }; 1167 1168 const mdb_modinfo_t * 1169 _mdb_init(void) 1170 { 1171 mdb_whatis_register("threads", whatis_run_ulwps, NULL, 1172 WHATIS_PRIO_EARLY, WHATIS_REG_NO_ID); 1173 1174 return (&modinfo); 1175 } 1176