xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_print.c (revision 7c478bd9)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
30*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_target.h>
31*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_argvec.h>
32*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_string.h>
33*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_stdlib.h>
34*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_err.h>
35*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_debug.h>
36*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_fmt.h>
37*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_ctf.h>
38*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_ctf_impl.h>
39*7c478bd9Sstevel@tonic-gate #include <mdb/mdb.h>
40*7c478bd9Sstevel@tonic-gate 
41*7c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
43*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
44*7c478bd9Sstevel@tonic-gate #include <strings.h>
45*7c478bd9Sstevel@tonic-gate #include <libctf.h>
46*7c478bd9Sstevel@tonic-gate #include <ctype.h>
47*7c478bd9Sstevel@tonic-gate 
48*7c478bd9Sstevel@tonic-gate typedef struct holeinfo {
49*7c478bd9Sstevel@tonic-gate 	ulong_t hi_offset;		/* expected offset */
50*7c478bd9Sstevel@tonic-gate 	uchar_t hi_isunion;		/* represents a union */
51*7c478bd9Sstevel@tonic-gate } holeinfo_t;
52*7c478bd9Sstevel@tonic-gate 
53*7c478bd9Sstevel@tonic-gate typedef struct printarg {
54*7c478bd9Sstevel@tonic-gate 	mdb_tgt_t *pa_tgt;		/* current target */
55*7c478bd9Sstevel@tonic-gate 	mdb_tgt_t *pa_realtgt;		/* real target (for -i) */
56*7c478bd9Sstevel@tonic-gate 	mdb_tgt_t *pa_immtgt;		/* immediate target (for -i) */
57*7c478bd9Sstevel@tonic-gate 	mdb_tgt_as_t pa_as;		/* address space to use for i/o */
58*7c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t pa_addr;		/* base address for i/o */
59*7c478bd9Sstevel@tonic-gate 	ulong_t pa_armemlim;		/* limit on array elements to print */
60*7c478bd9Sstevel@tonic-gate 	ulong_t pa_arstrlim;		/* limit on array chars to print */
61*7c478bd9Sstevel@tonic-gate 	const char *pa_delim;		/* element delimiter string */
62*7c478bd9Sstevel@tonic-gate 	const char *pa_prefix;		/* element prefix string */
63*7c478bd9Sstevel@tonic-gate 	const char *pa_suffix;		/* element suffix string */
64*7c478bd9Sstevel@tonic-gate 	holeinfo_t *pa_holes;		/* hole detection information */
65*7c478bd9Sstevel@tonic-gate 	int pa_nholes;			/* size of holes array */
66*7c478bd9Sstevel@tonic-gate 	int pa_flags;			/* formatting flags (see below) */
67*7c478bd9Sstevel@tonic-gate 	int pa_depth;			/* previous depth */
68*7c478bd9Sstevel@tonic-gate 	int pa_nest;			/* array nesting depth */
69*7c478bd9Sstevel@tonic-gate 	int pa_tab;			/* tabstop width */
70*7c478bd9Sstevel@tonic-gate } printarg_t;
71*7c478bd9Sstevel@tonic-gate 
72*7c478bd9Sstevel@tonic-gate #define	PA_SHOWTYPE	0x001		/* print type name */
73*7c478bd9Sstevel@tonic-gate #define	PA_SHOWNAME	0x002		/* print member name */
74*7c478bd9Sstevel@tonic-gate #define	PA_SHOWADDR	0x004		/* print address */
75*7c478bd9Sstevel@tonic-gate #define	PA_SHOWVAL	0x008		/* print value */
76*7c478bd9Sstevel@tonic-gate #define	PA_SHOWHOLES	0x010		/* print holes in structs */
77*7c478bd9Sstevel@tonic-gate #define	PA_INTHEX	0x020		/* print integer values in hex */
78*7c478bd9Sstevel@tonic-gate #define	PA_INTDEC	0x040		/* print integer values in decimal */
79*7c478bd9Sstevel@tonic-gate #define	PA_PRETTY	0x080		/* pretty print when possible */
80*7c478bd9Sstevel@tonic-gate #define	PA_NOSYMBOLIC	0x100		/* don't print ptrs as func+offset */
81*7c478bd9Sstevel@tonic-gate 
82*7c478bd9Sstevel@tonic-gate #define	IS_CHAR(e) \
83*7c478bd9Sstevel@tonic-gate 	(((e).cte_format & (CTF_INT_CHAR | CTF_INT_SIGNED)) == \
84*7c478bd9Sstevel@tonic-gate 	(CTF_INT_CHAR | CTF_INT_SIGNED) && (e).cte_bits == NBBY)
85*7c478bd9Sstevel@tonic-gate 
86*7c478bd9Sstevel@tonic-gate #define	SCALAR_MASK	((1 << CTF_K_INTEGER) | (1 << CTF_K_FLOAT) | \
87*7c478bd9Sstevel@tonic-gate 			(1 << CTF_K_POINTER) | (1 << CTF_K_ENUM) | \
88*7c478bd9Sstevel@tonic-gate 			(1 << CTF_K_ARRAY))
89*7c478bd9Sstevel@tonic-gate #define	IS_SCALAR(k)	(((1 << k) & SCALAR_MASK) != 0)
90*7c478bd9Sstevel@tonic-gate 
91*7c478bd9Sstevel@tonic-gate #define	COMPOSITE_MASK	((1 << CTF_K_STRUCT) | \
92*7c478bd9Sstevel@tonic-gate 			(1 << CTF_K_UNION) | (1 << CTF_K_ARRAY))
93*7c478bd9Sstevel@tonic-gate #define	IS_COMPOSITE(k)	(((1 << k) & COMPOSITE_MASK) != 0)
94*7c478bd9Sstevel@tonic-gate 
95*7c478bd9Sstevel@tonic-gate #define	SOU_MASK	((1 << CTF_K_STRUCT) | (1 << CTF_K_UNION))
96*7c478bd9Sstevel@tonic-gate #define	IS_SOU(k)	(((1 << k) & SOU_MASK) != 0)
97*7c478bd9Sstevel@tonic-gate 
98*7c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_ERR	-1
99*7c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_DONE	0
100*7c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_PTR	1
101*7c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_DOT	2
102*7c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_LBR	3
103*7c478bd9Sstevel@tonic-gate 
104*7c478bd9Sstevel@tonic-gate typedef int printarg_f(const char *, const char *,
105*7c478bd9Sstevel@tonic-gate     mdb_ctf_id_t, mdb_ctf_id_t, ulong_t, printarg_t *);
106*7c478bd9Sstevel@tonic-gate 
107*7c478bd9Sstevel@tonic-gate static int elt_print(const char *, mdb_ctf_id_t, ulong_t, int, void *);
108*7c478bd9Sstevel@tonic-gate static void print_close_sou(printarg_t *, int);
109*7c478bd9Sstevel@tonic-gate 
110*7c478bd9Sstevel@tonic-gate /*
111*7c478bd9Sstevel@tonic-gate  * Given an address, look up the symbol ID of the specified symbol in its
112*7c478bd9Sstevel@tonic-gate  * containing module.  We only support lookups for exact matches.
113*7c478bd9Sstevel@tonic-gate  */
114*7c478bd9Sstevel@tonic-gate static const char *
115*7c478bd9Sstevel@tonic-gate addr_to_sym(mdb_tgt_t *t, uintptr_t addr, char *name, size_t namelen,
116*7c478bd9Sstevel@tonic-gate     GElf_Sym *symp, mdb_syminfo_t *sip)
117*7c478bd9Sstevel@tonic-gate {
118*7c478bd9Sstevel@tonic-gate 	const mdb_map_t *mp;
119*7c478bd9Sstevel@tonic-gate 	const char *p;
120*7c478bd9Sstevel@tonic-gate 
121*7c478bd9Sstevel@tonic-gate 	if (mdb_tgt_lookup_by_addr(t, addr, MDB_TGT_SYM_EXACT, name,
122*7c478bd9Sstevel@tonic-gate 	    namelen, NULL, NULL) == -1)
123*7c478bd9Sstevel@tonic-gate 		return (NULL); /* address does not exactly match a symbol */
124*7c478bd9Sstevel@tonic-gate 
125*7c478bd9Sstevel@tonic-gate 	if ((p = strrsplit(name, '`')) != NULL) {
126*7c478bd9Sstevel@tonic-gate 		if (mdb_tgt_lookup_by_name(t, name, p, symp, sip) == -1)
127*7c478bd9Sstevel@tonic-gate 			return (NULL);
128*7c478bd9Sstevel@tonic-gate 		return (p);
129*7c478bd9Sstevel@tonic-gate 	}
130*7c478bd9Sstevel@tonic-gate 
131*7c478bd9Sstevel@tonic-gate 	if ((mp = mdb_tgt_addr_to_map(t, addr)) == NULL)
132*7c478bd9Sstevel@tonic-gate 		return (NULL); /* address does not fall within a mapping */
133*7c478bd9Sstevel@tonic-gate 
134*7c478bd9Sstevel@tonic-gate 	if (mdb_tgt_lookup_by_name(t, mp->map_name, name, symp, sip) == -1)
135*7c478bd9Sstevel@tonic-gate 		return (NULL);
136*7c478bd9Sstevel@tonic-gate 
137*7c478bd9Sstevel@tonic-gate 	return (name);
138*7c478bd9Sstevel@tonic-gate }
139*7c478bd9Sstevel@tonic-gate 
140*7c478bd9Sstevel@tonic-gate /*
141*7c478bd9Sstevel@tonic-gate  * This lets dcmds be a little fancy with their processing of type arguments
142*7c478bd9Sstevel@tonic-gate  * while still treating them more or less as a single argument.
143*7c478bd9Sstevel@tonic-gate  * For example, if a command is invokes like this:
144*7c478bd9Sstevel@tonic-gate  *
145*7c478bd9Sstevel@tonic-gate  *   ::<dcmd> proc_t ...
146*7c478bd9Sstevel@tonic-gate  *
147*7c478bd9Sstevel@tonic-gate  * this function will just copy "proc_t" into the provided buffer. If the
148*7c478bd9Sstevel@tonic-gate  * command is instead invoked like this:
149*7c478bd9Sstevel@tonic-gate  *
150*7c478bd9Sstevel@tonic-gate  *   ::<dcmd> struct proc ...
151*7c478bd9Sstevel@tonic-gate  *
152*7c478bd9Sstevel@tonic-gate  * this function will place the string "struct proc" into the provided buffer
153*7c478bd9Sstevel@tonic-gate  * and increment the caller's argv and argc. This allows the caller to still
154*7c478bd9Sstevel@tonic-gate  * treat the type argument logically as it would an other atomic argument.
155*7c478bd9Sstevel@tonic-gate  */
156*7c478bd9Sstevel@tonic-gate int
157*7c478bd9Sstevel@tonic-gate args_to_typename(int *argcp, const mdb_arg_t **argvp, char *buf, size_t len)
158*7c478bd9Sstevel@tonic-gate {
159*7c478bd9Sstevel@tonic-gate 	int argc = *argcp;
160*7c478bd9Sstevel@tonic-gate 	const mdb_arg_t *argv = *argvp;
161*7c478bd9Sstevel@tonic-gate 
162*7c478bd9Sstevel@tonic-gate 	if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
163*7c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
164*7c478bd9Sstevel@tonic-gate 
165*7c478bd9Sstevel@tonic-gate 	if (strcmp(argv->a_un.a_str, "struct") == 0 ||
166*7c478bd9Sstevel@tonic-gate 	    strcmp(argv->a_un.a_str, "enum") == 0 ||
167*7c478bd9Sstevel@tonic-gate 	    strcmp(argv->a_un.a_str, "union") == 0) {
168*7c478bd9Sstevel@tonic-gate 		if (argc <= 1) {
169*7c478bd9Sstevel@tonic-gate 			mdb_warn("%s is not a valid type\n", argv->a_un.a_str);
170*7c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
171*7c478bd9Sstevel@tonic-gate 		}
172*7c478bd9Sstevel@tonic-gate 
173*7c478bd9Sstevel@tonic-gate 		if (argv[1].a_type != MDB_TYPE_STRING)
174*7c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
175*7c478bd9Sstevel@tonic-gate 
176*7c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(buf, len, "%s %s",
177*7c478bd9Sstevel@tonic-gate 		    argv[0].a_un.a_str, argv[1].a_un.a_str);
178*7c478bd9Sstevel@tonic-gate 
179*7c478bd9Sstevel@tonic-gate 		*argcp = argc - 1;
180*7c478bd9Sstevel@tonic-gate 		*argvp = argv + 1;
181*7c478bd9Sstevel@tonic-gate 	} else {
182*7c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(buf, len, "%s", argv[0].a_un.a_str);
183*7c478bd9Sstevel@tonic-gate 	}
184*7c478bd9Sstevel@tonic-gate 
185*7c478bd9Sstevel@tonic-gate 	return (0);
186*7c478bd9Sstevel@tonic-gate }
187*7c478bd9Sstevel@tonic-gate 
188*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
189*7c478bd9Sstevel@tonic-gate int
190*7c478bd9Sstevel@tonic-gate cmd_sizeof(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
191*7c478bd9Sstevel@tonic-gate {
192*7c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
193*7c478bd9Sstevel@tonic-gate 	char tn[MDB_SYM_NAMLEN];
194*7c478bd9Sstevel@tonic-gate 	int ret;
195*7c478bd9Sstevel@tonic-gate 
196*7c478bd9Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC)
197*7c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
198*7c478bd9Sstevel@tonic-gate 
199*7c478bd9Sstevel@tonic-gate 	if ((ret = args_to_typename(&argc, &argv, tn, sizeof (tn))) != 0)
200*7c478bd9Sstevel@tonic-gate 		return (ret);
201*7c478bd9Sstevel@tonic-gate 
202*7c478bd9Sstevel@tonic-gate 	if (argc != 1)
203*7c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
204*7c478bd9Sstevel@tonic-gate 
205*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_lookup_by_name(tn, &id) != 0) {
206*7c478bd9Sstevel@tonic-gate 		mdb_warn("failed to look up type %s", tn);
207*7c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
208*7c478bd9Sstevel@tonic-gate 	}
209*7c478bd9Sstevel@tonic-gate 
210*7c478bd9Sstevel@tonic-gate 	if (flags & DCMD_PIPE_OUT)
211*7c478bd9Sstevel@tonic-gate 		mdb_printf("%#lr\n", mdb_ctf_type_size(id));
212*7c478bd9Sstevel@tonic-gate 	else
213*7c478bd9Sstevel@tonic-gate 		mdb_printf("sizeof (%s) = %#lr\n", tn, mdb_ctf_type_size(id));
214*7c478bd9Sstevel@tonic-gate 
215*7c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
216*7c478bd9Sstevel@tonic-gate }
217*7c478bd9Sstevel@tonic-gate 
218*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
219*7c478bd9Sstevel@tonic-gate int
220*7c478bd9Sstevel@tonic-gate cmd_offsetof(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
221*7c478bd9Sstevel@tonic-gate {
222*7c478bd9Sstevel@tonic-gate 	const char *member;
223*7c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
224*7c478bd9Sstevel@tonic-gate 	ulong_t off;
225*7c478bd9Sstevel@tonic-gate 	char tn[MDB_SYM_NAMLEN];
226*7c478bd9Sstevel@tonic-gate 	int ret;
227*7c478bd9Sstevel@tonic-gate 
228*7c478bd9Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC)
229*7c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
230*7c478bd9Sstevel@tonic-gate 
231*7c478bd9Sstevel@tonic-gate 	if ((ret = args_to_typename(&argc, &argv, tn, sizeof (tn))) != 0)
232*7c478bd9Sstevel@tonic-gate 		return (ret);
233*7c478bd9Sstevel@tonic-gate 
234*7c478bd9Sstevel@tonic-gate 	if (argc != 2 || argv[1].a_type != MDB_TYPE_STRING)
235*7c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
236*7c478bd9Sstevel@tonic-gate 
237*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_lookup_by_name(tn, &id) != 0) {
238*7c478bd9Sstevel@tonic-gate 		mdb_warn("failed to look up type %s", tn);
239*7c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
240*7c478bd9Sstevel@tonic-gate 	}
241*7c478bd9Sstevel@tonic-gate 
242*7c478bd9Sstevel@tonic-gate 	member = argv[1].a_un.a_str;
243*7c478bd9Sstevel@tonic-gate 
244*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_offsetof(id, member, &off) != 0) {
245*7c478bd9Sstevel@tonic-gate 		mdb_warn("failed to find member %s of type %s", member, tn);
246*7c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
247*7c478bd9Sstevel@tonic-gate 	}
248*7c478bd9Sstevel@tonic-gate 
249*7c478bd9Sstevel@tonic-gate 	if (off % NBBY == 0)
250*7c478bd9Sstevel@tonic-gate 		mdb_printf("offsetof (%s, %s) = %#lr\n",
251*7c478bd9Sstevel@tonic-gate 		    tn, member, off / NBBY);
252*7c478bd9Sstevel@tonic-gate 	else
253*7c478bd9Sstevel@tonic-gate 		mdb_printf("offsetof (%s, %s) = %#lr bits\n",
254*7c478bd9Sstevel@tonic-gate 		    tn, member, off);
255*7c478bd9Sstevel@tonic-gate 
256*7c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
257*7c478bd9Sstevel@tonic-gate }
258*7c478bd9Sstevel@tonic-gate 
259*7c478bd9Sstevel@tonic-gate struct enum_cbinfo {
260*7c478bd9Sstevel@tonic-gate 	uint_t		e_flags;
261*7c478bd9Sstevel@tonic-gate 	const char	*e_string;	/* NULL for value searches */
262*7c478bd9Sstevel@tonic-gate 	int		e_value;
263*7c478bd9Sstevel@tonic-gate 	uint_t		e_found;
264*7c478bd9Sstevel@tonic-gate };
265*7c478bd9Sstevel@tonic-gate #define	E_PRETTY		0x1
266*7c478bd9Sstevel@tonic-gate #define	E_HEX			0x2
267*7c478bd9Sstevel@tonic-gate #define	E_SEARCH_STRING		0x4
268*7c478bd9Sstevel@tonic-gate #define	E_SEARCH_VALUE		0x8
269*7c478bd9Sstevel@tonic-gate 
270*7c478bd9Sstevel@tonic-gate static int
271*7c478bd9Sstevel@tonic-gate enum_cb(const char *name, int value, void *arg)
272*7c478bd9Sstevel@tonic-gate {
273*7c478bd9Sstevel@tonic-gate 	struct enum_cbinfo *info = arg;
274*7c478bd9Sstevel@tonic-gate 	uint_t flags = info->e_flags;
275*7c478bd9Sstevel@tonic-gate 
276*7c478bd9Sstevel@tonic-gate 	if (flags & E_SEARCH_STRING) {
277*7c478bd9Sstevel@tonic-gate 		if (strcmp(name, info->e_string) != 0)
278*7c478bd9Sstevel@tonic-gate 			return (0);
279*7c478bd9Sstevel@tonic-gate 
280*7c478bd9Sstevel@tonic-gate 	} else if (flags & E_SEARCH_VALUE) {
281*7c478bd9Sstevel@tonic-gate 		if (value != info->e_value)
282*7c478bd9Sstevel@tonic-gate 			return (0);
283*7c478bd9Sstevel@tonic-gate 	}
284*7c478bd9Sstevel@tonic-gate 
285*7c478bd9Sstevel@tonic-gate 	if (flags & E_PRETTY) {
286*7c478bd9Sstevel@tonic-gate 		if (flags & E_HEX)
287*7c478bd9Sstevel@tonic-gate 			mdb_printf("%-8x %s\n", value, name);
288*7c478bd9Sstevel@tonic-gate 		else
289*7c478bd9Sstevel@tonic-gate 			mdb_printf("%-11d %s\n", value, name);
290*7c478bd9Sstevel@tonic-gate 	} else {
291*7c478bd9Sstevel@tonic-gate 		mdb_printf("%#r\n", value);
292*7c478bd9Sstevel@tonic-gate 	}
293*7c478bd9Sstevel@tonic-gate 
294*7c478bd9Sstevel@tonic-gate 	info->e_found = 1;
295*7c478bd9Sstevel@tonic-gate 	return (0);
296*7c478bd9Sstevel@tonic-gate }
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
299*7c478bd9Sstevel@tonic-gate int
300*7c478bd9Sstevel@tonic-gate cmd_enum(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
301*7c478bd9Sstevel@tonic-gate {
302*7c478bd9Sstevel@tonic-gate 	struct enum_cbinfo info;
303*7c478bd9Sstevel@tonic-gate 
304*7c478bd9Sstevel@tonic-gate 	const char *type;			/* type name we are using */
305*7c478bd9Sstevel@tonic-gate 	char tn[MDB_SYM_NAMLEN];
306*7c478bd9Sstevel@tonic-gate 	char tn2[MDB_SYM_NAMLEN + sizeof ("enum ")];
307*7c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
308*7c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t idr;
309*7c478bd9Sstevel@tonic-gate 
310*7c478bd9Sstevel@tonic-gate 	int i;
311*7c478bd9Sstevel@tonic-gate 	intmax_t search;
312*7c478bd9Sstevel@tonic-gate 
313*7c478bd9Sstevel@tonic-gate 	info.e_flags = (flags & DCMD_PIPE_OUT)? 0 : E_PRETTY;
314*7c478bd9Sstevel@tonic-gate 	info.e_string = NULL;
315*7c478bd9Sstevel@tonic-gate 	info.e_value = 0;
316*7c478bd9Sstevel@tonic-gate 	info.e_found = 0;
317*7c478bd9Sstevel@tonic-gate 
318*7c478bd9Sstevel@tonic-gate 	i = mdb_getopts(argc, argv,
319*7c478bd9Sstevel@tonic-gate 	    'x', MDB_OPT_SETBITS, E_HEX, &info.e_flags,
320*7c478bd9Sstevel@tonic-gate 	    NULL);
321*7c478bd9Sstevel@tonic-gate 
322*7c478bd9Sstevel@tonic-gate 	argc -= i;
323*7c478bd9Sstevel@tonic-gate 	argv += i;
324*7c478bd9Sstevel@tonic-gate 
325*7c478bd9Sstevel@tonic-gate 	if ((i = args_to_typename(&argc, &argv, tn, sizeof (tn))) != 0)
326*7c478bd9Sstevel@tonic-gate 		return (i);
327*7c478bd9Sstevel@tonic-gate 
328*7c478bd9Sstevel@tonic-gate 	type = NULL;
329*7c478bd9Sstevel@tonic-gate 	if (strchr(tn, ' ') == NULL) {
330*7c478bd9Sstevel@tonic-gate 		/*
331*7c478bd9Sstevel@tonic-gate 		 * Check as an enumeration tag first, and fall back
332*7c478bd9Sstevel@tonic-gate 		 * to checking for a typedef.  Yes, this means that
333*7c478bd9Sstevel@tonic-gate 		 * anonymous enumerations whose typedefs conflict with
334*7c478bd9Sstevel@tonic-gate 		 * an enum tag can't be accessed.  Don't do that.
335*7c478bd9Sstevel@tonic-gate 		 */
336*7c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(tn2, sizeof (tn2), "enum %s", tn);
337*7c478bd9Sstevel@tonic-gate 
338*7c478bd9Sstevel@tonic-gate 		if (mdb_ctf_lookup_by_name(tn2, &id) == 0) {
339*7c478bd9Sstevel@tonic-gate 			type = tn2;
340*7c478bd9Sstevel@tonic-gate 		} else if (mdb_ctf_lookup_by_name(tn, &id) == 0) {
341*7c478bd9Sstevel@tonic-gate 			type = tn;
342*7c478bd9Sstevel@tonic-gate 		} else {
343*7c478bd9Sstevel@tonic-gate 			mdb_warn("types '%s', '%s'", tn2, tn);
344*7c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
345*7c478bd9Sstevel@tonic-gate 		}
346*7c478bd9Sstevel@tonic-gate 	} else {
347*7c478bd9Sstevel@tonic-gate 		if (mdb_ctf_lookup_by_name(tn, &id) == 0) {
348*7c478bd9Sstevel@tonic-gate 			type = tn;
349*7c478bd9Sstevel@tonic-gate 		} else {
350*7c478bd9Sstevel@tonic-gate 			mdb_warn("'%s'", tn);
351*7c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
352*7c478bd9Sstevel@tonic-gate 		}
353*7c478bd9Sstevel@tonic-gate 	}
354*7c478bd9Sstevel@tonic-gate 
355*7c478bd9Sstevel@tonic-gate 	/* resolve it, and make sure we're looking at an enumeration */
356*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_resolve(id, &idr) == -1) {
357*7c478bd9Sstevel@tonic-gate 		mdb_warn("unable to resolve '%s'", type);
358*7c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
359*7c478bd9Sstevel@tonic-gate 	}
360*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_kind(idr) != CTF_K_ENUM) {
361*7c478bd9Sstevel@tonic-gate 		mdb_warn("'%s': not an enumeration\n", type);
362*7c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
363*7c478bd9Sstevel@tonic-gate 	}
364*7c478bd9Sstevel@tonic-gate 
365*7c478bd9Sstevel@tonic-gate 	if (argc > 2)
366*7c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
367*7c478bd9Sstevel@tonic-gate 
368*7c478bd9Sstevel@tonic-gate 	if (argc == 2) {
369*7c478bd9Sstevel@tonic-gate 		if (flags & DCMD_ADDRSPEC) {
370*7c478bd9Sstevel@tonic-gate 			mdb_warn("may only specify one of: name, address\n");
371*7c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
372*7c478bd9Sstevel@tonic-gate 		}
373*7c478bd9Sstevel@tonic-gate 
374*7c478bd9Sstevel@tonic-gate 		if (argv[1].a_type == MDB_TYPE_STRING) {
375*7c478bd9Sstevel@tonic-gate 			info.e_flags |= E_SEARCH_STRING;
376*7c478bd9Sstevel@tonic-gate 			info.e_string = argv[1].a_un.a_str;
377*7c478bd9Sstevel@tonic-gate 		} else if (argv[1].a_type == MDB_TYPE_IMMEDIATE) {
378*7c478bd9Sstevel@tonic-gate 			info.e_flags |= E_SEARCH_VALUE;
379*7c478bd9Sstevel@tonic-gate 			search = argv[1].a_un.a_val;
380*7c478bd9Sstevel@tonic-gate 		} else {
381*7c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
382*7c478bd9Sstevel@tonic-gate 		}
383*7c478bd9Sstevel@tonic-gate 	}
384*7c478bd9Sstevel@tonic-gate 
385*7c478bd9Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC) {
386*7c478bd9Sstevel@tonic-gate 		info.e_flags |= E_SEARCH_VALUE;
387*7c478bd9Sstevel@tonic-gate 		search = mdb_get_dot();
388*7c478bd9Sstevel@tonic-gate 	}
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate 	if (info.e_flags & E_SEARCH_VALUE) {
391*7c478bd9Sstevel@tonic-gate 		if ((int)search != search) {
392*7c478bd9Sstevel@tonic-gate 			mdb_warn("value '%lld' out of enumeration range\n",
393*7c478bd9Sstevel@tonic-gate 			    search);
394*7c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
395*7c478bd9Sstevel@tonic-gate 		}
396*7c478bd9Sstevel@tonic-gate 		info.e_value = search;
397*7c478bd9Sstevel@tonic-gate 	}
398*7c478bd9Sstevel@tonic-gate 
399*7c478bd9Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags) && (info.e_flags & E_PRETTY)) {
400*7c478bd9Sstevel@tonic-gate 		if (info.e_flags & E_HEX)
401*7c478bd9Sstevel@tonic-gate 			mdb_printf("%<b>%-8s %s%</b>\n", "VALUE", "NAME");
402*7c478bd9Sstevel@tonic-gate 		else
403*7c478bd9Sstevel@tonic-gate 			mdb_printf("%<b>%-11s %s%</b>\n", "VALUE", "NAME");
404*7c478bd9Sstevel@tonic-gate 	}
405*7c478bd9Sstevel@tonic-gate 
406*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_enum_iter(idr, enum_cb, &info) == -1) {
407*7c478bd9Sstevel@tonic-gate 		mdb_warn("cannot walk '%s' as enum", type);
408*7c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
409*7c478bd9Sstevel@tonic-gate 	}
410*7c478bd9Sstevel@tonic-gate 
411*7c478bd9Sstevel@tonic-gate 	if (info.e_found == 0 &&
412*7c478bd9Sstevel@tonic-gate 	    (info.e_flags & (E_SEARCH_STRING | E_SEARCH_VALUE)) != 0) {
413*7c478bd9Sstevel@tonic-gate 		if (info.e_flags & E_SEARCH_STRING)
414*7c478bd9Sstevel@tonic-gate 			mdb_warn("name \"%s\" not in '%s'\n", info.e_string,
415*7c478bd9Sstevel@tonic-gate 			    type);
416*7c478bd9Sstevel@tonic-gate 		else
417*7c478bd9Sstevel@tonic-gate 			mdb_warn("value %#d not in '%s'\n", info.e_value, type);
418*7c478bd9Sstevel@tonic-gate 
419*7c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
420*7c478bd9Sstevel@tonic-gate 	}
421*7c478bd9Sstevel@tonic-gate 
422*7c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
423*7c478bd9Sstevel@tonic-gate }
424*7c478bd9Sstevel@tonic-gate 
425*7c478bd9Sstevel@tonic-gate static int
426*7c478bd9Sstevel@tonic-gate setup_vcb(const char *name, uintptr_t addr)
427*7c478bd9Sstevel@tonic-gate {
428*7c478bd9Sstevel@tonic-gate 	const char *p;
429*7c478bd9Sstevel@tonic-gate 	mdb_var_t *v;
430*7c478bd9Sstevel@tonic-gate 
431*7c478bd9Sstevel@tonic-gate 	if ((v = mdb_nv_lookup(&mdb.m_nv, name)) == NULL) {
432*7c478bd9Sstevel@tonic-gate 		if ((p = strbadid(name)) != NULL) {
433*7c478bd9Sstevel@tonic-gate 			mdb_warn("'%c' may not be used in a variable "
434*7c478bd9Sstevel@tonic-gate 			    "name\n", *p);
435*7c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
436*7c478bd9Sstevel@tonic-gate 		}
437*7c478bd9Sstevel@tonic-gate 
438*7c478bd9Sstevel@tonic-gate 		if ((v = mdb_nv_insert(&mdb.m_nv, name, NULL, addr, 0)) == NULL)
439*7c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
440*7c478bd9Sstevel@tonic-gate 	} else {
441*7c478bd9Sstevel@tonic-gate 		if (v->v_flags & MDB_NV_RDONLY) {
442*7c478bd9Sstevel@tonic-gate 			mdb_warn("variable %s is read-only\n", name);
443*7c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
444*7c478bd9Sstevel@tonic-gate 		}
445*7c478bd9Sstevel@tonic-gate 	}
446*7c478bd9Sstevel@tonic-gate 
447*7c478bd9Sstevel@tonic-gate 	/*
448*7c478bd9Sstevel@tonic-gate 	 * If there already exists a vcb for this variable, we may be
449*7c478bd9Sstevel@tonic-gate 	 * calling the dcmd in a loop.  We only create a vcb for this
450*7c478bd9Sstevel@tonic-gate 	 * variable on the first invocation.
451*7c478bd9Sstevel@tonic-gate 	 */
452*7c478bd9Sstevel@tonic-gate 	if (mdb_vcb_find(v, mdb.m_frame) == NULL)
453*7c478bd9Sstevel@tonic-gate 		mdb_vcb_insert(mdb_vcb_create(v), mdb.m_frame);
454*7c478bd9Sstevel@tonic-gate 
455*7c478bd9Sstevel@tonic-gate 	return (0);
456*7c478bd9Sstevel@tonic-gate }
457*7c478bd9Sstevel@tonic-gate 
458*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
459*7c478bd9Sstevel@tonic-gate int
460*7c478bd9Sstevel@tonic-gate cmd_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
461*7c478bd9Sstevel@tonic-gate {
462*7c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
463*7c478bd9Sstevel@tonic-gate 	ulong_t offset;
464*7c478bd9Sstevel@tonic-gate 	uintptr_t a, tmp;
465*7c478bd9Sstevel@tonic-gate 	int ret;
466*7c478bd9Sstevel@tonic-gate 
467*7c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC) || argc == 0)
468*7c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
469*7c478bd9Sstevel@tonic-gate 
470*7c478bd9Sstevel@tonic-gate 	if (argv->a_type != MDB_TYPE_STRING) {
471*7c478bd9Sstevel@tonic-gate 		/*
472*7c478bd9Sstevel@tonic-gate 		 * We are being given a raw offset in lieu of a type and
473*7c478bd9Sstevel@tonic-gate 		 * member; confirm the arguments.
474*7c478bd9Sstevel@tonic-gate 		 */
475*7c478bd9Sstevel@tonic-gate 		if (argv->a_type != MDB_TYPE_IMMEDIATE)
476*7c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
477*7c478bd9Sstevel@tonic-gate 
478*7c478bd9Sstevel@tonic-gate 		offset = argv->a_un.a_val;
479*7c478bd9Sstevel@tonic-gate 
480*7c478bd9Sstevel@tonic-gate 		argv++;
481*7c478bd9Sstevel@tonic-gate 		argc--;
482*7c478bd9Sstevel@tonic-gate 
483*7c478bd9Sstevel@tonic-gate 		if (offset % sizeof (uintptr_t)) {
484*7c478bd9Sstevel@tonic-gate 			mdb_warn("offset must fall on a word boundary\n");
485*7c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
486*7c478bd9Sstevel@tonic-gate 		}
487*7c478bd9Sstevel@tonic-gate 	} else {
488*7c478bd9Sstevel@tonic-gate 		const char *member;
489*7c478bd9Sstevel@tonic-gate 		char buf[MDB_SYM_NAMLEN];
490*7c478bd9Sstevel@tonic-gate 		int ret;
491*7c478bd9Sstevel@tonic-gate 
492*7c478bd9Sstevel@tonic-gate 		ret = args_to_typename(&argc, &argv, buf, sizeof (buf));
493*7c478bd9Sstevel@tonic-gate 		if (ret != 0)
494*7c478bd9Sstevel@tonic-gate 			return (ret);
495*7c478bd9Sstevel@tonic-gate 
496*7c478bd9Sstevel@tonic-gate 		if (mdb_ctf_lookup_by_name(buf, &id) != 0) {
497*7c478bd9Sstevel@tonic-gate 			mdb_warn("failed to look up type %s", buf);
498*7c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
499*7c478bd9Sstevel@tonic-gate 		}
500*7c478bd9Sstevel@tonic-gate 
501*7c478bd9Sstevel@tonic-gate 		argv++;
502*7c478bd9Sstevel@tonic-gate 		argc--;
503*7c478bd9Sstevel@tonic-gate 
504*7c478bd9Sstevel@tonic-gate 		if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
505*7c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
506*7c478bd9Sstevel@tonic-gate 
507*7c478bd9Sstevel@tonic-gate 		member = argv->a_un.a_str;
508*7c478bd9Sstevel@tonic-gate 
509*7c478bd9Sstevel@tonic-gate 		argv++;
510*7c478bd9Sstevel@tonic-gate 		argc--;
511*7c478bd9Sstevel@tonic-gate 
512*7c478bd9Sstevel@tonic-gate 		if (mdb_ctf_offsetof(id, member, &offset) != 0) {
513*7c478bd9Sstevel@tonic-gate 			mdb_warn("failed to find member %s of type %s",
514*7c478bd9Sstevel@tonic-gate 			    member, buf);
515*7c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
516*7c478bd9Sstevel@tonic-gate 		}
517*7c478bd9Sstevel@tonic-gate 
518*7c478bd9Sstevel@tonic-gate 		if (offset % (sizeof (uintptr_t) * NBBY) != 0) {
519*7c478bd9Sstevel@tonic-gate 			mdb_warn("%s is not a word-aligned member\n", member);
520*7c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
521*7c478bd9Sstevel@tonic-gate 		}
522*7c478bd9Sstevel@tonic-gate 
523*7c478bd9Sstevel@tonic-gate 		offset /= NBBY;
524*7c478bd9Sstevel@tonic-gate 	}
525*7c478bd9Sstevel@tonic-gate 
526*7c478bd9Sstevel@tonic-gate 	/*
527*7c478bd9Sstevel@tonic-gate 	 * If we have any unchewed arguments, a variable name must be present.
528*7c478bd9Sstevel@tonic-gate 	 */
529*7c478bd9Sstevel@tonic-gate 	if (argc == 1) {
530*7c478bd9Sstevel@tonic-gate 		if (argv->a_type != MDB_TYPE_STRING)
531*7c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
532*7c478bd9Sstevel@tonic-gate 
533*7c478bd9Sstevel@tonic-gate 		if ((ret = setup_vcb(argv->a_un.a_str, addr)) != 0)
534*7c478bd9Sstevel@tonic-gate 			return (ret);
535*7c478bd9Sstevel@tonic-gate 
536*7c478bd9Sstevel@tonic-gate 	} else if (argc != 0) {
537*7c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
538*7c478bd9Sstevel@tonic-gate 	}
539*7c478bd9Sstevel@tonic-gate 
540*7c478bd9Sstevel@tonic-gate 	a = addr;
541*7c478bd9Sstevel@tonic-gate 
542*7c478bd9Sstevel@tonic-gate 	do {
543*7c478bd9Sstevel@tonic-gate 		mdb_printf("%lr\n", a);
544*7c478bd9Sstevel@tonic-gate 
545*7c478bd9Sstevel@tonic-gate 		if (mdb_vread(&tmp, sizeof (tmp), a + offset) == -1) {
546*7c478bd9Sstevel@tonic-gate 			mdb_warn("failed to read next pointer from object %p",
547*7c478bd9Sstevel@tonic-gate 			    a);
548*7c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
549*7c478bd9Sstevel@tonic-gate 		}
550*7c478bd9Sstevel@tonic-gate 
551*7c478bd9Sstevel@tonic-gate 		a = tmp;
552*7c478bd9Sstevel@tonic-gate 	} while (a != addr && a != NULL);
553*7c478bd9Sstevel@tonic-gate 
554*7c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
555*7c478bd9Sstevel@tonic-gate }
556*7c478bd9Sstevel@tonic-gate 
557*7c478bd9Sstevel@tonic-gate int
558*7c478bd9Sstevel@tonic-gate cmd_array(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
559*7c478bd9Sstevel@tonic-gate {
560*7c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
561*7c478bd9Sstevel@tonic-gate 	ssize_t elemsize = 0;
562*7c478bd9Sstevel@tonic-gate 	char tn[MDB_SYM_NAMLEN];
563*7c478bd9Sstevel@tonic-gate 	int ret, nelem = -1;
564*7c478bd9Sstevel@tonic-gate 
565*7c478bd9Sstevel@tonic-gate 	mdb_tgt_t *t = mdb.m_target;
566*7c478bd9Sstevel@tonic-gate 	GElf_Sym sym;
567*7c478bd9Sstevel@tonic-gate 	mdb_ctf_arinfo_t ar;
568*7c478bd9Sstevel@tonic-gate 	mdb_syminfo_t s_info;
569*7c478bd9Sstevel@tonic-gate 
570*7c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
571*7c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
572*7c478bd9Sstevel@tonic-gate 
573*7c478bd9Sstevel@tonic-gate 	if (argc >= 2) {
574*7c478bd9Sstevel@tonic-gate 		ret = args_to_typename(&argc, &argv, tn, sizeof (tn));
575*7c478bd9Sstevel@tonic-gate 		if (ret != 0)
576*7c478bd9Sstevel@tonic-gate 			return (ret);
577*7c478bd9Sstevel@tonic-gate 
578*7c478bd9Sstevel@tonic-gate 		if (argc == 1)	/* unquoted compound type without count */
579*7c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
580*7c478bd9Sstevel@tonic-gate 
581*7c478bd9Sstevel@tonic-gate 		if (mdb_ctf_lookup_by_name(tn, &id) != 0) {
582*7c478bd9Sstevel@tonic-gate 			mdb_warn("failed to look up type %s", tn);
583*7c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
584*7c478bd9Sstevel@tonic-gate 		}
585*7c478bd9Sstevel@tonic-gate 
586*7c478bd9Sstevel@tonic-gate 		if (argv[1].a_type == MDB_TYPE_IMMEDIATE)
587*7c478bd9Sstevel@tonic-gate 			nelem = argv[1].a_un.a_val;
588*7c478bd9Sstevel@tonic-gate 		else
589*7c478bd9Sstevel@tonic-gate 			nelem = mdb_strtoull(argv[1].a_un.a_str);
590*7c478bd9Sstevel@tonic-gate 
591*7c478bd9Sstevel@tonic-gate 		elemsize = mdb_ctf_type_size(id);
592*7c478bd9Sstevel@tonic-gate 	} else if (addr_to_sym(t, addr, tn, sizeof (tn), &sym, &s_info)
593*7c478bd9Sstevel@tonic-gate 		    != NULL && mdb_ctf_lookup_by_symbol(&sym, &s_info, &id)
594*7c478bd9Sstevel@tonic-gate 		    == 0 && mdb_ctf_type_kind(id) == CTF_K_ARRAY &&
595*7c478bd9Sstevel@tonic-gate 		    mdb_ctf_array_info(id, &ar) != -1) {
596*7c478bd9Sstevel@tonic-gate 			elemsize = mdb_ctf_type_size(id) / ar.mta_nelems;
597*7c478bd9Sstevel@tonic-gate 			nelem = ar.mta_nelems;
598*7c478bd9Sstevel@tonic-gate 	} else {
599*7c478bd9Sstevel@tonic-gate 		mdb_warn("no symbol information for %a", addr);
600*7c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
601*7c478bd9Sstevel@tonic-gate 	}
602*7c478bd9Sstevel@tonic-gate 
603*7c478bd9Sstevel@tonic-gate 	if (argc == 3 || argc == 1) {
604*7c478bd9Sstevel@tonic-gate 		if (argv[argc - 1].a_type != MDB_TYPE_STRING)
605*7c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
606*7c478bd9Sstevel@tonic-gate 
607*7c478bd9Sstevel@tonic-gate 		if ((ret = setup_vcb(argv[argc - 1].a_un.a_str, addr)) != 0)
608*7c478bd9Sstevel@tonic-gate 			return (ret);
609*7c478bd9Sstevel@tonic-gate 
610*7c478bd9Sstevel@tonic-gate 	} else if (argc > 3) {
611*7c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
612*7c478bd9Sstevel@tonic-gate 	}
613*7c478bd9Sstevel@tonic-gate 
614*7c478bd9Sstevel@tonic-gate 	for (; nelem > 0; nelem--) {
615*7c478bd9Sstevel@tonic-gate 		mdb_printf("%lr\n", addr);
616*7c478bd9Sstevel@tonic-gate 		addr = addr + elemsize;
617*7c478bd9Sstevel@tonic-gate 	}
618*7c478bd9Sstevel@tonic-gate 
619*7c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
620*7c478bd9Sstevel@tonic-gate }
621*7c478bd9Sstevel@tonic-gate 
622*7c478bd9Sstevel@tonic-gate /*
623*7c478bd9Sstevel@tonic-gate  * Print an integer bitfield in hexadecimal by reading the enclosing byte(s)
624*7c478bd9Sstevel@tonic-gate  * and then shifting and masking the data in the lower bits of a uint64_t.
625*7c478bd9Sstevel@tonic-gate  */
626*7c478bd9Sstevel@tonic-gate static int
627*7c478bd9Sstevel@tonic-gate print_bitfield(ulong_t off, printarg_t *pap, ctf_encoding_t *ep)
628*7c478bd9Sstevel@tonic-gate {
629*7c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
630*7c478bd9Sstevel@tonic-gate 	size_t size = (ep->cte_bits + (NBBY - 1)) / NBBY;
631*7c478bd9Sstevel@tonic-gate 	uint64_t mask = (1ULL << ep->cte_bits) - 1;
632*7c478bd9Sstevel@tonic-gate 	uint64_t value = 0;
633*7c478bd9Sstevel@tonic-gate 	uint8_t *buf = (uint8_t *)&value;
634*7c478bd9Sstevel@tonic-gate 	uint8_t shift;
635*7c478bd9Sstevel@tonic-gate 
636*7c478bd9Sstevel@tonic-gate 	const char *format;
637*7c478bd9Sstevel@tonic-gate 
638*7c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
639*7c478bd9Sstevel@tonic-gate 		return (0);
640*7c478bd9Sstevel@tonic-gate 
641*7c478bd9Sstevel@tonic-gate 	if (ep->cte_bits > sizeof (value) * NBBY - 1) {
642*7c478bd9Sstevel@tonic-gate 		mdb_printf("??? (invalid bitfield size %u)", ep->cte_bits);
643*7c478bd9Sstevel@tonic-gate 		return (0);
644*7c478bd9Sstevel@tonic-gate 	}
645*7c478bd9Sstevel@tonic-gate 
646*7c478bd9Sstevel@tonic-gate 	/*
647*7c478bd9Sstevel@tonic-gate 	 * On big-endian machines, we need to adjust the buf pointer to refer
648*7c478bd9Sstevel@tonic-gate 	 * to the lowest 'size' bytes in 'value', and we need shift based on
649*7c478bd9Sstevel@tonic-gate 	 * the offset from the end of the data, not the offset of the start.
650*7c478bd9Sstevel@tonic-gate 	 */
651*7c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN
652*7c478bd9Sstevel@tonic-gate 	buf += sizeof (value) - size;
653*7c478bd9Sstevel@tonic-gate 	off += ep->cte_bits;
654*7c478bd9Sstevel@tonic-gate #endif
655*7c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, buf, size, addr) != size) {
656*7c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %lu bytes at %llx",
657*7c478bd9Sstevel@tonic-gate 		    (ulong_t)size, addr);
658*7c478bd9Sstevel@tonic-gate 		return (1);
659*7c478bd9Sstevel@tonic-gate 	}
660*7c478bd9Sstevel@tonic-gate 
661*7c478bd9Sstevel@tonic-gate 	shift = off % NBBY;
662*7c478bd9Sstevel@tonic-gate 
663*7c478bd9Sstevel@tonic-gate 	/*
664*7c478bd9Sstevel@tonic-gate 	 * Offsets are counted from opposite ends on little- and
665*7c478bd9Sstevel@tonic-gate 	 * big-endian machines.
666*7c478bd9Sstevel@tonic-gate 	 */
667*7c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN
668*7c478bd9Sstevel@tonic-gate 	shift = NBBY - shift;
669*7c478bd9Sstevel@tonic-gate #endif
670*7c478bd9Sstevel@tonic-gate 
671*7c478bd9Sstevel@tonic-gate 	/*
672*7c478bd9Sstevel@tonic-gate 	 * If the bits we want do not begin on a byte boundary, shift the data
673*7c478bd9Sstevel@tonic-gate 	 * right so that the value is in the lowest 'cte_bits' of 'value'.
674*7c478bd9Sstevel@tonic-gate 	 */
675*7c478bd9Sstevel@tonic-gate 	if (off % NBBY != 0)
676*7c478bd9Sstevel@tonic-gate 		value >>= shift;
677*7c478bd9Sstevel@tonic-gate 	value &= mask;
678*7c478bd9Sstevel@tonic-gate 
679*7c478bd9Sstevel@tonic-gate 	/*
680*7c478bd9Sstevel@tonic-gate 	 * We default to printing signed bitfields as decimals,
681*7c478bd9Sstevel@tonic-gate 	 * and unsigned bitfields in hexadecimal.  If they specify
682*7c478bd9Sstevel@tonic-gate 	 * hexadecimal, we treat the field as unsigned.
683*7c478bd9Sstevel@tonic-gate 	 */
684*7c478bd9Sstevel@tonic-gate 	if ((pap->pa_flags & PA_INTHEX) ||
685*7c478bd9Sstevel@tonic-gate 	    !(ep->cte_format & CTF_INT_SIGNED)) {
686*7c478bd9Sstevel@tonic-gate 		format = (pap->pa_flags & PA_INTDEC)? "%#llu" : "%#llx";
687*7c478bd9Sstevel@tonic-gate 	} else {
688*7c478bd9Sstevel@tonic-gate 		int sshift = sizeof (value) * NBBY - ep->cte_bits;
689*7c478bd9Sstevel@tonic-gate 
690*7c478bd9Sstevel@tonic-gate 		/* sign-extend value, and print as a signed decimal */
691*7c478bd9Sstevel@tonic-gate 		value = ((int64_t)value << sshift) >> sshift;
692*7c478bd9Sstevel@tonic-gate 		format = "%#lld";
693*7c478bd9Sstevel@tonic-gate 	}
694*7c478bd9Sstevel@tonic-gate 	mdb_printf(format, value);
695*7c478bd9Sstevel@tonic-gate 
696*7c478bd9Sstevel@tonic-gate 	return (0);
697*7c478bd9Sstevel@tonic-gate }
698*7c478bd9Sstevel@tonic-gate 
699*7c478bd9Sstevel@tonic-gate /*
700*7c478bd9Sstevel@tonic-gate  * Print out a character or integer value.  We use some simple heuristics,
701*7c478bd9Sstevel@tonic-gate  * described below, to determine the appropriate radix to use for output.
702*7c478bd9Sstevel@tonic-gate  */
703*7c478bd9Sstevel@tonic-gate static int
704*7c478bd9Sstevel@tonic-gate print_int_val(const char *type, ctf_encoding_t *ep, ulong_t off,
705*7c478bd9Sstevel@tonic-gate     printarg_t *pap)
706*7c478bd9Sstevel@tonic-gate {
707*7c478bd9Sstevel@tonic-gate 	static const char *const sformat[] = { "%#d", "%#d", "%#d", "%#lld" };
708*7c478bd9Sstevel@tonic-gate 	static const char *const uformat[] = { "%#u", "%#u", "%#u", "%#llu" };
709*7c478bd9Sstevel@tonic-gate 	static const char *const xformat[] = { "%#x", "%#x", "%#x", "%#llx" };
710*7c478bd9Sstevel@tonic-gate 
711*7c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
712*7c478bd9Sstevel@tonic-gate 	const char *const *fsp;
713*7c478bd9Sstevel@tonic-gate 	size_t size;
714*7c478bd9Sstevel@tonic-gate 
715*7c478bd9Sstevel@tonic-gate 	union {
716*7c478bd9Sstevel@tonic-gate 		uint64_t i8;
717*7c478bd9Sstevel@tonic-gate 		uint32_t i4;
718*7c478bd9Sstevel@tonic-gate 		uint16_t i2;
719*7c478bd9Sstevel@tonic-gate 		uint8_t i1;
720*7c478bd9Sstevel@tonic-gate 		time_t t;
721*7c478bd9Sstevel@tonic-gate 	} u;
722*7c478bd9Sstevel@tonic-gate 
723*7c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
724*7c478bd9Sstevel@tonic-gate 		return (0);
725*7c478bd9Sstevel@tonic-gate 
726*7c478bd9Sstevel@tonic-gate 	if (ep->cte_format & CTF_INT_VARARGS) {
727*7c478bd9Sstevel@tonic-gate 		mdb_printf("...\n");
728*7c478bd9Sstevel@tonic-gate 		return (0);
729*7c478bd9Sstevel@tonic-gate 	}
730*7c478bd9Sstevel@tonic-gate 
731*7c478bd9Sstevel@tonic-gate 	/*
732*7c478bd9Sstevel@tonic-gate 	 * If the size is not a power-of-two number of bytes in the range 1-8
733*7c478bd9Sstevel@tonic-gate 	 * then we assume it is a bitfield and print it as such.
734*7c478bd9Sstevel@tonic-gate 	 */
735*7c478bd9Sstevel@tonic-gate 	size = ep->cte_bits / NBBY;
736*7c478bd9Sstevel@tonic-gate 	if (size > 8 || (ep->cte_bits % NBBY) != 0 || (size & (size - 1)) != 0)
737*7c478bd9Sstevel@tonic-gate 		return (print_bitfield(off, pap, ep));
738*7c478bd9Sstevel@tonic-gate 
739*7c478bd9Sstevel@tonic-gate 	if (IS_CHAR(*ep)) {
740*7c478bd9Sstevel@tonic-gate 		mdb_printf("'");
741*7c478bd9Sstevel@tonic-gate 		if (mdb_fmt_print(pap->pa_tgt, pap->pa_as,
742*7c478bd9Sstevel@tonic-gate 		    addr, 1, 'C') == addr)
743*7c478bd9Sstevel@tonic-gate 			return (1);
744*7c478bd9Sstevel@tonic-gate 		mdb_printf("'");
745*7c478bd9Sstevel@tonic-gate 		return (0);
746*7c478bd9Sstevel@tonic-gate 	}
747*7c478bd9Sstevel@tonic-gate 
748*7c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.i8, size, addr) != size) {
749*7c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %lu bytes at %llx",
750*7c478bd9Sstevel@tonic-gate 		    (ulong_t)size, addr);
751*7c478bd9Sstevel@tonic-gate 		return (1);
752*7c478bd9Sstevel@tonic-gate 	}
753*7c478bd9Sstevel@tonic-gate 
754*7c478bd9Sstevel@tonic-gate 	/*
755*7c478bd9Sstevel@tonic-gate 	 * We pretty-print time_t values as a calendar date and time.
756*7c478bd9Sstevel@tonic-gate 	 */
757*7c478bd9Sstevel@tonic-gate 	if ((pap->pa_flags & PA_PRETTY) &&
758*7c478bd9Sstevel@tonic-gate 	    !(pap->pa_flags & (PA_INTHEX | PA_INTDEC)) &&
759*7c478bd9Sstevel@tonic-gate 	    strcmp(type, "time_t") == 0 && u.t != 0) {
760*7c478bd9Sstevel@tonic-gate 		mdb_printf("%Y", u.t);
761*7c478bd9Sstevel@tonic-gate 		return (0);
762*7c478bd9Sstevel@tonic-gate 	}
763*7c478bd9Sstevel@tonic-gate 
764*7c478bd9Sstevel@tonic-gate 	/*
765*7c478bd9Sstevel@tonic-gate 	 * The default format is hexadecimal.
766*7c478bd9Sstevel@tonic-gate 	 */
767*7c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_INTDEC))
768*7c478bd9Sstevel@tonic-gate 		fsp = xformat;
769*7c478bd9Sstevel@tonic-gate 	else if (ep->cte_format & CTF_INT_SIGNED)
770*7c478bd9Sstevel@tonic-gate 		fsp = sformat;
771*7c478bd9Sstevel@tonic-gate 	else
772*7c478bd9Sstevel@tonic-gate 		fsp = uformat;
773*7c478bd9Sstevel@tonic-gate 
774*7c478bd9Sstevel@tonic-gate 	switch (size) {
775*7c478bd9Sstevel@tonic-gate 	case sizeof (uint8_t):
776*7c478bd9Sstevel@tonic-gate 		mdb_printf(fsp[0], u.i1);
777*7c478bd9Sstevel@tonic-gate 		break;
778*7c478bd9Sstevel@tonic-gate 	case sizeof (uint16_t):
779*7c478bd9Sstevel@tonic-gate 		mdb_printf(fsp[1], u.i2);
780*7c478bd9Sstevel@tonic-gate 		break;
781*7c478bd9Sstevel@tonic-gate 	case sizeof (uint32_t):
782*7c478bd9Sstevel@tonic-gate 		mdb_printf(fsp[2], u.i4);
783*7c478bd9Sstevel@tonic-gate 		break;
784*7c478bd9Sstevel@tonic-gate 	case sizeof (uint64_t):
785*7c478bd9Sstevel@tonic-gate 		mdb_printf(fsp[3], u.i8);
786*7c478bd9Sstevel@tonic-gate 		break;
787*7c478bd9Sstevel@tonic-gate 	}
788*7c478bd9Sstevel@tonic-gate 	return (0);
789*7c478bd9Sstevel@tonic-gate }
790*7c478bd9Sstevel@tonic-gate 
791*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
792*7c478bd9Sstevel@tonic-gate static int
793*7c478bd9Sstevel@tonic-gate print_int(const char *type, const char *name, mdb_ctf_id_t id,
794*7c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
795*7c478bd9Sstevel@tonic-gate {
796*7c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
797*7c478bd9Sstevel@tonic-gate 
798*7c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
799*7c478bd9Sstevel@tonic-gate 		return (0);
800*7c478bd9Sstevel@tonic-gate 
801*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_encoding(base, &e) != 0) {
802*7c478bd9Sstevel@tonic-gate 		mdb_printf("??? (%s)", mdb_strerror(errno));
803*7c478bd9Sstevel@tonic-gate 		return (0);
804*7c478bd9Sstevel@tonic-gate 	}
805*7c478bd9Sstevel@tonic-gate 
806*7c478bd9Sstevel@tonic-gate 	return (print_int_val(type, &e, off, pap));
807*7c478bd9Sstevel@tonic-gate }
808*7c478bd9Sstevel@tonic-gate 
809*7c478bd9Sstevel@tonic-gate /*
810*7c478bd9Sstevel@tonic-gate  * Print out a floating point value.  We only provide support for floats in
811*7c478bd9Sstevel@tonic-gate  * the ANSI-C float, double, and long double formats.
812*7c478bd9Sstevel@tonic-gate  */
813*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
814*7c478bd9Sstevel@tonic-gate static int
815*7c478bd9Sstevel@tonic-gate print_float(const char *type, const char *name, mdb_ctf_id_t id,
816*7c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
817*7c478bd9Sstevel@tonic-gate {
818*7c478bd9Sstevel@tonic-gate #ifndef _KMDB
819*7c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
820*7c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
821*7c478bd9Sstevel@tonic-gate 
822*7c478bd9Sstevel@tonic-gate 	union {
823*7c478bd9Sstevel@tonic-gate 		float f;
824*7c478bd9Sstevel@tonic-gate 		double d;
825*7c478bd9Sstevel@tonic-gate 		long double ld;
826*7c478bd9Sstevel@tonic-gate 	} u;
827*7c478bd9Sstevel@tonic-gate 
828*7c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
829*7c478bd9Sstevel@tonic-gate 		return (0);
830*7c478bd9Sstevel@tonic-gate 
831*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_encoding(base, &e) == 0) {
832*7c478bd9Sstevel@tonic-gate 		if (e.cte_format == CTF_FP_SINGLE &&
833*7c478bd9Sstevel@tonic-gate 		    e.cte_bits == sizeof (float) * NBBY) {
834*7c478bd9Sstevel@tonic-gate 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.f,
835*7c478bd9Sstevel@tonic-gate 			    sizeof (u.f), addr) != sizeof (u.f)) {
836*7c478bd9Sstevel@tonic-gate 				mdb_warn("failed to read float at %llx", addr);
837*7c478bd9Sstevel@tonic-gate 				return (1);
838*7c478bd9Sstevel@tonic-gate 			}
839*7c478bd9Sstevel@tonic-gate 			mdb_printf("%s", doubletos(u.f, 7, 'e'));
840*7c478bd9Sstevel@tonic-gate 
841*7c478bd9Sstevel@tonic-gate 		} else if (e.cte_format == CTF_FP_DOUBLE &&
842*7c478bd9Sstevel@tonic-gate 		    e.cte_bits == sizeof (double) * NBBY) {
843*7c478bd9Sstevel@tonic-gate 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.d,
844*7c478bd9Sstevel@tonic-gate 			    sizeof (u.d), addr) != sizeof (u.d)) {
845*7c478bd9Sstevel@tonic-gate 				mdb_warn("failed to read float at %llx", addr);
846*7c478bd9Sstevel@tonic-gate 				return (1);
847*7c478bd9Sstevel@tonic-gate 			}
848*7c478bd9Sstevel@tonic-gate 			mdb_printf("%s", doubletos(u.d, 7, 'e'));
849*7c478bd9Sstevel@tonic-gate 
850*7c478bd9Sstevel@tonic-gate 		} else if (e.cte_format == CTF_FP_LDOUBLE &&
851*7c478bd9Sstevel@tonic-gate 		    e.cte_bits == sizeof (long double) * NBBY) {
852*7c478bd9Sstevel@tonic-gate 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.ld,
853*7c478bd9Sstevel@tonic-gate 			    sizeof (u.ld), addr) != sizeof (u.ld)) {
854*7c478bd9Sstevel@tonic-gate 				mdb_warn("failed to read float at %llx", addr);
855*7c478bd9Sstevel@tonic-gate 				return (1);
856*7c478bd9Sstevel@tonic-gate 			}
857*7c478bd9Sstevel@tonic-gate 			mdb_printf("%s", longdoubletos(&u.ld, 16, 'e'));
858*7c478bd9Sstevel@tonic-gate 
859*7c478bd9Sstevel@tonic-gate 		} else {
860*7c478bd9Sstevel@tonic-gate 			mdb_printf("??? (unsupported FP format %u / %u bits\n",
861*7c478bd9Sstevel@tonic-gate 			    e.cte_format, e.cte_bits);
862*7c478bd9Sstevel@tonic-gate 		}
863*7c478bd9Sstevel@tonic-gate 	} else
864*7c478bd9Sstevel@tonic-gate 		mdb_printf("??? (%s)", mdb_strerror(errno));
865*7c478bd9Sstevel@tonic-gate #else
866*7c478bd9Sstevel@tonic-gate 	mdb_printf("<FLOAT>");
867*7c478bd9Sstevel@tonic-gate #endif
868*7c478bd9Sstevel@tonic-gate 	return (0);
869*7c478bd9Sstevel@tonic-gate }
870*7c478bd9Sstevel@tonic-gate 
871*7c478bd9Sstevel@tonic-gate 
872*7c478bd9Sstevel@tonic-gate /*
873*7c478bd9Sstevel@tonic-gate  * Print out a pointer value as a symbol name + offset or a hexadecimal value.
874*7c478bd9Sstevel@tonic-gate  * If the pointer itself is a char *, we attempt to read a bit of the data
875*7c478bd9Sstevel@tonic-gate  * referenced by the pointer and display it if it is a printable ASCII string.
876*7c478bd9Sstevel@tonic-gate  */
877*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
878*7c478bd9Sstevel@tonic-gate static int
879*7c478bd9Sstevel@tonic-gate print_ptr(const char *type, const char *name, mdb_ctf_id_t id,
880*7c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
881*7c478bd9Sstevel@tonic-gate {
882*7c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
883*7c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
884*7c478bd9Sstevel@tonic-gate 	uintptr_t value;
885*7c478bd9Sstevel@tonic-gate 	char buf[256];
886*7c478bd9Sstevel@tonic-gate 	ssize_t len;
887*7c478bd9Sstevel@tonic-gate 
888*7c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
889*7c478bd9Sstevel@tonic-gate 		return (0);
890*7c478bd9Sstevel@tonic-gate 
891*7c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as,
892*7c478bd9Sstevel@tonic-gate 	    &value, sizeof (value), addr) != sizeof (value)) {
893*7c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %s pointer at %llx", name, addr);
894*7c478bd9Sstevel@tonic-gate 		return (1);
895*7c478bd9Sstevel@tonic-gate 	}
896*7c478bd9Sstevel@tonic-gate 
897*7c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_NOSYMBOLIC) {
898*7c478bd9Sstevel@tonic-gate 		mdb_printf("%#lx", value);
899*7c478bd9Sstevel@tonic-gate 		return (0);
900*7c478bd9Sstevel@tonic-gate 	}
901*7c478bd9Sstevel@tonic-gate 
902*7c478bd9Sstevel@tonic-gate 	mdb_printf("%a", value);
903*7c478bd9Sstevel@tonic-gate 
904*7c478bd9Sstevel@tonic-gate 	if (value == NULL || strcmp(type, "caddr_t") == 0)
905*7c478bd9Sstevel@tonic-gate 		return (0);
906*7c478bd9Sstevel@tonic-gate 
907*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_kind(base) == CTF_K_POINTER &&
908*7c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_reference(base, &base) != -1 &&
909*7c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_resolve(base, &base) != -1 &&
910*7c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_encoding(base, &e) == 0 && IS_CHAR(e)) {
911*7c478bd9Sstevel@tonic-gate 		if ((len = mdb_tgt_readstr(pap->pa_realtgt, pap->pa_as,
912*7c478bd9Sstevel@tonic-gate 		    buf, sizeof (buf), value)) >= 0 && strisprint(buf)) {
913*7c478bd9Sstevel@tonic-gate 			if (len == sizeof (buf))
914*7c478bd9Sstevel@tonic-gate 				(void) strabbr(buf, sizeof (buf));
915*7c478bd9Sstevel@tonic-gate 			mdb_printf(" \"%s\"", buf);
916*7c478bd9Sstevel@tonic-gate 		}
917*7c478bd9Sstevel@tonic-gate 	}
918*7c478bd9Sstevel@tonic-gate 
919*7c478bd9Sstevel@tonic-gate 	return (0);
920*7c478bd9Sstevel@tonic-gate }
921*7c478bd9Sstevel@tonic-gate 
922*7c478bd9Sstevel@tonic-gate 
923*7c478bd9Sstevel@tonic-gate /*
924*7c478bd9Sstevel@tonic-gate  * Print out a fixed-size array.  We special-case arrays of characters
925*7c478bd9Sstevel@tonic-gate  * and attempt to print them out as ASCII strings if possible.  For other
926*7c478bd9Sstevel@tonic-gate  * arrays, we iterate over a maximum of pa_armemlim members and call
927*7c478bd9Sstevel@tonic-gate  * mdb_ctf_type_visit() again on each element to print its value.
928*7c478bd9Sstevel@tonic-gate  */
929*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
930*7c478bd9Sstevel@tonic-gate static int
931*7c478bd9Sstevel@tonic-gate print_array(const char *type, const char *name, mdb_ctf_id_t id,
932*7c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
933*7c478bd9Sstevel@tonic-gate {
934*7c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
935*7c478bd9Sstevel@tonic-gate 	printarg_t pa = *pap;
936*7c478bd9Sstevel@tonic-gate 	ssize_t eltsize;
937*7c478bd9Sstevel@tonic-gate 	mdb_ctf_arinfo_t r;
938*7c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
939*7c478bd9Sstevel@tonic-gate 	uint_t i, kind, limit;
940*7c478bd9Sstevel@tonic-gate 	int d, sou;
941*7c478bd9Sstevel@tonic-gate 	char buf[8];
942*7c478bd9Sstevel@tonic-gate 	char *str;
943*7c478bd9Sstevel@tonic-gate 
944*7c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
945*7c478bd9Sstevel@tonic-gate 		return (0);
946*7c478bd9Sstevel@tonic-gate 
947*7c478bd9Sstevel@tonic-gate 	/*
948*7c478bd9Sstevel@tonic-gate 	 * Determine the base type and size of the array's content.  If this
949*7c478bd9Sstevel@tonic-gate 	 * fails, we cannot print anything and just give up.
950*7c478bd9Sstevel@tonic-gate 	 */
951*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_array_info(base, &r) == -1 ||
952*7c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_resolve(r.mta_contents, &base) == -1 ||
953*7c478bd9Sstevel@tonic-gate 	    (eltsize = mdb_ctf_type_size(base)) == -1) {
954*7c478bd9Sstevel@tonic-gate 		mdb_printf("[ ??? ] (%s)", mdb_strerror(errno));
955*7c478bd9Sstevel@tonic-gate 		return (0);
956*7c478bd9Sstevel@tonic-gate 	}
957*7c478bd9Sstevel@tonic-gate 
958*7c478bd9Sstevel@tonic-gate 	/*
959*7c478bd9Sstevel@tonic-gate 	 * Read a few bytes and determine if the content appears to be
960*7c478bd9Sstevel@tonic-gate 	 * printable ASCII characters.  If so, read the entire array and
961*7c478bd9Sstevel@tonic-gate 	 * attempt to display it as a string if it is printable.
962*7c478bd9Sstevel@tonic-gate 	 */
963*7c478bd9Sstevel@tonic-gate 	if ((pap->pa_arstrlim == MDB_ARR_NOLIMIT ||
964*7c478bd9Sstevel@tonic-gate 	    r.mta_nelems <= pap->pa_arstrlim) &&
965*7c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_encoding(base, &e) == 0 && IS_CHAR(e) &&
966*7c478bd9Sstevel@tonic-gate 	    mdb_tgt_readstr(pap->pa_tgt, pap->pa_as, buf,
967*7c478bd9Sstevel@tonic-gate 	    MIN(sizeof (buf), r.mta_nelems), addr) > 0 && strisprint(buf)) {
968*7c478bd9Sstevel@tonic-gate 
969*7c478bd9Sstevel@tonic-gate 		str = mdb_alloc(r.mta_nelems + 1, UM_SLEEP | UM_GC);
970*7c478bd9Sstevel@tonic-gate 		str[r.mta_nelems] = '\0';
971*7c478bd9Sstevel@tonic-gate 
972*7c478bd9Sstevel@tonic-gate 		if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, str,
973*7c478bd9Sstevel@tonic-gate 		    r.mta_nelems, addr) != r.mta_nelems) {
974*7c478bd9Sstevel@tonic-gate 			mdb_warn("failed to read char array at %llx", addr);
975*7c478bd9Sstevel@tonic-gate 			return (1);
976*7c478bd9Sstevel@tonic-gate 		}
977*7c478bd9Sstevel@tonic-gate 
978*7c478bd9Sstevel@tonic-gate 		if (strisprint(str)) {
979*7c478bd9Sstevel@tonic-gate 			mdb_printf("[ \"%s\" ]", str);
980*7c478bd9Sstevel@tonic-gate 			return (0);
981*7c478bd9Sstevel@tonic-gate 		}
982*7c478bd9Sstevel@tonic-gate 	}
983*7c478bd9Sstevel@tonic-gate 
984*7c478bd9Sstevel@tonic-gate 	if (pap->pa_armemlim != MDB_ARR_NOLIMIT)
985*7c478bd9Sstevel@tonic-gate 		limit = MIN(r.mta_nelems, pap->pa_armemlim);
986*7c478bd9Sstevel@tonic-gate 	else
987*7c478bd9Sstevel@tonic-gate 		limit = r.mta_nelems;
988*7c478bd9Sstevel@tonic-gate 
989*7c478bd9Sstevel@tonic-gate 	if (limit == 0) {
990*7c478bd9Sstevel@tonic-gate 		mdb_printf("[ ... ]");
991*7c478bd9Sstevel@tonic-gate 		return (0);
992*7c478bd9Sstevel@tonic-gate 	}
993*7c478bd9Sstevel@tonic-gate 
994*7c478bd9Sstevel@tonic-gate 	kind = mdb_ctf_type_kind(base);
995*7c478bd9Sstevel@tonic-gate 	sou = IS_COMPOSITE(kind);
996*7c478bd9Sstevel@tonic-gate 
997*7c478bd9Sstevel@tonic-gate 	pa.pa_addr = addr;		/* set base address to start of array */
998*7c478bd9Sstevel@tonic-gate 	pa.pa_nest += pa.pa_depth + 1;	/* nesting level is current depth + 1 */
999*7c478bd9Sstevel@tonic-gate 	pa.pa_depth = 0;		/* reset depth to 0 for new scope */
1000*7c478bd9Sstevel@tonic-gate 	pa.pa_prefix = NULL;
1001*7c478bd9Sstevel@tonic-gate 
1002*7c478bd9Sstevel@tonic-gate 	if (sou) {
1003*7c478bd9Sstevel@tonic-gate 		pa.pa_delim = "\n";
1004*7c478bd9Sstevel@tonic-gate 		mdb_printf("[\n");
1005*7c478bd9Sstevel@tonic-gate 	} else {
1006*7c478bd9Sstevel@tonic-gate 		pa.pa_flags &= ~(PA_SHOWTYPE | PA_SHOWNAME | PA_SHOWADDR);
1007*7c478bd9Sstevel@tonic-gate 		pa.pa_delim = ", ";
1008*7c478bd9Sstevel@tonic-gate 		mdb_printf("[ ");
1009*7c478bd9Sstevel@tonic-gate 	}
1010*7c478bd9Sstevel@tonic-gate 
1011*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < limit; i++, pa.pa_addr += eltsize) {
1012*7c478bd9Sstevel@tonic-gate 		if (i == limit - 1 && !sou) {
1013*7c478bd9Sstevel@tonic-gate 			if (limit < r.mta_nelems)
1014*7c478bd9Sstevel@tonic-gate 				pa.pa_delim = ", ... ]";
1015*7c478bd9Sstevel@tonic-gate 			else
1016*7c478bd9Sstevel@tonic-gate 				pa.pa_delim = " ]";
1017*7c478bd9Sstevel@tonic-gate 		}
1018*7c478bd9Sstevel@tonic-gate 
1019*7c478bd9Sstevel@tonic-gate 		if (mdb_ctf_type_visit(r.mta_contents, elt_print, &pa) == -1) {
1020*7c478bd9Sstevel@tonic-gate 			mdb_warn("failed to print array data");
1021*7c478bd9Sstevel@tonic-gate 			return (1);
1022*7c478bd9Sstevel@tonic-gate 		}
1023*7c478bd9Sstevel@tonic-gate 	}
1024*7c478bd9Sstevel@tonic-gate 
1025*7c478bd9Sstevel@tonic-gate 	if (sou) {
1026*7c478bd9Sstevel@tonic-gate 		for (d = pa.pa_depth - 1; d >= 0; d--)
1027*7c478bd9Sstevel@tonic-gate 			print_close_sou(&pa, d);
1028*7c478bd9Sstevel@tonic-gate 
1029*7c478bd9Sstevel@tonic-gate 		if (limit < r.mta_nelems) {
1030*7c478bd9Sstevel@tonic-gate 			mdb_printf("%*s... ]",
1031*7c478bd9Sstevel@tonic-gate 			    (pap->pa_depth + pap->pa_nest) * pap->pa_tab, "");
1032*7c478bd9Sstevel@tonic-gate 		} else {
1033*7c478bd9Sstevel@tonic-gate 			mdb_printf("%*s]",
1034*7c478bd9Sstevel@tonic-gate 			    (pap->pa_depth + pap->pa_nest) * pap->pa_tab, "");
1035*7c478bd9Sstevel@tonic-gate 		}
1036*7c478bd9Sstevel@tonic-gate 	}
1037*7c478bd9Sstevel@tonic-gate 
1038*7c478bd9Sstevel@tonic-gate 	/* copy the hole array info, since it may have been grown */
1039*7c478bd9Sstevel@tonic-gate 	pap->pa_holes = pa.pa_holes;
1040*7c478bd9Sstevel@tonic-gate 	pap->pa_nholes = pa.pa_nholes;
1041*7c478bd9Sstevel@tonic-gate 
1042*7c478bd9Sstevel@tonic-gate 	return (0);
1043*7c478bd9Sstevel@tonic-gate }
1044*7c478bd9Sstevel@tonic-gate 
1045*7c478bd9Sstevel@tonic-gate /*
1046*7c478bd9Sstevel@tonic-gate  * Print out a struct or union header.  We need only print the open brace
1047*7c478bd9Sstevel@tonic-gate  * because mdb_ctf_type_visit() itself will automatically recurse through
1048*7c478bd9Sstevel@tonic-gate  * all members of the given struct or union.
1049*7c478bd9Sstevel@tonic-gate  */
1050*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1051*7c478bd9Sstevel@tonic-gate static int
1052*7c478bd9Sstevel@tonic-gate print_sou(const char *type, const char *name, mdb_ctf_id_t id,
1053*7c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
1054*7c478bd9Sstevel@tonic-gate {
1055*7c478bd9Sstevel@tonic-gate 	mdb_printf("{");
1056*7c478bd9Sstevel@tonic-gate 	pap->pa_delim = "\n";
1057*7c478bd9Sstevel@tonic-gate 	return (0);
1058*7c478bd9Sstevel@tonic-gate }
1059*7c478bd9Sstevel@tonic-gate 
1060*7c478bd9Sstevel@tonic-gate /*
1061*7c478bd9Sstevel@tonic-gate  * Print an enum value.  We attempt to convert the value to the corresponding
1062*7c478bd9Sstevel@tonic-gate  * enum name and print that if possible.
1063*7c478bd9Sstevel@tonic-gate  */
1064*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1065*7c478bd9Sstevel@tonic-gate static int
1066*7c478bd9Sstevel@tonic-gate print_enum(const char *type, const char *name, mdb_ctf_id_t id,
1067*7c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
1068*7c478bd9Sstevel@tonic-gate {
1069*7c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
1070*7c478bd9Sstevel@tonic-gate 	const char *ename;
1071*7c478bd9Sstevel@tonic-gate 	int value;
1072*7c478bd9Sstevel@tonic-gate 
1073*7c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
1074*7c478bd9Sstevel@tonic-gate 		return (0);
1075*7c478bd9Sstevel@tonic-gate 
1076*7c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as,
1077*7c478bd9Sstevel@tonic-gate 	    &value, sizeof (value), addr) != sizeof (value)) {
1078*7c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %s integer at %llx", name, addr);
1079*7c478bd9Sstevel@tonic-gate 		return (1);
1080*7c478bd9Sstevel@tonic-gate 	}
1081*7c478bd9Sstevel@tonic-gate 
1082*7c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_INTHEX)
1083*7c478bd9Sstevel@tonic-gate 		mdb_printf("%#x", value);
1084*7c478bd9Sstevel@tonic-gate 	else
1085*7c478bd9Sstevel@tonic-gate 		mdb_printf("%#d", value);
1086*7c478bd9Sstevel@tonic-gate 
1087*7c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_PRETTY) {
1088*7c478bd9Sstevel@tonic-gate 		ename = mdb_ctf_enum_name(base, value);
1089*7c478bd9Sstevel@tonic-gate 		mdb_printf(" (%s)", (ename != NULL)? ename : "???");
1090*7c478bd9Sstevel@tonic-gate 	}
1091*7c478bd9Sstevel@tonic-gate 
1092*7c478bd9Sstevel@tonic-gate 	return (0);
1093*7c478bd9Sstevel@tonic-gate }
1094*7c478bd9Sstevel@tonic-gate 
1095*7c478bd9Sstevel@tonic-gate /*
1096*7c478bd9Sstevel@tonic-gate  * Just print a semicolon if we run into a forward tag.
1097*7c478bd9Sstevel@tonic-gate  */
1098*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1099*7c478bd9Sstevel@tonic-gate static int
1100*7c478bd9Sstevel@tonic-gate print_tag(const char *type, const char *name, mdb_ctf_id_t id,
1101*7c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
1102*7c478bd9Sstevel@tonic-gate {
1103*7c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWVAL)
1104*7c478bd9Sstevel@tonic-gate 		mdb_printf("; ");
1105*7c478bd9Sstevel@tonic-gate 
1106*7c478bd9Sstevel@tonic-gate 	mdb_printf("(forward declaration)");
1107*7c478bd9Sstevel@tonic-gate 	return (0);
1108*7c478bd9Sstevel@tonic-gate }
1109*7c478bd9Sstevel@tonic-gate 
1110*7c478bd9Sstevel@tonic-gate static void
1111*7c478bd9Sstevel@tonic-gate print_hole(printarg_t *pap, int depth, ulong_t off, ulong_t endoff)
1112*7c478bd9Sstevel@tonic-gate {
1113*7c478bd9Sstevel@tonic-gate 	ulong_t bits = endoff - off;
1114*7c478bd9Sstevel@tonic-gate 	ulong_t size = bits / NBBY;
1115*7c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
1116*7c478bd9Sstevel@tonic-gate 
1117*7c478bd9Sstevel@tonic-gate 	static const char *const name = "<<HOLE>>";
1118*7c478bd9Sstevel@tonic-gate 	char type[MDB_SYM_NAMLEN];
1119*7c478bd9Sstevel@tonic-gate 
1120*7c478bd9Sstevel@tonic-gate 	int bitfield =
1121*7c478bd9Sstevel@tonic-gate 	    (off % NBBY != 0 ||
1122*7c478bd9Sstevel@tonic-gate 	    bits % NBBY != 0 ||
1123*7c478bd9Sstevel@tonic-gate 	    size > 8 ||
1124*7c478bd9Sstevel@tonic-gate 	    (size & (size - 1)) != 0);
1125*7c478bd9Sstevel@tonic-gate 
1126*7c478bd9Sstevel@tonic-gate 	ASSERT(off < endoff);
1127*7c478bd9Sstevel@tonic-gate 
1128*7c478bd9Sstevel@tonic-gate 	if (bits > NBBY * sizeof (uint64_t)) {
1129*7c478bd9Sstevel@tonic-gate 		ulong_t end;
1130*7c478bd9Sstevel@tonic-gate 
1131*7c478bd9Sstevel@tonic-gate 		/*
1132*7c478bd9Sstevel@tonic-gate 		 * The hole is larger than the largest integer type.  To
1133*7c478bd9Sstevel@tonic-gate 		 * handle this, we split up the hole at 8-byte-aligned
1134*7c478bd9Sstevel@tonic-gate 		 * boundaries, recursing to print each subsection.  For
1135*7c478bd9Sstevel@tonic-gate 		 * normal C structures, we'll loop at most twice.
1136*7c478bd9Sstevel@tonic-gate 		 */
1137*7c478bd9Sstevel@tonic-gate 		for (; off < endoff; off = end) {
1138*7c478bd9Sstevel@tonic-gate 			end = P2END(off, NBBY * sizeof (uint64_t));
1139*7c478bd9Sstevel@tonic-gate 			if (end > endoff)
1140*7c478bd9Sstevel@tonic-gate 				end = endoff;
1141*7c478bd9Sstevel@tonic-gate 
1142*7c478bd9Sstevel@tonic-gate 			ASSERT((end - off) <= NBBY * sizeof (uint64_t));
1143*7c478bd9Sstevel@tonic-gate 			print_hole(pap, depth, off, end);
1144*7c478bd9Sstevel@tonic-gate 		}
1145*7c478bd9Sstevel@tonic-gate 		ASSERT(end == endoff);
1146*7c478bd9Sstevel@tonic-gate 
1147*7c478bd9Sstevel@tonic-gate 		return;
1148*7c478bd9Sstevel@tonic-gate 	}
1149*7c478bd9Sstevel@tonic-gate 
1150*7c478bd9Sstevel@tonic-gate 	if (bitfield)
1151*7c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(type, sizeof (type), "unsigned");
1152*7c478bd9Sstevel@tonic-gate 	else
1153*7c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(type, sizeof (type), "uint%d_t", bits);
1154*7c478bd9Sstevel@tonic-gate 
1155*7c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & (PA_SHOWTYPE | PA_SHOWNAME | PA_SHOWADDR))
1156*7c478bd9Sstevel@tonic-gate 		mdb_printf("%*s", (depth + pap->pa_nest) * pap->pa_tab, "");
1157*7c478bd9Sstevel@tonic-gate 
1158*7c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWADDR) {
1159*7c478bd9Sstevel@tonic-gate 		if (off % NBBY == 0 || !(pap->pa_flags & PA_PRETTY))
1160*7c478bd9Sstevel@tonic-gate 			mdb_printf("%llx ", pap->pa_addr + off / NBBY);
1161*7c478bd9Sstevel@tonic-gate 		else
1162*7c478bd9Sstevel@tonic-gate 			mdb_printf("%llx.%lx ",
1163*7c478bd9Sstevel@tonic-gate 			    pap->pa_addr + off / NBBY, off % NBBY);
1164*7c478bd9Sstevel@tonic-gate 	}
1165*7c478bd9Sstevel@tonic-gate 
1166*7c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWTYPE)
1167*7c478bd9Sstevel@tonic-gate 		mdb_printf("%s ", type);
1168*7c478bd9Sstevel@tonic-gate 
1169*7c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWNAME)
1170*7c478bd9Sstevel@tonic-gate 		mdb_printf("%s", name);
1171*7c478bd9Sstevel@tonic-gate 
1172*7c478bd9Sstevel@tonic-gate 	if (bitfield && (pap->pa_flags & PA_SHOWTYPE))
1173*7c478bd9Sstevel@tonic-gate 		mdb_printf(" :%d", bits);
1174*7c478bd9Sstevel@tonic-gate 
1175*7c478bd9Sstevel@tonic-gate 	mdb_printf("%s ", (pap->pa_flags & PA_SHOWVAL)? " =" : "");
1176*7c478bd9Sstevel@tonic-gate 
1177*7c478bd9Sstevel@tonic-gate 	/*
1178*7c478bd9Sstevel@tonic-gate 	 * We fake up a ctf_encoding_t, and use print_int_val() to print
1179*7c478bd9Sstevel@tonic-gate 	 * the value.  Holes are always processed as unsigned integers.
1180*7c478bd9Sstevel@tonic-gate 	 */
1181*7c478bd9Sstevel@tonic-gate 	bzero(&e, sizeof (e));
1182*7c478bd9Sstevel@tonic-gate 	e.cte_format = 0;
1183*7c478bd9Sstevel@tonic-gate 	e.cte_offset = 0;
1184*7c478bd9Sstevel@tonic-gate 	e.cte_bits = bits;
1185*7c478bd9Sstevel@tonic-gate 
1186*7c478bd9Sstevel@tonic-gate 	if (print_int_val(type, &e, off, pap) != 0)
1187*7c478bd9Sstevel@tonic-gate 		mdb_iob_discard(mdb.m_out);
1188*7c478bd9Sstevel@tonic-gate 	else
1189*7c478bd9Sstevel@tonic-gate 		mdb_iob_puts(mdb.m_out, pap->pa_delim);
1190*7c478bd9Sstevel@tonic-gate }
1191*7c478bd9Sstevel@tonic-gate 
1192*7c478bd9Sstevel@tonic-gate /*
1193*7c478bd9Sstevel@tonic-gate  * The print_close_sou() function is called for each structure or union
1194*7c478bd9Sstevel@tonic-gate  * which has been completed.  For structures, we detect and print any holes
1195*7c478bd9Sstevel@tonic-gate  * before printing the closing brace.
1196*7c478bd9Sstevel@tonic-gate  */
1197*7c478bd9Sstevel@tonic-gate static void
1198*7c478bd9Sstevel@tonic-gate print_close_sou(printarg_t *pap, int newdepth)
1199*7c478bd9Sstevel@tonic-gate {
1200*7c478bd9Sstevel@tonic-gate 	int d = newdepth + pap->pa_nest;
1201*7c478bd9Sstevel@tonic-gate 
1202*7c478bd9Sstevel@tonic-gate 	if ((pap->pa_flags & PA_SHOWHOLES) && !pap->pa_holes[d].hi_isunion) {
1203*7c478bd9Sstevel@tonic-gate 		ulong_t end = pap->pa_holes[d + 1].hi_offset;
1204*7c478bd9Sstevel@tonic-gate 		ulong_t expected = pap->pa_holes[d].hi_offset;
1205*7c478bd9Sstevel@tonic-gate 
1206*7c478bd9Sstevel@tonic-gate 		if (end < expected)
1207*7c478bd9Sstevel@tonic-gate 			print_hole(pap, newdepth + 1, end, expected);
1208*7c478bd9Sstevel@tonic-gate 	}
1209*7c478bd9Sstevel@tonic-gate 	mdb_printf("%*s}\n", d * pap->pa_tab, "");
1210*7c478bd9Sstevel@tonic-gate }
1211*7c478bd9Sstevel@tonic-gate 
1212*7c478bd9Sstevel@tonic-gate static printarg_f *const printfuncs[] = {
1213*7c478bd9Sstevel@tonic-gate 	print_int,	/* CTF_K_INTEGER */
1214*7c478bd9Sstevel@tonic-gate 	print_float,	/* CTF_K_FLOAT */
1215*7c478bd9Sstevel@tonic-gate 	print_ptr,	/* CTF_K_POINTER */
1216*7c478bd9Sstevel@tonic-gate 	print_array,	/* CTF_K_ARRAY */
1217*7c478bd9Sstevel@tonic-gate 	print_ptr,	/* CTF_K_FUNCTION */
1218*7c478bd9Sstevel@tonic-gate 	print_sou,	/* CTF_K_STRUCT */
1219*7c478bd9Sstevel@tonic-gate 	print_sou,	/* CTF_K_UNION */
1220*7c478bd9Sstevel@tonic-gate 	print_enum,	/* CTF_K_ENUM */
1221*7c478bd9Sstevel@tonic-gate 	print_tag	/* CTF_K_FORWARD */
1222*7c478bd9Sstevel@tonic-gate };
1223*7c478bd9Sstevel@tonic-gate 
1224*7c478bd9Sstevel@tonic-gate /*
1225*7c478bd9Sstevel@tonic-gate  * The elt_print function is used as the mdb_ctf_type_visit callback.  For
1226*7c478bd9Sstevel@tonic-gate  * each element, we print an appropriate name prefix and then call the
1227*7c478bd9Sstevel@tonic-gate  * print subroutine for this type class in the array above.
1228*7c478bd9Sstevel@tonic-gate  */
1229*7c478bd9Sstevel@tonic-gate static int
1230*7c478bd9Sstevel@tonic-gate elt_print(const char *name, mdb_ctf_id_t id, ulong_t off, int depth, void *data)
1231*7c478bd9Sstevel@tonic-gate {
1232*7c478bd9Sstevel@tonic-gate 	char type[MDB_SYM_NAMLEN];
1233*7c478bd9Sstevel@tonic-gate 	int kind, rc, d;
1234*7c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t base;
1235*7c478bd9Sstevel@tonic-gate 	printarg_t *pap = data;
1236*7c478bd9Sstevel@tonic-gate 
1237*7c478bd9Sstevel@tonic-gate 	for (d = pap->pa_depth - 1; d >= depth; d--)
1238*7c478bd9Sstevel@tonic-gate 		print_close_sou(pap, d);
1239*7c478bd9Sstevel@tonic-gate 
1240*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_resolve(id, &base) == -1 ||
1241*7c478bd9Sstevel@tonic-gate 	    (kind = mdb_ctf_type_kind(base)) == -1)
1242*7c478bd9Sstevel@tonic-gate 		return (-1); /* errno is set for us */
1243*7c478bd9Sstevel@tonic-gate 
1244*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_name(id, type, sizeof (type)) == NULL)
1245*7c478bd9Sstevel@tonic-gate 		(void) strcpy(type, "(?)");
1246*7c478bd9Sstevel@tonic-gate 
1247*7c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWHOLES) {
1248*7c478bd9Sstevel@tonic-gate 		ctf_encoding_t e;
1249*7c478bd9Sstevel@tonic-gate 		ssize_t nsize;
1250*7c478bd9Sstevel@tonic-gate 		ulong_t newoff;
1251*7c478bd9Sstevel@tonic-gate 		holeinfo_t *hole;
1252*7c478bd9Sstevel@tonic-gate 		int extra = IS_COMPOSITE(kind)? 1 : 0;
1253*7c478bd9Sstevel@tonic-gate 
1254*7c478bd9Sstevel@tonic-gate 		/*
1255*7c478bd9Sstevel@tonic-gate 		 * grow the hole array, if necessary
1256*7c478bd9Sstevel@tonic-gate 		 */
1257*7c478bd9Sstevel@tonic-gate 		if (pap->pa_nest + depth + extra >= pap->pa_nholes) {
1258*7c478bd9Sstevel@tonic-gate 			int new = MAX(MAX(8, pap->pa_nholes * 2),
1259*7c478bd9Sstevel@tonic-gate 			    pap->pa_nest + depth + extra + 1);
1260*7c478bd9Sstevel@tonic-gate 
1261*7c478bd9Sstevel@tonic-gate 			holeinfo_t *nhi = mdb_zalloc(
1262*7c478bd9Sstevel@tonic-gate 			    sizeof (*nhi) * new, UM_NOSLEEP | UM_GC);
1263*7c478bd9Sstevel@tonic-gate 
1264*7c478bd9Sstevel@tonic-gate 			bcopy(pap->pa_holes, nhi,
1265*7c478bd9Sstevel@tonic-gate 			    pap->pa_nholes * sizeof (*nhi));
1266*7c478bd9Sstevel@tonic-gate 
1267*7c478bd9Sstevel@tonic-gate 			pap->pa_holes = nhi;
1268*7c478bd9Sstevel@tonic-gate 			pap->pa_nholes = new;
1269*7c478bd9Sstevel@tonic-gate 		}
1270*7c478bd9Sstevel@tonic-gate 
1271*7c478bd9Sstevel@tonic-gate 		hole = &pap->pa_holes[depth + pap->pa_nest];
1272*7c478bd9Sstevel@tonic-gate 
1273*7c478bd9Sstevel@tonic-gate 		if (depth != 0 && off > hole->hi_offset)
1274*7c478bd9Sstevel@tonic-gate 			print_hole(pap, depth, hole->hi_offset, off);
1275*7c478bd9Sstevel@tonic-gate 
1276*7c478bd9Sstevel@tonic-gate 		/* compute the next expected offset */
1277*7c478bd9Sstevel@tonic-gate 		if (kind == CTF_K_INTEGER &&
1278*7c478bd9Sstevel@tonic-gate 		    mdb_ctf_type_encoding(base, &e) == 0)
1279*7c478bd9Sstevel@tonic-gate 			newoff = off + e.cte_bits;
1280*7c478bd9Sstevel@tonic-gate 		else if ((nsize = mdb_ctf_type_size(base)) >= 0)
1281*7c478bd9Sstevel@tonic-gate 			newoff = off + nsize * NBBY;
1282*7c478bd9Sstevel@tonic-gate 		else {
1283*7c478bd9Sstevel@tonic-gate 			/* something bad happened, disable hole checking */
1284*7c478bd9Sstevel@tonic-gate 			newoff = -1UL;		/* ULONG_MAX */
1285*7c478bd9Sstevel@tonic-gate 		}
1286*7c478bd9Sstevel@tonic-gate 
1287*7c478bd9Sstevel@tonic-gate 		hole->hi_offset = newoff;
1288*7c478bd9Sstevel@tonic-gate 
1289*7c478bd9Sstevel@tonic-gate 		if (IS_COMPOSITE(kind)) {
1290*7c478bd9Sstevel@tonic-gate 			hole->hi_isunion = (kind == CTF_K_UNION);
1291*7c478bd9Sstevel@tonic-gate 			hole++;
1292*7c478bd9Sstevel@tonic-gate 			hole->hi_offset = off;
1293*7c478bd9Sstevel@tonic-gate 		}
1294*7c478bd9Sstevel@tonic-gate 	}
1295*7c478bd9Sstevel@tonic-gate 
1296*7c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & (PA_SHOWTYPE | PA_SHOWNAME | PA_SHOWADDR))
1297*7c478bd9Sstevel@tonic-gate 		mdb_printf("%*s", (depth + pap->pa_nest) * pap->pa_tab, "");
1298*7c478bd9Sstevel@tonic-gate 
1299*7c478bd9Sstevel@tonic-gate 	if (depth != 0) {
1300*7c478bd9Sstevel@tonic-gate 		if (pap->pa_flags & PA_SHOWADDR) {
1301*7c478bd9Sstevel@tonic-gate 			if (off % NBBY == 0 || !(pap->pa_flags & PA_PRETTY))
1302*7c478bd9Sstevel@tonic-gate 				mdb_printf("%llx ", pap->pa_addr + off / NBBY);
1303*7c478bd9Sstevel@tonic-gate 			else
1304*7c478bd9Sstevel@tonic-gate 				mdb_printf("%llx.%lx ",
1305*7c478bd9Sstevel@tonic-gate 				    pap->pa_addr + off / NBBY, off % NBBY);
1306*7c478bd9Sstevel@tonic-gate 		}
1307*7c478bd9Sstevel@tonic-gate 
1308*7c478bd9Sstevel@tonic-gate 		if (pap->pa_flags & PA_SHOWTYPE) {
1309*7c478bd9Sstevel@tonic-gate 			mdb_printf("%s", type);
1310*7c478bd9Sstevel@tonic-gate 			/*
1311*7c478bd9Sstevel@tonic-gate 			 * We want to avoid printing a trailing space when
1312*7c478bd9Sstevel@tonic-gate 			 * dealing with pointers in a structure, so we end
1313*7c478bd9Sstevel@tonic-gate 			 * up with:
1314*7c478bd9Sstevel@tonic-gate 			 *
1315*7c478bd9Sstevel@tonic-gate 			 *	label_t *t_onfault = 0
1316*7c478bd9Sstevel@tonic-gate 			 */
1317*7c478bd9Sstevel@tonic-gate 			if (type[strlen(type) - 1] != '*')
1318*7c478bd9Sstevel@tonic-gate 				mdb_printf(" ");
1319*7c478bd9Sstevel@tonic-gate 		}
1320*7c478bd9Sstevel@tonic-gate 
1321*7c478bd9Sstevel@tonic-gate 		if (pap->pa_flags & PA_SHOWNAME) {
1322*7c478bd9Sstevel@tonic-gate 			if (depth == 1 && pap->pa_prefix != NULL)
1323*7c478bd9Sstevel@tonic-gate 				mdb_printf("%s%s", pap->pa_prefix,
1324*7c478bd9Sstevel@tonic-gate 				    pap->pa_suffix);
1325*7c478bd9Sstevel@tonic-gate 			mdb_printf("%s", name);
1326*7c478bd9Sstevel@tonic-gate 		}
1327*7c478bd9Sstevel@tonic-gate 
1328*7c478bd9Sstevel@tonic-gate 		if ((pap->pa_flags & PA_SHOWTYPE) && kind == CTF_K_INTEGER) {
1329*7c478bd9Sstevel@tonic-gate 			ctf_encoding_t e;
1330*7c478bd9Sstevel@tonic-gate 
1331*7c478bd9Sstevel@tonic-gate 			if (mdb_ctf_type_encoding(base, &e) == 0) {
1332*7c478bd9Sstevel@tonic-gate 				ulong_t bits = e.cte_bits;
1333*7c478bd9Sstevel@tonic-gate 				ulong_t size = bits / NBBY;
1334*7c478bd9Sstevel@tonic-gate 
1335*7c478bd9Sstevel@tonic-gate 				if (bits % NBBY != 0 ||
1336*7c478bd9Sstevel@tonic-gate 				    off % NBBY != 0 ||
1337*7c478bd9Sstevel@tonic-gate 				    size > 8 ||
1338*7c478bd9Sstevel@tonic-gate 				    size != mdb_ctf_type_size(base))
1339*7c478bd9Sstevel@tonic-gate 					mdb_printf(" :%d", bits);
1340*7c478bd9Sstevel@tonic-gate 			}
1341*7c478bd9Sstevel@tonic-gate 		}
1342*7c478bd9Sstevel@tonic-gate 
1343*7c478bd9Sstevel@tonic-gate 		mdb_printf("%s ", pap->pa_flags & PA_SHOWVAL ? " =" : "");
1344*7c478bd9Sstevel@tonic-gate 	} else if (IS_SCALAR(kind)) {
1345*7c478bd9Sstevel@tonic-gate 		if (pap->pa_flags & PA_SHOWADDR) {
1346*7c478bd9Sstevel@tonic-gate 			if (off % NBBY == 0 || !(pap->pa_flags & PA_PRETTY))
1347*7c478bd9Sstevel@tonic-gate 				mdb_printf("%llx ", pap->pa_addr + off / NBBY);
1348*7c478bd9Sstevel@tonic-gate 			else
1349*7c478bd9Sstevel@tonic-gate 				mdb_printf("%llx.%lx ",
1350*7c478bd9Sstevel@tonic-gate 				    pap->pa_addr + off / NBBY, off % NBBY);
1351*7c478bd9Sstevel@tonic-gate 		}
1352*7c478bd9Sstevel@tonic-gate 
1353*7c478bd9Sstevel@tonic-gate 		if (pap->pa_flags & PA_SHOWTYPE) {
1354*7c478bd9Sstevel@tonic-gate 			mdb_printf("%s", type);
1355*7c478bd9Sstevel@tonic-gate 			/*
1356*7c478bd9Sstevel@tonic-gate 			 * For the zero-depth case, we always print the trailing
1357*7c478bd9Sstevel@tonic-gate 			 * space unless we also have a prefix.
1358*7c478bd9Sstevel@tonic-gate 			 */
1359*7c478bd9Sstevel@tonic-gate 			if (type[strlen(type) - 1] != '*' ||
1360*7c478bd9Sstevel@tonic-gate 			    !((pap->pa_flags & PA_SHOWNAME) &&
1361*7c478bd9Sstevel@tonic-gate 			    pap->pa_prefix != NULL))
1362*7c478bd9Sstevel@tonic-gate 				mdb_printf(" ", type);
1363*7c478bd9Sstevel@tonic-gate 		}
1364*7c478bd9Sstevel@tonic-gate 
1365*7c478bd9Sstevel@tonic-gate 		if ((pap->pa_flags & PA_SHOWNAME) && pap->pa_prefix != NULL)
1366*7c478bd9Sstevel@tonic-gate 			mdb_printf("%s", pap->pa_prefix);
1367*7c478bd9Sstevel@tonic-gate 
1368*7c478bd9Sstevel@tonic-gate 		if ((pap->pa_flags & PA_SHOWTYPE) &&
1369*7c478bd9Sstevel@tonic-gate 		    kind == CTF_K_INTEGER) {
1370*7c478bd9Sstevel@tonic-gate 			ctf_encoding_t e;
1371*7c478bd9Sstevel@tonic-gate 
1372*7c478bd9Sstevel@tonic-gate 			if (mdb_ctf_type_encoding(base, &e) == 0) {
1373*7c478bd9Sstevel@tonic-gate 				ulong_t bits = e.cte_bits;
1374*7c478bd9Sstevel@tonic-gate 				ulong_t size = bits / NBBY;
1375*7c478bd9Sstevel@tonic-gate 
1376*7c478bd9Sstevel@tonic-gate 				if (bits % NBBY != 0 ||
1377*7c478bd9Sstevel@tonic-gate 				    off % NBBY != 0 ||
1378*7c478bd9Sstevel@tonic-gate 				    size > 8 ||
1379*7c478bd9Sstevel@tonic-gate 				    size != mdb_ctf_type_size(base))
1380*7c478bd9Sstevel@tonic-gate 					mdb_printf(" :%d", bits);
1381*7c478bd9Sstevel@tonic-gate 			}
1382*7c478bd9Sstevel@tonic-gate 		}
1383*7c478bd9Sstevel@tonic-gate 
1384*7c478bd9Sstevel@tonic-gate 		if ((pap->pa_flags & PA_SHOWNAME) && pap->pa_prefix != NULL)
1385*7c478bd9Sstevel@tonic-gate 			mdb_printf("%s ",
1386*7c478bd9Sstevel@tonic-gate 			    pap->pa_flags & PA_SHOWVAL ? " =" : "");
1387*7c478bd9Sstevel@tonic-gate 
1388*7c478bd9Sstevel@tonic-gate 		if (pap->pa_prefix != NULL)
1389*7c478bd9Sstevel@tonic-gate 			name = pap->pa_prefix;
1390*7c478bd9Sstevel@tonic-gate 	}
1391*7c478bd9Sstevel@tonic-gate 
1392*7c478bd9Sstevel@tonic-gate 	pap->pa_depth = depth;
1393*7c478bd9Sstevel@tonic-gate 	ASSERT(kind > CTF_K_UNKNOWN && kind < CTF_K_TYPEDEF);
1394*7c478bd9Sstevel@tonic-gate 	rc = printfuncs[kind - 1](type, name, id, base, off, pap);
1395*7c478bd9Sstevel@tonic-gate 
1396*7c478bd9Sstevel@tonic-gate 	if (rc != 0)
1397*7c478bd9Sstevel@tonic-gate 		mdb_iob_discard(mdb.m_out);
1398*7c478bd9Sstevel@tonic-gate 	else
1399*7c478bd9Sstevel@tonic-gate 		mdb_iob_puts(mdb.m_out, pap->pa_delim);
1400*7c478bd9Sstevel@tonic-gate 
1401*7c478bd9Sstevel@tonic-gate 	return (rc);
1402*7c478bd9Sstevel@tonic-gate }
1403*7c478bd9Sstevel@tonic-gate 
1404*7c478bd9Sstevel@tonic-gate static int
1405*7c478bd9Sstevel@tonic-gate parse_delimiter(char **strp)
1406*7c478bd9Sstevel@tonic-gate {
1407*7c478bd9Sstevel@tonic-gate 	switch (**strp) {
1408*7c478bd9Sstevel@tonic-gate 	case '\0':
1409*7c478bd9Sstevel@tonic-gate 		return (MEMBER_DELIM_DONE);
1410*7c478bd9Sstevel@tonic-gate 
1411*7c478bd9Sstevel@tonic-gate 	case '.':
1412*7c478bd9Sstevel@tonic-gate 		*strp = *strp + 1;
1413*7c478bd9Sstevel@tonic-gate 		return (MEMBER_DELIM_DOT);
1414*7c478bd9Sstevel@tonic-gate 
1415*7c478bd9Sstevel@tonic-gate 	case '[':
1416*7c478bd9Sstevel@tonic-gate 		*strp = *strp + 1;
1417*7c478bd9Sstevel@tonic-gate 		return (MEMBER_DELIM_LBR);
1418*7c478bd9Sstevel@tonic-gate 
1419*7c478bd9Sstevel@tonic-gate 	case '-':
1420*7c478bd9Sstevel@tonic-gate 		*strp = *strp + 1;
1421*7c478bd9Sstevel@tonic-gate 		if (**strp == '>') {
1422*7c478bd9Sstevel@tonic-gate 			*strp = *strp + 1;
1423*7c478bd9Sstevel@tonic-gate 			return (MEMBER_DELIM_PTR);
1424*7c478bd9Sstevel@tonic-gate 		}
1425*7c478bd9Sstevel@tonic-gate 		*strp = *strp - 1;
1426*7c478bd9Sstevel@tonic-gate 		/*FALLTHROUGH*/
1427*7c478bd9Sstevel@tonic-gate 	default:
1428*7c478bd9Sstevel@tonic-gate 		return (MEMBER_DELIM_ERR);
1429*7c478bd9Sstevel@tonic-gate 	}
1430*7c478bd9Sstevel@tonic-gate }
1431*7c478bd9Sstevel@tonic-gate 
1432*7c478bd9Sstevel@tonic-gate static int
1433*7c478bd9Sstevel@tonic-gate deref(printarg_t *pap, size_t size)
1434*7c478bd9Sstevel@tonic-gate {
1435*7c478bd9Sstevel@tonic-gate 	uint32_t a32;
1436*7c478bd9Sstevel@tonic-gate 	mdb_tgt_as_t as = pap->pa_as;
1437*7c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t *ap = &pap->pa_addr;
1438*7c478bd9Sstevel@tonic-gate 
1439*7c478bd9Sstevel@tonic-gate 	if (size == sizeof (mdb_tgt_addr_t)) {
1440*7c478bd9Sstevel@tonic-gate 		if (mdb_tgt_aread(mdb.m_target, as, ap, size, *ap) == -1) {
1441*7c478bd9Sstevel@tonic-gate 			mdb_warn("could not dereference pointer %llx\n", *ap);
1442*7c478bd9Sstevel@tonic-gate 			return (-1);
1443*7c478bd9Sstevel@tonic-gate 		}
1444*7c478bd9Sstevel@tonic-gate 	} else {
1445*7c478bd9Sstevel@tonic-gate 		if (mdb_tgt_aread(mdb.m_target, as, &a32, size, *ap) == -1) {
1446*7c478bd9Sstevel@tonic-gate 			mdb_warn("could not dereference pointer %x\n", *ap);
1447*7c478bd9Sstevel@tonic-gate 			return (-1);
1448*7c478bd9Sstevel@tonic-gate 		}
1449*7c478bd9Sstevel@tonic-gate 
1450*7c478bd9Sstevel@tonic-gate 		*ap = (mdb_tgt_addr_t)a32;
1451*7c478bd9Sstevel@tonic-gate 	}
1452*7c478bd9Sstevel@tonic-gate 
1453*7c478bd9Sstevel@tonic-gate 	/*
1454*7c478bd9Sstevel@tonic-gate 	 * We've dereferenced at least once, we must be on the real
1455*7c478bd9Sstevel@tonic-gate 	 * target. If we were in the immediate target, reset to the real
1456*7c478bd9Sstevel@tonic-gate 	 * target; it's reset as needed when we return to the print
1457*7c478bd9Sstevel@tonic-gate 	 * routines.
1458*7c478bd9Sstevel@tonic-gate 	 */
1459*7c478bd9Sstevel@tonic-gate 	if (pap->pa_tgt == pap->pa_immtgt)
1460*7c478bd9Sstevel@tonic-gate 		pap->pa_tgt = pap->pa_realtgt;
1461*7c478bd9Sstevel@tonic-gate 
1462*7c478bd9Sstevel@tonic-gate 	return (0);
1463*7c478bd9Sstevel@tonic-gate }
1464*7c478bd9Sstevel@tonic-gate 
1465*7c478bd9Sstevel@tonic-gate static int
1466*7c478bd9Sstevel@tonic-gate parse_member(printarg_t *pap, const char *str, mdb_ctf_id_t id,
1467*7c478bd9Sstevel@tonic-gate     mdb_ctf_id_t *idp, ulong_t *offp, int *last_deref)
1468*7c478bd9Sstevel@tonic-gate {
1469*7c478bd9Sstevel@tonic-gate 	int delim;
1470*7c478bd9Sstevel@tonic-gate 	char member[64];
1471*7c478bd9Sstevel@tonic-gate 	char buf[128];
1472*7c478bd9Sstevel@tonic-gate 	uint_t index;
1473*7c478bd9Sstevel@tonic-gate 	char *start = (char *)str;
1474*7c478bd9Sstevel@tonic-gate 	char *end;
1475*7c478bd9Sstevel@tonic-gate 	ulong_t off = 0;
1476*7c478bd9Sstevel@tonic-gate 	mdb_ctf_arinfo_t ar;
1477*7c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t rid;
1478*7c478bd9Sstevel@tonic-gate 	int kind;
1479*7c478bd9Sstevel@tonic-gate 	ssize_t size;
1480*7c478bd9Sstevel@tonic-gate 	int non_array = FALSE;
1481*7c478bd9Sstevel@tonic-gate 
1482*7c478bd9Sstevel@tonic-gate 	/*
1483*7c478bd9Sstevel@tonic-gate 	 * id always has the unresolved type for printing error messages
1484*7c478bd9Sstevel@tonic-gate 	 * that include the type; rid always has the resolved type for
1485*7c478bd9Sstevel@tonic-gate 	 * use in mdb_ctf_* calls.  It is possible for this command to fail,
1486*7c478bd9Sstevel@tonic-gate 	 * however, if the resolved type is in the parent and it is currently
1487*7c478bd9Sstevel@tonic-gate 	 * unavailable.  Note that we also can't print out the name of the
1488*7c478bd9Sstevel@tonic-gate 	 * type, since that would also rely on looking up the resolved name.
1489*7c478bd9Sstevel@tonic-gate 	 */
1490*7c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_resolve(id, &rid) != 0) {
1491*7c478bd9Sstevel@tonic-gate 		mdb_warn("failed to resolve type");
1492*7c478bd9Sstevel@tonic-gate 		return (-1);
1493*7c478bd9Sstevel@tonic-gate 	}
1494*7c478bd9Sstevel@tonic-gate 
1495*7c478bd9Sstevel@tonic-gate 	delim = parse_delimiter(&start);
1496*7c478bd9Sstevel@tonic-gate 	/*
1497*7c478bd9Sstevel@tonic-gate 	 * If the user fails to specify an initial delimiter, guess -> for
1498*7c478bd9Sstevel@tonic-gate 	 * pointer types and . for non-pointer types.
1499*7c478bd9Sstevel@tonic-gate 	 */
1500*7c478bd9Sstevel@tonic-gate 	if (delim == MEMBER_DELIM_ERR)
1501*7c478bd9Sstevel@tonic-gate 		delim = (mdb_ctf_type_kind(rid) == CTF_K_POINTER) ?
1502*7c478bd9Sstevel@tonic-gate 		    MEMBER_DELIM_PTR : MEMBER_DELIM_DOT;
1503*7c478bd9Sstevel@tonic-gate 
1504*7c478bd9Sstevel@tonic-gate 	*last_deref = FALSE;
1505*7c478bd9Sstevel@tonic-gate 
1506*7c478bd9Sstevel@tonic-gate 	while (delim != MEMBER_DELIM_DONE) {
1507*7c478bd9Sstevel@tonic-gate 		switch (delim) {
1508*7c478bd9Sstevel@tonic-gate 		case MEMBER_DELIM_PTR:
1509*7c478bd9Sstevel@tonic-gate 			kind = mdb_ctf_type_kind(rid);
1510*7c478bd9Sstevel@tonic-gate 			if (kind != CTF_K_POINTER) {
1511*7c478bd9Sstevel@tonic-gate 				mdb_warn("%s is not a pointer type\n",
1512*7c478bd9Sstevel@tonic-gate 				    mdb_ctf_type_name(id, buf, sizeof (buf)));
1513*7c478bd9Sstevel@tonic-gate 				return (-1);
1514*7c478bd9Sstevel@tonic-gate 			}
1515*7c478bd9Sstevel@tonic-gate 
1516*7c478bd9Sstevel@tonic-gate 			size = mdb_ctf_type_size(id);
1517*7c478bd9Sstevel@tonic-gate 			if (deref(pap, size) != 0)
1518*7c478bd9Sstevel@tonic-gate 				return (-1);
1519*7c478bd9Sstevel@tonic-gate 
1520*7c478bd9Sstevel@tonic-gate 			(void) mdb_ctf_type_reference(rid, &id);
1521*7c478bd9Sstevel@tonic-gate 			(void) mdb_ctf_type_resolve(id, &rid);
1522*7c478bd9Sstevel@tonic-gate 
1523*7c478bd9Sstevel@tonic-gate 			off = 0;
1524*7c478bd9Sstevel@tonic-gate 			break;
1525*7c478bd9Sstevel@tonic-gate 
1526*7c478bd9Sstevel@tonic-gate 		case MEMBER_DELIM_DOT:
1527*7c478bd9Sstevel@tonic-gate 			kind = mdb_ctf_type_kind(rid);
1528*7c478bd9Sstevel@tonic-gate 			if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) {
1529*7c478bd9Sstevel@tonic-gate 				mdb_warn("%s is not a struct or union type\n",
1530*7c478bd9Sstevel@tonic-gate 				    mdb_ctf_type_name(id, buf, sizeof (buf)));
1531*7c478bd9Sstevel@tonic-gate 				return (-1);
1532*7c478bd9Sstevel@tonic-gate 			}
1533*7c478bd9Sstevel@tonic-gate 			break;
1534*7c478bd9Sstevel@tonic-gate 
1535*7c478bd9Sstevel@tonic-gate 		case MEMBER_DELIM_LBR:
1536*7c478bd9Sstevel@tonic-gate 			end = strchr(start, ']');
1537*7c478bd9Sstevel@tonic-gate 			if (end == NULL) {
1538*7c478bd9Sstevel@tonic-gate 				mdb_warn("no trailing ']'\n");
1539*7c478bd9Sstevel@tonic-gate 				return (-1);
1540*7c478bd9Sstevel@tonic-gate 			}
1541*7c478bd9Sstevel@tonic-gate 
1542*7c478bd9Sstevel@tonic-gate 			(void) mdb_snprintf(member, end - start + 1, start);
1543*7c478bd9Sstevel@tonic-gate 
1544*7c478bd9Sstevel@tonic-gate 			index = mdb_strtoull(member);
1545*7c478bd9Sstevel@tonic-gate 
1546*7c478bd9Sstevel@tonic-gate 			switch (mdb_ctf_type_kind(rid)) {
1547*7c478bd9Sstevel@tonic-gate 			case CTF_K_POINTER:
1548*7c478bd9Sstevel@tonic-gate 				size = mdb_ctf_type_size(rid);
1549*7c478bd9Sstevel@tonic-gate 
1550*7c478bd9Sstevel@tonic-gate 				if (deref(pap, size) != 0)
1551*7c478bd9Sstevel@tonic-gate 					return (-1);
1552*7c478bd9Sstevel@tonic-gate 
1553*7c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_type_reference(rid, &id);
1554*7c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_type_resolve(id, &rid);
1555*7c478bd9Sstevel@tonic-gate 
1556*7c478bd9Sstevel@tonic-gate 				size = mdb_ctf_type_size(id);
1557*7c478bd9Sstevel@tonic-gate 				if (size <= 0) {
1558*7c478bd9Sstevel@tonic-gate 					mdb_warn("cannot dereference void "
1559*7c478bd9Sstevel@tonic-gate 					    "type\n");
1560*7c478bd9Sstevel@tonic-gate 					return (-1);
1561*7c478bd9Sstevel@tonic-gate 				}
1562*7c478bd9Sstevel@tonic-gate 
1563*7c478bd9Sstevel@tonic-gate 				pap->pa_addr += index * size;
1564*7c478bd9Sstevel@tonic-gate 				off = 0;
1565*7c478bd9Sstevel@tonic-gate 
1566*7c478bd9Sstevel@tonic-gate 				if (index == 0 && non_array)
1567*7c478bd9Sstevel@tonic-gate 					*last_deref = TRUE;
1568*7c478bd9Sstevel@tonic-gate 				break;
1569*7c478bd9Sstevel@tonic-gate 
1570*7c478bd9Sstevel@tonic-gate 			case CTF_K_ARRAY:
1571*7c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_array_info(rid, &ar);
1572*7c478bd9Sstevel@tonic-gate 
1573*7c478bd9Sstevel@tonic-gate 				if (index >= ar.mta_nelems) {
1574*7c478bd9Sstevel@tonic-gate 					mdb_warn("index %r is outside of "
1575*7c478bd9Sstevel@tonic-gate 					    "array bounds [0 .. %r]\n",
1576*7c478bd9Sstevel@tonic-gate 					    index, ar.mta_nelems - 1);
1577*7c478bd9Sstevel@tonic-gate 				}
1578*7c478bd9Sstevel@tonic-gate 
1579*7c478bd9Sstevel@tonic-gate 				id = ar.mta_contents;
1580*7c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_type_resolve(id, &rid);
1581*7c478bd9Sstevel@tonic-gate 
1582*7c478bd9Sstevel@tonic-gate 				size = mdb_ctf_type_size(id);
1583*7c478bd9Sstevel@tonic-gate 				if (size <= 0) {
1584*7c478bd9Sstevel@tonic-gate 					mdb_warn("cannot dereference void "
1585*7c478bd9Sstevel@tonic-gate 					    "type\n");
1586*7c478bd9Sstevel@tonic-gate 					return (-1);
1587*7c478bd9Sstevel@tonic-gate 				}
1588*7c478bd9Sstevel@tonic-gate 
1589*7c478bd9Sstevel@tonic-gate 				pap->pa_addr += index * size;
1590*7c478bd9Sstevel@tonic-gate 				off = 0;
1591*7c478bd9Sstevel@tonic-gate 				break;
1592*7c478bd9Sstevel@tonic-gate 
1593*7c478bd9Sstevel@tonic-gate 			default:
1594*7c478bd9Sstevel@tonic-gate 				mdb_warn("cannot index into non-array, "
1595*7c478bd9Sstevel@tonic-gate 				    "non-pointer type\n");
1596*7c478bd9Sstevel@tonic-gate 				return (-1);
1597*7c478bd9Sstevel@tonic-gate 			}
1598*7c478bd9Sstevel@tonic-gate 
1599*7c478bd9Sstevel@tonic-gate 			start = end + 1;
1600*7c478bd9Sstevel@tonic-gate 			delim = parse_delimiter(&start);
1601*7c478bd9Sstevel@tonic-gate 			continue;
1602*7c478bd9Sstevel@tonic-gate 
1603*7c478bd9Sstevel@tonic-gate 		case MEMBER_DELIM_ERR:
1604*7c478bd9Sstevel@tonic-gate 		default:
1605*7c478bd9Sstevel@tonic-gate 			mdb_warn("'%c' is not a valid delimiter\n", *start);
1606*7c478bd9Sstevel@tonic-gate 			return (-1);
1607*7c478bd9Sstevel@tonic-gate 		}
1608*7c478bd9Sstevel@tonic-gate 
1609*7c478bd9Sstevel@tonic-gate 		*last_deref = FALSE;
1610*7c478bd9Sstevel@tonic-gate 		non_array = TRUE;
1611*7c478bd9Sstevel@tonic-gate 
1612*7c478bd9Sstevel@tonic-gate 		/*
1613*7c478bd9Sstevel@tonic-gate 		 * Find the end of the member name; assume that a member
1614*7c478bd9Sstevel@tonic-gate 		 * name is at least one character long.
1615*7c478bd9Sstevel@tonic-gate 		 */
1616*7c478bd9Sstevel@tonic-gate 		for (end = start + 1; isalnum(*end) || *end == '_'; end++)
1617*7c478bd9Sstevel@tonic-gate 			continue;
1618*7c478bd9Sstevel@tonic-gate 
1619*7c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(member, end - start + 1, start);
1620*7c478bd9Sstevel@tonic-gate 
1621*7c478bd9Sstevel@tonic-gate 		if (mdb_ctf_member_info(rid, member, &off, &id) != 0) {
1622*7c478bd9Sstevel@tonic-gate 			mdb_warn("failed to find member %s of %s", member,
1623*7c478bd9Sstevel@tonic-gate 			    mdb_ctf_type_name(id, buf, sizeof (buf)));
1624*7c478bd9Sstevel@tonic-gate 			return (-1);
1625*7c478bd9Sstevel@tonic-gate 		}
1626*7c478bd9Sstevel@tonic-gate 		(void) mdb_ctf_type_resolve(id, &rid);
1627*7c478bd9Sstevel@tonic-gate 
1628*7c478bd9Sstevel@tonic-gate 		pap->pa_addr += off / NBBY;
1629*7c478bd9Sstevel@tonic-gate 
1630*7c478bd9Sstevel@tonic-gate 		start = end;
1631*7c478bd9Sstevel@tonic-gate 		delim = parse_delimiter(&start);
1632*7c478bd9Sstevel@tonic-gate 	}
1633*7c478bd9Sstevel@tonic-gate 
1634*7c478bd9Sstevel@tonic-gate 
1635*7c478bd9Sstevel@tonic-gate 	*idp = id;
1636*7c478bd9Sstevel@tonic-gate 	*offp = off;
1637*7c478bd9Sstevel@tonic-gate 
1638*7c478bd9Sstevel@tonic-gate 	return (0);
1639*7c478bd9Sstevel@tonic-gate }
1640*7c478bd9Sstevel@tonic-gate 
1641*7c478bd9Sstevel@tonic-gate /*
1642*7c478bd9Sstevel@tonic-gate  * Recursively descend a print a given data structure.  We create a struct of
1643*7c478bd9Sstevel@tonic-gate  * the relevant print arguments and then call mdb_ctf_type_visit() to do the
1644*7c478bd9Sstevel@tonic-gate  * traversal, using elt_print() as the callback for each element.
1645*7c478bd9Sstevel@tonic-gate  */
1646*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1647*7c478bd9Sstevel@tonic-gate int
1648*7c478bd9Sstevel@tonic-gate cmd_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1649*7c478bd9Sstevel@tonic-gate {
1650*7c478bd9Sstevel@tonic-gate 	uintptr_t opt_c = MDB_ARR_NOLIMIT, opt_l = MDB_ARR_NOLIMIT;
1651*7c478bd9Sstevel@tonic-gate 	uint_t opt_C = FALSE, opt_L = FALSE, opt_p = FALSE, opt_i = FALSE;
1652*7c478bd9Sstevel@tonic-gate 	int uflags = (flags & DCMD_ADDRSPEC) ? PA_SHOWVAL : 0;
1653*7c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
1654*7c478bd9Sstevel@tonic-gate 	int err = DCMD_OK;
1655*7c478bd9Sstevel@tonic-gate 
1656*7c478bd9Sstevel@tonic-gate 	mdb_tgt_t *t = mdb.m_target;
1657*7c478bd9Sstevel@tonic-gate 	printarg_t pa;
1658*7c478bd9Sstevel@tonic-gate 	int d, i;
1659*7c478bd9Sstevel@tonic-gate 
1660*7c478bd9Sstevel@tonic-gate 	char s_name[MDB_SYM_NAMLEN];
1661*7c478bd9Sstevel@tonic-gate 	mdb_syminfo_t s_info;
1662*7c478bd9Sstevel@tonic-gate 	GElf_Sym sym;
1663*7c478bd9Sstevel@tonic-gate 
1664*7c478bd9Sstevel@tonic-gate 	i = mdb_getopts(argc, argv,
1665*7c478bd9Sstevel@tonic-gate 	    'a', MDB_OPT_SETBITS, PA_SHOWADDR, &uflags,
1666*7c478bd9Sstevel@tonic-gate 	    'C', MDB_OPT_SETBITS, TRUE, &opt_C,
1667*7c478bd9Sstevel@tonic-gate 	    'd', MDB_OPT_SETBITS, PA_INTDEC, &uflags,
1668*7c478bd9Sstevel@tonic-gate 	    'h', MDB_OPT_SETBITS, PA_SHOWHOLES, &uflags,
1669*7c478bd9Sstevel@tonic-gate 	    'L', MDB_OPT_SETBITS, TRUE, &opt_L,
1670*7c478bd9Sstevel@tonic-gate 	    'n', MDB_OPT_SETBITS, PA_NOSYMBOLIC, &uflags,
1671*7c478bd9Sstevel@tonic-gate 	    'p', MDB_OPT_SETBITS, TRUE, &opt_p,
1672*7c478bd9Sstevel@tonic-gate 	    't', MDB_OPT_SETBITS, PA_SHOWTYPE, &uflags,
1673*7c478bd9Sstevel@tonic-gate 	    'x', MDB_OPT_SETBITS, PA_INTHEX, &uflags,
1674*7c478bd9Sstevel@tonic-gate 	    'c', MDB_OPT_UINTPTR, &opt_c,
1675*7c478bd9Sstevel@tonic-gate 	    'l', MDB_OPT_UINTPTR, &opt_l,
1676*7c478bd9Sstevel@tonic-gate 	    'i', MDB_OPT_SETBITS, TRUE, &opt_i,
1677*7c478bd9Sstevel@tonic-gate 	    NULL);
1678*7c478bd9Sstevel@tonic-gate 
1679*7c478bd9Sstevel@tonic-gate 	if (uflags & PA_INTHEX)
1680*7c478bd9Sstevel@tonic-gate 		uflags &= ~PA_INTDEC;	/* -x and -d are mutually exclusive */
1681*7c478bd9Sstevel@tonic-gate 
1682*7c478bd9Sstevel@tonic-gate 	if (flags & DCMD_PIPE_OUT)
1683*7c478bd9Sstevel@tonic-gate 		uflags &= ~(PA_SHOWADDR | PA_SHOWTYPE);
1684*7c478bd9Sstevel@tonic-gate 	else
1685*7c478bd9Sstevel@tonic-gate 		uflags |= PA_SHOWNAME | PA_PRETTY;
1686*7c478bd9Sstevel@tonic-gate 
1687*7c478bd9Sstevel@tonic-gate 	if (opt_p && opt_i) {
1688*7c478bd9Sstevel@tonic-gate 		mdb_warn("-p and -i options are incompatible\n");
1689*7c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
1690*7c478bd9Sstevel@tonic-gate 	}
1691*7c478bd9Sstevel@tonic-gate 
1692*7c478bd9Sstevel@tonic-gate 	argc -= i;
1693*7c478bd9Sstevel@tonic-gate 	argv += i;
1694*7c478bd9Sstevel@tonic-gate 
1695*7c478bd9Sstevel@tonic-gate 	if (argc != 0 && argv->a_type == MDB_TYPE_STRING) {
1696*7c478bd9Sstevel@tonic-gate 		const char *t_name = s_name;
1697*7c478bd9Sstevel@tonic-gate 		int ret;
1698*7c478bd9Sstevel@tonic-gate 
1699*7c478bd9Sstevel@tonic-gate 		if (strchr("+-", argv->a_un.a_str[0]) != NULL)
1700*7c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
1701*7c478bd9Sstevel@tonic-gate 
1702*7c478bd9Sstevel@tonic-gate 		if ((ret = args_to_typename(&argc, &argv, s_name,
1703*7c478bd9Sstevel@tonic-gate 		    sizeof (s_name))) != 0)
1704*7c478bd9Sstevel@tonic-gate 			return (ret);
1705*7c478bd9Sstevel@tonic-gate 
1706*7c478bd9Sstevel@tonic-gate 		if (mdb_ctf_lookup_by_name(t_name, &id) != 0) {
1707*7c478bd9Sstevel@tonic-gate 			if (!(flags & DCMD_ADDRSPEC) || opt_i ||
1708*7c478bd9Sstevel@tonic-gate 			    addr_to_sym(t, addr, s_name, sizeof (s_name),
1709*7c478bd9Sstevel@tonic-gate 				&sym, &s_info) == NULL ||
1710*7c478bd9Sstevel@tonic-gate 			    mdb_ctf_lookup_by_symbol(&sym, &s_info, &id) != 0) {
1711*7c478bd9Sstevel@tonic-gate 
1712*7c478bd9Sstevel@tonic-gate 				mdb_warn("failed to look up type %s", t_name);
1713*7c478bd9Sstevel@tonic-gate 				return (DCMD_ABORT);
1714*7c478bd9Sstevel@tonic-gate 			}
1715*7c478bd9Sstevel@tonic-gate 		} else {
1716*7c478bd9Sstevel@tonic-gate 			argc--;
1717*7c478bd9Sstevel@tonic-gate 			argv++;
1718*7c478bd9Sstevel@tonic-gate 		}
1719*7c478bd9Sstevel@tonic-gate 
1720*7c478bd9Sstevel@tonic-gate 	} else if (!(flags & DCMD_ADDRSPEC) || opt_i) {
1721*7c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
1722*7c478bd9Sstevel@tonic-gate 
1723*7c478bd9Sstevel@tonic-gate 	} else if (addr_to_sym(t, addr, s_name, sizeof (s_name),
1724*7c478bd9Sstevel@tonic-gate 	    &sym, &s_info) == NULL) {
1725*7c478bd9Sstevel@tonic-gate 		mdb_warn("no symbol information for %a", addr);
1726*7c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
1727*7c478bd9Sstevel@tonic-gate 
1728*7c478bd9Sstevel@tonic-gate 	} else if (mdb_ctf_lookup_by_symbol(&sym, &s_info, &id) != 0) {
1729*7c478bd9Sstevel@tonic-gate 		mdb_warn("no type data available for %a [%u]", addr,
1730*7c478bd9Sstevel@tonic-gate 		    s_info.sym_id);
1731*7c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
1732*7c478bd9Sstevel@tonic-gate 	}
1733*7c478bd9Sstevel@tonic-gate 
1734*7c478bd9Sstevel@tonic-gate 	pa.pa_tgt = mdb.m_target;
1735*7c478bd9Sstevel@tonic-gate 	pa.pa_realtgt = pa.pa_tgt;
1736*7c478bd9Sstevel@tonic-gate 	pa.pa_immtgt = NULL;
1737*7c478bd9Sstevel@tonic-gate 	pa.pa_as = opt_p ? MDB_TGT_AS_PHYS : MDB_TGT_AS_VIRT;
1738*7c478bd9Sstevel@tonic-gate 	pa.pa_armemlim = mdb.m_armemlim;
1739*7c478bd9Sstevel@tonic-gate 	pa.pa_arstrlim = mdb.m_arstrlim;
1740*7c478bd9Sstevel@tonic-gate 	pa.pa_delim = "\n";
1741*7c478bd9Sstevel@tonic-gate 	pa.pa_flags = uflags;
1742*7c478bd9Sstevel@tonic-gate 	pa.pa_nest = 0;
1743*7c478bd9Sstevel@tonic-gate 	pa.pa_tab = 4;
1744*7c478bd9Sstevel@tonic-gate 	pa.pa_prefix = NULL;
1745*7c478bd9Sstevel@tonic-gate 	pa.pa_suffix = NULL;
1746*7c478bd9Sstevel@tonic-gate 	pa.pa_holes = NULL;
1747*7c478bd9Sstevel@tonic-gate 	pa.pa_nholes = 0;
1748*7c478bd9Sstevel@tonic-gate 	pa.pa_depth = 0;
1749*7c478bd9Sstevel@tonic-gate 
1750*7c478bd9Sstevel@tonic-gate 	if ((flags & DCMD_ADDRSPEC) && !opt_i)
1751*7c478bd9Sstevel@tonic-gate 		pa.pa_addr = opt_p ? mdb_get_dot() : addr;
1752*7c478bd9Sstevel@tonic-gate 	else
1753*7c478bd9Sstevel@tonic-gate 		pa.pa_addr = NULL;
1754*7c478bd9Sstevel@tonic-gate 
1755*7c478bd9Sstevel@tonic-gate 	if (opt_i) {
1756*7c478bd9Sstevel@tonic-gate 		const char *vargv[2];
1757*7c478bd9Sstevel@tonic-gate 		uintmax_t dot = mdb_get_dot();
1758*7c478bd9Sstevel@tonic-gate 		size_t outsize = mdb_ctf_type_size(id);
1759*7c478bd9Sstevel@tonic-gate 		vargv[0] = (const char *)&dot;
1760*7c478bd9Sstevel@tonic-gate 		vargv[1] = (const char *)&outsize;
1761*7c478bd9Sstevel@tonic-gate 		pa.pa_immtgt = mdb_tgt_create(mdb_value_tgt_create,
1762*7c478bd9Sstevel@tonic-gate 			0, 2, vargv);
1763*7c478bd9Sstevel@tonic-gate 		pa.pa_tgt = pa.pa_immtgt;
1764*7c478bd9Sstevel@tonic-gate 	}
1765*7c478bd9Sstevel@tonic-gate 
1766*7c478bd9Sstevel@tonic-gate 	if (opt_c != MDB_ARR_NOLIMIT)
1767*7c478bd9Sstevel@tonic-gate 		pa.pa_arstrlim = opt_c;
1768*7c478bd9Sstevel@tonic-gate 	if (opt_C)
1769*7c478bd9Sstevel@tonic-gate 		pa.pa_arstrlim = MDB_ARR_NOLIMIT;
1770*7c478bd9Sstevel@tonic-gate 	if (opt_l != MDB_ARR_NOLIMIT)
1771*7c478bd9Sstevel@tonic-gate 		pa.pa_armemlim = opt_l;
1772*7c478bd9Sstevel@tonic-gate 	if (opt_L)
1773*7c478bd9Sstevel@tonic-gate 		pa.pa_armemlim = MDB_ARR_NOLIMIT;
1774*7c478bd9Sstevel@tonic-gate 
1775*7c478bd9Sstevel@tonic-gate 	if (argc > 0) {
1776*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < argc; i++) {
1777*7c478bd9Sstevel@tonic-gate 			mdb_ctf_id_t mid;
1778*7c478bd9Sstevel@tonic-gate 			int last_deref;
1779*7c478bd9Sstevel@tonic-gate 			ulong_t off;
1780*7c478bd9Sstevel@tonic-gate 			int kind;
1781*7c478bd9Sstevel@tonic-gate 			char buf[MDB_SYM_NAMLEN];
1782*7c478bd9Sstevel@tonic-gate 
1783*7c478bd9Sstevel@tonic-gate 			mdb_tgt_t *oldtgt = pa.pa_tgt;
1784*7c478bd9Sstevel@tonic-gate 			mdb_tgt_as_t oldas = pa.pa_as;
1785*7c478bd9Sstevel@tonic-gate 			mdb_tgt_addr_t oldaddr = pa.pa_addr;
1786*7c478bd9Sstevel@tonic-gate 
1787*7c478bd9Sstevel@tonic-gate 			if (argv->a_type == MDB_TYPE_STRING) {
1788*7c478bd9Sstevel@tonic-gate 				const char *member = argv[i].a_un.a_str;
1789*7c478bd9Sstevel@tonic-gate 				mdb_ctf_id_t rid;
1790*7c478bd9Sstevel@tonic-gate 
1791*7c478bd9Sstevel@tonic-gate 				if (parse_member(&pa, member, id, &mid,
1792*7c478bd9Sstevel@tonic-gate 				    &off, &last_deref) != 0) {
1793*7c478bd9Sstevel@tonic-gate 					err = DCMD_ABORT;
1794*7c478bd9Sstevel@tonic-gate 					goto out;
1795*7c478bd9Sstevel@tonic-gate 				}
1796*7c478bd9Sstevel@tonic-gate 
1797*7c478bd9Sstevel@tonic-gate 				/*
1798*7c478bd9Sstevel@tonic-gate 				 * If the member string ends with a "[0]"
1799*7c478bd9Sstevel@tonic-gate 				 * (last_deref * is true) and the type is a
1800*7c478bd9Sstevel@tonic-gate 				 * structure or union, * print "->" rather
1801*7c478bd9Sstevel@tonic-gate 				 * than "[0]." in elt_print.
1802*7c478bd9Sstevel@tonic-gate 				 */
1803*7c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_type_resolve(mid, &rid);
1804*7c478bd9Sstevel@tonic-gate 				kind = mdb_ctf_type_kind(rid);
1805*7c478bd9Sstevel@tonic-gate 				if (last_deref && IS_SOU(kind)) {
1806*7c478bd9Sstevel@tonic-gate 					char *end;
1807*7c478bd9Sstevel@tonic-gate 					(void) mdb_snprintf(buf, sizeof (buf),
1808*7c478bd9Sstevel@tonic-gate 					    "%s", member);
1809*7c478bd9Sstevel@tonic-gate 					end = strrchr(buf, '[');
1810*7c478bd9Sstevel@tonic-gate 					*end = '\0';
1811*7c478bd9Sstevel@tonic-gate 					pa.pa_suffix = "->";
1812*7c478bd9Sstevel@tonic-gate 					member = &buf[0];
1813*7c478bd9Sstevel@tonic-gate 				} else if (IS_SOU(kind)) {
1814*7c478bd9Sstevel@tonic-gate 					pa.pa_suffix = ".";
1815*7c478bd9Sstevel@tonic-gate 				} else {
1816*7c478bd9Sstevel@tonic-gate 					pa.pa_suffix = "";
1817*7c478bd9Sstevel@tonic-gate 				}
1818*7c478bd9Sstevel@tonic-gate 
1819*7c478bd9Sstevel@tonic-gate 				pa.pa_prefix = member;
1820*7c478bd9Sstevel@tonic-gate 			} else {
1821*7c478bd9Sstevel@tonic-gate 				ulong_t moff;
1822*7c478bd9Sstevel@tonic-gate 
1823*7c478bd9Sstevel@tonic-gate 				moff = (ulong_t)argv[i].a_un.a_val;
1824*7c478bd9Sstevel@tonic-gate 
1825*7c478bd9Sstevel@tonic-gate 				if (mdb_ctf_offset_to_name(id, moff * NBBY,
1826*7c478bd9Sstevel@tonic-gate 				    buf, sizeof (buf), 0, &mid, &off) == -1) {
1827*7c478bd9Sstevel@tonic-gate 					mdb_warn("invalid offset %lx\n", moff);
1828*7c478bd9Sstevel@tonic-gate 					err = DCMD_ABORT;
1829*7c478bd9Sstevel@tonic-gate 					goto out;
1830*7c478bd9Sstevel@tonic-gate 				}
1831*7c478bd9Sstevel@tonic-gate 
1832*7c478bd9Sstevel@tonic-gate 				pa.pa_prefix = buf;
1833*7c478bd9Sstevel@tonic-gate 				pa.pa_addr += moff - off / NBBY;
1834*7c478bd9Sstevel@tonic-gate 				pa.pa_suffix = strlen(buf) == 0 ? "" : ".";
1835*7c478bd9Sstevel@tonic-gate 			}
1836*7c478bd9Sstevel@tonic-gate 
1837*7c478bd9Sstevel@tonic-gate 			off %= NBBY;
1838*7c478bd9Sstevel@tonic-gate 			if (off != 0) {
1839*7c478bd9Sstevel@tonic-gate 				if (elt_print("", mid, off, 0, &pa) != 0) {
1840*7c478bd9Sstevel@tonic-gate 					mdb_warn("failed to print type");
1841*7c478bd9Sstevel@tonic-gate 					err = DCMD_ERR;
1842*7c478bd9Sstevel@tonic-gate 					goto out;
1843*7c478bd9Sstevel@tonic-gate 				}
1844*7c478bd9Sstevel@tonic-gate 			} else {
1845*7c478bd9Sstevel@tonic-gate 				if (mdb_ctf_type_visit(mid, elt_print,
1846*7c478bd9Sstevel@tonic-gate 				    &pa) == -1) {
1847*7c478bd9Sstevel@tonic-gate 					mdb_warn("failed to print type");
1848*7c478bd9Sstevel@tonic-gate 					err = DCMD_ERR;
1849*7c478bd9Sstevel@tonic-gate 					goto out;
1850*7c478bd9Sstevel@tonic-gate 				}
1851*7c478bd9Sstevel@tonic-gate 
1852*7c478bd9Sstevel@tonic-gate 				for (d = pa.pa_depth - 1; d >= 0; d--)
1853*7c478bd9Sstevel@tonic-gate 					print_close_sou(&pa, d);
1854*7c478bd9Sstevel@tonic-gate 			}
1855*7c478bd9Sstevel@tonic-gate 
1856*7c478bd9Sstevel@tonic-gate 			pa.pa_depth = 0;
1857*7c478bd9Sstevel@tonic-gate 			pa.pa_tgt = oldtgt;
1858*7c478bd9Sstevel@tonic-gate 			pa.pa_as = oldas;
1859*7c478bd9Sstevel@tonic-gate 			pa.pa_addr = oldaddr;
1860*7c478bd9Sstevel@tonic-gate 			pa.pa_delim = "\n";
1861*7c478bd9Sstevel@tonic-gate 		}
1862*7c478bd9Sstevel@tonic-gate 
1863*7c478bd9Sstevel@tonic-gate 	} else {
1864*7c478bd9Sstevel@tonic-gate 		if (mdb_ctf_type_visit(id, elt_print, &pa) == -1) {
1865*7c478bd9Sstevel@tonic-gate 			mdb_warn("failed to print type");
1866*7c478bd9Sstevel@tonic-gate 			err = DCMD_ERR;
1867*7c478bd9Sstevel@tonic-gate 			goto out;
1868*7c478bd9Sstevel@tonic-gate 		}
1869*7c478bd9Sstevel@tonic-gate 
1870*7c478bd9Sstevel@tonic-gate 		for (d = pa.pa_depth - 1; d >= 0; d--)
1871*7c478bd9Sstevel@tonic-gate 			print_close_sou(&pa, d);
1872*7c478bd9Sstevel@tonic-gate 	}
1873*7c478bd9Sstevel@tonic-gate 
1874*7c478bd9Sstevel@tonic-gate 	mdb_set_dot(addr + mdb_ctf_type_size(id));
1875*7c478bd9Sstevel@tonic-gate 	err = DCMD_OK;
1876*7c478bd9Sstevel@tonic-gate out:
1877*7c478bd9Sstevel@tonic-gate 	if (pa.pa_immtgt)
1878*7c478bd9Sstevel@tonic-gate 		mdb_tgt_destroy(pa.pa_immtgt);
1879*7c478bd9Sstevel@tonic-gate 	return (err);
1880*7c478bd9Sstevel@tonic-gate }
1881*7c478bd9Sstevel@tonic-gate 
1882*7c478bd9Sstevel@tonic-gate void
1883*7c478bd9Sstevel@tonic-gate print_help(void)
1884*7c478bd9Sstevel@tonic-gate {
1885*7c478bd9Sstevel@tonic-gate 	mdb_printf("-a         show address of object\n"
1886*7c478bd9Sstevel@tonic-gate 	    "-c limit   limit the length of character arrays\n"
1887*7c478bd9Sstevel@tonic-gate 	    "-C         unlimit the length of character arrays\n"
1888*7c478bd9Sstevel@tonic-gate 	    "-d         output values in decimal\n"
1889*7c478bd9Sstevel@tonic-gate 	    "-h         print holes in structures\n"
1890*7c478bd9Sstevel@tonic-gate 	    "-l limit   limit the length of standard arrays\n"
1891*7c478bd9Sstevel@tonic-gate 	    "-L         unlimit the length of standard arrays\n"
1892*7c478bd9Sstevel@tonic-gate 	    "-n		don't print pointers as symbol offsets\n"
1893*7c478bd9Sstevel@tonic-gate 	    "-p         interpret address as a physical memory address\n"
1894*7c478bd9Sstevel@tonic-gate 	    "-t         show type of object\n"
1895*7c478bd9Sstevel@tonic-gate 	    "-i         interpret address as data of the given type\n"
1896*7c478bd9Sstevel@tonic-gate 	    "-x         output values in hexadecimal\n"
1897*7c478bd9Sstevel@tonic-gate 	    "\n"
1898*7c478bd9Sstevel@tonic-gate 	    "type may be omitted if the C type of addr can be inferred.\n"
1899*7c478bd9Sstevel@tonic-gate 	    "\n"
1900*7c478bd9Sstevel@tonic-gate 	    "Members may be specified with standard C syntax using the\n"
1901*7c478bd9Sstevel@tonic-gate 	    "array indexing operator \"[index]\", structure member\n"
1902*7c478bd9Sstevel@tonic-gate 	    "operator \".\", or structure pointer operator \"->\".\n"
1903*7c478bd9Sstevel@tonic-gate 	    "\n"
1904*7c478bd9Sstevel@tonic-gate 	    "Offsets must use the $[ expression ] syntax\n");
1905*7c478bd9Sstevel@tonic-gate }
1906