xref: /illumos-gate/usr/src/cmd/mdb/common/modules/libc/libc.c (revision 445784c00080f22524a1a4659cf8b7b2e1ad84d1)
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