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