xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/genunix.c (revision 07de4b8b08600eb9a66746ea6cc4a9fbc7981e4f)
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  
127  static char
128  pstat2ch(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  
150  static int
151  ps_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  
181  typedef 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  
188  static int
189  pflags(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  
270  typedef 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  
293  int
294  ps(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  
396  static void
397  ps_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  
444  typedef 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  
455  typedef 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*/
463  static int
464  pgrep_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*/
526  int
527  pgrep(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  
605  int
606  task(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  
636  int
637  project(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. */
662  int
663  callout_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). */
677  int
678  callout_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  
704  void
705  callout_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   */
714  int
715  callout_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  
725  int
726  callout_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  
747  void
748  callout_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) */
754  typedef 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  
764  int
765  callout_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  
794  int
795  callout_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  
849  void
850  callout_table_walk_fini(mdb_walk_state_t *wsp)
851  {
852  	mdb_free(wsp->walk_data, sizeof (cot_data_t));
853  }
854  
855  static 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 */
899  typedef 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*/
920  static int
921  callouts_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*/
1102  static int
1103  callout_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. */
1255  static int
1256  callout_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   */
1447  int
1448  callout_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*/
1482  int
1483  callout(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*/
1662  int
1663  calloutid(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  
1777  void
1778  callout_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  
1814  void
1815  calloutid_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*/
1825  int
1826  class(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  
1865  int
1866  vnode2path(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  
1905  int
1906  ld_walk_init(mdb_walk_state_t *wsp)
1907  {
1908  	wsp->walk_data = (void *)wsp->walk_addr;
1909  	return (WALK_NEXT);
1910  }
1911  
1912  int
1913  ld_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  
1935  int
1936  lg_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  
1951  typedef 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   */
1963  static int
1964  lg_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  
1974  int
1975  lg_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  
2021  typedef struct mdb_lminfo_proc {
2022  	struct {
2023  		char		u_comm[MAXCOMLEN + 1];
2024  	} p_user;
2025  } mdb_lminfo_proc_t;
2026  
2027  /*ARGSUSED*/
2028  static int
2029  lminfo_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*/
2057  int
2058  lminfo(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  
2067  typedef 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*/
2074  int
2075  whereopen_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*/
2092  int
2093  whereopen_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*/
2111  int
2112  whereopen(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  
2131  typedef struct datafmt {
2132  	char	*hdr1;
2133  	char	*hdr2;
2134  	char	*dashes;
2135  	char	*fmt;
2136  } datafmt_t;
2137  
2138  static 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  
2150  static 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*/
2162  static int
2163  kmastat_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*/
2183  static int
2184  kmastat_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*/
2192  static int
2193  kmastat_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  
2200  typedef 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  
2208  typedef struct kmastat_args {
2209  	kmastat_vmem_t **ka_kvpp;
2210  	uint_t ka_shift;
2211  } kmastat_args_t;
2212  
2213  static int
2214  kmastat_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;
2248  out:
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  
2267  static int
2268  kmastat_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*/
2291  static int
2292  kmastat_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*/
2329  int
2330  kmastat(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   */
2417  typedef 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  
2428  static int
2429  kgrep_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*/
2444  static int
2445  kgrep_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*/
2459  static int
2460  kgrep_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  
2465  static int
2466  kgrep_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  
2487  int
2488  kgrep_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  
2555  size_t
2556  kgrep_subr_pagesize(void)
2557  {
2558  	return (PAGESIZE);
2559  }
2560  
2561  typedef 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  
2568  typedef 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  
2577  int
2578  file_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  
2621  int
2622  file_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  
2628  again:
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  
2639  int
2640  allfile_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  
2657  void
2658  file_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  
2666  int
2667  port_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  
2681  int
2682  port_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  
2705  typedef 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  
2711  int
2712  portev_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  
2759  int
2760  portev_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  
2782  void
2783  portev_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  
2791  typedef struct proc_walk_data {
2792  	uintptr_t *pw_stack;
2793  	int pw_depth;
2794  	int pw_max;
2795  } proc_walk_data_t;
2796  
2797  int
2798  proc_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  
2825  typedef struct mdb_walk_proc {
2826  	struct proc	*p_child;
2827  	struct proc	*p_sibling;
2828  } mdb_walk_proc_t;
2829  
2830  int
2831  proc_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  
2882  sib:
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  
2908  void
2909  proc_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  
2917  int
2918  task_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  
2931  typedef struct mdb_task_proc {
2932  	struct proc	*p_tasknext;
2933  } mdb_task_proc_t;
2934  
2935  int
2936  task_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  
2956  int
2957  project_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  
2969  int
2970  project_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  
2989  static int
2990  generic_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  
2996  static int
2997  cpu_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  
3015  typedef struct cpu_walk {
3016  	uintptr_t *cw_array;
3017  	int cw_ndx;
3018  } cpu_walk_t;
3019  
3020  int
3021  cpu_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  
3090  int
3091  cpu_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  
3108  typedef 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  
3117  int
3118  cpuinfo_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  
3180  typedef struct mdb_cpuinfo_proc {
3181  	struct {
3182  		char		u_comm[MAXCOMLEN + 1];
3183  	} p_user;
3184  } mdb_cpuinfo_proc_t;
3185  
3186  int
3187  cpuinfo_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  
3419  int
3420  cpuinfo(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*/
3492  int
3493  flipone(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  
3506  typedef struct mdb_as2proc_proc {
3507  	struct as *p_as;
3508  } mdb_as2proc_proc_t;
3509  
3510  /*ARGSUSED*/
3511  int
3512  as2proc_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*/
3524  int
3525  as2proc(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  
3538  typedef 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*/
3546  int
3547  ptree_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  
3568  void
3569  ptree_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*/
3586  int
3587  ptree(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  
3602  typedef 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*/
3612  static int
3613  fd(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*/
3653  static int
3654  pid2proc(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  
3670  static 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  
3683  static char *sysfile_ops[] = { "", "=", "&", "|" };
3684  
3685  /*ARGSUSED*/
3686  static int
3687  sysfile_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*/
3697  static int
3698  sysfile(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  
3771  int
3772  didmatch(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*/
3783  int
3784  did2thread(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  
3803  static int
3804  errorq_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  
3815  static int
3816  errorq_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  
3833  typedef 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   */
3848  static void
3849  eqd_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  
3870  static int
3871  eqd_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  
3935  static int
3936  eqd_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  
3954  static void
3955  eqd_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  
3966  static int
3967  errorq(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*/
4042  static int
4043  panicinfo(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*/
4099  static int
4100  time(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  
4127  void
4128  time_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  
4141  extern int cmd_refstr(uintptr_t, uint_t, int, const mdb_arg_t *);
4142  
4143  static 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  
4509  static 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  
4906  static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
4907  
4908  /*ARGSUSED*/
4909  static void
4910  genunix_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  
4921  const 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  
4936  void
4937  _mdb_fini(void)
4938  {
4939  	leaky_cleanup(1);
4940  	stacks_cleanup(1);
4941  }
4942