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 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
28 */
29
30#include <mdb/mdb_modapi.h>
31#include <mdb/mdb_ks.h>
32
33#include <ctype.h>
34#include <sys/types.h>
35#include <sys/strsubr.h>
36#include <sys/stream.h>
37#include <sys/modctl.h>
38#include <sys/strft.h>
39#include <sys/strsun.h>
40#include <sys/sysmacros.h>
41
42#include "streams.h"
43
44typedef struct str_flags {
45	uint_t strf_flag;
46	const char *strf_name;
47	const char *strf_descr;
48} strflags_t;
49
50typedef struct str_types {
51	const char *strt_name;
52	int strt_value;
53	const char *strt_descr;
54} strtypes_t;
55
56typedef struct ftblk_data {
57	ftblk_t ft_data;	/* Copy of ftblk */
58	int	ft_ix;		/* Index in event list */
59	boolean_t ft_in_evlist;	/* Iterating through evlist */
60} ftblkdata_t;
61
62typedef void qprint_func(queue_t *, queue_t *);
63typedef void sdprint_func(stdata_t *, stdata_t *);
64
65#define	SF(flag)	flag, #flag
66
67/*
68 * Queue flags
69 */
70static const strflags_t qf[] = {
71	{ SF(QENAB),		"Queue is already enabled to run"	},
72	{ SF(QWANTR),		"Someone wants to read Q"		},
73	{ SF(QWANTW),		"Someone wants to write Q"		},
74	{ SF(QFULL),		"Q is considered full"			},
75	{ SF(QREADR),		"This is the reader (first) Q"		},
76	{ SF(QUSE),		"This queue in use (allocation)"	},
77	{ SF(QNOENB),		"Don't enable Q via putq"		},
78	{ SF(QWANTRMQSYNC),	"Want to remove sync stream Q"		},
79	{ SF(QBACK),		"queue has been back-enabled"		},
80	{ SF(0x00000200),	"unused (was QHLIST)"			},
81	{ SF(0x00000400),	"unused (was QUNSAFE)"			},
82	{ SF(QPAIR),		"per queue-pair syncq"			},
83	{ SF(QPERQ),		"per queue-instance syncq"		},
84	{ SF(QPERMOD),		"per module syncq"			},
85	{ SF(QMTSAFE),		"stream module is MT-safe"		},
86	{ SF(QMTOUTPERIM),	"Has outer perimeter"			},
87	{ SF(QINSERVICE),	"service routine executing"		},
88	{ SF(QWCLOSE),		"will not be enabled"			},
89	{ SF(QEND),		"last queue in stream"			},
90	{ SF(QWANTWSYNC),	"Streamhead wants to write Q"		},
91	{ SF(QSYNCSTR),		"Q supports Synchronous STREAMS"	},
92	{ SF(QISDRV),		"the Queue is attached to a driver"	},
93	{ SF(0x00400000),	"unused (was QHOT)"			},
94	{ SF(0x00800000),	"unused (was QNEXTHOT)"			},
95	{ SF(0x01000000),	"unused (was _QNEXTLESS)"		},
96	{ SF(0x02000000),	"unused"				},
97	{ SF(_QINSERTING),	"module is inserted with _I_INSERT"	},
98	{ SF(_QREMOVING)	"module is removed with _I_REMOVE"	},
99	{ SF(_QASSOCIATED),	"queue is associated with a device"	},
100	{ 0, NULL,		NULL					}
101};
102
103/*
104 * Syncq flags
105 */
106static const struct str_flags sqf[] = {
107	{ SF(SQ_EXCL),		"Exclusive access to inner perimeter"	},
108	{ SF(SQ_BLOCKED),	"qprocsoff in progress"			},
109	{ SF(SQ_FROZEN),	"freezestr in progress"			},
110	{ SF(SQ_WRITER),	"qwriter(OUTER) pending or running"	},
111	{ SF(SQ_MESSAGES),	"There are messages on syncq"		},
112	{ SF(SQ_WANTWAKEUP)	"Thread waiting on sq_wait"		},
113	{ SF(SQ_WANTEXWAKEUP),	"Thread waiting on sq_exwait"		},
114	{ SF(SQ_EVENTS),	"There are events on syncq"		},
115	{ 0, NULL,		NULL					}
116};
117
118/*
119 * Syncq types
120 */
121static const struct str_flags sqt[] = {
122	{ SF(SQ_CIPUT),		"Concurrent inner put procedure"	},
123	{ SF(SQ_CISVC),		"Concurrent inner svc procedure"	},
124	{ SF(SQ_CIOC),		"Concurrent inner open/close"		},
125	{ SF(SQ_CICB),		"Concurrent inner callback"		},
126	{ SF(SQ_COPUT),		"Concurrent outer put procedure"	},
127	{ SF(SQ_COSVC),		"Concurrent outer svc procedure"	},
128	{ SF(SQ_COOC),		"Concurrent outer open/close"		},
129	{ SF(SQ_COCB),		"Concurrent outer callback"		},
130	{ 0, NULL,		NULL					}
131};
132
133/*
134 * Stdata flags
135 */
136static const struct str_flags stdf[] = {
137	{ SF(IOCWAIT),		"someone is doing an ioctl"		},
138	{ SF(RSLEEP),		"someone wants to read/recv msg"	},
139	{ SF(WSLEEP),		"someone wants to write"		},
140	{ SF(STRPRI),		"an M_PCPROTO is at stream head"	},
141	{ SF(STRHUP),		"device has vanished"			},
142	{ SF(STWOPEN),		"waiting for 1st open"			},
143	{ SF(STPLEX),		"stream is being multiplexed"		},
144	{ SF(STRISTTY),		"stream is a terminal"			},
145	{ SF(STRGETINPROG),	"(k)strgetmsg is running"		},
146	{ SF(IOCWAITNE),	"STR_NOERROR ioctl running"		},
147	{ SF(STRDERR),		"fatal read error from M_ERROR"		},
148	{ SF(STWRERR),		"fatal write error from M_ERROR"	},
149	{ SF(STRDERRNONPERSIST), "nonpersistent read errors"		},
150	{ SF(STWRERRNONPERSIST), "nonpersistent write errors"		},
151	{ SF(STRCLOSE),		"wait for a close to complete"		},
152	{ SF(SNDMREAD),		"used for read notification"		},
153	{ SF(OLDNDELAY),	"use old NDELAY TTY semantics"		},
154	{ SF(STRXPG4TTY),	"Use XPG4 TTY semantics"		},
155	{ SF(0x00040000),	"unused"				},
156	{ SF(STRTOSTOP),	"block background writes"		},
157	{ SF(STRCMDWAIT),	"someone is doing an _I_CMD"		},
158	{ SF(0x00200000),	"unused"				},
159	{ SF(STRMOUNT),		"stream is mounted"			},
160	{ SF(STRNOTATMARK),	"Not at mark (when empty read q)"	},
161	{ SF(STRDELIM),		"generate delimited messages"		},
162	{ SF(STRATMARK),	"at mark (due to MSGMARKNEXT)"		},
163	{ SF(STZCNOTIFY),	"wait for zerocopy mblk to be acked"	},
164	{ SF(STRPLUMB),		"stream plumbing changes in progress"	},
165	{ SF(STREOF),		"End-of-file indication"		},
166	{ SF(STREOPENFAIL),	"re-open has failed"			},
167	{ SF(STRMATE),		"this stream is a mate"			},
168	{ SF(STRHASLINKS),	"there are I_LINKs under this stream"	},
169	{ 0, NULL,		NULL					}
170};
171
172static const struct str_flags mbf[] = {
173	{ SF(MSGMARK),		"last byte of message is marked"	},
174	{ SF(MSGNOLOOP),	"don't loop message to write side"	},
175	{ SF(MSGDELIM),		"message is delimited"			},
176	{ SF(0x08),		"unused"				},
177	{ SF(MSGMARKNEXT),	"Private: b_next's first byte marked"	},
178	{ SF(MSGNOTMARKNEXT),	"Private: ... not marked"		},
179	{ 0, NULL,		NULL					}
180};
181
182#define	M_DATA_T 0xff
183
184static const strtypes_t mbt[] = {
185	{ "M_DATA",	M_DATA_T,	"regular data"			},
186	{ "M_PROTO",	M_PROTO,	"protocol control"		},
187	{ "M_MULTIDATA", M_MULTIDATA,	"multidata"			},
188	{ "M_BREAK",	M_BREAK,	"line break"			},
189	{ "M_PASSFP",	M_PASSFP,	"pass file pointer"		},
190	{ "M_EVENT",	M_EVENT,	"Obsoleted: do not use"		},
191	{ "M_SIG",	M_SIG,		"generate process signal"	},
192	{ "M_DELAY",	M_DELAY,	"real-time xmit delay"		},
193	{ "M_CTL",	M_CTL,		"device-specific control message" },
194	{ "M_IOCTL",	M_IOCTL,	"ioctl; set/get params"		},
195	{ "M_SETOPTS",	M_SETOPTS,	"set stream head options"	},
196	{ "M_RSE",	M_RSE,		"reserved for RSE use only"	},
197	{ "M_IOCACK",	M_IOCACK,	"acknowledge ioctl"		},
198	{ "M_IOCNAK",	M_IOCNAK,	"negative ioctl acknowledge"	},
199	{ "M_PCPROTO",	M_PCPROTO,	"priority proto message"	},
200	{ "M_PCSIG",	M_PCSIG,	"generate process signal"	},
201	{ "M_READ",	M_READ,		"generate read notification"	},
202	{ "M_FLUSH",	M_FLUSH,	"flush your queues"		},
203	{ "M_STOP",	M_STOP,		"stop transmission immediately" },
204	{ "M_START",	M_START,	"restart transmission after stop" },
205	{ "M_HANGUP",	M_HANGUP,	"line disconnect"		},
206	{ "M_ERROR",	M_ERROR,	"send error to stream head"	},
207	{ "M_COPYIN",	M_COPYIN,	"request to copyin data"	},
208	{ "M_COPYOUT",	M_COPYOUT,	"request to copyout data"	},
209	{ "M_IOCDATA",	M_IOCDATA,	"response to M_COPYIN and M_COPYOUT" },
210	{ "M_PCRSE",	M_PCRSE,	"reserved for RSE use only"	},
211	{ "M_STOPI",	M_STOPI,	"stop reception immediately"	},
212	{ "M_STARTI",	M_STARTI,	"restart reception after stop"	},
213	{ "M_PCEVENT",	M_PCEVENT,	"Obsoleted: do not use"		},
214	{ "M_UNHANGUP",	M_UNHANGUP,	"line reconnect"		},
215	{ "M_CMD",	M_CMD,		"out-of-band ioctl command"	},
216	{ NULL,		0,		NULL				}
217};
218
219/* Allocation flow trace events, starting from 0 */
220static const char *ftev_alloc[] = {
221/* 0 */	"allocb",
222/* 1 */	"esballoc",
223/* 2 */	"desballoc",
224/* 3 */	"esballoca",
225/* 4 */	"desballoca",
226/* 5 */	"allocbig",
227/* 6 */	"allocbw",
228/* 7 */	"bcallocb",
229/* 8 */	"freeb",
230/* 9 */	"dupb",
231/* A */	"copyb",
232};
233
234#define	FTEV_PROC_START FTEV_PUT
235
236/* Procedures recorded by flow tracing, starting from 0x100 */
237static const char *ftev_proc[] = {
238/* 100 */	"put",
239/* 101 */	"0x101",
240/* 102 */	"0x102",
241/* 103 */	"0x103",
242/* 104 */	"0x104",
243/* 105 */	"putq",
244/* 106 */	"getq",
245/* 107 */	"rmvq",
246/* 108 */	"insq",
247/* 109 */	"putbq",
248/* 10A */	"flushq",
249/* 10B */	"0x10b",
250/* 10C */	"0x10c",
251/* 10D */	"putnext",
252/* 10E */	"rwnext",
253};
254
255static const char *db_control_types[] = {
256/* 00 */	"data",
257/* 01 */	"proto",
258/* 02 */	"multidata",
259/* 03 */	"0x03",
260/* 04 */	"0x04",
261/* 05 */	"0x05",
262/* 06 */	"0x06",
263/* 07 */	"0x07",
264/* 08 */	"break",
265/* 09 */	"passfp",
266/* 0a */	"event",
267/* 0b */	"sig",
268/* 0c */	"delay",
269/* 0d */	"ctl",
270/* 0e */	"ioctl",
271/* 0f */	"unused",
272/* 10 */	"setopts",
273/* 11 */	"rse",
274};
275
276static const char *db_control_hipri_types[] = {
277/* 81 */	"iocack",
278/* 82 */	"iocnak",
279/* 83 */	"pcproto",
280/* 84 */	"pcsig",
281/* 85 */	"read",
282/* 86 */	"flush",
283/* 87 */	"stop",
284/* 88 */	"start",
285/* 89 */	"hangup",
286/* 8a */	"error",
287/* 8b */	"copyin",
288/* 8c */	"copyout",
289/* 8d */	"iocdata",
290/* 8e */	"pcrse",
291/* 8f */	"stopi",
292/* 90 */	"starti",
293/* 91 */	"pcevent",
294/* 92 */	"unhangup",
295/* 93 */	"cmd",
296};
297
298#define	A_SIZE(a) (sizeof (a) / sizeof (a[0]))
299
300static void ft_printevent(ushort_t);
301
302static int
303streams_parse_flag(const strflags_t ftable[], const char *arg, uint32_t *flag)
304{
305	int i;
306
307	for (i = 0; ftable[i].strf_name != NULL; i++) {
308		if (strcasecmp(arg, ftable[i].strf_name) == 0) {
309			*flag |= (1 << i);
310			return (0);
311		}
312	}
313
314	return (-1);
315}
316
317static void
318streams_flag_usage(const strflags_t ftable[])
319{
320	int i;
321
322	for (i = 0; ftable[i].strf_name != NULL; i++)
323		mdb_printf("%-14s %s\n",
324		    ftable[i].strf_name, ftable[i].strf_descr);
325}
326
327static int
328streams_parse_type(const strtypes_t ftable[], const char *arg, uint32_t *flag)
329{
330	int i;
331
332	for (i = 0; ftable[i].strt_name != NULL; i++) {
333		if (strcasecmp(arg, ftable[i].strt_name) == 0) {
334			*flag = ftable[i].strt_value;
335			return (0);
336		}
337	}
338
339	return (-1);
340}
341
342static void
343streams_type_usage(const strtypes_t ftable[])
344{
345	int i;
346
347	for (i = 0; ftable[i].strt_name != NULL; i++)
348		mdb_printf("%-12s %s\n",
349		    ftable[i].strt_name, ftable[i].strt_descr);
350}
351
352int
353queue(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
354{
355	const int QUEUE_FLGDELT = (int)(sizeof (uintptr_t) * 2 + 15);
356
357	char name[MODMAXNAMELEN];
358	int nblks = 0;
359	uintptr_t maddr;
360	mblk_t mblk;
361	queue_t q;
362
363	const char *mod = NULL, *flag = NULL, *not_flag = NULL;
364	uint_t quiet = FALSE;
365	uint_t verbose = FALSE;
366	uint32_t mask = 0, not_mask = 0;
367	uintptr_t syncq = 0;
368
369	if (!(flags & DCMD_ADDRSPEC)) {
370		if (mdb_walk_dcmd("genunix`queue_cache", "genunix`queue",
371		    argc, argv) == -1) {
372			mdb_warn("failed to walk queue cache");
373			return (DCMD_ERR);
374		}
375		return (DCMD_OK);
376	}
377
378	if (flags & DCMD_PIPE_OUT)
379		quiet = TRUE;
380
381	if (mdb_getopts(argc, argv,
382	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
383	    'q', MDB_OPT_SETBITS, TRUE, &quiet,
384	    'm', MDB_OPT_STR, &mod,
385	    'f', MDB_OPT_STR, &flag,
386	    'F', MDB_OPT_STR, &not_flag,
387	    's', MDB_OPT_UINTPTR, &syncq,
388	    NULL) != argc)
389		return (DCMD_USAGE);
390
391	/*
392	 * If any of the filtering flags is specified, don't print anything
393	 * except the matching pointer.
394	 */
395	if (flag != NULL || not_flag != NULL || mod != NULL || syncq != 0)
396		quiet = TRUE;
397
398	if (DCMD_HDRSPEC(flags) && !quiet) {
399		mdb_printf("%?s %-13s %6s %4s\n",
400		    "ADDR", "MODULE", "FLAGS", "NBLK");
401	}
402
403	if (flag != NULL && streams_parse_flag(qf, flag, &mask) == -1) {
404		mdb_warn("unrecognized queue flag '%s'\n", flag);
405		streams_flag_usage(qf);
406		return (DCMD_USAGE);
407	}
408
409	if (not_flag != NULL &&
410	    streams_parse_flag(qf, not_flag, &not_mask) == -1) {
411		mdb_warn("unrecognized queue flag '%s'\n", flag);
412		streams_flag_usage(qf);
413		return (DCMD_USAGE);
414	}
415
416	if (mdb_vread(&q, sizeof (q), addr) == -1) {
417		mdb_warn("couldn't read queue at %p", addr);
418		return (DCMD_ERR);
419	}
420
421	for (maddr = (uintptr_t)q.q_first; maddr != 0; nblks++) {
422		if (mdb_vread(&mblk, sizeof (mblk), maddr) == -1) {
423			mdb_warn("couldn't read mblk %p for queue %p",
424			    maddr, addr);
425			break;
426		}
427		maddr = (uintptr_t)mblk.b_next;
428	}
429
430	(void) mdb_qname(&q, name, sizeof (name));
431
432	/*
433	 * If queue doesn't pass filtering criteria, don't print anything and
434	 * just return.
435	 */
436
437	if (mod != NULL && strcmp(mod, name) != 0)
438		return (DCMD_OK);
439
440	if (mask != 0 && !(q.q_flag & mask))
441		return (DCMD_OK);
442
443	if (not_mask != 0 && (q.q_flag & not_mask))
444		return (DCMD_OK);
445
446	if (syncq != 0 && q.q_syncq != (syncq_t *)syncq)
447		return (DCMD_OK);
448
449	/*
450	 * Options are specified for filtering, so If any option is specified on
451	 * the command line, just print address and exit.
452	 */
453	if (quiet) {
454		mdb_printf("%0?p\n", addr);
455		return (DCMD_OK);
456	}
457
458	mdb_printf("%0?p %-13s %06x %4d %0?p\n",
459	    addr, name, q.q_flag, nblks, q.q_first);
460
461	if (verbose) {
462		int i, arm = 0;
463
464		for (i = 0; qf[i].strf_name != NULL; i++) {
465			if (!(q.q_flag & (1 << i)))
466				continue;
467			if (!arm) {
468				mdb_printf("%*s|\n%*s+-->  ",
469				    QUEUE_FLGDELT, "", QUEUE_FLGDELT, "");
470				arm = 1;
471			} else
472				mdb_printf("%*s      ", QUEUE_FLGDELT, "");
473
474			mdb_printf("%-12s %s\n",
475			    qf[i].strf_name, qf[i].strf_descr);
476		}
477	}
478
479	return (DCMD_OK);
480}
481
482int
483syncq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
484{
485	const int SYNC_FLGDELT = (int)(sizeof (uintptr_t) * 2 + 1);
486	const int SYNC_TYPDELT = (int)(sizeof (uintptr_t) * 2 + 5);
487	syncq_t sq;
488
489	const char *flag = NULL, *not_flag = NULL;
490	const char *typ = NULL, *not_typ = NULL;
491	uint_t verbose = FALSE;
492	uint_t quiet = FALSE;
493	uint32_t mask = 0, not_mask = 0;
494	uint32_t tmask = 0, not_tmask = 0;
495	uint8_t sqtype = 0;
496
497	if (!(flags & DCMD_ADDRSPEC)) {
498		if (mdb_walk_dcmd("genunix`syncq_cache", "genunix`syncq",
499		    argc, argv) == -1) {
500			mdb_warn("failed to walk syncq cache");
501			return (DCMD_ERR);
502		}
503		return (DCMD_OK);
504	}
505
506	if (flags & DCMD_PIPE_OUT)
507		quiet = TRUE;
508
509	if (mdb_getopts(argc, argv,
510	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
511	    'q', MDB_OPT_SETBITS, TRUE, &quiet,
512	    'f', MDB_OPT_STR, &flag,
513	    'F', MDB_OPT_STR, &not_flag,
514	    't', MDB_OPT_STR, &typ,
515	    'T', MDB_OPT_STR, &not_typ,
516	    NULL) != argc)
517		return (DCMD_USAGE);
518
519	/*
520	 * If any of the filtering flags is specified, don't print anything
521	 * except the matching pointer.
522	 */
523	if (flag != NULL || not_flag != NULL || typ != NULL || not_typ != NULL)
524		quiet = TRUE;
525
526	if (DCMD_HDRSPEC(flags) && !quiet) {
527		mdb_printf("%?s %s %s %s %s %?s %s %s\n",
528		    "ADDR", "FLG", "TYP", "CNT", "NQS", "OUTER", "SF", "PRI");
529	}
530
531	if (flag != NULL && streams_parse_flag(sqf, flag, &mask) == -1) {
532		mdb_warn("unrecognized syncq flag '%s'\n", flag);
533		streams_flag_usage(sqf);
534		return (DCMD_USAGE);
535	}
536
537	if (typ != NULL && streams_parse_flag(sqt, typ, &tmask) == -1) {
538		mdb_warn("unrecognized syncq type '%s'\n", typ);
539		streams_flag_usage(sqt);
540		return (DCMD_USAGE);
541	}
542
543	if (not_flag != NULL && streams_parse_flag(sqf, not_flag, &not_mask)
544	    == -1) {
545		mdb_warn("unrecognized syncq flag '%s'\n", not_flag);
546		streams_flag_usage(sqf);
547		return (DCMD_USAGE);
548	}
549
550	if (not_typ != NULL && streams_parse_flag(sqt, not_typ, &not_tmask)
551	    == -1) {
552		mdb_warn("unrecognized syncq type '%s'\n", not_typ);
553		streams_flag_usage(sqt);
554		return (DCMD_USAGE);
555	}
556
557	if (mdb_vread(&sq, sizeof (sq), addr) == -1) {
558		mdb_warn("couldn't read syncq at %p", addr);
559		return (DCMD_ERR);
560	}
561
562	if (mask != 0 && !(sq.sq_flags & mask))
563		return (DCMD_OK);
564
565	if (not_mask != 0 && (sq.sq_flags & not_mask))
566		return (DCMD_OK);
567
568	sqtype = (sq.sq_type >> 8) & 0xff;
569
570	if (tmask != 0 && !(sqtype & tmask))
571		return (DCMD_OK);
572
573	if (not_tmask != 0 && (sqtype & not_tmask))
574		return (DCMD_OK);
575
576	/*
577	 * Options are specified for filtering, so If any option is specified on
578	 * the command line, just print address and exit.
579	 */
580	if (quiet) {
581		mdb_printf("%0?p\n", addr);
582		return (DCMD_OK);
583	}
584
585	mdb_printf("%0?p %02x  %02x  %-3u %-3u %0?p  %1x %-3d\n",
586	    addr, sq.sq_flags & 0xff, sqtype, sq.sq_count,
587	    sq.sq_nqueues, sq.sq_outer, sq.sq_svcflags, sq.sq_pri);
588
589	if (verbose) {
590		int i, arm = 0;
591
592		for (i = 0; sqf[i].strf_name != NULL; i++) {
593			if (!(sq.sq_flags & (1 << i)))
594				continue;
595			if (!arm) {
596				mdb_printf("%*s|\n%*s+-->  ",
597				    SYNC_FLGDELT, "", SYNC_FLGDELT, "");
598				arm = 1;
599			} else
600				mdb_printf("%*s      ", SYNC_FLGDELT, "");
601
602			mdb_printf("%-12s %s\n",
603			    sqf[i].strf_name, sqf[i].strf_descr);
604		}
605
606		for (i = 0; sqt[i].strf_name != NULL; i++) {
607			if (!(sqtype & (1 << i)))
608				continue;
609			if (!arm) {
610				mdb_printf("%*s|\n%*s+-->  ",
611				    SYNC_TYPDELT, "", SYNC_TYPDELT, "");
612				arm = 1;
613			} else
614				mdb_printf("%*s      ", SYNC_TYPDELT, "");
615
616			mdb_printf("%-12s %s\n",
617			    sqt[i].strf_name, sqt[i].strf_descr);
618		}
619	}
620
621	return (DCMD_OK);
622}
623
624int
625stdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
626{
627	const int STREAM_FLGDELT = (int)(sizeof (uintptr_t) * 2 + 10);
628
629	stdata_t  sd;
630
631	const char *flag = NULL, *not_flag = NULL;
632	uint_t verbose = FALSE;
633	uint_t quiet = FALSE;
634	uint32_t mask = 0, not_mask = 0;
635
636	if (!(flags & DCMD_ADDRSPEC)) {
637		if (mdb_walk_dcmd("genunix`stream_head_cache",
638		    "genunix`stdata", argc, argv) == -1) {
639			mdb_warn("failed to walk stream head cache");
640			return (DCMD_ERR);
641		}
642		return (DCMD_OK);
643	}
644
645	if (flags & DCMD_PIPE_OUT)
646		quiet = TRUE;
647
648	if (mdb_getopts(argc, argv,
649	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
650	    'q', MDB_OPT_SETBITS, TRUE, &quiet,
651	    'f', MDB_OPT_STR, &flag,
652	    'F', MDB_OPT_STR, &not_flag,
653	    NULL) != argc)
654		return (DCMD_USAGE);
655
656	/*
657	 * If any of the filtering flags is specified, don't print anything
658	 * except the matching pointer.
659	 */
660	if (flag != NULL || not_flag != NULL)
661		quiet = TRUE;
662
663	if (DCMD_HDRSPEC(flags) && !quiet) {
664		mdb_printf("%?s %?s %8s %?s %s %s\n",
665		    "ADDR", "WRQ", "FLAGS", "VNODE", "N/A", "REF");
666	}
667
668	if (flag != NULL && streams_parse_flag(stdf, flag, &mask) == -1) {
669		mdb_warn("unrecognized stream flag '%s'\n", flag);
670		streams_flag_usage(stdf);
671		return (DCMD_USAGE);
672	}
673
674	if (not_flag != NULL &&
675	    streams_parse_flag(stdf, not_flag, &not_mask) == -1) {
676		mdb_warn("unrecognized stream flag '%s'\n", flag);
677		streams_flag_usage(stdf);
678		return (DCMD_USAGE);
679	}
680
681	if (mdb_vread(&sd, sizeof (sd), addr) == -1) {
682		mdb_warn("couldn't read stdata at %p", addr);
683		return (DCMD_ERR);
684	}
685
686	/*
687	 * If stream doesn't pass filtering criteria, don't print anything and
688	 * just return.
689	 */
690
691	if (mask != 0 && !(sd.sd_flag & mask))
692		return (DCMD_OK);
693
694	if (not_mask != 0 && (sd.sd_flag & not_mask))
695		return (DCMD_OK);
696
697	/*
698	 * Options are specified for filtering, so If any option is specified on
699	 * the command line, just print address and exit.
700	 */
701	if (quiet) {
702		mdb_printf("%0?p\n", addr);
703		return (DCMD_OK);
704	}
705
706	mdb_printf("%0?p %0?p %08x %0?p %d/%d %d\n",
707	    addr, sd.sd_wrq, sd.sd_flag, sd.sd_vnode,
708	    sd.sd_pushcnt, sd.sd_anchor, sd.sd_refcnt);
709
710	if (verbose) {
711		int i, arm = 0;
712
713		for (i = 0; stdf[i].strf_name != NULL; i++) {
714			if (!(sd.sd_flag & (1 << i)))
715				continue;
716			if (!arm) {
717				mdb_printf("%*s|\n%*s+-->  ",
718				    STREAM_FLGDELT, "", STREAM_FLGDELT, "");
719				arm = 1;
720			} else
721				mdb_printf("%*s      ", STREAM_FLGDELT, "");
722
723			mdb_printf("%-12s %s\n",
724			    stdf[i].strf_name, stdf[i].strf_descr);
725		}
726	}
727
728	return (DCMD_OK);
729}
730
731/*ARGSUSED*/
732static void
733qprint_syncq(queue_t *addr, queue_t *q)
734{
735	mdb_printf("%p\n", q->q_syncq);
736}
737
738/*ARGSUSED*/
739static void
740qprint_stream(queue_t *addr, queue_t *q)
741{
742	mdb_printf("%p\n", q->q_stream);
743}
744
745static void
746qprint_wrq(queue_t *addr, queue_t *q)
747{
748	mdb_printf("%p\n", ((q)->q_flag & QREADR? (addr)+1: (addr)));
749}
750
751static void
752qprint_rdq(queue_t *addr, queue_t *q)
753{
754	mdb_printf("%p\n", ((q)->q_flag & QREADR? (addr): (addr)-1));
755}
756
757static void
758qprint_otherq(queue_t *addr, queue_t *q)
759{
760	mdb_printf("%p\n", ((q)->q_flag & QREADR? (addr)+1: (addr)-1));
761}
762
763static int
764q2x(uintptr_t addr, int argc, qprint_func prfunc)
765{
766	queue_t q;
767
768	if (argc != 0)
769		return (DCMD_USAGE);
770
771	if (mdb_vread(&q, sizeof (q), addr) == -1) {
772		mdb_warn("couldn't read queue at %p", addr);
773		return (DCMD_ERR);
774	}
775
776	prfunc((queue_t *)addr, &q);
777
778	return (DCMD_OK);
779}
780
781/*ARGSUSED*/
782int
783q2syncq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
784{
785	return (q2x(addr, argc, qprint_syncq));
786}
787
788/*ARGSUSED*/
789int
790q2stream(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
791{
792	return (q2x(addr, argc, qprint_stream));
793}
794
795/*ARGSUSED*/
796int
797q2rdq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
798{
799	return (q2x(addr, argc, qprint_rdq));
800}
801
802/*ARGSUSED*/
803int
804q2wrq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
805{
806	return (q2x(addr, argc, qprint_wrq));
807}
808
809/*ARGSUSED*/
810int
811q2otherq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
812{
813	return (q2x(addr, argc, qprint_otherq));
814}
815
816static int
817sd2x(uintptr_t addr, int argc, sdprint_func prfunc)
818{
819	stdata_t sd;
820
821	if (argc != 0)
822		return (DCMD_USAGE);
823
824	if (mdb_vread(&sd, sizeof (sd), addr) == -1) {
825		mdb_warn("couldn't read stream head at %p", addr);
826		return (DCMD_ERR);
827	}
828
829	prfunc((stdata_t *)addr, &sd);
830
831	return (DCMD_OK);
832}
833
834/*ARGSUSED*/
835static void
836sdprint_wrq(stdata_t *addr, stdata_t *sd)
837{
838	mdb_printf("%p\n", sd->sd_wrq);
839}
840
841static void
842sdprint_mate(stdata_t *addr, stdata_t *sd)
843{
844	mdb_printf("%p\n", sd->sd_mate ? sd->sd_mate : addr);
845}
846
847/*ARGSUSED*/
848int
849str2mate(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
850{
851	return (sd2x(addr, argc, sdprint_mate));
852}
853
854/*ARGSUSED*/
855int
856str2wrq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
857{
858	return (sd2x(addr, argc, sdprint_wrq));
859}
860
861/*
862 * If this syncq is a part of the queue pair structure, find the queue for it.
863 */
864/*ARGSUSED*/
865int
866syncq2q(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
867{
868	syncq_t sq;
869	queue_t q;
870	queue_t *qp;
871
872	if (argc != 0)
873		return (DCMD_USAGE);
874
875	if (mdb_vread(&sq, sizeof (sq), addr) == -1) {
876		mdb_warn("couldn't read syncq at %p", addr);
877		return (DCMD_ERR);
878	}
879
880	/* Try to find its queue */
881	qp = (queue_t *)addr - 2;
882
883	if ((mdb_vread(&q, sizeof (q), (uintptr_t)qp) == -1) ||
884	    (q.q_syncq != (syncq_t *)addr)) {
885		mdb_warn("syncq2q: %p is not part of any queue\n", addr);
886		return (DCMD_ERR);
887	} else
888		mdb_printf("%p\n", qp);
889
890	return (DCMD_OK);
891}
892
893int
894queue_walk_init(mdb_walk_state_t *wsp)
895{
896	if (wsp->walk_addr == 0 &&
897	    mdb_readvar(&wsp->walk_addr, "qhead") == -1) {
898		mdb_warn("failed to read 'qhead'");
899		return (WALK_ERR);
900	}
901
902	wsp->walk_data = mdb_alloc(sizeof (queue_t), UM_SLEEP);
903	return (WALK_NEXT);
904}
905
906int
907queue_link_step(mdb_walk_state_t *wsp)
908{
909	int status;
910
911	if (wsp->walk_addr == 0)
912		return (WALK_DONE);
913
914	if (mdb_vread(wsp->walk_data, sizeof (queue_t), wsp->walk_addr) == -1) {
915		mdb_warn("failed to read queue at %p", wsp->walk_addr);
916		return (WALK_DONE);
917	}
918
919	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
920	    wsp->walk_cbdata);
921
922	wsp->walk_addr = (uintptr_t)(((queue_t *)wsp->walk_data)->q_link);
923	return (status);
924}
925
926int
927queue_next_step(mdb_walk_state_t *wsp)
928{
929	int status;
930
931	if (wsp->walk_addr == 0)
932		return (WALK_DONE);
933
934	if (mdb_vread(wsp->walk_data, sizeof (queue_t), wsp->walk_addr) == -1) {
935		mdb_warn("failed to read queue at %p", wsp->walk_addr);
936		return (WALK_DONE);
937	}
938
939	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
940	    wsp->walk_cbdata);
941
942	wsp->walk_addr = (uintptr_t)(((queue_t *)wsp->walk_data)->q_next);
943	return (status);
944}
945
946void
947queue_walk_fini(mdb_walk_state_t *wsp)
948{
949	mdb_free(wsp->walk_data, sizeof (queue_t));
950}
951
952int
953str_walk_init(mdb_walk_state_t *wsp)
954{
955	stdata_t s;
956
957	if (wsp->walk_addr == 0) {
958		mdb_warn("walk must begin at address of stdata_t\n");
959		return (WALK_ERR);
960	}
961
962	if (mdb_vread(&s, sizeof (s), wsp->walk_addr) == -1) {
963		mdb_warn("failed to read stdata at %p", wsp->walk_addr);
964		return (WALK_ERR);
965	}
966
967	wsp->walk_addr = (uintptr_t)s.sd_wrq;
968	wsp->walk_data = mdb_alloc(sizeof (queue_t) * 2, UM_SLEEP);
969
970	return (WALK_NEXT);
971}
972
973int
974strr_walk_step(mdb_walk_state_t *wsp)
975{
976	queue_t *rq = wsp->walk_data, *wq = rq + 1;
977	int status;
978
979	if (wsp->walk_addr == 0)
980		return (WALK_DONE);
981
982	if (mdb_vread(wsp->walk_data, sizeof (queue_t) * 2,
983	    wsp->walk_addr - sizeof (queue_t)) == -1) {
984		mdb_warn("failed to read queue pair at %p",
985		    wsp->walk_addr - sizeof (queue_t));
986		return (WALK_DONE);
987	}
988
989	status = wsp->walk_callback(wsp->walk_addr - sizeof (queue_t),
990	    rq, wsp->walk_cbdata);
991
992	if (wq->q_next != NULL)
993		wsp->walk_addr = (uintptr_t)wq->q_next;
994	else
995		wsp->walk_addr = mdb_qwnext(wq);
996
997	return (status);
998}
999
1000int
1001strw_walk_step(mdb_walk_state_t *wsp)
1002{
1003	queue_t *rq = wsp->walk_data, *wq = rq + 1;
1004	int status;
1005
1006	if (wsp->walk_addr == 0)
1007		return (WALK_DONE);
1008
1009	if (mdb_vread(wsp->walk_data, sizeof (queue_t) * 2,
1010	    wsp->walk_addr - sizeof (queue_t)) == -1) {
1011		mdb_warn("failed to read queue pair at %p",
1012		    wsp->walk_addr - sizeof (queue_t));
1013		return (WALK_DONE);
1014	}
1015
1016	status = wsp->walk_callback(wsp->walk_addr, wq, wsp->walk_cbdata);
1017
1018	if (wq->q_next != NULL)
1019		wsp->walk_addr = (uintptr_t)wq->q_next;
1020	else
1021		wsp->walk_addr = mdb_qwnext(wq);
1022
1023	return (status);
1024}
1025
1026void
1027str_walk_fini(mdb_walk_state_t *wsp)
1028{
1029	mdb_free(wsp->walk_data, sizeof (queue_t) * 2);
1030}
1031
1032static int
1033print_qpair(uintptr_t addr, const queue_t *q, uint_t *depth)
1034{
1035	static const char box_lid[] =
1036	    "+-----------------------+-----------------------+\n";
1037	static const char box_sep[] =
1038	    "|                       |                       |\n";
1039
1040	char wname[32], rname[32], info1[256], *info2;
1041
1042	if (*depth != 0) {
1043		mdb_printf("            |                       ^\n");
1044		mdb_printf("            v                       |\n");
1045	} else
1046		mdb_printf("\n");
1047
1048	(void) mdb_qname(_WR(q), wname, sizeof (wname));
1049	(void) mdb_qname(_RD(q), rname, sizeof (rname));
1050
1051	mdb_qinfo(_WR(q), info1, sizeof (info1));
1052	if ((info2 = strchr(info1, '\n')) != NULL)
1053		*info2++ = '\0';
1054	else
1055		info2 = "";
1056
1057	mdb_printf(box_lid);
1058	mdb_printf("| 0x%-19p | 0x%-19p | %s\n",
1059	    addr, addr - sizeof (queue_t), info1);
1060
1061	mdb_printf("| %<b>%-21s%</b> | %<b>%-21s%</b> |", wname, rname);
1062	mdb_flush(); /* Account for buffered terminal sequences */
1063
1064	mdb_printf(" %s\n", info2);
1065	mdb_printf(box_sep);
1066
1067	mdb_qinfo(_RD(q), info1, sizeof (info1));
1068	if ((info2 = strchr(info1, '\n')) != NULL)
1069		*info2++ = '\0';
1070	else
1071		info2 = "";
1072
1073	mdb_printf("| cnt = 0t%-13lu | cnt = 0t%-13lu | %s\n",
1074	    _WR(q)->q_count, _RD(q)->q_count, info1);
1075
1076	mdb_printf("| flg = 0x%08x      | flg = 0x%08x      | %s\n",
1077	    _WR(q)->q_flag, _RD(q)->q_flag, info2);
1078
1079	mdb_printf(box_lid);
1080	*depth += 1;
1081	return (0);
1082}
1083
1084/*ARGSUSED*/
1085int
1086stream(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1087{
1088	uint_t d = 0;	/* Depth counter for print_qpair */
1089
1090	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
1091		return (DCMD_USAGE);
1092
1093	if (mdb_pwalk("writeq", (mdb_walk_cb_t)print_qpair, &d, addr) == -1) {
1094		mdb_warn("failed to walk writeq");
1095		return (DCMD_ERR);
1096	}
1097
1098	return (DCMD_OK);
1099}
1100
1101int
1102mblk_walk_init(mdb_walk_state_t *wsp)
1103{
1104	wsp->walk_data = mdb_alloc(sizeof (mblk_t), UM_SLEEP);
1105	return (WALK_NEXT);
1106}
1107
1108int
1109b_cont_step(mdb_walk_state_t *wsp)
1110{
1111	int status;
1112
1113	if (wsp->walk_addr == 0)
1114		return (WALK_DONE);
1115
1116	if (mdb_vread(wsp->walk_data, sizeof (mblk_t), wsp->walk_addr) == -1) {
1117		mdb_warn("failed to read mblk at %p", wsp->walk_addr);
1118		return (WALK_DONE);
1119	}
1120
1121	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
1122	    wsp->walk_cbdata);
1123
1124	wsp->walk_addr = (uintptr_t)(((mblk_t *)wsp->walk_data)->b_cont);
1125	return (status);
1126}
1127
1128int
1129b_next_step(mdb_walk_state_t *wsp)
1130{
1131	int status;
1132
1133	if (wsp->walk_addr == 0)
1134		return (WALK_DONE);
1135
1136	if (mdb_vread(wsp->walk_data, sizeof (mblk_t), wsp->walk_addr) == -1) {
1137		mdb_warn("failed to read mblk at %p", wsp->walk_addr);
1138		return (WALK_DONE);
1139	}
1140
1141	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
1142	    wsp->walk_cbdata);
1143
1144	wsp->walk_addr = (uintptr_t)(((mblk_t *)wsp->walk_data)->b_next);
1145	return (status);
1146}
1147
1148void
1149mblk_walk_fini(mdb_walk_state_t *wsp)
1150{
1151	mdb_free(wsp->walk_data, sizeof (mblk_t));
1152}
1153
1154/* ARGSUSED */
1155int
1156mblk2dblk(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1157{
1158	mblk_t mb;
1159
1160	if (argc != 0)
1161		return (DCMD_USAGE);
1162
1163	if (mdb_vread(&mb, sizeof (mb), addr) == -1) {
1164		mdb_warn("couldn't read mblk at %p", addr);
1165		return (DCMD_ERR);
1166	}
1167
1168	mdb_printf("%p\n", mb.b_datap);
1169	return (DCMD_OK);
1170}
1171
1172static void
1173mblk_error(int *error, uintptr_t addr, char *message)
1174{
1175	if (!*error)
1176		mdb_printf("%?lx: ", addr);
1177	else
1178		mdb_printf(", ");
1179	mdb_printf("%s", message);
1180	*error = 1;
1181}
1182
1183int
1184mblk_verify(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1185{
1186	mblk_t	mb;
1187	dblk_t	db;
1188	int	error = 0;
1189
1190	if (!(flags & DCMD_ADDRSPEC)) {
1191		if (mdb_walk_dcmd("streams_mblk", "mblk_verify", argc, argv) ==
1192		    -1) {
1193			mdb_warn("can't walk mblk cache");
1194			return (DCMD_ERR);
1195		}
1196		return (DCMD_OK);
1197	}
1198
1199	if (mdb_vread(&mb, sizeof (mblk_t), addr) == -1) {
1200		mdb_warn("can't read mblk_t at 0x%lx", addr);
1201		return (DCMD_ERR);
1202	}
1203
1204	if (mdb_vread(&db, sizeof (dblk_t), (uintptr_t)mb.b_datap) == -1) {
1205		mdb_warn("%?lx: invalid b_datap pointer\n", addr);
1206		return (DCMD_ERR);
1207	}
1208
1209	if (mb.b_rptr < db.db_base || mb.b_rptr > db.db_lim)
1210		mblk_error(&error, addr, "b_rptr out of range");
1211
1212	if (mb.b_wptr < db.db_base || mb.b_wptr > db.db_lim)
1213		mblk_error(&error, addr, "b_wptr out of range");
1214
1215	if (error)
1216		mdb_printf("\n");
1217
1218	return (error ? DCMD_ERR : DCMD_OK);
1219}
1220
1221int
1222mblk_prt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1223{
1224	const int MBLK_FLGDELT = (int)(sizeof (uintptr_t) * 2 + 15);
1225	mblk_t mblk;
1226	dblk_t dblk;
1227	int b_flag;
1228	int db_type;
1229	int mblklen;
1230	uint64_t len = ~0UL;
1231	uint64_t glen = ~0UL;
1232	uint64_t llen = ~0UL;
1233	uint64_t blen = ~0UL;
1234	const char *dbtype;
1235	const char *flag = NULL, *not_flag = NULL;
1236	const char *typ = NULL, *not_typ = NULL;
1237	uintptr_t  dbaddr = 0;
1238	uint32_t tmask = 0, not_tmask = 0;
1239	uint32_t mask = 0, not_mask = 0;
1240	uint_t quiet = FALSE;
1241	uint_t verbose = FALSE;
1242
1243	if (!(flags & DCMD_ADDRSPEC)) {
1244		if (mdb_walk_dcmd("genunix`streams_mblk", "genunix`mblk",
1245		    argc, argv) == -1) {
1246			mdb_warn("failed to walk mblk cache");
1247			return (DCMD_ERR);
1248		}
1249		return (DCMD_OK);
1250	}
1251
1252	if (flags & DCMD_PIPE_OUT)
1253		quiet = TRUE;
1254
1255	if (mdb_getopts(argc, argv,
1256	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
1257	    'q', MDB_OPT_SETBITS, TRUE, &quiet,
1258	    'f', MDB_OPT_STR, &flag,
1259	    'F', MDB_OPT_STR, &not_flag,
1260	    't', MDB_OPT_STR, &typ,
1261	    'T', MDB_OPT_STR, &not_typ,
1262	    'l', MDB_OPT_UINT64, &len,
1263	    'L', MDB_OPT_UINT64, &llen,
1264	    'G', MDB_OPT_UINT64, &glen,
1265	    'b', MDB_OPT_UINT64, &blen,
1266	    'd', MDB_OPT_UINTPTR, &dbaddr,
1267	    NULL) != argc)
1268		return (DCMD_USAGE);
1269
1270	/*
1271	 * If any of the filtering flags is specified, don't print anything
1272	 * except the matching pointer.
1273	 */
1274	if ((flag != NULL) || (not_flag != NULL) || (typ != NULL) ||
1275	    (not_typ != NULL) || (len != ~0UL) || (glen != ~0UL) ||
1276	    (llen != ~0UL) || (blen != ~0UL) || (dbaddr != 0))
1277		quiet = TRUE;
1278
1279	if (flag != NULL && streams_parse_flag(mbf, flag, &mask) == -1) {
1280		mdb_warn("unrecognized mblk flag '%s'\n", flag);
1281		streams_flag_usage(mbf);
1282		return (DCMD_USAGE);
1283	}
1284
1285	if (not_flag != NULL &&
1286	    streams_parse_flag(mbf, not_flag, &not_mask) == -1) {
1287		mdb_warn("unrecognized mblk flag '%s'\n", flag);
1288		streams_flag_usage(mbf);
1289		return (DCMD_USAGE);
1290	}
1291
1292	if (typ != NULL && streams_parse_type(mbt, typ, &tmask) == -1) {
1293		mdb_warn("unrecognized dblk type '%s'\n", typ);
1294		streams_type_usage(mbt);
1295		return (DCMD_USAGE);
1296	}
1297
1298	if (not_typ != NULL && streams_parse_type(mbt, not_typ, &not_tmask)
1299	    == -1) {
1300		mdb_warn("unrecognized dblk type '%s'\n", not_typ);
1301		streams_type_usage(mbt);
1302		return (DCMD_USAGE);
1303	}
1304
1305	if (DCMD_HDRSPEC(flags) && !quiet) {
1306		mdb_printf("%?s %2s %-7s %-5s %-5s %?s %?s\n",
1307		    "ADDR", "FL", "TYPE", "LEN", "BLEN", "RPTR", "DBLK");
1308	}
1309
1310	if (mdb_vread(&mblk, sizeof (mblk), addr) == -1) {
1311		mdb_warn("couldn't read mblk at %p", addr);
1312		return (DCMD_ERR);
1313	}
1314	b_flag = mblk.b_flag;
1315
1316	if (mask != 0 && !(b_flag & mask))
1317		return (DCMD_OK);
1318
1319	if (not_mask != 0 && (b_flag & not_mask))
1320		return (DCMD_OK);
1321
1322	if (mdb_vread(&dblk, sizeof (dblk), (uintptr_t)(mblk.b_datap)) == -1) {
1323		mdb_warn("couldn't read dblk at %p/%p", addr, mblk.b_datap);
1324		return (DCMD_ERR);
1325	}
1326	db_type = dblk.db_type;
1327
1328	/* M_DATA is 0, so tmask has special value 0xff for it */
1329	if (tmask != 0) {
1330		if ((tmask == M_DATA_T && db_type != M_DATA) ||
1331		    (tmask != M_DATA_T && db_type != tmask))
1332			return (DCMD_OK);
1333	}
1334
1335	if (not_tmask != 0) {
1336		if ((not_tmask == M_DATA_T && db_type == M_DATA) ||
1337		    (db_type == not_tmask))
1338			return (DCMD_OK);
1339	}
1340
1341	if (dbaddr != 0 && (uintptr_t)mblk.b_datap != dbaddr)
1342		return (DCMD_OK);
1343
1344	mblklen = MBLKL(&mblk);
1345
1346	if ((len != ~0UL) && (len != mblklen))
1347		return (DCMD_OK);
1348
1349	if ((llen != ~0Ul) && (mblklen > (int)llen))
1350		return (DCMD_OK);
1351
1352	if ((glen != ~0Ul) && (mblklen < (int)glen))
1353		return (DCMD_OK);
1354
1355	if ((blen != ~0UL) && (blen != (dblk.db_lim - dblk.db_base)))
1356		return (DCMD_OK);
1357
1358	/*
1359	 * Options are specified for filtering, so If any option is specified on
1360	 * the command line, just print address and exit.
1361	 */
1362	if (quiet) {
1363		mdb_printf("%0?p\n", addr);
1364		return (DCMD_OK);
1365	}
1366
1367	/* Figure out symbolic DB_TYPE */
1368	if (db_type < A_SIZE(db_control_types)) {
1369		dbtype = db_control_types[db_type];
1370	} else {
1371		/*
1372		 * Must be a high-priority message -- adjust so that
1373		 * "QPCTL + 1" corresponds to db_control_hipri_types[0]
1374		 */
1375		db_type -= (QPCTL + 1);
1376		if (db_type >= 0 && db_type < A_SIZE(db_control_hipri_types))
1377			dbtype = db_control_hipri_types[db_type];
1378		else
1379			dbtype = "UNKNOWN";
1380	}
1381
1382	mdb_printf("%0?p %-2x %-7s %-5d %-5d %0?p %0?p\n",
1383	    addr, b_flag, dbtype, mblklen, dblk.db_lim - dblk.db_base,
1384	    mblk.b_rptr, mblk.b_datap);
1385
1386	if (verbose) {
1387		int i, arm = 0;
1388
1389		for (i = 0; mbf[i].strf_name != NULL; i++) {
1390			if (!(b_flag & (1 << i)))
1391				continue;
1392			if (!arm) {
1393				mdb_printf("%*s|\n%*s+-->  ",
1394				    MBLK_FLGDELT, "", MBLK_FLGDELT, "");
1395				arm = 1;
1396			} else
1397				mdb_printf("%*s      ", MBLK_FLGDELT, "");
1398
1399			mdb_printf("%-12s %s\n",
1400			    mbf[i].strf_name, mbf[i].strf_descr);
1401		}
1402	}
1403	return (DCMD_OK);
1404}
1405
1406/*
1407 * Streams flow trace walkers.
1408 */
1409
1410int
1411strftblk_walk_init(mdb_walk_state_t *wsp)
1412{
1413	ftblkdata_t *ftd;
1414	dblk_t	db;
1415
1416	/* Get the dblock from the address */
1417	if (mdb_vread(&db, sizeof (dblk_t), wsp->walk_addr) == -1) {
1418		mdb_warn("failed to read dblk at %p", wsp->walk_addr);
1419		return (WALK_ERR);
1420	}
1421
1422	/* Is there any flow trace data? */
1423	if (db.db_fthdr == NULL) {
1424		return (WALK_DONE);
1425	}
1426
1427	wsp->walk_addr = (uintptr_t)((char *)db.db_fthdr +
1428	    offsetof(fthdr_t, first));
1429
1430	ftd = mdb_alloc(sizeof (ftblkdata_t), UM_SLEEP);
1431	ftd->ft_ix = 0;
1432	ftd->ft_in_evlist = B_FALSE;
1433	wsp->walk_data = ftd;
1434
1435	return (WALK_NEXT);
1436}
1437
1438int
1439strftblk_step(mdb_walk_state_t *wsp)
1440{
1441	ftblkdata_t *ftd;
1442	ftblk_t *ftbp;
1443	int status = WALK_NEXT;
1444
1445	if (wsp->walk_addr == 0)
1446		return (WALK_DONE);
1447
1448	ftd = (ftblkdata_t *)wsp->walk_data;
1449	ftbp = &(ftd->ft_data);
1450
1451	if (! ftd->ft_in_evlist) {
1452		/* Read a new ft block */
1453		if (mdb_vread(ftbp, sizeof (ftblk_t),
1454		    wsp->walk_addr) == -1) {
1455			mdb_warn("failed to read ftblk at %p", wsp->walk_addr);
1456			return (WALK_ERR);
1457		}
1458		/*
1459		 * Check correctness of the index field.
1460		 */
1461		if (ftbp->ix < 0 || ftbp->ix > FTBLK_EVNTS) {
1462			mdb_warn("ftblk: incorrect index value %i\n", ftbp->ix);
1463			return (WALK_ERR);
1464		}
1465		ftd->ft_ix = 1;
1466		ftd->ft_in_evlist = B_TRUE;
1467	}
1468
1469	if (ftd->ft_ix > ftbp->ix) {
1470		ftd->ft_in_evlist = B_FALSE;
1471		/* End of event list reached - move to the next event block */
1472		wsp->walk_addr = (uintptr_t)ftbp->nxt;
1473	} else {
1474		/* Print event address */
1475		status = wsp->walk_callback((uintptr_t)((char *)wsp->walk_addr +
1476		    offsetof(ftblk_t, ev) +
1477		    (ftd->ft_ix - 1) * sizeof (struct ftevnt)),
1478		    wsp->walk_data, wsp->walk_cbdata);
1479		ftd->ft_ix++;
1480	}
1481
1482	return (status);
1483}
1484
1485void
1486strftblk_walk_fini(mdb_walk_state_t *wsp)
1487{
1488	mdb_free(wsp->walk_data, sizeof (ftblkdata_t));
1489}
1490
1491static const char *
1492getqname(const void *nameptr, char *buf, uint_t bufsize)
1493{
1494	char *cp;
1495
1496	if (mdb_readstr(buf, bufsize, (uintptr_t)nameptr) == -1)
1497		goto fail;
1498
1499	/*
1500	 * Sanity-check the name we read.  This is needed because the pointer
1501	 * value may have been recycled for some other purpose in the kernel
1502	 * (e.g., if the STREAMS module was unloaded).
1503	 */
1504	for (cp = buf; *cp != '\0'; cp++) {
1505		if (!isprint(*cp))
1506			goto fail;
1507	}
1508	return (buf);
1509fail:
1510	return (strncpy(buf, "?", bufsize));
1511}
1512
1513/*ARGSUSED*/
1514int
1515strftevent(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1516{
1517	int i;
1518	struct ftstk stk;
1519	struct ftevnt ev;
1520	char name[FMNAMESZ + 1];
1521	boolean_t havestk = B_FALSE;
1522
1523	if (!(flags & DCMD_ADDRSPEC))
1524		return (DCMD_USAGE);
1525
1526	if (DCMD_HDRSPEC(flags)) {
1527		mdb_printf("%?s %-18s %-9s %-18s %4s %s\n",
1528		    "ADDR", "Q/CALLER", "QNEXT", "STACK", "DATA", "EVENT");
1529	}
1530
1531	if (mdb_vread(&ev, sizeof (ev), addr) == -1) {
1532		mdb_warn("couldn't read struct ftevnt at %p", addr);
1533		return (DCMD_ERR);
1534	}
1535
1536	mdb_printf("%0?p", addr);
1537
1538	if (ev.evnt & FTEV_QMASK)
1539		mdb_printf(" %-18s", getqname(ev.mid, name, sizeof (name)));
1540	else
1541		mdb_printf(" %-18a", ev.mid);
1542
1543	if ((ev.evnt & FTEV_MASK) == FTEV_PUTNEXT)
1544		mdb_printf(" %-9s", getqname(ev.midnext, name, sizeof (name)));
1545	else
1546		mdb_printf(" %-9s", "--");
1547
1548	if (ev.stk == NULL) {
1549		mdb_printf(" %-18s", "--");
1550	} else if (mdb_vread(&stk, sizeof (stk), (uintptr_t)ev.stk) == -1) {
1551		mdb_printf(" %-18s", "?");
1552	} else {
1553		mdb_printf(" %-18a", stk.fs_stk[0]);
1554		havestk = B_TRUE;
1555	}
1556
1557	mdb_printf(" %4x", ev.data);
1558	ft_printevent(ev.evnt);
1559	mdb_printf("\n");
1560
1561	if (havestk) {
1562		for (i = 1; i < stk.fs_depth; i++) {
1563			mdb_printf("%?s %-18s %-9s %-18a\n", "", "", "",
1564			    stk.fs_stk[i]);
1565		}
1566	}
1567
1568	return (DCMD_OK);
1569}
1570
1571static void
1572ft_printevent(ushort_t ev)
1573{
1574	ushort_t proc_ev = (ev & (FTEV_PROC_START | 0xFF)) - FTEV_PROC_START;
1575	ushort_t alloc_ev = ev & FTEV_CALLER;
1576
1577	/* Get event class first */
1578	if (ev & FTEV_PROC_START) {
1579		if (proc_ev >= A_SIZE(ftev_proc))
1580			mdb_printf(" undefined");
1581		else
1582			mdb_printf(" %s", ftev_proc[proc_ev]);
1583	} else if (alloc_ev >= A_SIZE(ftev_alloc)) {
1584		mdb_printf(" undefined");
1585	} else {
1586		mdb_printf(" %s", ftev_alloc[alloc_ev]);
1587	}
1588
1589	/* Print event modifiers, if any */
1590	if (ev & (FTEV_PS | FTEV_CS | FTEV_ISWR)) {
1591		mdb_printf("|");
1592		if (ev & FTEV_ISWR)
1593			mdb_printf("W");
1594		if (ev & FTEV_CS)
1595			mdb_printf("C");
1596		if (ev & FTEV_PS)
1597			mdb_printf("P");
1598	}
1599}
1600
1601/*
1602 * Help functions for STREAMS debugging facilities.
1603 */
1604void
1605queue_help(void)
1606{
1607	mdb_printf("Print queue information for a given queue pointer.\n"
1608	    "\nWithout the address of a \"queue_t\" structure given, print "
1609	    "information about all\n"
1610	    "queues in the \"queue_cache\".\n\n"
1611	    "Options:\n"
1612	    "	-v:\t\tbe verbose - print symbolic flags falues\n"
1613	    "	-q:\t\tbe quiet - print queue pointer only\n"
1614	    "	-f flag:\tprint only queues with flag set\n"
1615	    "	-F flag:\tprint only queues with flag NOT set\n"
1616	    "	-m modname:\tprint only queues with specified module name\n"
1617	    "	-s syncq_addr:\tprint only queues which use specified syncq\n\n"
1618	    "Available conversions:\n"
1619	    "	q2rdq:		given a queue addr print read queue pointer\n"
1620	    "	q2wrq:		given a queue addr print write queue pointer\n"
1621	    "	q2otherq:	given a queue addr print other queue pointer\n"
1622	    "	q2syncq:	given a queue addr print syncq pointer"
1623	    " (::help syncq)\n"
1624	    "	q2stream:	given a queue addr print its stream pointer\n"
1625	    "\t\t(see ::help stream and ::help stdata)\n\n"
1626	    "To walk q_next pointer of the queue use\n"
1627	    "	queue_addr::walk qnext\n");
1628}
1629
1630void
1631syncq_help(void)
1632{
1633	mdb_printf("Print syncq information for a given syncq pointer.\n"
1634	    "\nWithout the address of a \"syncq_t\" structure given, print "
1635	    "information about all\n"
1636	    "syncqs in the \"syncq_cache\".\n\n"
1637	    "Options:\n"
1638	    "	-v:\t\tbe verbose - print symbolic flags falues\n"
1639	    "	-q:\t\tbe quiet - print syncq pointer only\n"
1640	    "	-f flag:\tprint only syncqs with flag set\n"
1641	    "	-F flag:\tprint only syncqs with flag NOT set\n"
1642	    "	-t type:\tprint only syncqs with specified type\n"
1643	    "	-T type:\tprint only syncqs with do NOT have specified type\n\n"
1644	    "Available conversions:\n"
1645	    "	syncq2q:\tgiven a syncq addr print queue address of the\n"
1646	    "\t\t\tenclosing queue, if it is part of a queue\n\n"
1647	    "See also: \"::help queue\" and \"::help stdata\"\n");
1648}
1649
1650void
1651stdata_help(void)
1652{
1653	mdb_printf("Print stdata information for a given stdata pointer.\n"
1654	    "\nWithout the address of a \"stdata_t\" structure given, print "
1655	    "information about all\n"
1656	    "stream head pointers from the \"stream_head_cache\".\n\n"
1657	    "Fields printed:\n"
1658	    "	ADDR:\tstream head address\n"
1659	    "	WRQ:\twrite queue pointer\n"
1660	    "	FLAGS:\tstream head flags (use -v to show in symbolic form)\n"
1661	    "	VNODE:\tstream vnode pointer\n"
1662	    "	N/A:\tpushcount and anchor positions\n"
1663	    "	REF:\tstream head reference counter\n\n"
1664	    "Options:\n"
1665	    "	-v:\t\tbe verbose - print symbolic flags falues\n"
1666	    "	-q:\t\tbe quiet - print stdata pointer only\n"
1667	    "	-f flag:\tprint only stdatas with flag set\n"
1668	    "	-F flag:\tprint only stdatas with flag NOT set\n\n"
1669	    "Available conversions:\n"
1670	    "	str2mate:\tgiven a stream head addr print its mate\n"
1671	    "	str2wrq:\tgiven a stream head addr print its write queue\n\n"
1672	    "See also: \"::help queue\" and \"::help syncq\"\n");
1673}
1674
1675void
1676mblk_help(void)
1677{
1678	mdb_printf("Print mblock information for a given mblk pointer.\n"
1679	    "Without the address, print information about all mblocks.\n\n"
1680	    "Fields printed:\n"
1681	    "	ADDR:\tmblk address\n"
1682	    "	FL:\tFlags\n"
1683	    "	TYPE:\tType of corresponding dblock\n"
1684	    "	LEN:\tData length as b_wptr - b_rptr\n"
1685	    "	BLEN:\tDblock space as db_lim - db_base\n"
1686	    "	RPTR:\tRead pointer\n"
1687	    "	DBLK:\tDblock pointer\n\n"
1688	    "Options:\n"
1689	    "	-v:\t\tbe verbose - print symbolic flags falues\n"
1690	    "	-q:\t\tbe quiet - print mblk pointer only\n"
1691	    "	-d dbaddr:\t\tprint mblks with specified dblk address\n"
1692	    "	-f flag:\tprint only mblks with flag set\n"
1693	    "	-F flag:\tprint only mblks with flag NOT set\n"
1694	    "	-t type:\tprint only mblks of specified db_type\n"
1695	    "	-T type:\tprint only mblks other then the specified db_type\n"
1696	    "	-l len:\t\ttprint only mblks with MBLKL == len\n"
1697	    "	-L len:\t\tprint only mblks with MBLKL <= len \n"
1698	    "	-G len:\t\tprint only mblks with MBLKL >= len \n"
1699	    "	-b len:\t\tprint only mblks with db_lim - db_base == len\n"
1700	    "\n");
1701}
1702