xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_print.c (revision 24537d3e)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5bf5197d8Sjwadams  * Common Development and Distribution License (the "License").
6bf5197d8Sjwadams  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22cce40297SJonathan Adams  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
263b6e0a59SMatt Amdur /*
27e9f82d69SAlex Reece  * Copyright (c) 2012, 2014 by Delphix. All rights reserved.
28f11c6b60SJohn Levon  * Copyright 2020 Joyent, Inc.
2938ce19d2SJosef 'Jeff' Sipek  * Copyright (c) 2014 Nexenta Systems, Inc. All rights reserved.
303b6e0a59SMatt Amdur  */
313b6e0a59SMatt Amdur 
327c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
337c478bd9Sstevel@tonic-gate #include <mdb/mdb_target.h>
347c478bd9Sstevel@tonic-gate #include <mdb/mdb_argvec.h>
357c478bd9Sstevel@tonic-gate #include <mdb/mdb_string.h>
367c478bd9Sstevel@tonic-gate #include <mdb/mdb_stdlib.h>
377c478bd9Sstevel@tonic-gate #include <mdb/mdb_err.h>
387c478bd9Sstevel@tonic-gate #include <mdb/mdb_debug.h>
397c478bd9Sstevel@tonic-gate #include <mdb/mdb_fmt.h>
407c478bd9Sstevel@tonic-gate #include <mdb/mdb_ctf.h>
417c478bd9Sstevel@tonic-gate #include <mdb/mdb_ctf_impl.h>
427c478bd9Sstevel@tonic-gate #include <mdb/mdb.h>
433b6e0a59SMatt Amdur #include <mdb/mdb_tab.h>
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h>
467c478bd9Sstevel@tonic-gate #include <sys/param.h>
477c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
483ddcfaddSBryan Cantrill #include <netinet/in.h>
497c478bd9Sstevel@tonic-gate #include <strings.h>
507c478bd9Sstevel@tonic-gate #include <libctf.h>
517c478bd9Sstevel@tonic-gate #include <ctype.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate typedef struct holeinfo {
547c478bd9Sstevel@tonic-gate 	ulong_t hi_offset;		/* expected offset */
557c478bd9Sstevel@tonic-gate 	uchar_t hi_isunion;		/* represents a union */
567c478bd9Sstevel@tonic-gate } holeinfo_t;
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate typedef struct printarg {
597c478bd9Sstevel@tonic-gate 	mdb_tgt_t *pa_tgt;		/* current target */
607c478bd9Sstevel@tonic-gate 	mdb_tgt_t *pa_realtgt;		/* real target (for -i) */
617c478bd9Sstevel@tonic-gate 	mdb_tgt_t *pa_immtgt;		/* immediate target (for -i) */
627c478bd9Sstevel@tonic-gate 	mdb_tgt_as_t pa_as;		/* address space to use for i/o */
637c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t pa_addr;		/* base address for i/o */
647c478bd9Sstevel@tonic-gate 	ulong_t pa_armemlim;		/* limit on array elements to print */
657c478bd9Sstevel@tonic-gate 	ulong_t pa_arstrlim;		/* limit on array chars to print */
667c478bd9Sstevel@tonic-gate 	const char *pa_delim;		/* element delimiter string */
677c478bd9Sstevel@tonic-gate 	const char *pa_prefix;		/* element prefix string */
687c478bd9Sstevel@tonic-gate 	const char *pa_suffix;		/* element suffix string */
697c478bd9Sstevel@tonic-gate 	holeinfo_t *pa_holes;		/* hole detection information */
707c478bd9Sstevel@tonic-gate 	int pa_nholes;			/* size of holes array */
717c478bd9Sstevel@tonic-gate 	int pa_flags;			/* formatting flags (see below) */
727c478bd9Sstevel@tonic-gate 	int pa_depth;			/* previous depth */
737c478bd9Sstevel@tonic-gate 	int pa_nest;			/* array nesting depth */
747c478bd9Sstevel@tonic-gate 	int pa_tab;			/* tabstop width */
75bf5197d8Sjwadams 	uint_t pa_maxdepth;		/* Limit max depth */
76838d7172SSebastien Roy 	uint_t pa_nooutdepth;		/* don't print output past this depth */
777c478bd9Sstevel@tonic-gate } printarg_t;
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate #define	PA_SHOWTYPE	0x001		/* print type name */
80cce40297SJonathan Adams #define	PA_SHOWBASETYPE	0x002		/* print base type name */
81cce40297SJonathan Adams #define	PA_SHOWNAME	0x004		/* print member name */
82cce40297SJonathan Adams #define	PA_SHOWADDR	0x008		/* print address */
83cce40297SJonathan Adams #define	PA_SHOWVAL	0x010		/* print value */
84cce40297SJonathan Adams #define	PA_SHOWHOLES	0x020		/* print holes in structs */
85cce40297SJonathan Adams #define	PA_INTHEX	0x040		/* print integer values in hex */
86cce40297SJonathan Adams #define	PA_INTDEC	0x080		/* print integer values in decimal */
87cce40297SJonathan Adams #define	PA_NOSYMBOLIC	0x100		/* don't print ptrs as func+offset */
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate #define	IS_CHAR(e) \
907c478bd9Sstevel@tonic-gate 	(((e).cte_format & (CTF_INT_CHAR | CTF_INT_SIGNED)) == \
917c478bd9Sstevel@tonic-gate 	(CTF_INT_CHAR | CTF_INT_SIGNED) && (e).cte_bits == NBBY)
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate #define	COMPOSITE_MASK	((1 << CTF_K_STRUCT) | \
947c478bd9Sstevel@tonic-gate 			(1 << CTF_K_UNION) | (1 << CTF_K_ARRAY))
957c478bd9Sstevel@tonic-gate #define	IS_COMPOSITE(k)	(((1 << k) & COMPOSITE_MASK) != 0)
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate #define	SOU_MASK	((1 << CTF_K_STRUCT) | (1 << CTF_K_UNION))
987c478bd9Sstevel@tonic-gate #define	IS_SOU(k)	(((1 << k) & SOU_MASK) != 0)
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_ERR	-1
1017c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_DONE	0
1027c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_PTR	1
1037c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_DOT	2
1047c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_LBR	3
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate typedef int printarg_f(const char *, const char *,
1077c478bd9Sstevel@tonic-gate     mdb_ctf_id_t, mdb_ctf_id_t, ulong_t, printarg_t *);
1087c478bd9Sstevel@tonic-gate 
109cce40297SJonathan Adams static int elt_print(const char *, mdb_ctf_id_t, mdb_ctf_id_t, ulong_t, int,
110cce40297SJonathan Adams     void *);
1117c478bd9Sstevel@tonic-gate static void print_close_sou(printarg_t *, int);
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate /*
1147c478bd9Sstevel@tonic-gate  * Given an address, look up the symbol ID of the specified symbol in its
1157c478bd9Sstevel@tonic-gate  * containing module.  We only support lookups for exact matches.
1167c478bd9Sstevel@tonic-gate  */
1177c478bd9Sstevel@tonic-gate static const char *
1187c478bd9Sstevel@tonic-gate addr_to_sym(mdb_tgt_t *t, uintptr_t addr, char *name, size_t namelen,
1197c478bd9Sstevel@tonic-gate     GElf_Sym *symp, mdb_syminfo_t *sip)
1207c478bd9Sstevel@tonic-gate {
1217c478bd9Sstevel@tonic-gate 	const mdb_map_t *mp;
1227c478bd9Sstevel@tonic-gate 	const char *p;
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate 	if (mdb_tgt_lookup_by_addr(t, addr, MDB_TGT_SYM_EXACT, name,
1257c478bd9Sstevel@tonic-gate 	    namelen, NULL, NULL) == -1)
1267c478bd9Sstevel@tonic-gate 		return (NULL); /* address does not exactly match a symbol */
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate 	if ((p = strrsplit(name, '`')) != NULL) {
1297c478bd9Sstevel@tonic-gate 		if (mdb_tgt_lookup_by_name(t, name, p, symp, sip) == -1)
1307c478bd9Sstevel@tonic-gate 			return (NULL);
1317c478bd9Sstevel@tonic-gate 		return (p);
1327c478bd9Sstevel@tonic-gate 	}
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	if ((mp = mdb_tgt_addr_to_map(t, addr)) == NULL)
1357c478bd9Sstevel@tonic-gate 		return (NULL); /* address does not fall within a mapping */
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 	if (mdb_tgt_lookup_by_name(t, mp->map_name, name, symp, sip) == -1)
1387c478bd9Sstevel@tonic-gate 		return (NULL);
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 	return (name);
1417c478bd9Sstevel@tonic-gate }
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate /*
1447c478bd9Sstevel@tonic-gate  * This lets dcmds be a little fancy with their processing of type arguments
1457c478bd9Sstevel@tonic-gate  * while still treating them more or less as a single argument.
1467c478bd9Sstevel@tonic-gate  * For example, if a command is invokes like this:
1477c478bd9Sstevel@tonic-gate  *
1487c478bd9Sstevel@tonic-gate  *   ::<dcmd> proc_t ...
1497c478bd9Sstevel@tonic-gate  *
1507c478bd9Sstevel@tonic-gate  * this function will just copy "proc_t" into the provided buffer. If the
1517c478bd9Sstevel@tonic-gate  * command is instead invoked like this:
1527c478bd9Sstevel@tonic-gate  *
1537c478bd9Sstevel@tonic-gate  *   ::<dcmd> struct proc ...
1547c478bd9Sstevel@tonic-gate  *
1557c478bd9Sstevel@tonic-gate  * this function will place the string "struct proc" into the provided buffer
1567c478bd9Sstevel@tonic-gate  * and increment the caller's argv and argc. This allows the caller to still
1577c478bd9Sstevel@tonic-gate  * treat the type argument logically as it would an other atomic argument.
1587c478bd9Sstevel@tonic-gate  */
1597c478bd9Sstevel@tonic-gate int
1607c478bd9Sstevel@tonic-gate args_to_typename(int *argcp, const mdb_arg_t **argvp, char *buf, size_t len)
1617c478bd9Sstevel@tonic-gate {
1627c478bd9Sstevel@tonic-gate 	int argc = *argcp;
1637c478bd9Sstevel@tonic-gate 	const mdb_arg_t *argv = *argvp;
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
1667c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 	if (strcmp(argv->a_un.a_str, "struct") == 0 ||
1697c478bd9Sstevel@tonic-gate 	    strcmp(argv->a_un.a_str, "enum") == 0 ||
1707c478bd9Sstevel@tonic-gate 	    strcmp(argv->a_un.a_str, "union") == 0) {
1717c478bd9Sstevel@tonic-gate 		if (argc <= 1) {
1727c478bd9Sstevel@tonic-gate 			mdb_warn("%s is not a valid type\n", argv->a_un.a_str);
1737c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
1747c478bd9Sstevel@tonic-gate 		}
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 		if (argv[1].a_type != MDB_TYPE_STRING)
1777c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(buf, len, "%s %s",
1807c478bd9Sstevel@tonic-gate 		    argv[0].a_un.a_str, argv[1].a_un.a_str);
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 		*argcp = argc - 1;
1837c478bd9Sstevel@tonic-gate 		*argvp = argv + 1;
1847c478bd9Sstevel@tonic-gate 	} else {
1857c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(buf, len, "%s", argv[0].a_un.a_str);
1867c478bd9Sstevel@tonic-gate 	}
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	return (0);
1897c478bd9Sstevel@tonic-gate }
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1927c478bd9Sstevel@tonic-gate int
1937c478bd9Sstevel@tonic-gate cmd_sizeof(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1947c478bd9Sstevel@tonic-gate {
1957c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
1967c478bd9Sstevel@tonic-gate 	char tn[MDB_SYM_NAMLEN];
1977c478bd9Sstevel@tonic-gate 	int ret;
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC)
2007c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	if ((ret = args_to_typename(&argc, &argv, tn, sizeof (tn))) != 0)
2037c478bd9Sstevel@tonic-gate 		return (ret);
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	if (argc != 1)
2067c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	if (mdb_ctf_lookup_by_name(tn, &id) != 0) {
2097c478bd9Sstevel@tonic-gate 		mdb_warn("failed to look up type %s", tn);
2107c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
2117c478bd9Sstevel@tonic-gate 	}
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	if (flags & DCMD_PIPE_OUT)
2147c478bd9Sstevel@tonic-gate 		mdb_printf("%#lr\n", mdb_ctf_type_size(id));
2157c478bd9Sstevel@tonic-gate 	else
2167c478bd9Sstevel@tonic-gate 		mdb_printf("sizeof (%s) = %#lr\n", tn, mdb_ctf_type_size(id));
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
2197c478bd9Sstevel@tonic-gate }
2207c478bd9Sstevel@tonic-gate 
2213b6e0a59SMatt Amdur int
2223b6e0a59SMatt Amdur cmd_sizeof_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
2233b6e0a59SMatt Amdur     const mdb_arg_t *argv)
2243b6e0a59SMatt Amdur {
2253b6e0a59SMatt Amdur 	char tn[MDB_SYM_NAMLEN];
2263b6e0a59SMatt Amdur 	int ret;
2273b6e0a59SMatt Amdur 
2283b6e0a59SMatt Amdur 	if (argc == 0 && !(flags & DCMD_TAB_SPACE))
2293b6e0a59SMatt Amdur 		return (0);
2303b6e0a59SMatt Amdur 
2313b6e0a59SMatt Amdur 	if (argc == 0 && (flags & DCMD_TAB_SPACE))
2323b6e0a59SMatt Amdur 		return (mdb_tab_complete_type(mcp, NULL, MDB_TABC_NOPOINT));
2333b6e0a59SMatt Amdur 
2343b6e0a59SMatt Amdur 	if ((ret = mdb_tab_typename(&argc, &argv, tn, sizeof (tn))) < 0)
2353b6e0a59SMatt Amdur 		return (ret);
2363b6e0a59SMatt Amdur 
2373b6e0a59SMatt Amdur 	if (argc == 1)
2383b6e0a59SMatt Amdur 		return (mdb_tab_complete_type(mcp, tn, MDB_TABC_NOPOINT));
2393b6e0a59SMatt Amdur 
2403b6e0a59SMatt Amdur 	return (0);
2413b6e0a59SMatt Amdur }
2423b6e0a59SMatt Amdur 
2437c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2447c478bd9Sstevel@tonic-gate int
2457c478bd9Sstevel@tonic-gate cmd_offsetof(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2467c478bd9Sstevel@tonic-gate {
2477c478bd9Sstevel@tonic-gate 	const char *member;
2487c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
2497c478bd9Sstevel@tonic-gate 	ulong_t off;
2507c478bd9Sstevel@tonic-gate 	char tn[MDB_SYM_NAMLEN];
251e0ad97e3SJonathan Adams 	ssize_t sz;
2527c478bd9Sstevel@tonic-gate 	int ret;
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC)
2557c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	if ((ret = args_to_typename(&argc, &argv, tn, sizeof (tn))) != 0)
2587c478bd9Sstevel@tonic-gate 		return (ret);
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate 	if (argc != 2 || argv[1].a_type != MDB_TYPE_STRING)
2617c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate 	if (mdb_ctf_lookup_by_name(tn, &id) != 0) {
2647c478bd9Sstevel@tonic-gate 		mdb_warn("failed to look up type %s", tn);
2657c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
2667c478bd9Sstevel@tonic-gate 	}
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate 	member = argv[1].a_un.a_str;
2697c478bd9Sstevel@tonic-gate 
270e0ad97e3SJonathan Adams 	if (mdb_ctf_member_info(id, member, &off, &id) != 0) {
2717c478bd9Sstevel@tonic-gate 		mdb_warn("failed to find member %s of type %s", member, tn);
2727c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
2737c478bd9Sstevel@tonic-gate 	}
2747c478bd9Sstevel@tonic-gate 
275e0ad97e3SJonathan Adams 	if (flags & DCMD_PIPE_OUT) {
276e0ad97e3SJonathan Adams 		if (off % NBBY != 0) {
277e0ad97e3SJonathan Adams 			mdb_warn("member %s of type %s is not byte-aligned\n",
278e0ad97e3SJonathan Adams 			    member, tn);
279e0ad97e3SJonathan Adams 			return (DCMD_ERR);
280e0ad97e3SJonathan Adams 		}
281e0ad97e3SJonathan Adams 		mdb_printf("%#lr", off / NBBY);
282e0ad97e3SJonathan Adams 		return (DCMD_OK);
283e0ad97e3SJonathan Adams 	}
284e0ad97e3SJonathan Adams 
285e0ad97e3SJonathan Adams 	mdb_printf("offsetof (%s, %s) = %#lr",
286e0ad97e3SJonathan Adams 	    tn, member, off / NBBY);
287e0ad97e3SJonathan Adams 	if (off % NBBY != 0)
288e0ad97e3SJonathan Adams 		mdb_printf(".%lr", off % NBBY);
289e0ad97e3SJonathan Adams 
290e0ad97e3SJonathan Adams 	if ((sz = mdb_ctf_type_size(id)) > 0)
291e0ad97e3SJonathan Adams 		mdb_printf(", sizeof (...->%s) = %#lr", member, sz);
292e0ad97e3SJonathan Adams 
293e0ad97e3SJonathan Adams 	mdb_printf("\n");
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
2967c478bd9Sstevel@tonic-gate }
2977c478bd9Sstevel@tonic-gate 
298e0ad97e3SJonathan Adams /*ARGSUSED*/
299e0ad97e3SJonathan Adams static int
300e0ad97e3SJonathan Adams enum_prefix_scan_cb(const char *name, int value, void *arg)
301e0ad97e3SJonathan Adams {
302e0ad97e3SJonathan Adams 	char *str = arg;
303e0ad97e3SJonathan Adams 
304e0ad97e3SJonathan Adams 	/*
305e0ad97e3SJonathan Adams 	 * This function is called with every name in the enum.  We make
306e0ad97e3SJonathan Adams 	 * "arg" be the common prefix, if any.
307e0ad97e3SJonathan Adams 	 */
308e0ad97e3SJonathan Adams 	if (str[0] == 0) {
309e0ad97e3SJonathan Adams 		if (strlcpy(arg, name, MDB_SYM_NAMLEN) >= MDB_SYM_NAMLEN)
310e0ad97e3SJonathan Adams 			return (1);
311e0ad97e3SJonathan Adams 		return (0);
312e0ad97e3SJonathan Adams 	}
313e0ad97e3SJonathan Adams 
314e0ad97e3SJonathan Adams 	while (*name == *str) {
315e0ad97e3SJonathan Adams 		if (*str == 0) {
316e0ad97e3SJonathan Adams 			if (str != arg) {
317e0ad97e3SJonathan Adams 				str--;	/* don't smother a name completely */
318e0ad97e3SJonathan Adams 			}
319e0ad97e3SJonathan Adams 			break;
320e0ad97e3SJonathan Adams 		}
321e0ad97e3SJonathan Adams 		name++;
322e0ad97e3SJonathan Adams 		str++;
323e0ad97e3SJonathan Adams 	}
324e0ad97e3SJonathan Adams 	*str = 0;
325e0ad97e3SJonathan Adams 
326e0ad97e3SJonathan Adams 	return (str == arg);	/* only continue if prefix is non-empty */
327e0ad97e3SJonathan Adams }
328e0ad97e3SJonathan Adams 
329cce40297SJonathan Adams struct enum_p2_info {
330e0ad97e3SJonathan Adams 	intmax_t e_value;	/* value we're processing */
331e0ad97e3SJonathan Adams 	char	*e_buf;		/* buffer for holding names */
332e0ad97e3SJonathan Adams 	size_t	e_size;		/* size of buffer */
333e0ad97e3SJonathan Adams 	size_t	e_prefix;	/* length of initial prefix */
334e0ad97e3SJonathan Adams 	uint_t	e_allprefix;	/* apply prefix to first guy, too */
335e0ad97e3SJonathan Adams 	uint_t	e_bits;		/* bits seen */
336e0ad97e3SJonathan Adams 	uint8_t	e_found;	/* have we seen anything? */
337e0ad97e3SJonathan Adams 	uint8_t	e_first;	/* does buf contain the first one? */
338e0ad97e3SJonathan Adams 	uint8_t	e_zero;		/* have we seen a zero value? */
339cce40297SJonathan Adams };
340cce40297SJonathan Adams 
341cce40297SJonathan Adams static int
342cce40297SJonathan Adams enum_p2_cb(const char *name, int bit_arg, void *arg)
343cce40297SJonathan Adams {
344cce40297SJonathan Adams 	struct enum_p2_info *eiip = arg;
345e0ad97e3SJonathan Adams 	uintmax_t bit = bit_arg;
346cce40297SJonathan Adams 
347cce40297SJonathan Adams 	if (bit != 0 && !ISP2(bit))
348cce40297SJonathan Adams 		return (1);	/* non-power-of-2; abort processing */
349cce40297SJonathan Adams 
350cce40297SJonathan Adams 	if ((bit == 0 && eiip->e_zero) ||
351cce40297SJonathan Adams 	    (bit != 0 && (eiip->e_bits & bit) != 0)) {
352cce40297SJonathan Adams 		return (0);	/* already seen this value */
353cce40297SJonathan Adams 	}
354cce40297SJonathan Adams 
355cce40297SJonathan Adams 	if (bit == 0)
356cce40297SJonathan Adams 		eiip->e_zero = 1;
357cce40297SJonathan Adams 	else
358cce40297SJonathan Adams 		eiip->e_bits |= bit;
359cce40297SJonathan Adams 
360cce40297SJonathan Adams 	if (eiip->e_buf != NULL && (eiip->e_value & bit) != 0) {
361e0ad97e3SJonathan Adams 		char *buf = eiip->e_buf;
362e0ad97e3SJonathan Adams 		size_t prefix = eiip->e_prefix;
363e0ad97e3SJonathan Adams 
364e0ad97e3SJonathan Adams 		if (eiip->e_found) {
365e0ad97e3SJonathan Adams 			(void) strlcat(buf, "|", eiip->e_size);
366e0ad97e3SJonathan Adams 
367e0ad97e3SJonathan Adams 			if (eiip->e_first && !eiip->e_allprefix && prefix > 0) {
368e0ad97e3SJonathan Adams 				char c1 = buf[prefix];
369e0ad97e3SJonathan Adams 				char c2 = buf[prefix + 1];
370e0ad97e3SJonathan Adams 				buf[prefix] = '{';
371e0ad97e3SJonathan Adams 				buf[prefix + 1] = 0;
372e0ad97e3SJonathan Adams 				mdb_printf("%s", buf);
373e0ad97e3SJonathan Adams 				buf[prefix] = c1;
374e0ad97e3SJonathan Adams 				buf[prefix + 1] = c2;
375e0ad97e3SJonathan Adams 				mdb_printf("%s", buf + prefix);
376e0ad97e3SJonathan Adams 			} else {
377e0ad97e3SJonathan Adams 				mdb_printf("%s", buf);
378e0ad97e3SJonathan Adams 			}
379cce40297SJonathan Adams 
380e0ad97e3SJonathan Adams 		}
381e0ad97e3SJonathan Adams 		/* skip the common prefix as necessary */
382e0ad97e3SJonathan Adams 		if ((eiip->e_found || eiip->e_allprefix) &&
383e0ad97e3SJonathan Adams 		    strlen(name) > prefix)
384e0ad97e3SJonathan Adams 			name += prefix;
385cce40297SJonathan Adams 
386e0ad97e3SJonathan Adams 		(void) strlcpy(eiip->e_buf, name, eiip->e_size);
387e0ad97e3SJonathan Adams 		eiip->e_first = !eiip->e_found;
388cce40297SJonathan Adams 		eiip->e_found = 1;
389cce40297SJonathan Adams 	}
390cce40297SJonathan Adams 	return (0);
391cce40297SJonathan Adams }
392cce40297SJonathan Adams 
393cce40297SJonathan Adams static int
394e0ad97e3SJonathan Adams enum_is_p2(mdb_ctf_id_t id)
395cce40297SJonathan Adams {
396cce40297SJonathan Adams 	struct enum_p2_info eii;
397e0ad97e3SJonathan Adams 	bzero(&eii, sizeof (eii));
398e0ad97e3SJonathan Adams 
399e0ad97e3SJonathan Adams 	return (mdb_ctf_type_kind(id) == CTF_K_ENUM &&
400e0ad97e3SJonathan Adams 	    mdb_ctf_enum_iter(id, enum_p2_cb, &eii) == 0 &&
401e0ad97e3SJonathan Adams 	    eii.e_bits != 0);
402e0ad97e3SJonathan Adams }
403e0ad97e3SJonathan Adams 
404e0ad97e3SJonathan Adams static int
405e0ad97e3SJonathan Adams enum_value_print_p2(mdb_ctf_id_t id, intmax_t value, uint_t allprefix)
406e0ad97e3SJonathan Adams {
407e0ad97e3SJonathan Adams 	struct enum_p2_info eii;
408e0ad97e3SJonathan Adams 	char prefix[MDB_SYM_NAMLEN + 2];
409e0ad97e3SJonathan Adams 	intmax_t missed;
410cce40297SJonathan Adams 
411cce40297SJonathan Adams 	bzero(&eii, sizeof (eii));
412cce40297SJonathan Adams 
413e0ad97e3SJonathan Adams 	eii.e_value = value;
414e0ad97e3SJonathan Adams 	eii.e_buf = prefix;
415e0ad97e3SJonathan Adams 	eii.e_size = sizeof (prefix);
416e0ad97e3SJonathan Adams 	eii.e_allprefix = allprefix;
417cce40297SJonathan Adams 
418e0ad97e3SJonathan Adams 	prefix[0] = 0;
419e0ad97e3SJonathan Adams 	if (mdb_ctf_enum_iter(id, enum_prefix_scan_cb, prefix) == 0)
420e0ad97e3SJonathan Adams 		eii.e_prefix = strlen(prefix);
421cce40297SJonathan Adams 
422e0ad97e3SJonathan Adams 	if (mdb_ctf_enum_iter(id, enum_p2_cb, &eii) != 0 || eii.e_bits == 0)
423cce40297SJonathan Adams 		return (-1);
424cce40297SJonathan Adams 
425e0ad97e3SJonathan Adams 	missed = (value & ~(intmax_t)eii.e_bits);
426cce40297SJonathan Adams 
427e0ad97e3SJonathan Adams 	if (eii.e_found) {
428e0ad97e3SJonathan Adams 		/* push out any final value, with a | if we missed anything */
429e0ad97e3SJonathan Adams 		if (!eii.e_first)
430e0ad97e3SJonathan Adams 			(void) strlcat(prefix, "}", sizeof (prefix));
431e0ad97e3SJonathan Adams 		if (missed != 0)
432e0ad97e3SJonathan Adams 			(void) strlcat(prefix, "|", sizeof (prefix));
433cce40297SJonathan Adams 
434e0ad97e3SJonathan Adams 		mdb_printf("%s", prefix);
435e0ad97e3SJonathan Adams 	}
436e0ad97e3SJonathan Adams 
437e0ad97e3SJonathan Adams 	if (!eii.e_found || missed) {
438e0ad97e3SJonathan Adams 		mdb_printf("%#llx", missed);
439cce40297SJonathan Adams 	}
440cce40297SJonathan Adams 
441cce40297SJonathan Adams 	return (0);
442cce40297SJonathan Adams }
443cce40297SJonathan Adams 
4447c478bd9Sstevel@tonic-gate struct enum_cbinfo {
4457c478bd9Sstevel@tonic-gate 	uint_t		e_flags;
4467c478bd9Sstevel@tonic-gate 	const char	*e_string;	/* NULL for value searches */
447e0ad97e3SJonathan Adams 	size_t		e_prefix;
448e0ad97e3SJonathan Adams 	intmax_t	e_value;
4497c478bd9Sstevel@tonic-gate 	uint_t		e_found;
450e0ad97e3SJonathan Adams 	mdb_ctf_id_t	e_id;
4517c478bd9Sstevel@tonic-gate };
452e0ad97e3SJonathan Adams #define	E_PRETTY		0x01
453e0ad97e3SJonathan Adams #define	E_HEX			0x02
454e0ad97e3SJonathan Adams #define	E_SEARCH_STRING		0x04
455e0ad97e3SJonathan Adams #define	E_SEARCH_VALUE		0x08
456e0ad97e3SJonathan Adams #define	E_ELIDE_PREFIX		0x10
4577c478bd9Sstevel@tonic-gate 
458cce40297SJonathan Adams static void
459cce40297SJonathan Adams enum_print(struct enum_cbinfo *info, const char *name, int value)
460cce40297SJonathan Adams {
461cce40297SJonathan Adams 	uint_t flags = info->e_flags;
462e0ad97e3SJonathan Adams 	uint_t elide_prefix = (info->e_flags & E_ELIDE_PREFIX);
463e0ad97e3SJonathan Adams 
464e0ad97e3SJonathan Adams 	if (name != NULL && info->e_prefix && strlen(name) > info->e_prefix)
465e0ad97e3SJonathan Adams 		name += info->e_prefix;
466cce40297SJonathan Adams 
467cce40297SJonathan Adams 	if (flags & E_PRETTY) {
468e0ad97e3SJonathan Adams 		uint_t indent = 5 + ((flags & E_HEX) ? 8 : 11);
469e0ad97e3SJonathan Adams 
470e0ad97e3SJonathan Adams 		mdb_printf((flags & E_HEX)? "%8x " : "%11d ", value);
471e0ad97e3SJonathan Adams 		(void) mdb_inc_indent(indent);
472e0ad97e3SJonathan Adams 		if (name != NULL) {
473e0ad97e3SJonathan Adams 			mdb_iob_puts(mdb.m_out, name);
474e0ad97e3SJonathan Adams 		} else {
475e0ad97e3SJonathan Adams 			(void) enum_value_print_p2(info->e_id, value,
476e0ad97e3SJonathan Adams 			    elide_prefix);
477e0ad97e3SJonathan Adams 		}
478e0ad97e3SJonathan Adams 		(void) mdb_dec_indent(indent);
479e0ad97e3SJonathan Adams 		mdb_printf("\n");
480cce40297SJonathan Adams 	} else {
481cce40297SJonathan Adams 		mdb_printf("%#r\n", value);
482cce40297SJonathan Adams 	}
483cce40297SJonathan Adams }
484cce40297SJonathan Adams 
4857c478bd9Sstevel@tonic-gate static int
4867c478bd9Sstevel@tonic-gate enum_cb(const char *name, int value, void *arg)
4877c478bd9Sstevel@tonic-gate {
4887c478bd9Sstevel@tonic-gate 	struct enum_cbinfo *info = arg;
4897c478bd9Sstevel@tonic-gate 	uint_t flags = info->e_flags;
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	if (flags & E_SEARCH_STRING) {
4927c478bd9Sstevel@tonic-gate 		if (strcmp(name, info->e_string) != 0)
4937c478bd9Sstevel@tonic-gate 			return (0);
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 	} else if (flags & E_SEARCH_VALUE) {
4967c478bd9Sstevel@tonic-gate 		if (value != info->e_value)
4977c478bd9Sstevel@tonic-gate 			return (0);
4987c478bd9Sstevel@tonic-gate 	}
4997c478bd9Sstevel@tonic-gate 
500cce40297SJonathan Adams 	enum_print(info, name, value);
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate 	info->e_found = 1;
5037c478bd9Sstevel@tonic-gate 	return (0);
5047c478bd9Sstevel@tonic-gate }
5057c478bd9Sstevel@tonic-gate 
506e0ad97e3SJonathan Adams void
507e0ad97e3SJonathan Adams enum_help(void)
508e0ad97e3SJonathan Adams {
509e0ad97e3SJonathan Adams 	mdb_printf("%s",
510e0ad97e3SJonathan Adams "Without an address and name, print all values for the enumeration \"enum\".\n"
511e0ad97e3SJonathan Adams "With an address, look up a particular value in \"enum\".  With a name, look\n"
512e0ad97e3SJonathan Adams "up a particular name in \"enum\".\n");
513e0ad97e3SJonathan Adams 
514e0ad97e3SJonathan Adams 	(void) mdb_dec_indent(2);
515e0ad97e3SJonathan Adams 	mdb_printf("\n%<b>OPTIONS%</b>\n");
516e0ad97e3SJonathan Adams 	(void) mdb_inc_indent(2);
517e0ad97e3SJonathan Adams 
518e0ad97e3SJonathan Adams 	mdb_printf("%s",
519e0ad97e3SJonathan Adams "   -e    remove common prefixes from enum names\n"
520e0ad97e3SJonathan Adams "   -x    report enum values in hexadecimal\n");
521e0ad97e3SJonathan Adams }
522e0ad97e3SJonathan Adams 
5237c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5247c478bd9Sstevel@tonic-gate int
5257c478bd9Sstevel@tonic-gate cmd_enum(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
5267c478bd9Sstevel@tonic-gate {
5277c478bd9Sstevel@tonic-gate 	struct enum_cbinfo info;
5287c478bd9Sstevel@tonic-gate 
529cce40297SJonathan Adams 	char type[MDB_SYM_NAMLEN + sizeof ("enum ")];
5307c478bd9Sstevel@tonic-gate 	char tn2[MDB_SYM_NAMLEN + sizeof ("enum ")];
531e0ad97e3SJonathan Adams 	char prefix[MDB_SYM_NAMLEN];
5327c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
5337c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t idr;
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate 	int i;
536*24537d3eSToomas Soome 	intmax_t search = 0;
537e0ad97e3SJonathan Adams 	uint_t isp2;
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 	info.e_flags = (flags & DCMD_PIPE_OUT)? 0 : E_PRETTY;
5407c478bd9Sstevel@tonic-gate 	info.e_string = NULL;
5417c478bd9Sstevel@tonic-gate 	info.e_value = 0;
5427c478bd9Sstevel@tonic-gate 	info.e_found = 0;
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	i = mdb_getopts(argc, argv,
545e0ad97e3SJonathan Adams 	    'e', MDB_OPT_SETBITS, E_ELIDE_PREFIX, &info.e_flags,
5467c478bd9Sstevel@tonic-gate 	    'x', MDB_OPT_SETBITS, E_HEX, &info.e_flags,
5477c478bd9Sstevel@tonic-gate 	    NULL);
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	argc -= i;
5507c478bd9Sstevel@tonic-gate 	argv += i;
5517c478bd9Sstevel@tonic-gate 
552cce40297SJonathan Adams 	if ((i = args_to_typename(&argc, &argv, type, MDB_SYM_NAMLEN)) != 0)
5537c478bd9Sstevel@tonic-gate 		return (i);
5547c478bd9Sstevel@tonic-gate 
555cce40297SJonathan Adams 	if (strchr(type, ' ') == NULL) {
5567c478bd9Sstevel@tonic-gate 		/*
5577c478bd9Sstevel@tonic-gate 		 * Check as an enumeration tag first, and fall back
5587c478bd9Sstevel@tonic-gate 		 * to checking for a typedef.  Yes, this means that
5597c478bd9Sstevel@tonic-gate 		 * anonymous enumerations whose typedefs conflict with
5607c478bd9Sstevel@tonic-gate 		 * an enum tag can't be accessed.  Don't do that.
5617c478bd9Sstevel@tonic-gate 		 */
562cce40297SJonathan Adams 		(void) mdb_snprintf(tn2, sizeof (tn2), "enum %s", type);
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 		if (mdb_ctf_lookup_by_name(tn2, &id) == 0) {
56580148899SSurya Prakki 			(void) strcpy(type, tn2);
566cce40297SJonathan Adams 		} else if (mdb_ctf_lookup_by_name(type, &id) != 0) {
567cce40297SJonathan Adams 			mdb_warn("types '%s', '%s'", tn2, type);
5687c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
5697c478bd9Sstevel@tonic-gate 		}
5707c478bd9Sstevel@tonic-gate 	} else {
571cce40297SJonathan Adams 		if (mdb_ctf_lookup_by_name(type, &id) != 0) {
572cce40297SJonathan Adams 			mdb_warn("'%s'", type);
5737c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
5747c478bd9Sstevel@tonic-gate 		}
5757c478bd9Sstevel@tonic-gate 	}
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 	/* resolve it, and make sure we're looking at an enumeration */
5787c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_resolve(id, &idr) == -1) {
5797c478bd9Sstevel@tonic-gate 		mdb_warn("unable to resolve '%s'", type);
5807c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
5817c478bd9Sstevel@tonic-gate 	}
5827c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_kind(idr) != CTF_K_ENUM) {
5837c478bd9Sstevel@tonic-gate 		mdb_warn("'%s': not an enumeration\n", type);
5847c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
5857c478bd9Sstevel@tonic-gate 	}
5867c478bd9Sstevel@tonic-gate 
587e0ad97e3SJonathan Adams 	info.e_id = idr;
588e0ad97e3SJonathan Adams 
5897c478bd9Sstevel@tonic-gate 	if (argc > 2)
5907c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 	if (argc == 2) {
5937c478bd9Sstevel@tonic-gate 		if (flags & DCMD_ADDRSPEC) {
5947c478bd9Sstevel@tonic-gate 			mdb_warn("may only specify one of: name, address\n");
5957c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
5967c478bd9Sstevel@tonic-gate 		}
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 		if (argv[1].a_type == MDB_TYPE_STRING) {
5997c478bd9Sstevel@tonic-gate 			info.e_flags |= E_SEARCH_STRING;
6007c478bd9Sstevel@tonic-gate 			info.e_string = argv[1].a_un.a_str;
6017c478bd9Sstevel@tonic-gate 		} else if (argv[1].a_type == MDB_TYPE_IMMEDIATE) {
6027c478bd9Sstevel@tonic-gate 			info.e_flags |= E_SEARCH_VALUE;
6037c478bd9Sstevel@tonic-gate 			search = argv[1].a_un.a_val;
6047c478bd9Sstevel@tonic-gate 		} else {
6057c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
6067c478bd9Sstevel@tonic-gate 		}
6077c478bd9Sstevel@tonic-gate 	}
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC) {
6107c478bd9Sstevel@tonic-gate 		info.e_flags |= E_SEARCH_VALUE;
6117c478bd9Sstevel@tonic-gate 		search = mdb_get_dot();
6127c478bd9Sstevel@tonic-gate 	}
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	if (info.e_flags & E_SEARCH_VALUE) {
6157c478bd9Sstevel@tonic-gate 		if ((int)search != search) {
6167c478bd9Sstevel@tonic-gate 			mdb_warn("value '%lld' out of enumeration range\n",
6177c478bd9Sstevel@tonic-gate 			    search);
6187c478bd9Sstevel@tonic-gate 		}
6197c478bd9Sstevel@tonic-gate 		info.e_value = search;
6207c478bd9Sstevel@tonic-gate 	}
6217c478bd9Sstevel@tonic-gate 
622e0ad97e3SJonathan Adams 	isp2 = enum_is_p2(idr);
623e0ad97e3SJonathan Adams 	if (isp2)
624e0ad97e3SJonathan Adams 		info.e_flags |= E_HEX;
625e0ad97e3SJonathan Adams 
6267c478bd9Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags) && (info.e_flags & E_PRETTY)) {
6277c478bd9Sstevel@tonic-gate 		if (info.e_flags & E_HEX)
628e0ad97e3SJonathan Adams 			mdb_printf("%<u>%8s %-64s%</u>\n", "VALUE", "NAME");
6297c478bd9Sstevel@tonic-gate 		else
630e0ad97e3SJonathan Adams 			mdb_printf("%<u>%11s %-64s%</u>\n", "VALUE", "NAME");
6317c478bd9Sstevel@tonic-gate 	}
6327c478bd9Sstevel@tonic-gate 
633cce40297SJonathan Adams 	/* if the enum is a power-of-two one, process it that way */
634e0ad97e3SJonathan Adams 	if ((info.e_flags & E_SEARCH_VALUE) && isp2) {
635e0ad97e3SJonathan Adams 		enum_print(&info, NULL, info.e_value);
636cce40297SJonathan Adams 		return (DCMD_OK);
637cce40297SJonathan Adams 	}
638cce40297SJonathan Adams 
639e0ad97e3SJonathan Adams 	prefix[0] = 0;
640e0ad97e3SJonathan Adams 	if ((info.e_flags & E_ELIDE_PREFIX) &&
641e0ad97e3SJonathan Adams 	    mdb_ctf_enum_iter(id, enum_prefix_scan_cb, prefix) == 0)
642e0ad97e3SJonathan Adams 		info.e_prefix = strlen(prefix);
643e0ad97e3SJonathan Adams 
6447c478bd9Sstevel@tonic-gate 	if (mdb_ctf_enum_iter(idr, enum_cb, &info) == -1) {
6457c478bd9Sstevel@tonic-gate 		mdb_warn("cannot walk '%s' as enum", type);
6467c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
6477c478bd9Sstevel@tonic-gate 	}
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 	if (info.e_found == 0 &&
6507c478bd9Sstevel@tonic-gate 	    (info.e_flags & (E_SEARCH_STRING | E_SEARCH_VALUE)) != 0) {
6517c478bd9Sstevel@tonic-gate 		if (info.e_flags & E_SEARCH_STRING)
6527c478bd9Sstevel@tonic-gate 			mdb_warn("name \"%s\" not in '%s'\n", info.e_string,
6537c478bd9Sstevel@tonic-gate 			    type);
6547c478bd9Sstevel@tonic-gate 		else
655e0ad97e3SJonathan Adams 			mdb_warn("value %#lld not in '%s'\n", info.e_value,
656e0ad97e3SJonathan Adams 			    type);
6577c478bd9Sstevel@tonic-gate 
6587c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
6597c478bd9Sstevel@tonic-gate 	}
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
6627c478bd9Sstevel@tonic-gate }
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate static int
6657c478bd9Sstevel@tonic-gate setup_vcb(const char *name, uintptr_t addr)
6667c478bd9Sstevel@tonic-gate {
6677c478bd9Sstevel@tonic-gate 	const char *p;
6687c478bd9Sstevel@tonic-gate 	mdb_var_t *v;
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 	if ((v = mdb_nv_lookup(&mdb.m_nv, name)) == NULL) {
6717c478bd9Sstevel@tonic-gate 		if ((p = strbadid(name)) != NULL) {
6727c478bd9Sstevel@tonic-gate 			mdb_warn("'%c' may not be used in a variable "
6737c478bd9Sstevel@tonic-gate 			    "name\n", *p);
6747c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
6757c478bd9Sstevel@tonic-gate 		}
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 		if ((v = mdb_nv_insert(&mdb.m_nv, name, NULL, addr, 0)) == NULL)
6787c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
6797c478bd9Sstevel@tonic-gate 	} else {
6807c478bd9Sstevel@tonic-gate 		if (v->v_flags & MDB_NV_RDONLY) {
6817c478bd9Sstevel@tonic-gate 			mdb_warn("variable %s is read-only\n", name);
6827c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
6837c478bd9Sstevel@tonic-gate 		}
6847c478bd9Sstevel@tonic-gate 	}
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 	/*
6877c478bd9Sstevel@tonic-gate 	 * If there already exists a vcb for this variable, we may be
6887c478bd9Sstevel@tonic-gate 	 * calling the dcmd in a loop.  We only create a vcb for this
6897c478bd9Sstevel@tonic-gate 	 * variable on the first invocation.
6907c478bd9Sstevel@tonic-gate 	 */
6917c478bd9Sstevel@tonic-gate 	if (mdb_vcb_find(v, mdb.m_frame) == NULL)
6927c478bd9Sstevel@tonic-gate 		mdb_vcb_insert(mdb_vcb_create(v), mdb.m_frame);
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 	return (0);
6957c478bd9Sstevel@tonic-gate }
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6987c478bd9Sstevel@tonic-gate int
6997c478bd9Sstevel@tonic-gate cmd_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
7007c478bd9Sstevel@tonic-gate {
70128e4da25SMatthew Ahrens 	int offset;
7027c478bd9Sstevel@tonic-gate 	uintptr_t a, tmp;
7037c478bd9Sstevel@tonic-gate 	int ret;
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC) || argc == 0)
7067c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 	if (argv->a_type != MDB_TYPE_STRING) {
7097c478bd9Sstevel@tonic-gate 		/*
7107c478bd9Sstevel@tonic-gate 		 * We are being given a raw offset in lieu of a type and
71123edb310SMax Grossman 		 * member; confirm the number of arguments and argument
71223edb310SMax Grossman 		 * type.
7137c478bd9Sstevel@tonic-gate 		 */
71423edb310SMax Grossman 		if (argc != 1 || argv->a_type != MDB_TYPE_IMMEDIATE)
7157c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate 		offset = argv->a_un.a_val;
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 		argv++;
7207c478bd9Sstevel@tonic-gate 		argc--;
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 		if (offset % sizeof (uintptr_t)) {
7237c478bd9Sstevel@tonic-gate 			mdb_warn("offset must fall on a word boundary\n");
7247c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
7257c478bd9Sstevel@tonic-gate 		}
7267c478bd9Sstevel@tonic-gate 	} else {
7277c478bd9Sstevel@tonic-gate 		const char *member;
7287c478bd9Sstevel@tonic-gate 		char buf[MDB_SYM_NAMLEN];
7297c478bd9Sstevel@tonic-gate 		int ret;
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 		ret = args_to_typename(&argc, &argv, buf, sizeof (buf));
7327c478bd9Sstevel@tonic-gate 		if (ret != 0)
7337c478bd9Sstevel@tonic-gate 			return (ret);
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 		argv++;
7367c478bd9Sstevel@tonic-gate 		argc--;
7377c478bd9Sstevel@tonic-gate 
738b3eeeb30SCody Peter Mello 		/*
739b3eeeb30SCody Peter Mello 		 * If we make it here, we were provided a type name. We should
740b3eeeb30SCody Peter Mello 		 * only continue if we still have arguments left (e.g. member
741b3eeeb30SCody Peter Mello 		 * name and potentially a variable name).
742b3eeeb30SCody Peter Mello 		 */
743b3eeeb30SCody Peter Mello 		if (argc == 0)
744b3eeeb30SCody Peter Mello 			return (DCMD_USAGE);
745b3eeeb30SCody Peter Mello 
7467c478bd9Sstevel@tonic-gate 		member = argv->a_un.a_str;
74728e4da25SMatthew Ahrens 		offset = mdb_ctf_offsetof_by_name(buf, member);
74828e4da25SMatthew Ahrens 		if (offset == -1)
74928e4da25SMatthew Ahrens 			return (DCMD_ABORT);
7507c478bd9Sstevel@tonic-gate 
7517c478bd9Sstevel@tonic-gate 		argv++;
7527c478bd9Sstevel@tonic-gate 		argc--;
7537c478bd9Sstevel@tonic-gate 
75428e4da25SMatthew Ahrens 		if (offset % (sizeof (uintptr_t)) != 0) {
7557c478bd9Sstevel@tonic-gate 			mdb_warn("%s is not a word-aligned member\n", member);
7567c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
7577c478bd9Sstevel@tonic-gate 		}
7587c478bd9Sstevel@tonic-gate 	}
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate 	/*
7617c478bd9Sstevel@tonic-gate 	 * If we have any unchewed arguments, a variable name must be present.
7627c478bd9Sstevel@tonic-gate 	 */
7637c478bd9Sstevel@tonic-gate 	if (argc == 1) {
7647c478bd9Sstevel@tonic-gate 		if (argv->a_type != MDB_TYPE_STRING)
7657c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 		if ((ret = setup_vcb(argv->a_un.a_str, addr)) != 0)
7687c478bd9Sstevel@tonic-gate 			return (ret);
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 	} else if (argc != 0) {
7717c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
7727c478bd9Sstevel@tonic-gate 	}
7737c478bd9Sstevel@tonic-gate 
7747c478bd9Sstevel@tonic-gate 	a = addr;
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 	do {
7777c478bd9Sstevel@tonic-gate 		mdb_printf("%lr\n", a);
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 		if (mdb_vread(&tmp, sizeof (tmp), a + offset) == -1) {
7807c478bd9Sstevel@tonic-gate 			mdb_warn("failed to read next pointer from object %p",
7817c478bd9Sstevel@tonic-gate 			    a);
7827c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
7837c478bd9Sstevel@tonic-gate 		}
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate 		a = tmp;
786892ad162SToomas Soome 	} while (a != addr && a != 0);
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
7897c478bd9Sstevel@tonic-gate }
7907c478bd9Sstevel@tonic-gate 
7917c478bd9Sstevel@tonic-gate int
7927c478bd9Sstevel@tonic-gate cmd_array(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
7937c478bd9Sstevel@tonic-gate {
7947c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
7957c478bd9Sstevel@tonic-gate 	ssize_t elemsize = 0;
7967c478bd9Sstevel@tonic-gate 	char tn[MDB_SYM_NAMLEN];
7977c478bd9Sstevel@tonic-gate 	int ret, nelem = -1;
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 	mdb_tgt_t *t = mdb.m_target;
8007c478bd9Sstevel@tonic-gate 	GElf_Sym sym;
8017c478bd9Sstevel@tonic-gate 	mdb_ctf_arinfo_t ar;
8027c478bd9Sstevel@tonic-gate 	mdb_syminfo_t s_info;
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
8057c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 	if (argc >= 2) {
8087c478bd9Sstevel@tonic-gate 		ret = args_to_typename(&argc, &argv, tn, sizeof (tn));
8097c478bd9Sstevel@tonic-gate 		if (ret != 0)
8107c478bd9Sstevel@tonic-gate 			return (ret);
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 		if (argc == 1)	/* unquoted compound type without count */
8137c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate 		if (mdb_ctf_lookup_by_name(tn, &id) != 0) {
8167c478bd9Sstevel@tonic-gate 			mdb_warn("failed to look up type %s", tn);
8177c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
8187c478bd9Sstevel@tonic-gate 		}
8197c478bd9Sstevel@tonic-gate 
8207c478bd9Sstevel@tonic-gate 		if (argv[1].a_type == MDB_TYPE_IMMEDIATE)
8217c478bd9Sstevel@tonic-gate 			nelem = argv[1].a_un.a_val;
8227c478bd9Sstevel@tonic-gate 		else
8237c478bd9Sstevel@tonic-gate 			nelem = mdb_strtoull(argv[1].a_un.a_str);
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate 		elemsize = mdb_ctf_type_size(id);
8267c478bd9Sstevel@tonic-gate 	} else if (addr_to_sym(t, addr, tn, sizeof (tn), &sym, &s_info)
827cce40297SJonathan Adams 	    != NULL && mdb_ctf_lookup_by_symbol(&sym, &s_info, &id)
828cce40297SJonathan Adams 	    == 0 && mdb_ctf_type_kind(id) == CTF_K_ARRAY &&
829cce40297SJonathan Adams 	    mdb_ctf_array_info(id, &ar) != -1) {
830cce40297SJonathan Adams 		elemsize = mdb_ctf_type_size(id) / ar.mta_nelems;
831cce40297SJonathan Adams 		nelem = ar.mta_nelems;
8327c478bd9Sstevel@tonic-gate 	} else {
8337c478bd9Sstevel@tonic-gate 		mdb_warn("no symbol information for %a", addr);
8347c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
8357c478bd9Sstevel@tonic-gate 	}
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 	if (argc == 3 || argc == 1) {
8387c478bd9Sstevel@tonic-gate 		if (argv[argc - 1].a_type != MDB_TYPE_STRING)
8397c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
8407c478bd9Sstevel@tonic-gate 
8417c478bd9Sstevel@tonic-gate 		if ((ret = setup_vcb(argv[argc - 1].a_un.a_str, addr)) != 0)
8427c478bd9Sstevel@tonic-gate 			return (ret);
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 	} else if (argc > 3) {
8457c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
8467c478bd9Sstevel@tonic-gate 	}
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 	for (; nelem > 0; nelem--) {
8497c478bd9Sstevel@tonic-gate 		mdb_printf("%lr\n", addr);
8507c478bd9Sstevel@tonic-gate 		addr = addr + elemsize;
8517c478bd9Sstevel@tonic-gate 	}
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
8547c478bd9Sstevel@tonic-gate }
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate /*
8577c478bd9Sstevel@tonic-gate  * Print an integer bitfield in hexadecimal by reading the enclosing byte(s)
8587c478bd9Sstevel@tonic-gate  * and then shifting and masking the data in the lower bits of a uint64_t.
8597c478bd9Sstevel@tonic-gate  */
8607c478bd9Sstevel@tonic-gate static int
8617c478bd9Sstevel@tonic-gate print_bitfield(ulong_t off, printarg_t *pap, ctf_encoding_t *ep)
8627c478bd9Sstevel@tonic-gate {
8637c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
8647c478bd9Sstevel@tonic-gate 	size_t size = (ep->cte_bits + (NBBY - 1)) / NBBY;
8657c478bd9Sstevel@tonic-gate 	uint64_t mask = (1ULL << ep->cte_bits) - 1;
8667c478bd9Sstevel@tonic-gate 	uint64_t value = 0;
8677c478bd9Sstevel@tonic-gate 	uint8_t *buf = (uint8_t *)&value;
8687c478bd9Sstevel@tonic-gate 	uint8_t shift;
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 	const char *format;
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
8737c478bd9Sstevel@tonic-gate 		return (0);
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	if (ep->cte_bits > sizeof (value) * NBBY - 1) {
8767c478bd9Sstevel@tonic-gate 		mdb_printf("??? (invalid bitfield size %u)", ep->cte_bits);
8777c478bd9Sstevel@tonic-gate 		return (0);
8787c478bd9Sstevel@tonic-gate 	}
8797c478bd9Sstevel@tonic-gate 
8807c478bd9Sstevel@tonic-gate 	/*
8817c478bd9Sstevel@tonic-gate 	 * On big-endian machines, we need to adjust the buf pointer to refer
8827c478bd9Sstevel@tonic-gate 	 * to the lowest 'size' bytes in 'value', and we need shift based on
8837c478bd9Sstevel@tonic-gate 	 * the offset from the end of the data, not the offset of the start.
8847c478bd9Sstevel@tonic-gate 	 */
8857c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN
8867c478bd9Sstevel@tonic-gate 	buf += sizeof (value) - size;
8877c478bd9Sstevel@tonic-gate 	off += ep->cte_bits;
8887c478bd9Sstevel@tonic-gate #endif
8897c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, buf, size, addr) != size) {
8907c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %lu bytes at %llx",
8917c478bd9Sstevel@tonic-gate 		    (ulong_t)size, addr);
8927c478bd9Sstevel@tonic-gate 		return (1);
8937c478bd9Sstevel@tonic-gate 	}
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 	shift = off % NBBY;
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate 	/*
8987c478bd9Sstevel@tonic-gate 	 * Offsets are counted from opposite ends on little- and
8997c478bd9Sstevel@tonic-gate 	 * big-endian machines.
9007c478bd9Sstevel@tonic-gate 	 */
9017c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN
9027c478bd9Sstevel@tonic-gate 	shift = NBBY - shift;
9037c478bd9Sstevel@tonic-gate #endif
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 	/*
9067c478bd9Sstevel@tonic-gate 	 * If the bits we want do not begin on a byte boundary, shift the data
9077c478bd9Sstevel@tonic-gate 	 * right so that the value is in the lowest 'cte_bits' of 'value'.
9087c478bd9Sstevel@tonic-gate 	 */
9097c478bd9Sstevel@tonic-gate 	if (off % NBBY != 0)
9107c478bd9Sstevel@tonic-gate 		value >>= shift;
9117c478bd9Sstevel@tonic-gate 	value &= mask;
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 	/*
9147c478bd9Sstevel@tonic-gate 	 * We default to printing signed bitfields as decimals,
9157c478bd9Sstevel@tonic-gate 	 * and unsigned bitfields in hexadecimal.  If they specify
9167c478bd9Sstevel@tonic-gate 	 * hexadecimal, we treat the field as unsigned.
9177c478bd9Sstevel@tonic-gate 	 */
9187c478bd9Sstevel@tonic-gate 	if ((pap->pa_flags & PA_INTHEX) ||
9197c478bd9Sstevel@tonic-gate 	    !(ep->cte_format & CTF_INT_SIGNED)) {
9207c478bd9Sstevel@tonic-gate 		format = (pap->pa_flags & PA_INTDEC)? "%#llu" : "%#llx";
9217c478bd9Sstevel@tonic-gate 	} else {
9227c478bd9Sstevel@tonic-gate 		int sshift = sizeof (value) * NBBY - ep->cte_bits;
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 		/* sign-extend value, and print as a signed decimal */
9257c478bd9Sstevel@tonic-gate 		value = ((int64_t)value << sshift) >> sshift;
9267c478bd9Sstevel@tonic-gate 		format = "%#lld";
9277c478bd9Sstevel@tonic-gate 	}
9287c478bd9Sstevel@tonic-gate 	mdb_printf(format, value);
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 	return (0);
9317c478bd9Sstevel@tonic-gate }
9327c478bd9Sstevel@tonic-gate 
933f11c6b60SJohn Levon /*
934f11c6b60SJohn Levon  * We want to print an escaped char as e.g. '\0'. We don't use mdb_fmt_print()
935f11c6b60SJohn Levon  * as it won't get auto-wrap right here (although even now, we don't include any
936f11c6b60SJohn Levon  * trailing comma).
937f11c6b60SJohn Levon  */
938f11c6b60SJohn Levon static int
939f11c6b60SJohn Levon print_char_val(mdb_tgt_addr_t addr, printarg_t *pap)
940f11c6b60SJohn Levon {
941f11c6b60SJohn Levon 	char cval;
942f11c6b60SJohn Levon 	char *s;
943f11c6b60SJohn Levon 
944f11c6b60SJohn Levon 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &cval, 1, addr) != 1)
945f11c6b60SJohn Levon 		return (1);
946f11c6b60SJohn Levon 
947f11c6b60SJohn Levon 	if (mdb.m_flags & MDB_FL_ADB)
948f11c6b60SJohn Levon 		s = strchr2adb(&cval, 1);
949f11c6b60SJohn Levon 	else
950f11c6b60SJohn Levon 		s = strchr2esc(&cval, 1);
951f11c6b60SJohn Levon 
952f11c6b60SJohn Levon 	mdb_printf("'%s'", s);
953f11c6b60SJohn Levon 	strfree(s);
954f11c6b60SJohn Levon 	return (0);
955f11c6b60SJohn Levon }
956f11c6b60SJohn Levon 
9577c478bd9Sstevel@tonic-gate /*
9587c478bd9Sstevel@tonic-gate  * Print out a character or integer value.  We use some simple heuristics,
9597c478bd9Sstevel@tonic-gate  * described below, to determine the appropriate radix to use for output.
9607c478bd9Sstevel@tonic-gate  */
9617c478bd9Sstevel@tonic-gate static int
9627c478bd9Sstevel@tonic-gate print_int_val(const char *type, ctf_encoding_t *ep, ulong_t off,
9637c478bd9Sstevel@tonic-gate     printarg_t *pap)
9647c478bd9Sstevel@tonic-gate {
9657c478bd9Sstevel@tonic-gate 	static const char *const sformat[] = { "%#d", "%#d", "%#d", "%#lld" };
9667c478bd9Sstevel@tonic-gate 	static const char *const uformat[] = { "%#u", "%#u", "%#u", "%#llu" };
9677c478bd9Sstevel@tonic-gate 	static const char *const xformat[] = { "%#x", "%#x", "%#x", "%#llx" };
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
9707c478bd9Sstevel@tonic-gate 	const char *const *fsp;
9717c478bd9Sstevel@tonic-gate 	size_t size;
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate 	union {
9747c478bd9Sstevel@tonic-gate 		uint64_t i8;
9757c478bd9Sstevel@tonic-gate 		uint32_t i4;
9767c478bd9Sstevel@tonic-gate 		uint16_t i2;
9777c478bd9Sstevel@tonic-gate 		uint8_t i1;
9787c478bd9Sstevel@tonic-gate 		time_t t;
979838d7172SSebastien Roy 		ipaddr_t I;
9807c478bd9Sstevel@tonic-gate 	} u;
9817c478bd9Sstevel@tonic-gate 
9827c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
9837c478bd9Sstevel@tonic-gate 		return (0);
9847c478bd9Sstevel@tonic-gate 
9857c478bd9Sstevel@tonic-gate 	if (ep->cte_format & CTF_INT_VARARGS) {
9867c478bd9Sstevel@tonic-gate 		mdb_printf("...\n");
9877c478bd9Sstevel@tonic-gate 		return (0);
9887c478bd9Sstevel@tonic-gate 	}
9897c478bd9Sstevel@tonic-gate 
9907c478bd9Sstevel@tonic-gate 	/*
9917c478bd9Sstevel@tonic-gate 	 * If the size is not a power-of-two number of bytes in the range 1-8
9927c478bd9Sstevel@tonic-gate 	 * then we assume it is a bitfield and print it as such.
9937c478bd9Sstevel@tonic-gate 	 */
9947c478bd9Sstevel@tonic-gate 	size = ep->cte_bits / NBBY;
9957c478bd9Sstevel@tonic-gate 	if (size > 8 || (ep->cte_bits % NBBY) != 0 || (size & (size - 1)) != 0)
9967c478bd9Sstevel@tonic-gate 		return (print_bitfield(off, pap, ep));
9977c478bd9Sstevel@tonic-gate 
998f11c6b60SJohn Levon 	if (IS_CHAR(*ep))
999f11c6b60SJohn Levon 		return (print_char_val(addr, pap));
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.i8, size, addr) != size) {
10027c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %lu bytes at %llx",
10037c478bd9Sstevel@tonic-gate 		    (ulong_t)size, addr);
10047c478bd9Sstevel@tonic-gate 		return (1);
10057c478bd9Sstevel@tonic-gate 	}
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate 	/*
1008838d7172SSebastien Roy 	 * We pretty-print some integer based types.  time_t values are
1009838d7172SSebastien Roy 	 * printed as a calendar date and time, and IPv4 addresses as human
1010838d7172SSebastien Roy 	 * readable dotted quads.
10117c478bd9Sstevel@tonic-gate 	 */
1012838d7172SSebastien Roy 	if (!(pap->pa_flags & (PA_INTHEX | PA_INTDEC))) {
1013838d7172SSebastien Roy 		if (strcmp(type, "time_t") == 0 && u.t != 0) {
1014838d7172SSebastien Roy 			mdb_printf("%Y", u.t);
1015838d7172SSebastien Roy 			return (0);
1016838d7172SSebastien Roy 		}
1017838d7172SSebastien Roy 		if (strcmp(type, "ipaddr_t") == 0 ||
1018838d7172SSebastien Roy 		    strcmp(type, "in_addr_t") == 0) {
1019838d7172SSebastien Roy 			mdb_printf("%I", u.I);
1020838d7172SSebastien Roy 			return (0);
1021838d7172SSebastien Roy 		}
10227c478bd9Sstevel@tonic-gate 	}
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 	/*
10257c478bd9Sstevel@tonic-gate 	 * The default format is hexadecimal.
10267c478bd9Sstevel@tonic-gate 	 */
10277c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_INTDEC))
10287c478bd9Sstevel@tonic-gate 		fsp = xformat;
10297c478bd9Sstevel@tonic-gate 	else if (ep->cte_format & CTF_INT_SIGNED)
10307c478bd9Sstevel@tonic-gate 		fsp = sformat;
10317c478bd9Sstevel@tonic-gate 	else
10327c478bd9Sstevel@tonic-gate 		fsp = uformat;
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 	switch (size) {
10357c478bd9Sstevel@tonic-gate 	case sizeof (uint8_t):
10367c478bd9Sstevel@tonic-gate 		mdb_printf(fsp[0], u.i1);
10377c478bd9Sstevel@tonic-gate 		break;
10387c478bd9Sstevel@tonic-gate 	case sizeof (uint16_t):
10397c478bd9Sstevel@tonic-gate 		mdb_printf(fsp[1], u.i2);
10407c478bd9Sstevel@tonic-gate 		break;
10417c478bd9Sstevel@tonic-gate 	case sizeof (uint32_t):
10427c478bd9Sstevel@tonic-gate 		mdb_printf(fsp[2], u.i4);
10437c478bd9Sstevel@tonic-gate 		break;
10447c478bd9Sstevel@tonic-gate 	case sizeof (uint64_t):
10457c478bd9Sstevel@tonic-gate 		mdb_printf(fsp[3], u.i8);
10467c478bd9Sstevel@tonic-gate 		break;
10477c478bd9Sstevel@tonic-gate 	}
10487c478bd9Sstevel@tonic-gate 	return (0);
10497c478bd9Sstevel@tonic-gate }
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10527c478bd9Sstevel@tonic-gate static int
10537c478bd9Sstevel@tonic-gate print_int(const char *type, const char *name, mdb_ctf_id_t id,
10547c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
10557c478bd9Sstevel@tonic-gate {
10567c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
10597c478bd9Sstevel@tonic-gate 		return (0);
10607c478bd9Sstevel@tonic-gate 
10617c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_encoding(base, &e) != 0) {
10627c478bd9Sstevel@tonic-gate 		mdb_printf("??? (%s)", mdb_strerror(errno));
10637c478bd9Sstevel@tonic-gate 		return (0);
10647c478bd9Sstevel@tonic-gate 	}
10657c478bd9Sstevel@tonic-gate 
10667c478bd9Sstevel@tonic-gate 	return (print_int_val(type, &e, off, pap));
10677c478bd9Sstevel@tonic-gate }
10687c478bd9Sstevel@tonic-gate 
10697c478bd9Sstevel@tonic-gate /*
10707c478bd9Sstevel@tonic-gate  * Print out a floating point value.  We only provide support for floats in
10717c478bd9Sstevel@tonic-gate  * the ANSI-C float, double, and long double formats.
10727c478bd9Sstevel@tonic-gate  */
10737c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10747c478bd9Sstevel@tonic-gate static int
10757c478bd9Sstevel@tonic-gate print_float(const char *type, const char *name, mdb_ctf_id_t id,
10767c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
10777c478bd9Sstevel@tonic-gate {
10787c478bd9Sstevel@tonic-gate #ifndef _KMDB
10797c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
10807c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 	union {
10837c478bd9Sstevel@tonic-gate 		float f;
10847c478bd9Sstevel@tonic-gate 		double d;
10857c478bd9Sstevel@tonic-gate 		long double ld;
10867c478bd9Sstevel@tonic-gate 	} u;
10877c478bd9Sstevel@tonic-gate 
10887c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
10897c478bd9Sstevel@tonic-gate 		return (0);
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_encoding(base, &e) == 0) {
10927c478bd9Sstevel@tonic-gate 		if (e.cte_format == CTF_FP_SINGLE &&
10937c478bd9Sstevel@tonic-gate 		    e.cte_bits == sizeof (float) * NBBY) {
10947c478bd9Sstevel@tonic-gate 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.f,
10957c478bd9Sstevel@tonic-gate 			    sizeof (u.f), addr) != sizeof (u.f)) {
10967c478bd9Sstevel@tonic-gate 				mdb_warn("failed to read float at %llx", addr);
10977c478bd9Sstevel@tonic-gate 				return (1);
10987c478bd9Sstevel@tonic-gate 			}
10997c478bd9Sstevel@tonic-gate 			mdb_printf("%s", doubletos(u.f, 7, 'e'));
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate 		} else if (e.cte_format == CTF_FP_DOUBLE &&
11027c478bd9Sstevel@tonic-gate 		    e.cte_bits == sizeof (double) * NBBY) {
11037c478bd9Sstevel@tonic-gate 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.d,
11047c478bd9Sstevel@tonic-gate 			    sizeof (u.d), addr) != sizeof (u.d)) {
11057c478bd9Sstevel@tonic-gate 				mdb_warn("failed to read float at %llx", addr);
11067c478bd9Sstevel@tonic-gate 				return (1);
11077c478bd9Sstevel@tonic-gate 			}
11087c478bd9Sstevel@tonic-gate 			mdb_printf("%s", doubletos(u.d, 7, 'e'));
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate 		} else if (e.cte_format == CTF_FP_LDOUBLE &&
11117c478bd9Sstevel@tonic-gate 		    e.cte_bits == sizeof (long double) * NBBY) {
11127c478bd9Sstevel@tonic-gate 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.ld,
11137c478bd9Sstevel@tonic-gate 			    sizeof (u.ld), addr) != sizeof (u.ld)) {
11147c478bd9Sstevel@tonic-gate 				mdb_warn("failed to read float at %llx", addr);
11157c478bd9Sstevel@tonic-gate 				return (1);
11167c478bd9Sstevel@tonic-gate 			}
11177c478bd9Sstevel@tonic-gate 			mdb_printf("%s", longdoubletos(&u.ld, 16, 'e'));
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate 		} else {
11207c478bd9Sstevel@tonic-gate 			mdb_printf("??? (unsupported FP format %u / %u bits\n",
11217c478bd9Sstevel@tonic-gate 			    e.cte_format, e.cte_bits);
11227c478bd9Sstevel@tonic-gate 		}
11237c478bd9Sstevel@tonic-gate 	} else
11247c478bd9Sstevel@tonic-gate 		mdb_printf("??? (%s)", mdb_strerror(errno));
11257c478bd9Sstevel@tonic-gate #else
11267c478bd9Sstevel@tonic-gate 	mdb_printf("<FLOAT>");
11277c478bd9Sstevel@tonic-gate #endif
11287c478bd9Sstevel@tonic-gate 	return (0);
11297c478bd9Sstevel@tonic-gate }
11307c478bd9Sstevel@tonic-gate 
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate /*
11337c478bd9Sstevel@tonic-gate  * Print out a pointer value as a symbol name + offset or a hexadecimal value.
11347c478bd9Sstevel@tonic-gate  * If the pointer itself is a char *, we attempt to read a bit of the data
11357c478bd9Sstevel@tonic-gate  * referenced by the pointer and display it if it is a printable ASCII string.
11367c478bd9Sstevel@tonic-gate  */
11377c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11387c478bd9Sstevel@tonic-gate static int
11397c478bd9Sstevel@tonic-gate print_ptr(const char *type, const char *name, mdb_ctf_id_t id,
11407c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
11417c478bd9Sstevel@tonic-gate {
11427c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
11437c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
11447c478bd9Sstevel@tonic-gate 	uintptr_t value;
11457c478bd9Sstevel@tonic-gate 	char buf[256];
11467c478bd9Sstevel@tonic-gate 	ssize_t len;
11477c478bd9Sstevel@tonic-gate 
11487c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
11497c478bd9Sstevel@tonic-gate 		return (0);
11507c478bd9Sstevel@tonic-gate 
11517c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as,
11527c478bd9Sstevel@tonic-gate 	    &value, sizeof (value), addr) != sizeof (value)) {
11537c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %s pointer at %llx", name, addr);
11547c478bd9Sstevel@tonic-gate 		return (1);
11557c478bd9Sstevel@tonic-gate 	}
11567c478bd9Sstevel@tonic-gate 
11577c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_NOSYMBOLIC) {
11587c478bd9Sstevel@tonic-gate 		mdb_printf("%#lx", value);
11597c478bd9Sstevel@tonic-gate 		return (0);
11607c478bd9Sstevel@tonic-gate 	}
11617c478bd9Sstevel@tonic-gate 
11627c478bd9Sstevel@tonic-gate 	mdb_printf("%a", value);
11637c478bd9Sstevel@tonic-gate 
1164892ad162SToomas Soome 	if (value == 0 || strcmp(type, "caddr_t") == 0)
11657c478bd9Sstevel@tonic-gate 		return (0);
11667c478bd9Sstevel@tonic-gate 
11677c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_kind(base) == CTF_K_POINTER &&
11687c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_reference(base, &base) != -1 &&
11697c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_resolve(base, &base) != -1 &&
11707c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_encoding(base, &e) == 0 && IS_CHAR(e)) {
11717c478bd9Sstevel@tonic-gate 		if ((len = mdb_tgt_readstr(pap->pa_realtgt, pap->pa_as,
11727c478bd9Sstevel@tonic-gate 		    buf, sizeof (buf), value)) >= 0 && strisprint(buf)) {
11737c478bd9Sstevel@tonic-gate 			if (len == sizeof (buf))
11747c478bd9Sstevel@tonic-gate 				(void) strabbr(buf, sizeof (buf));
11757c478bd9Sstevel@tonic-gate 			mdb_printf(" \"%s\"", buf);
11767c478bd9Sstevel@tonic-gate 		}
11777c478bd9Sstevel@tonic-gate 	}
11787c478bd9Sstevel@tonic-gate 
11797c478bd9Sstevel@tonic-gate 	return (0);
11807c478bd9Sstevel@tonic-gate }
11817c478bd9Sstevel@tonic-gate 
11827c478bd9Sstevel@tonic-gate 
11837c478bd9Sstevel@tonic-gate /*
11847c478bd9Sstevel@tonic-gate  * Print out a fixed-size array.  We special-case arrays of characters
11857c478bd9Sstevel@tonic-gate  * and attempt to print them out as ASCII strings if possible.  For other
11867c478bd9Sstevel@tonic-gate  * arrays, we iterate over a maximum of pa_armemlim members and call
11877c478bd9Sstevel@tonic-gate  * mdb_ctf_type_visit() again on each element to print its value.
11887c478bd9Sstevel@tonic-gate  */
11897c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11907c478bd9Sstevel@tonic-gate static int
11917c478bd9Sstevel@tonic-gate print_array(const char *type, const char *name, mdb_ctf_id_t id,
11927c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
11937c478bd9Sstevel@tonic-gate {
11947c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
11957c478bd9Sstevel@tonic-gate 	printarg_t pa = *pap;
11967c478bd9Sstevel@tonic-gate 	ssize_t eltsize;
11977c478bd9Sstevel@tonic-gate 	mdb_ctf_arinfo_t r;
11987c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
11997c478bd9Sstevel@tonic-gate 	uint_t i, kind, limit;
12007c478bd9Sstevel@tonic-gate 	int d, sou;
12017c478bd9Sstevel@tonic-gate 	char buf[8];
12027c478bd9Sstevel@tonic-gate 	char *str;
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
12057c478bd9Sstevel@tonic-gate 		return (0);
12067c478bd9Sstevel@tonic-gate 
1207bf5197d8Sjwadams 	if (pap->pa_depth == pap->pa_maxdepth) {
1208bf5197d8Sjwadams 		mdb_printf("[ ... ]");
1209bf5197d8Sjwadams 		return (0);
1210bf5197d8Sjwadams 	}
1211bf5197d8Sjwadams 
12127c478bd9Sstevel@tonic-gate 	/*
12137c478bd9Sstevel@tonic-gate 	 * Determine the base type and size of the array's content.  If this
12147c478bd9Sstevel@tonic-gate 	 * fails, we cannot print anything and just give up.
12157c478bd9Sstevel@tonic-gate 	 */
12167c478bd9Sstevel@tonic-gate 	if (mdb_ctf_array_info(base, &r) == -1 ||
12177c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_resolve(r.mta_contents, &base) == -1 ||
12187c478bd9Sstevel@tonic-gate 	    (eltsize = mdb_ctf_type_size(base)) == -1) {
12197c478bd9Sstevel@tonic-gate 		mdb_printf("[ ??? ] (%s)", mdb_strerror(errno));
12207c478bd9Sstevel@tonic-gate 		return (0);
12217c478bd9Sstevel@tonic-gate 	}
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 	/*
12247c478bd9Sstevel@tonic-gate 	 * Read a few bytes and determine if the content appears to be
12257c478bd9Sstevel@tonic-gate 	 * printable ASCII characters.  If so, read the entire array and
12267c478bd9Sstevel@tonic-gate 	 * attempt to display it as a string if it is printable.
12277c478bd9Sstevel@tonic-gate 	 */
12287c478bd9Sstevel@tonic-gate 	if ((pap->pa_arstrlim == MDB_ARR_NOLIMIT ||
12297c478bd9Sstevel@tonic-gate 	    r.mta_nelems <= pap->pa_arstrlim) &&
12307c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_encoding(base, &e) == 0 && IS_CHAR(e) &&
12317c478bd9Sstevel@tonic-gate 	    mdb_tgt_readstr(pap->pa_tgt, pap->pa_as, buf,
12327c478bd9Sstevel@tonic-gate 	    MIN(sizeof (buf), r.mta_nelems), addr) > 0 && strisprint(buf)) {
12337c478bd9Sstevel@tonic-gate 
12347c478bd9Sstevel@tonic-gate 		str = mdb_alloc(r.mta_nelems + 1, UM_SLEEP | UM_GC);
12357c478bd9Sstevel@tonic-gate 		str[r.mta_nelems] = '\0';
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate 		if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, str,
12387c478bd9Sstevel@tonic-gate 		    r.mta_nelems, addr) != r.mta_nelems) {
12397c478bd9Sstevel@tonic-gate 			mdb_warn("failed to read char array at %llx", addr);
12407c478bd9Sstevel@tonic-gate 			return (1);
12417c478bd9Sstevel@tonic-gate 		}
12427c478bd9Sstevel@tonic-gate 
12437c478bd9Sstevel@tonic-gate 		if (strisprint(str)) {
12447c478bd9Sstevel@tonic-gate 			mdb_printf("[ \"%s\" ]", str);
12457c478bd9Sstevel@tonic-gate 			return (0);
12467c478bd9Sstevel@tonic-gate 		}
12477c478bd9Sstevel@tonic-gate 	}
12487c478bd9Sstevel@tonic-gate 
12497c478bd9Sstevel@tonic-gate 	if (pap->pa_armemlim != MDB_ARR_NOLIMIT)
12507c478bd9Sstevel@tonic-gate 		limit = MIN(r.mta_nelems, pap->pa_armemlim);
12517c478bd9Sstevel@tonic-gate 	else
12527c478bd9Sstevel@tonic-gate 		limit = r.mta_nelems;
12537c478bd9Sstevel@tonic-gate 
12547c478bd9Sstevel@tonic-gate 	if (limit == 0) {
12557c478bd9Sstevel@tonic-gate 		mdb_printf("[ ... ]");
12567c478bd9Sstevel@tonic-gate 		return (0);
12577c478bd9Sstevel@tonic-gate 	}
12587c478bd9Sstevel@tonic-gate 
12597c478bd9Sstevel@tonic-gate 	kind = mdb_ctf_type_kind(base);
12607c478bd9Sstevel@tonic-gate 	sou = IS_COMPOSITE(kind);
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate 	pa.pa_addr = addr;		/* set base address to start of array */
1263cce40297SJonathan Adams 	pa.pa_maxdepth = pa.pa_maxdepth - pa.pa_depth - 1;
12647c478bd9Sstevel@tonic-gate 	pa.pa_nest += pa.pa_depth + 1;	/* nesting level is current depth + 1 */
12657c478bd9Sstevel@tonic-gate 	pa.pa_depth = 0;		/* reset depth to 0 for new scope */
12667c478bd9Sstevel@tonic-gate 	pa.pa_prefix = NULL;
12677c478bd9Sstevel@tonic-gate 
12687c478bd9Sstevel@tonic-gate 	if (sou) {
12697c478bd9Sstevel@tonic-gate 		pa.pa_delim = "\n";
12707c478bd9Sstevel@tonic-gate 		mdb_printf("[\n");
12717c478bd9Sstevel@tonic-gate 	} else {
12727c478bd9Sstevel@tonic-gate 		pa.pa_flags &= ~(PA_SHOWTYPE | PA_SHOWNAME | PA_SHOWADDR);
12737c478bd9Sstevel@tonic-gate 		pa.pa_delim = ", ";
12747c478bd9Sstevel@tonic-gate 		mdb_printf("[ ");
12757c478bd9Sstevel@tonic-gate 	}
12767c478bd9Sstevel@tonic-gate 
12777c478bd9Sstevel@tonic-gate 	for (i = 0; i < limit; i++, pa.pa_addr += eltsize) {
12787c478bd9Sstevel@tonic-gate 		if (i == limit - 1 && !sou) {
12797c478bd9Sstevel@tonic-gate 			if (limit < r.mta_nelems)
12807c478bd9Sstevel@tonic-gate 				pa.pa_delim = ", ... ]";
12817c478bd9Sstevel@tonic-gate 			else
12827c478bd9Sstevel@tonic-gate 				pa.pa_delim = " ]";
12837c478bd9Sstevel@tonic-gate 		}
12847c478bd9Sstevel@tonic-gate 
12857c478bd9Sstevel@tonic-gate 		if (mdb_ctf_type_visit(r.mta_contents, elt_print, &pa) == -1) {
12867c478bd9Sstevel@tonic-gate 			mdb_warn("failed to print array data");
12877c478bd9Sstevel@tonic-gate 			return (1);
12887c478bd9Sstevel@tonic-gate 		}
12897c478bd9Sstevel@tonic-gate 	}
12907c478bd9Sstevel@tonic-gate 
12917c478bd9Sstevel@tonic-gate 	if (sou) {
12927c478bd9Sstevel@tonic-gate 		for (d = pa.pa_depth - 1; d >= 0; d--)
12937c478bd9Sstevel@tonic-gate 			print_close_sou(&pa, d);
12947c478bd9Sstevel@tonic-gate 
12957c478bd9Sstevel@tonic-gate 		if (limit < r.mta_nelems) {
12967c478bd9Sstevel@tonic-gate 			mdb_printf("%*s... ]",
12977c478bd9Sstevel@tonic-gate 			    (pap->pa_depth + pap->pa_nest) * pap->pa_tab, "");
12987c478bd9Sstevel@tonic-gate 		} else {
12997c478bd9Sstevel@tonic-gate 			mdb_printf("%*s]",
13007c478bd9Sstevel@tonic-gate 			    (pap->pa_depth + pap->pa_nest) * pap->pa_tab, "");
13017c478bd9Sstevel@tonic-gate 		}
13027c478bd9Sstevel@tonic-gate 	}
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate 	/* copy the hole array info, since it may have been grown */
13057c478bd9Sstevel@tonic-gate 	pap->pa_holes = pa.pa_holes;
13067c478bd9Sstevel@tonic-gate 	pap->pa_nholes = pa.pa_nholes;
13077c478bd9Sstevel@tonic-gate 
13087c478bd9Sstevel@tonic-gate 	return (0);
13097c478bd9Sstevel@tonic-gate }
13107c478bd9Sstevel@tonic-gate 
13117c478bd9Sstevel@tonic-gate /*
13127c478bd9Sstevel@tonic-gate  * Print out a struct or union header.  We need only print the open brace
13137c478bd9Sstevel@tonic-gate  * because mdb_ctf_type_visit() itself will automatically recurse through
13147c478bd9Sstevel@tonic-gate  * all members of the given struct or union.
13157c478bd9Sstevel@tonic-gate  */
13167c478bd9Sstevel@tonic-gate /*ARGSUSED*/
13177c478bd9Sstevel@tonic-gate static int
13187c478bd9Sstevel@tonic-gate print_sou(const char *type, const char *name, mdb_ctf_id_t id,
13197c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
13207c478bd9Sstevel@tonic-gate {
1321838d7172SSebastien Roy 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
1322838d7172SSebastien Roy 
1323838d7172SSebastien Roy 	/*
1324838d7172SSebastien Roy 	 * We have pretty-printing for some structures where displaying
1325838d7172SSebastien Roy 	 * structure contents has no value.
1326838d7172SSebastien Roy 	 */
1327838d7172SSebastien Roy 	if (pap->pa_flags & PA_SHOWVAL) {
1328838d7172SSebastien Roy 		if (strcmp(type, "in6_addr_t") == 0 ||
1329838d7172SSebastien Roy 		    strcmp(type, "struct in6_addr") == 0) {
1330838d7172SSebastien Roy 			in6_addr_t in6addr;
1331838d7172SSebastien Roy 
1332838d7172SSebastien Roy 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &in6addr,
1333838d7172SSebastien Roy 			    sizeof (in6addr), addr) != sizeof (in6addr)) {
1334838d7172SSebastien Roy 				mdb_warn("failed to read %s pointer at %llx",
1335838d7172SSebastien Roy 				    name, addr);
1336838d7172SSebastien Roy 				return (1);
1337838d7172SSebastien Roy 			}
1338838d7172SSebastien Roy 			mdb_printf("%N", &in6addr);
1339838d7172SSebastien Roy 			/*
1340838d7172SSebastien Roy 			 * Don't print anything further down in the
1341838d7172SSebastien Roy 			 * structure.
1342838d7172SSebastien Roy 			 */
1343838d7172SSebastien Roy 			pap->pa_nooutdepth = pap->pa_depth;
1344838d7172SSebastien Roy 			return (0);
1345838d7172SSebastien Roy 		}
1346838d7172SSebastien Roy 		if (strcmp(type, "struct in_addr") == 0) {
1347838d7172SSebastien Roy 			in_addr_t inaddr;
1348838d7172SSebastien Roy 
1349838d7172SSebastien Roy 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &inaddr,
1350838d7172SSebastien Roy 			    sizeof (inaddr), addr) != sizeof (inaddr)) {
1351838d7172SSebastien Roy 				mdb_warn("failed to read %s pointer at %llx",
1352838d7172SSebastien Roy 				    name, addr);
1353838d7172SSebastien Roy 				return (1);
1354838d7172SSebastien Roy 			}
1355838d7172SSebastien Roy 			mdb_printf("%I", inaddr);
1356838d7172SSebastien Roy 			pap->pa_nooutdepth = pap->pa_depth;
1357838d7172SSebastien Roy 			return (0);
1358838d7172SSebastien Roy 		}
1359838d7172SSebastien Roy 	}
1360838d7172SSebastien Roy 
1361bf5197d8Sjwadams 	if (pap->pa_depth == pap->pa_maxdepth)
1362bf5197d8Sjwadams 		mdb_printf("{ ... }");
1363bf5197d8Sjwadams 	else
1364bf5197d8Sjwadams 		mdb_printf("{");
13657c478bd9Sstevel@tonic-gate 	pap->pa_delim = "\n";
13667c478bd9Sstevel@tonic-gate 	return (0);
13677c478bd9Sstevel@tonic-gate }
13687c478bd9Sstevel@tonic-gate 
13697c478bd9Sstevel@tonic-gate /*
13707c478bd9Sstevel@tonic-gate  * Print an enum value.  We attempt to convert the value to the corresponding
13717c478bd9Sstevel@tonic-gate  * enum name and print that if possible.
13727c478bd9Sstevel@tonic-gate  */
13737c478bd9Sstevel@tonic-gate /*ARGSUSED*/
13747c478bd9Sstevel@tonic-gate static int
13757c478bd9Sstevel@tonic-gate print_enum(const char *type, const char *name, mdb_ctf_id_t id,
13767c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
13777c478bd9Sstevel@tonic-gate {
13787c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
13797c478bd9Sstevel@tonic-gate 	const char *ename;
13807c478bd9Sstevel@tonic-gate 	int value;
1381e0ad97e3SJonathan Adams 	int isp2 = enum_is_p2(base);
1382e0ad97e3SJonathan Adams 	int flags = pap->pa_flags | (isp2 ? PA_INTHEX : 0);
13837c478bd9Sstevel@tonic-gate 
1384e0ad97e3SJonathan Adams 	if (!(flags & PA_SHOWVAL))
13857c478bd9Sstevel@tonic-gate 		return (0);
13867c478bd9Sstevel@tonic-gate 
13877c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as,
13887c478bd9Sstevel@tonic-gate 	    &value, sizeof (value), addr) != sizeof (value)) {
13897c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %s integer at %llx", name, addr);
13907c478bd9Sstevel@tonic-gate 		return (1);
13917c478bd9Sstevel@tonic-gate 	}
13927c478bd9Sstevel@tonic-gate 
1393e0ad97e3SJonathan Adams 	if (flags & PA_INTHEX)
13947c478bd9Sstevel@tonic-gate 		mdb_printf("%#x", value);
13957c478bd9Sstevel@tonic-gate 	else
13967c478bd9Sstevel@tonic-gate 		mdb_printf("%#d", value);
13977c478bd9Sstevel@tonic-gate 
1398e0ad97e3SJonathan Adams 	(void) mdb_inc_indent(8);
1399e0ad97e3SJonathan Adams 	mdb_printf(" (");
1400cce40297SJonathan Adams 
1401e0ad97e3SJonathan Adams 	if (!isp2 || enum_value_print_p2(base, value, 0) != 0) {
1402e0ad97e3SJonathan Adams 		ename = mdb_ctf_enum_name(base, value);
1403e0ad97e3SJonathan Adams 		if (ename == NULL) {
1404e0ad97e3SJonathan Adams 			ename = "???";
1405e0ad97e3SJonathan Adams 		}
1406e0ad97e3SJonathan Adams 		mdb_printf("%s", ename);
1407e0ad97e3SJonathan Adams 	}
1408e0ad97e3SJonathan Adams 	mdb_printf(")");
1409e0ad97e3SJonathan Adams 	(void) mdb_dec_indent(8);
14107c478bd9Sstevel@tonic-gate 
14117c478bd9Sstevel@tonic-gate 	return (0);
14127c478bd9Sstevel@tonic-gate }
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate /*
1415cce40297SJonathan Adams  * This will only get called if the structure isn't found in any available CTF
1416cce40297SJonathan Adams  * data.
14177c478bd9Sstevel@tonic-gate  */
14187c478bd9Sstevel@tonic-gate /*ARGSUSED*/
14197c478bd9Sstevel@tonic-gate static int
14207c478bd9Sstevel@tonic-gate print_tag(const char *type, const char *name, mdb_ctf_id_t id,
14217c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
14227c478bd9Sstevel@tonic-gate {
1423cce40297SJonathan Adams 	char basename[MDB_SYM_NAMLEN];
1424cce40297SJonathan Adams 
14257c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWVAL)
14267c478bd9Sstevel@tonic-gate 		mdb_printf("; ");
14277c478bd9Sstevel@tonic-gate 
1428cce40297SJonathan Adams 	if (mdb_ctf_type_name(base, basename, sizeof (basename)) != NULL)
1429cce40297SJonathan Adams 		mdb_printf("<forward declaration of %s>", basename);
1430cce40297SJonathan Adams 	else
1431cce40297SJonathan Adams 		mdb_printf("<forward declaration of unknown type>");
1432cce40297SJonathan Adams 
14337c478bd9Sstevel@tonic-gate 	return (0);
14347c478bd9Sstevel@tonic-gate }
14357c478bd9Sstevel@tonic-gate 
14367c478bd9Sstevel@tonic-gate static void
14377c478bd9Sstevel@tonic-gate print_hole(printarg_t *pap, int depth, ulong_t off, ulong_t endoff)
14387c478bd9Sstevel@tonic-gate {
14397c478bd9Sstevel@tonic-gate 	ulong_t bits = endoff - off;
14407c478bd9Sstevel@tonic-gate 	ulong_t size = bits / NBBY;
14417c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
14427c478bd9Sstevel@tonic-gate 
14437c478bd9Sstevel@tonic-gate 	static const char *const name = "<<HOLE>>";
14447c478bd9Sstevel@tonic-gate 	char type[MDB_SYM_NAMLEN];
14457c478bd9Sstevel@tonic-gate 
14467c478bd9Sstevel@tonic-gate 	int bitfield =
14477c478bd9Sstevel@tonic-gate 	    (off % NBBY != 0 ||
14487c478bd9Sstevel@tonic-gate 	    bits % NBBY != 0 ||
14497c478bd9Sstevel@tonic-gate 	    size > 8 ||
14507c478bd9Sstevel@tonic-gate 	    (size & (size - 1)) != 0);
14517c478bd9Sstevel@tonic-gate 
14527c478bd9Sstevel@tonic-gate 	ASSERT(off < endoff);
14537c478bd9Sstevel@tonic-gate 
14547c478bd9Sstevel@tonic-gate 	if (bits > NBBY * sizeof (uint64_t)) {
14557c478bd9Sstevel@tonic-gate 		ulong_t end;
14567c478bd9Sstevel@tonic-gate 
14577c478bd9Sstevel@tonic-gate 		/*
14587c478bd9Sstevel@tonic-gate 		 * The hole is larger than the largest integer type.  To
14597c478bd9Sstevel@tonic-gate 		 * handle this, we split up the hole at 8-byte-aligned
14607c478bd9Sstevel@tonic-gate 		 * boundaries, recursing to print each subsection.  For
14617c478bd9Sstevel@tonic-gate 		 * normal C structures, we'll loop at most twice.
14627c478bd9Sstevel@tonic-gate 		 */
14637c478bd9Sstevel@tonic-gate 		for (; off < endoff; off = end) {
14647c478bd9Sstevel@tonic-gate 			end = P2END(off, NBBY * sizeof (uint64_t));
14657c478bd9Sstevel@tonic-gate 			if (end > endoff)
14667c478bd9Sstevel@tonic-gate 				end = endoff;
14677c478bd9Sstevel@tonic-gate 
14687c478bd9Sstevel@tonic-gate 			ASSERT((end - off) <= NBBY * sizeof (uint64_t));
14697c478bd9Sstevel@tonic-gate 			print_hole(pap, depth, off, end);
14707c478bd9Sstevel@tonic-gate 		}
14717c478bd9Sstevel@tonic-gate 		ASSERT(end == endoff);
14727c478bd9Sstevel@tonic-gate 
14737c478bd9Sstevel@tonic-gate 		return;
14747c478bd9Sstevel@tonic-gate 	}
14757c478bd9Sstevel@tonic-gate 
14767c478bd9Sstevel@tonic-gate 	if (bitfield)
14777c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(type, sizeof (type), "unsigned");
14787c478bd9Sstevel@tonic-gate 	else
14797c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(type, sizeof (type), "uint%d_t", bits);
14807c478bd9Sstevel@tonic-gate 
14817c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & (PA_SHOWTYPE | PA_SHOWNAME | PA_SHOWADDR))
14827c478bd9Sstevel@tonic-gate 		mdb_printf("%*s", (depth + pap->pa_nest) * pap->pa_tab, "");
14837c478bd9Sstevel@tonic-gate 
14847c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWADDR) {
1485bf5197d8Sjwadams 		if (off % NBBY == 0)
14867c478bd9Sstevel@tonic-gate 			mdb_printf("%llx ", pap->pa_addr + off / NBBY);
14877c478bd9Sstevel@tonic-gate 		else
14887c478bd9Sstevel@tonic-gate 			mdb_printf("%llx.%lx ",
14897c478bd9Sstevel@tonic-gate 			    pap->pa_addr + off / NBBY, off % NBBY);
14907c478bd9Sstevel@tonic-gate 	}
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWTYPE)
14937c478bd9Sstevel@tonic-gate 		mdb_printf("%s ", type);
14947c478bd9Sstevel@tonic-gate 
14957c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWNAME)
14967c478bd9Sstevel@tonic-gate 		mdb_printf("%s", name);
14977c478bd9Sstevel@tonic-gate 
14987c478bd9Sstevel@tonic-gate 	if (bitfield && (pap->pa_flags & PA_SHOWTYPE))
14997c478bd9Sstevel@tonic-gate 		mdb_printf(" :%d", bits);
15007c478bd9Sstevel@tonic-gate 
15017c478bd9Sstevel@tonic-gate 	mdb_printf("%s ", (pap->pa_flags & PA_SHOWVAL)? " =" : "");
15027c478bd9Sstevel@tonic-gate 
15037c478bd9Sstevel@tonic-gate 	/*
15047c478bd9Sstevel@tonic-gate 	 * We fake up a ctf_encoding_t, and use print_int_val() to print
15057c478bd9Sstevel@tonic-gate 	 * the value.  Holes are always processed as unsigned integers.
15067c478bd9Sstevel@tonic-gate 	 */
15077c478bd9Sstevel@tonic-gate 	bzero(&e, sizeof (e));
15087c478bd9Sstevel@tonic-gate 	e.cte_format = 0;
15097c478bd9Sstevel@tonic-gate 	e.cte_offset = 0;
15107c478bd9Sstevel@tonic-gate 	e.cte_bits = bits;
15117c478bd9Sstevel@tonic-gate 
15127c478bd9Sstevel@tonic-gate 	if (print_int_val(type, &e, off, pap) != 0)
15137c478bd9Sstevel@tonic-gate 		mdb_iob_discard(mdb.m_out);
15147c478bd9Sstevel@tonic-gate 	else
15157c478bd9Sstevel@tonic-gate 		mdb_iob_puts(mdb.m_out, pap->pa_delim);
15167c478bd9Sstevel@tonic-gate }
15177c478bd9Sstevel@tonic-gate 
15187c478bd9Sstevel@tonic-gate /*
15197c478bd9Sstevel@tonic-gate  * The print_close_sou() function is called for each structure or union
15207c478bd9Sstevel@tonic-gate  * which has been completed.  For structures, we detect and print any holes
15217c478bd9Sstevel@tonic-gate  * before printing the closing brace.
15227c478bd9Sstevel@tonic-gate  */
15237c478bd9Sstevel@tonic-gate static void
15247c478bd9Sstevel@tonic-gate print_close_sou(printarg_t *pap, int newdepth)
15257c478bd9Sstevel@tonic-gate {
15267c478bd9Sstevel@tonic-gate 	int d = newdepth + pap->pa_nest;
15277c478bd9Sstevel@tonic-gate 
15287c478bd9Sstevel@tonic-gate 	if ((pap->pa_flags & PA_SHOWHOLES) && !pap->pa_holes[d].hi_isunion) {
15297c478bd9Sstevel@tonic-gate 		ulong_t end = pap->pa_holes[d + 1].hi_offset;
15307c478bd9Sstevel@tonic-gate 		ulong_t expected = pap->pa_holes[d].hi_offset;
15317c478bd9Sstevel@tonic-gate 
15327c478bd9Sstevel@tonic-gate 		if (end < expected)
15337c478bd9Sstevel@tonic-gate 			print_hole(pap, newdepth + 1, end, expected);
15347c478bd9Sstevel@tonic-gate 	}
1535cce40297SJonathan Adams 	/* if the struct is an array element, print a comma after the } */
1536cce40297SJonathan Adams 	mdb_printf("%*s}%s\n", d * pap->pa_tab, "",
1537cce40297SJonathan Adams 	    (newdepth == 0 && pap->pa_nest > 0)? "," : "");
15387c478bd9Sstevel@tonic-gate }
15397c478bd9Sstevel@tonic-gate 
15407c478bd9Sstevel@tonic-gate static printarg_f *const printfuncs[] = {
15417c478bd9Sstevel@tonic-gate 	print_int,	/* CTF_K_INTEGER */
15427c478bd9Sstevel@tonic-gate 	print_float,	/* CTF_K_FLOAT */
15437c478bd9Sstevel@tonic-gate 	print_ptr,	/* CTF_K_POINTER */
15447c478bd9Sstevel@tonic-gate 	print_array,	/* CTF_K_ARRAY */
15457c478bd9Sstevel@tonic-gate 	print_ptr,	/* CTF_K_FUNCTION */
15467c478bd9Sstevel@tonic-gate 	print_sou,	/* CTF_K_STRUCT */
15477c478bd9Sstevel@tonic-gate 	print_sou,	/* CTF_K_UNION */
15487c478bd9Sstevel@tonic-gate 	print_enum,	/* CTF_K_ENUM */
15497c478bd9Sstevel@tonic-gate 	print_tag	/* CTF_K_FORWARD */
15507c478bd9Sstevel@tonic-gate };
15517c478bd9Sstevel@tonic-gate 
15527c478bd9Sstevel@tonic-gate /*
15537c478bd9Sstevel@tonic-gate  * The elt_print function is used as the mdb_ctf_type_visit callback.  For
15547c478bd9Sstevel@tonic-gate  * each element, we print an appropriate name prefix and then call the
15557c478bd9Sstevel@tonic-gate  * print subroutine for this type class in the array above.
15567c478bd9Sstevel@tonic-gate  */
15577c478bd9Sstevel@tonic-gate static int
1558cce40297SJonathan Adams elt_print(const char *name, mdb_ctf_id_t id, mdb_ctf_id_t base,
1559cce40297SJonathan Adams     ulong_t off, int depth, void *data)
15607c478bd9Sstevel@tonic-gate {
1561cce40297SJonathan Adams 	char type[MDB_SYM_NAMLEN + sizeof (" <<12345678...>>")];
15627c478bd9Sstevel@tonic-gate 	int kind, rc, d;
15637c478bd9Sstevel@tonic-gate 	printarg_t *pap = data;
15647c478bd9Sstevel@tonic-gate 
1565838d7172SSebastien Roy 	for (d = pap->pa_depth - 1; d >= depth; d--) {
1566838d7172SSebastien Roy 		if (d < pap->pa_nooutdepth)
1567838d7172SSebastien Roy 			print_close_sou(pap, d);
1568838d7172SSebastien Roy 	}
1569838d7172SSebastien Roy 
1570838d7172SSebastien Roy 	/*
1571838d7172SSebastien Roy 	 * Reset pa_nooutdepth if we've come back out of the structure we
1572838d7172SSebastien Roy 	 * didn't want to print.
1573838d7172SSebastien Roy 	 */
1574838d7172SSebastien Roy 	if (depth <= pap->pa_nooutdepth)
1575838d7172SSebastien Roy 		pap->pa_nooutdepth = (uint_t)-1;
15767c478bd9Sstevel@tonic-gate 
1577838d7172SSebastien Roy 	if (depth > pap->pa_maxdepth || depth > pap->pa_nooutdepth)
1578bf5197d8Sjwadams 		return (0);
1579bf5197d8Sjwadams 
1580cce40297SJonathan Adams 	if (!mdb_ctf_type_valid(base) ||
15817c478bd9Sstevel@tonic-gate 	    (kind = mdb_ctf_type_kind(base)) == -1)
15827c478bd9Sstevel@tonic-gate 		return (-1); /* errno is set for us */
15837c478bd9Sstevel@tonic-gate 
1584cce40297SJonathan Adams 	if (mdb_ctf_type_name(id, type, MDB_SYM_NAMLEN) == NULL)
15857c478bd9Sstevel@tonic-gate 		(void) strcpy(type, "(?)");
15867c478bd9Sstevel@tonic-gate 
1587cce40297SJonathan Adams 	if (pap->pa_flags & PA_SHOWBASETYPE) {
1588cce40297SJonathan Adams 		/*
1589cce40297SJonathan Adams 		 * If basetype is different and informative, concatenate
1590cce40297SJonathan Adams 		 * <<basetype>> (or <<baset...>> if it doesn't fit)
1591cce40297SJonathan Adams 		 *
1592cce40297SJonathan Adams 		 * We just use the end of the buffer to store the type name, and
1593cce40297SJonathan Adams 		 * only connect it up if that's necessary.
1594cce40297SJonathan Adams 		 */
1595cce40297SJonathan Adams 
1596cce40297SJonathan Adams 		char *type_end = type + strlen(type);
1597cce40297SJonathan Adams 		char *basetype;
1598cce40297SJonathan Adams 		size_t sz;
1599cce40297SJonathan Adams 
1600cce40297SJonathan Adams 		(void) strlcat(type, " <<", sizeof (type));
1601cce40297SJonathan Adams 
1602cce40297SJonathan Adams 		basetype = type + strlen(type);
1603cce40297SJonathan Adams 		sz = sizeof (type) - (basetype - type);
1604cce40297SJonathan Adams 
1605cce40297SJonathan Adams 		*type_end = '\0'; /* restore the end of type for strcmp() */
1606cce40297SJonathan Adams 
1607cce40297SJonathan Adams 		if (mdb_ctf_type_name(base, basetype, sz) != NULL &&
1608cce40297SJonathan Adams 		    strcmp(basetype, type) != 0 &&
1609cce40297SJonathan Adams 		    strcmp(basetype, "struct ") != 0 &&
1610cce40297SJonathan Adams 		    strcmp(basetype, "enum ") != 0 &&
1611cce40297SJonathan Adams 		    strcmp(basetype, "union ") != 0) {
1612cce40297SJonathan Adams 			type_end[0] = ' ';	/* reconnect */
1613cce40297SJonathan Adams 			if (strlcat(type, ">>", sizeof (type)) >= sizeof (type))
1614cce40297SJonathan Adams 				(void) strlcpy(
1615cce40297SJonathan Adams 				    type + sizeof (type) - 6, "...>>", 6);
1616cce40297SJonathan Adams 		}
1617cce40297SJonathan Adams 	}
1618cce40297SJonathan Adams 
16197c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWHOLES) {
16207c478bd9Sstevel@tonic-gate 		ctf_encoding_t e;
16217c478bd9Sstevel@tonic-gate 		ssize_t nsize;
16227c478bd9Sstevel@tonic-gate 		ulong_t newoff;
16237c478bd9Sstevel@tonic-gate 		holeinfo_t *hole;
16247c478bd9Sstevel@tonic-gate 		int extra = IS_COMPOSITE(kind)? 1 : 0;
16257c478bd9Sstevel@tonic-gate 
16267c478bd9Sstevel@tonic-gate 		/*
16277c478bd9Sstevel@tonic-gate 		 * grow the hole array, if necessary
16287c478bd9Sstevel@tonic-gate 		 */
16297c478bd9Sstevel@tonic-gate 		if (pap->pa_nest + depth + extra >= pap->pa_nholes) {
16307c478bd9Sstevel@tonic-gate 			int new = MAX(MAX(8, pap->pa_nholes * 2),
16317c478bd9Sstevel@tonic-gate 			    pap->pa_nest + depth + extra + 1);
16327c478bd9Sstevel@tonic-gate 
16337c478bd9Sstevel@tonic-gate 			holeinfo_t *nhi = mdb_zalloc(
16347c478bd9Sstevel@tonic-gate 			    sizeof (*nhi) * new, UM_NOSLEEP | UM_GC);
16357c478bd9Sstevel@tonic-gate 
16367c478bd9Sstevel@tonic-gate 			bcopy(pap->pa_holes, nhi,
16377c478bd9Sstevel@tonic-gate 			    pap->pa_nholes * sizeof (*nhi));
16387c478bd9Sstevel@tonic-gate 
16397c478bd9Sstevel@tonic-gate 			pap->pa_holes = nhi;
16407c478bd9Sstevel@tonic-gate 			pap->pa_nholes = new;
16417c478bd9Sstevel@tonic-gate 		}
16427c478bd9Sstevel@tonic-gate 
16437c478bd9Sstevel@tonic-gate 		hole = &pap->pa_holes[depth + pap->pa_nest];
16447c478bd9Sstevel@tonic-gate 
16457c478bd9Sstevel@tonic-gate 		if (depth != 0 && off > hole->hi_offset)
16467c478bd9Sstevel@tonic-gate 			print_hole(pap, depth, hole->hi_offset, off);
16477c478bd9Sstevel@tonic-gate 
16487c478bd9Sstevel@tonic-gate 		/* compute the next expected offset */
16497c478bd9Sstevel@tonic-gate 		if (kind == CTF_K_INTEGER &&
16507c478bd9Sstevel@tonic-gate 		    mdb_ctf_type_encoding(base, &e) == 0)
16517c478bd9Sstevel@tonic-gate 			newoff = off + e.cte_bits;
16527c478bd9Sstevel@tonic-gate 		else if ((nsize = mdb_ctf_type_size(base)) >= 0)
16537c478bd9Sstevel@tonic-gate 			newoff = off + nsize * NBBY;
16547c478bd9Sstevel@tonic-gate 		else {
16557c478bd9Sstevel@tonic-gate 			/* something bad happened, disable hole checking */
16567c478bd9Sstevel@tonic-gate 			newoff = -1UL;		/* ULONG_MAX */
16577c478bd9Sstevel@tonic-gate 		}
16587c478bd9Sstevel@tonic-gate 
16597c478bd9Sstevel@tonic-gate 		hole->hi_offset = newoff;
16607c478bd9Sstevel@tonic-gate 
16617c478bd9Sstevel@tonic-gate 		if (IS_COMPOSITE(kind)) {
16627c478bd9Sstevel@tonic-gate 			hole->hi_isunion = (kind == CTF_K_UNION);
16637c478bd9Sstevel@tonic-gate 			hole++;
16647c478bd9Sstevel@tonic-gate 			hole->hi_offset = off;
16657c478bd9Sstevel@tonic-gate 		}
16667c478bd9Sstevel@tonic-gate 	}
16677c478bd9Sstevel@tonic-gate 
16687c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & (PA_SHOWTYPE | PA_SHOWNAME | PA_SHOWADDR))
16697c478bd9Sstevel@tonic-gate 		mdb_printf("%*s", (depth + pap->pa_nest) * pap->pa_tab, "");
16707c478bd9Sstevel@tonic-gate 
1671cce40297SJonathan Adams 	if (pap->pa_flags & PA_SHOWADDR) {
1672cce40297SJonathan Adams 		if (off % NBBY == 0)
1673cce40297SJonathan Adams 			mdb_printf("%llx ", pap->pa_addr + off / NBBY);
1674cce40297SJonathan Adams 		else
1675cce40297SJonathan Adams 			mdb_printf("%llx.%lx ",
1676cce40297SJonathan Adams 			    pap->pa_addr + off / NBBY, off % NBBY);
1677cce40297SJonathan Adams 	}
16787c478bd9Sstevel@tonic-gate 
1679cce40297SJonathan Adams 	if ((pap->pa_flags & PA_SHOWTYPE)) {
1680cce40297SJonathan Adams 		mdb_printf("%s", type);
1681cce40297SJonathan Adams 		/*
1682cce40297SJonathan Adams 		 * We want to avoid printing a trailing space when
1683cce40297SJonathan Adams 		 * dealing with pointers in a structure, so we end
1684cce40297SJonathan Adams 		 * up with:
1685cce40297SJonathan Adams 		 *
1686cce40297SJonathan Adams 		 *	label_t *t_onfault = 0
1687cce40297SJonathan Adams 		 *
1688cce40297SJonathan Adams 		 * If depth is zero, always print the trailing space unless
1689cce40297SJonathan Adams 		 * we also have a prefix.
1690cce40297SJonathan Adams 		 */
1691cce40297SJonathan Adams 		if (type[strlen(type) - 1] != '*' ||
1692cce40297SJonathan Adams 		    (depth == 0 && (!(pap->pa_flags & PA_SHOWNAME) ||
1693cce40297SJonathan Adams 		    pap->pa_prefix == NULL)))
1694cce40297SJonathan Adams 			mdb_printf(" ");
1695cce40297SJonathan Adams 	}
16967c478bd9Sstevel@tonic-gate 
1697cce40297SJonathan Adams 	if (pap->pa_flags & PA_SHOWNAME) {
1698cce40297SJonathan Adams 		if (pap->pa_prefix != NULL && depth <= 1)
1699cce40297SJonathan Adams 			mdb_printf("%s%s", pap->pa_prefix,
1700cce40297SJonathan Adams 			    (depth == 0) ? "" : pap->pa_suffix);
1701cce40297SJonathan Adams 		mdb_printf("%s", name);
1702cce40297SJonathan Adams 	}
17037c478bd9Sstevel@tonic-gate 
1704cce40297SJonathan Adams 	if ((pap->pa_flags & PA_SHOWTYPE) && kind == CTF_K_INTEGER) {
1705cce40297SJonathan Adams 		ctf_encoding_t e;
17067c478bd9Sstevel@tonic-gate 
1707cce40297SJonathan Adams 		if (mdb_ctf_type_encoding(base, &e) == 0) {
1708cce40297SJonathan Adams 			ulong_t bits = e.cte_bits;
1709cce40297SJonathan Adams 			ulong_t size = bits / NBBY;
17107c478bd9Sstevel@tonic-gate 
1711cce40297SJonathan Adams 			if (bits % NBBY != 0 ||
1712cce40297SJonathan Adams 			    off % NBBY != 0 ||
1713cce40297SJonathan Adams 			    size > 8 ||
1714cce40297SJonathan Adams 			    size != mdb_ctf_type_size(base))
1715cce40297SJonathan Adams 				mdb_printf(" :%d", bits);
17167c478bd9Sstevel@tonic-gate 		}
1717cce40297SJonathan Adams 	}
17187c478bd9Sstevel@tonic-gate 
1719cce40297SJonathan Adams 	if (depth != 0 ||
1720cce40297SJonathan Adams 	    ((pap->pa_flags & PA_SHOWNAME) && pap->pa_prefix != NULL))
17217c478bd9Sstevel@tonic-gate 		mdb_printf("%s ", pap->pa_flags & PA_SHOWVAL ? " =" : "");
17227c478bd9Sstevel@tonic-gate 
1723cce40297SJonathan Adams 	if (depth == 0 && pap->pa_prefix != NULL)
1724cce40297SJonathan Adams 		name = pap->pa_prefix;
17257c478bd9Sstevel@tonic-gate 
17267c478bd9Sstevel@tonic-gate 	pap->pa_depth = depth;
17278a6a72fdSaf 	if (kind <= CTF_K_UNKNOWN || kind >= CTF_K_TYPEDEF) {
17288a6a72fdSaf 		mdb_warn("unknown ctf for %s type %s kind %d\n",
1729cce40297SJonathan Adams 		    name, type, kind);
17308a6a72fdSaf 		return (-1);
17318a6a72fdSaf 	}
17327c478bd9Sstevel@tonic-gate 	rc = printfuncs[kind - 1](type, name, id, base, off, pap);
17337c478bd9Sstevel@tonic-gate 
17347c478bd9Sstevel@tonic-gate 	if (rc != 0)
17357c478bd9Sstevel@tonic-gate 		mdb_iob_discard(mdb.m_out);
17367c478bd9Sstevel@tonic-gate 	else
17377c478bd9Sstevel@tonic-gate 		mdb_iob_puts(mdb.m_out, pap->pa_delim);
17387c478bd9Sstevel@tonic-gate 
17397c478bd9Sstevel@tonic-gate 	return (rc);
17407c478bd9Sstevel@tonic-gate }
17417c478bd9Sstevel@tonic-gate 
1742bf5197d8Sjwadams /*
1743bf5197d8Sjwadams  * Special semantics for pipelines.
1744bf5197d8Sjwadams  */
1745bf5197d8Sjwadams static int
1746bf5197d8Sjwadams pipe_print(mdb_ctf_id_t id, ulong_t off, void *data)
1747bf5197d8Sjwadams {
1748bf5197d8Sjwadams 	printarg_t *pap = data;
1749bf5197d8Sjwadams 	ssize_t size;
1750bf5197d8Sjwadams 	static const char *const fsp[] = { "%#r", "%#r", "%#r", "%#llr" };
1751bf5197d8Sjwadams 	uintptr_t value;
1752bf5197d8Sjwadams 	uintptr_t addr = pap->pa_addr + off / NBBY;
1753bf5197d8Sjwadams 	mdb_ctf_id_t base;
1754e9f82d69SAlex Reece 	int enum_value;
1755bf5197d8Sjwadams 	ctf_encoding_t e;
1756bf5197d8Sjwadams 
1757bf5197d8Sjwadams 	union {
1758bf5197d8Sjwadams 		uint64_t i8;
1759bf5197d8Sjwadams 		uint32_t i4;
1760bf5197d8Sjwadams 		uint16_t i2;
1761bf5197d8Sjwadams 		uint8_t i1;
1762bf5197d8Sjwadams 	} u;
1763bf5197d8Sjwadams 
1764bf5197d8Sjwadams 	if (mdb_ctf_type_resolve(id, &base) == -1) {
17653ddcfaddSBryan Cantrill 		mdb_warn("could not resolve type");
1766bf5197d8Sjwadams 		return (-1);
1767bf5197d8Sjwadams 	}
1768bf5197d8Sjwadams 
1769bf5197d8Sjwadams 	/*
1770bf5197d8Sjwadams 	 * If the user gives -a, then always print out the address of the
1771bf5197d8Sjwadams 	 * member.
1772bf5197d8Sjwadams 	 */
1773bf5197d8Sjwadams 	if ((pap->pa_flags & PA_SHOWADDR)) {
1774bf5197d8Sjwadams 		mdb_printf("%#lr\n", addr);
1775bf5197d8Sjwadams 		return (0);
1776bf5197d8Sjwadams 	}
1777bf5197d8Sjwadams 
1778bf5197d8Sjwadams again:
1779bf5197d8Sjwadams 	switch (mdb_ctf_type_kind(base)) {
1780bf5197d8Sjwadams 	case CTF_K_POINTER:
1781bf5197d8Sjwadams 		if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as,
1782bf5197d8Sjwadams 		    &value, sizeof (value), addr) != sizeof (value)) {
1783bf5197d8Sjwadams 			mdb_warn("failed to read pointer at %p", addr);
1784bf5197d8Sjwadams 			return (-1);
1785bf5197d8Sjwadams 		}
1786bf5197d8Sjwadams 		mdb_printf("%#lr\n", value);
1787bf5197d8Sjwadams 		break;
1788bf5197d8Sjwadams 
1789bf5197d8Sjwadams 	case CTF_K_ENUM:
1790e9f82d69SAlex Reece 		if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &enum_value,
1791e9f82d69SAlex Reece 		    sizeof (enum_value), addr) != sizeof (enum_value)) {
1792e9f82d69SAlex Reece 			mdb_warn("failed to read enum at %llx", addr);
1793e9f82d69SAlex Reece 			return (-1);
1794e9f82d69SAlex Reece 		}
1795e9f82d69SAlex Reece 		mdb_printf("%#r\n", enum_value);
1796e9f82d69SAlex Reece 		break;
1797e9f82d69SAlex Reece 
1798e9f82d69SAlex Reece 	case CTF_K_INTEGER:
1799bf5197d8Sjwadams 		if (mdb_ctf_type_encoding(base, &e) != 0) {
1800e9f82d69SAlex Reece 			mdb_warn("could not get type encoding\n");
1801bf5197d8Sjwadams 			return (-1);
1802bf5197d8Sjwadams 		}
1803bf5197d8Sjwadams 
1804bf5197d8Sjwadams 		/*
1805bf5197d8Sjwadams 		 * For immediate values, we just print out the value.
1806bf5197d8Sjwadams 		 */
1807bf5197d8Sjwadams 		size = e.cte_bits / NBBY;
1808bf5197d8Sjwadams 		if (size > 8 || (e.cte_bits % NBBY) != 0 ||
1809bf5197d8Sjwadams 		    (size & (size - 1)) != 0) {
1810bf5197d8Sjwadams 			return (print_bitfield(off, pap, &e));
1811bf5197d8Sjwadams 		}
1812bf5197d8Sjwadams 
1813bf5197d8Sjwadams 		if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.i8, size,
1814bf5197d8Sjwadams 		    addr) != size) {
1815bf5197d8Sjwadams 			mdb_warn("failed to read %lu bytes at %p",
1816bf5197d8Sjwadams 			    (ulong_t)size, pap->pa_addr);
1817bf5197d8Sjwadams 			return (-1);
1818bf5197d8Sjwadams 		}
1819bf5197d8Sjwadams 
1820bf5197d8Sjwadams 		switch (size) {
1821bf5197d8Sjwadams 		case sizeof (uint8_t):
1822bf5197d8Sjwadams 			mdb_printf(fsp[0], u.i1);
1823bf5197d8Sjwadams 			break;
1824bf5197d8Sjwadams 		case sizeof (uint16_t):
1825bf5197d8Sjwadams 			mdb_printf(fsp[1], u.i2);
1826bf5197d8Sjwadams 			break;
1827bf5197d8Sjwadams 		case sizeof (uint32_t):
1828bf5197d8Sjwadams 			mdb_printf(fsp[2], u.i4);
1829bf5197d8Sjwadams 			break;
1830bf5197d8Sjwadams 		case sizeof (uint64_t):
1831bf5197d8Sjwadams 			mdb_printf(fsp[3], u.i8);
1832bf5197d8Sjwadams 			break;
1833bf5197d8Sjwadams 		}
1834bf5197d8Sjwadams 		mdb_printf("\n");
1835bf5197d8Sjwadams 		break;
1836bf5197d8Sjwadams 
1837bf5197d8Sjwadams 	case CTF_K_FUNCTION:
1838bf5197d8Sjwadams 	case CTF_K_FLOAT:
1839bf5197d8Sjwadams 	case CTF_K_ARRAY:
1840bf5197d8Sjwadams 	case CTF_K_UNKNOWN:
1841bf5197d8Sjwadams 	case CTF_K_STRUCT:
1842bf5197d8Sjwadams 	case CTF_K_UNION:
1843bf5197d8Sjwadams 	case CTF_K_FORWARD:
1844bf5197d8Sjwadams 		/*
1845bf5197d8Sjwadams 		 * For these types, always print the address of the member
1846bf5197d8Sjwadams 		 */
1847bf5197d8Sjwadams 		mdb_printf("%#lr\n", addr);
1848bf5197d8Sjwadams 		break;
1849bf5197d8Sjwadams 
1850bf5197d8Sjwadams 	default:
1851bf5197d8Sjwadams 		mdb_warn("unknown type %d", mdb_ctf_type_kind(base));
1852bf5197d8Sjwadams 		break;
1853bf5197d8Sjwadams 	}
1854bf5197d8Sjwadams 
1855bf5197d8Sjwadams 	return (0);
1856bf5197d8Sjwadams }
1857bf5197d8Sjwadams 
18587c478bd9Sstevel@tonic-gate static int
18597c478bd9Sstevel@tonic-gate parse_delimiter(char **strp)
18607c478bd9Sstevel@tonic-gate {
18617c478bd9Sstevel@tonic-gate 	switch (**strp) {
18627c478bd9Sstevel@tonic-gate 	case '\0':
18637c478bd9Sstevel@tonic-gate 		return (MEMBER_DELIM_DONE);
18647c478bd9Sstevel@tonic-gate 
18657c478bd9Sstevel@tonic-gate 	case '.':
18667c478bd9Sstevel@tonic-gate 		*strp = *strp + 1;
18677c478bd9Sstevel@tonic-gate 		return (MEMBER_DELIM_DOT);
18687c478bd9Sstevel@tonic-gate 
18697c478bd9Sstevel@tonic-gate 	case '[':
18707c478bd9Sstevel@tonic-gate 		*strp = *strp + 1;
18717c478bd9Sstevel@tonic-gate 		return (MEMBER_DELIM_LBR);
18727c478bd9Sstevel@tonic-gate 
18737c478bd9Sstevel@tonic-gate 	case '-':
18747c478bd9Sstevel@tonic-gate 		*strp = *strp + 1;
18757c478bd9Sstevel@tonic-gate 		if (**strp == '>') {
18767c478bd9Sstevel@tonic-gate 			*strp = *strp + 1;
18777c478bd9Sstevel@tonic-gate 			return (MEMBER_DELIM_PTR);
18787c478bd9Sstevel@tonic-gate 		}
18797c478bd9Sstevel@tonic-gate 		*strp = *strp - 1;
18807c478bd9Sstevel@tonic-gate 		/*FALLTHROUGH*/
18817c478bd9Sstevel@tonic-gate 	default:
18827c478bd9Sstevel@tonic-gate 		return (MEMBER_DELIM_ERR);
18837c478bd9Sstevel@tonic-gate 	}
18847c478bd9Sstevel@tonic-gate }
18857c478bd9Sstevel@tonic-gate 
18867c478bd9Sstevel@tonic-gate static int
18877c478bd9Sstevel@tonic-gate deref(printarg_t *pap, size_t size)
18887c478bd9Sstevel@tonic-gate {
18897c478bd9Sstevel@tonic-gate 	uint32_t a32;
18907c478bd9Sstevel@tonic-gate 	mdb_tgt_as_t as = pap->pa_as;
18917c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t *ap = &pap->pa_addr;
18927c478bd9Sstevel@tonic-gate 
18937c478bd9Sstevel@tonic-gate 	if (size == sizeof (mdb_tgt_addr_t)) {
18947c478bd9Sstevel@tonic-gate 		if (mdb_tgt_aread(mdb.m_target, as, ap, size, *ap) == -1) {
18957c478bd9Sstevel@tonic-gate 			mdb_warn("could not dereference pointer %llx\n", *ap);
18967c478bd9Sstevel@tonic-gate 			return (-1);
18977c478bd9Sstevel@tonic-gate 		}
18987c478bd9Sstevel@tonic-gate 	} else {
18997c478bd9Sstevel@tonic-gate 		if (mdb_tgt_aread(mdb.m_target, as, &a32, size, *ap) == -1) {
19007c478bd9Sstevel@tonic-gate 			mdb_warn("could not dereference pointer %x\n", *ap);
19017c478bd9Sstevel@tonic-gate 			return (-1);
19027c478bd9Sstevel@tonic-gate 		}
19037c478bd9Sstevel@tonic-gate 
19047c478bd9Sstevel@tonic-gate 		*ap = (mdb_tgt_addr_t)a32;
19057c478bd9Sstevel@tonic-gate 	}
19067c478bd9Sstevel@tonic-gate 
19077c478bd9Sstevel@tonic-gate 	/*
19087c478bd9Sstevel@tonic-gate 	 * We've dereferenced at least once, we must be on the real
19097c478bd9Sstevel@tonic-gate 	 * target. If we were in the immediate target, reset to the real
19107c478bd9Sstevel@tonic-gate 	 * target; it's reset as needed when we return to the print
19117c478bd9Sstevel@tonic-gate 	 * routines.
19127c478bd9Sstevel@tonic-gate 	 */
19137c478bd9Sstevel@tonic-gate 	if (pap->pa_tgt == pap->pa_immtgt)
19147c478bd9Sstevel@tonic-gate 		pap->pa_tgt = pap->pa_realtgt;
19157c478bd9Sstevel@tonic-gate 
19167c478bd9Sstevel@tonic-gate 	return (0);
19177c478bd9Sstevel@tonic-gate }
19187c478bd9Sstevel@tonic-gate 
19197c478bd9Sstevel@tonic-gate static int
19207c478bd9Sstevel@tonic-gate parse_member(printarg_t *pap, const char *str, mdb_ctf_id_t id,
19217c478bd9Sstevel@tonic-gate     mdb_ctf_id_t *idp, ulong_t *offp, int *last_deref)
19227c478bd9Sstevel@tonic-gate {
19237c478bd9Sstevel@tonic-gate 	int delim;
19247c478bd9Sstevel@tonic-gate 	char member[64];
19257c478bd9Sstevel@tonic-gate 	char buf[128];
19267c478bd9Sstevel@tonic-gate 	uint_t index;
19277c478bd9Sstevel@tonic-gate 	char *start = (char *)str;
19287c478bd9Sstevel@tonic-gate 	char *end;
19297c478bd9Sstevel@tonic-gate 	ulong_t off = 0;
19307c478bd9Sstevel@tonic-gate 	mdb_ctf_arinfo_t ar;
19317c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t rid;
19327c478bd9Sstevel@tonic-gate 	int kind;
19337c478bd9Sstevel@tonic-gate 	ssize_t size;
19347c478bd9Sstevel@tonic-gate 	int non_array = FALSE;
19357c478bd9Sstevel@tonic-gate 
19367c478bd9Sstevel@tonic-gate 	/*
19377c478bd9Sstevel@tonic-gate 	 * id always has the unresolved type for printing error messages
19387c478bd9Sstevel@tonic-gate 	 * that include the type; rid always has the resolved type for
19397c478bd9Sstevel@tonic-gate 	 * use in mdb_ctf_* calls.  It is possible for this command to fail,
19407c478bd9Sstevel@tonic-gate 	 * however, if the resolved type is in the parent and it is currently
19417c478bd9Sstevel@tonic-gate 	 * unavailable.  Note that we also can't print out the name of the
19427c478bd9Sstevel@tonic-gate 	 * type, since that would also rely on looking up the resolved name.
19437c478bd9Sstevel@tonic-gate 	 */
19447c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_resolve(id, &rid) != 0) {
19457c478bd9Sstevel@tonic-gate 		mdb_warn("failed to resolve type");
19467c478bd9Sstevel@tonic-gate 		return (-1);
19477c478bd9Sstevel@tonic-gate 	}
19487c478bd9Sstevel@tonic-gate 
19497c478bd9Sstevel@tonic-gate 	delim = parse_delimiter(&start);
19507c478bd9Sstevel@tonic-gate 	/*
19517c478bd9Sstevel@tonic-gate 	 * If the user fails to specify an initial delimiter, guess -> for
19527c478bd9Sstevel@tonic-gate 	 * pointer types and . for non-pointer types.
19537c478bd9Sstevel@tonic-gate 	 */
19547c478bd9Sstevel@tonic-gate 	if (delim == MEMBER_DELIM_ERR)
19557c478bd9Sstevel@tonic-gate 		delim = (mdb_ctf_type_kind(rid) == CTF_K_POINTER) ?
19567c478bd9Sstevel@tonic-gate 		    MEMBER_DELIM_PTR : MEMBER_DELIM_DOT;
19577c478bd9Sstevel@tonic-gate 
19587c478bd9Sstevel@tonic-gate 	*last_deref = FALSE;
19597c478bd9Sstevel@tonic-gate 
19607c478bd9Sstevel@tonic-gate 	while (delim != MEMBER_DELIM_DONE) {
19617c478bd9Sstevel@tonic-gate 		switch (delim) {
19627c478bd9Sstevel@tonic-gate 		case MEMBER_DELIM_PTR:
19637c478bd9Sstevel@tonic-gate 			kind = mdb_ctf_type_kind(rid);
19647c478bd9Sstevel@tonic-gate 			if (kind != CTF_K_POINTER) {
19657c478bd9Sstevel@tonic-gate 				mdb_warn("%s is not a pointer type\n",
19667c478bd9Sstevel@tonic-gate 				    mdb_ctf_type_name(id, buf, sizeof (buf)));
19677c478bd9Sstevel@tonic-gate 				return (-1);
19687c478bd9Sstevel@tonic-gate 			}
19697c478bd9Sstevel@tonic-gate 
19707c478bd9Sstevel@tonic-gate 			size = mdb_ctf_type_size(id);
19717c478bd9Sstevel@tonic-gate 			if (deref(pap, size) != 0)
19727c478bd9Sstevel@tonic-gate 				return (-1);
19737c478bd9Sstevel@tonic-gate 
19747c478bd9Sstevel@tonic-gate 			(void) mdb_ctf_type_reference(rid, &id);
19757c478bd9Sstevel@tonic-gate 			(void) mdb_ctf_type_resolve(id, &rid);
19767c478bd9Sstevel@tonic-gate 
19777c478bd9Sstevel@tonic-gate 			off = 0;
19787c478bd9Sstevel@tonic-gate 			break;
19797c478bd9Sstevel@tonic-gate 
19807c478bd9Sstevel@tonic-gate 		case MEMBER_DELIM_DOT:
19817c478bd9Sstevel@tonic-gate 			kind = mdb_ctf_type_kind(rid);
19827c478bd9Sstevel@tonic-gate 			if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) {
19837c478bd9Sstevel@tonic-gate 				mdb_warn("%s is not a struct or union type\n",
19847c478bd9Sstevel@tonic-gate 				    mdb_ctf_type_name(id, buf, sizeof (buf)));
19857c478bd9Sstevel@tonic-gate 				return (-1);
19867c478bd9Sstevel@tonic-gate 			}
19877c478bd9Sstevel@tonic-gate 			break;
19887c478bd9Sstevel@tonic-gate 
19897c478bd9Sstevel@tonic-gate 		case MEMBER_DELIM_LBR:
19907c478bd9Sstevel@tonic-gate 			end = strchr(start, ']');
19917c478bd9Sstevel@tonic-gate 			if (end == NULL) {
19927c478bd9Sstevel@tonic-gate 				mdb_warn("no trailing ']'\n");
19937c478bd9Sstevel@tonic-gate 				return (-1);
19947c478bd9Sstevel@tonic-gate 			}
19957c478bd9Sstevel@tonic-gate 
19963b6e0a59SMatt Amdur 			(void) mdb_snprintf(member, end - start + 1, "%s",
19973b6e0a59SMatt Amdur 			    start);
19987c478bd9Sstevel@tonic-gate 
19997c478bd9Sstevel@tonic-gate 			index = mdb_strtoull(member);
20007c478bd9Sstevel@tonic-gate 
20017c478bd9Sstevel@tonic-gate 			switch (mdb_ctf_type_kind(rid)) {
20027c478bd9Sstevel@tonic-gate 			case CTF_K_POINTER:
20037c478bd9Sstevel@tonic-gate 				size = mdb_ctf_type_size(rid);
20047c478bd9Sstevel@tonic-gate 
20057c478bd9Sstevel@tonic-gate 				if (deref(pap, size) != 0)
20067c478bd9Sstevel@tonic-gate 					return (-1);
20077c478bd9Sstevel@tonic-gate 
20087c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_type_reference(rid, &id);
20097c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_type_resolve(id, &rid);
20107c478bd9Sstevel@tonic-gate 
20117c478bd9Sstevel@tonic-gate 				size = mdb_ctf_type_size(id);
20127c478bd9Sstevel@tonic-gate 				if (size <= 0) {
20137c478bd9Sstevel@tonic-gate 					mdb_warn("cannot dereference void "
20147c478bd9Sstevel@tonic-gate 					    "type\n");
20157c478bd9Sstevel@tonic-gate 					return (-1);
20167c478bd9Sstevel@tonic-gate 				}
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 				pap->pa_addr += index * size;
20197c478bd9Sstevel@tonic-gate 				off = 0;
20207c478bd9Sstevel@tonic-gate 
20217c478bd9Sstevel@tonic-gate 				if (index == 0 && non_array)
20227c478bd9Sstevel@tonic-gate 					*last_deref = TRUE;
20237c478bd9Sstevel@tonic-gate 				break;
20247c478bd9Sstevel@tonic-gate 
20257c478bd9Sstevel@tonic-gate 			case CTF_K_ARRAY:
20267c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_array_info(rid, &ar);
20277c478bd9Sstevel@tonic-gate 
20287c478bd9Sstevel@tonic-gate 				if (index >= ar.mta_nelems) {
20297c478bd9Sstevel@tonic-gate 					mdb_warn("index %r is outside of "
20307c478bd9Sstevel@tonic-gate 					    "array bounds [0 .. %r]\n",
20317c478bd9Sstevel@tonic-gate 					    index, ar.mta_nelems - 1);
20327c478bd9Sstevel@tonic-gate 				}
20337c478bd9Sstevel@tonic-gate 
20347c478bd9Sstevel@tonic-gate 				id = ar.mta_contents;
20357c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_type_resolve(id, &rid);
20367c478bd9Sstevel@tonic-gate 
20377c478bd9Sstevel@tonic-gate 				size = mdb_ctf_type_size(id);
20387c478bd9Sstevel@tonic-gate 				if (size <= 0) {
20397c478bd9Sstevel@tonic-gate 					mdb_warn("cannot dereference void "
20407c478bd9Sstevel@tonic-gate 					    "type\n");
20417c478bd9Sstevel@tonic-gate 					return (-1);
20427c478bd9Sstevel@tonic-gate 				}
20437c478bd9Sstevel@tonic-gate 
20447c478bd9Sstevel@tonic-gate 				pap->pa_addr += index * size;
20457c478bd9Sstevel@tonic-gate 				off = 0;
20467c478bd9Sstevel@tonic-gate 				break;
20477c478bd9Sstevel@tonic-gate 
20487c478bd9Sstevel@tonic-gate 			default:
20497c478bd9Sstevel@tonic-gate 				mdb_warn("cannot index into non-array, "
20507c478bd9Sstevel@tonic-gate 				    "non-pointer type\n");
20517c478bd9Sstevel@tonic-gate 				return (-1);
20527c478bd9Sstevel@tonic-gate 			}
20537c478bd9Sstevel@tonic-gate 
20547c478bd9Sstevel@tonic-gate 			start = end + 1;
20557c478bd9Sstevel@tonic-gate 			delim = parse_delimiter(&start);
20567c478bd9Sstevel@tonic-gate 			continue;
20577c478bd9Sstevel@tonic-gate 
20587c478bd9Sstevel@tonic-gate 		case MEMBER_DELIM_ERR:
20597c478bd9Sstevel@tonic-gate 		default:
20607c478bd9Sstevel@tonic-gate 			mdb_warn("'%c' is not a valid delimiter\n", *start);
20617c478bd9Sstevel@tonic-gate 			return (-1);
20627c478bd9Sstevel@tonic-gate 		}
20637c478bd9Sstevel@tonic-gate 
20647c478bd9Sstevel@tonic-gate 		*last_deref = FALSE;
20657c478bd9Sstevel@tonic-gate 		non_array = TRUE;
20667c478bd9Sstevel@tonic-gate 
20677c478bd9Sstevel@tonic-gate 		/*
20687c478bd9Sstevel@tonic-gate 		 * Find the end of the member name; assume that a member
20697c478bd9Sstevel@tonic-gate 		 * name is at least one character long.
20707c478bd9Sstevel@tonic-gate 		 */
20717c478bd9Sstevel@tonic-gate 		for (end = start + 1; isalnum(*end) || *end == '_'; end++)
20727c478bd9Sstevel@tonic-gate 			continue;
20737c478bd9Sstevel@tonic-gate 
20743b6e0a59SMatt Amdur 		(void) mdb_snprintf(member, end - start + 1, "%s", start);
20757c478bd9Sstevel@tonic-gate 
20767c478bd9Sstevel@tonic-gate 		if (mdb_ctf_member_info(rid, member, &off, &id) != 0) {
20777c478bd9Sstevel@tonic-gate 			mdb_warn("failed to find member %s of %s", member,
20787c478bd9Sstevel@tonic-gate 			    mdb_ctf_type_name(id, buf, sizeof (buf)));
20797c478bd9Sstevel@tonic-gate 			return (-1);
20807c478bd9Sstevel@tonic-gate 		}
20817c478bd9Sstevel@tonic-gate 		(void) mdb_ctf_type_resolve(id, &rid);
20827c478bd9Sstevel@tonic-gate 
20837c478bd9Sstevel@tonic-gate 		pap->pa_addr += off / NBBY;
20847c478bd9Sstevel@tonic-gate 
20857c478bd9Sstevel@tonic-gate 		start = end;
20867c478bd9Sstevel@tonic-gate 		delim = parse_delimiter(&start);
20877c478bd9Sstevel@tonic-gate 	}
20887c478bd9Sstevel@tonic-gate 
20897c478bd9Sstevel@tonic-gate 	*idp = id;
20907c478bd9Sstevel@tonic-gate 	*offp = off;
20917c478bd9Sstevel@tonic-gate 
20927c478bd9Sstevel@tonic-gate 	return (0);
20937c478bd9Sstevel@tonic-gate }
20947c478bd9Sstevel@tonic-gate 
20952f045fd6SRobert Mustacchi static int
20962f045fd6SRobert Mustacchi cmd_print_tab_common(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
20973b6e0a59SMatt Amdur     const mdb_arg_t *argv)
20983b6e0a59SMatt Amdur {
20993b6e0a59SMatt Amdur 	char tn[MDB_SYM_NAMLEN];
21003b6e0a59SMatt Amdur 	char member[64];
21012f045fd6SRobert Mustacchi 	int delim, kind;
21023b6e0a59SMatt Amdur 	int ret = 0;
21033b6e0a59SMatt Amdur 	mdb_ctf_id_t id, rid;
21043b6e0a59SMatt Amdur 	mdb_ctf_arinfo_t ar;
21053b6e0a59SMatt Amdur 	char *start, *end;
21063b6e0a59SMatt Amdur 	ulong_t dul;
21073b6e0a59SMatt Amdur 
21083b6e0a59SMatt Amdur 	if (argc == 0 && !(flags & DCMD_TAB_SPACE))
21093b6e0a59SMatt Amdur 		return (0);
21103b6e0a59SMatt Amdur 
21113b6e0a59SMatt Amdur 	if (argc == 0 && (flags & DCMD_TAB_SPACE))
21123b6e0a59SMatt Amdur 		return (mdb_tab_complete_type(mcp, NULL, MDB_TABC_NOPOINT |
21133b6e0a59SMatt Amdur 		    MDB_TABC_NOARRAY));
21143b6e0a59SMatt Amdur 
21153b6e0a59SMatt Amdur 	if ((ret = mdb_tab_typename(&argc, &argv, tn, sizeof (tn))) < 0)
21163b6e0a59SMatt Amdur 		return (ret);
21173b6e0a59SMatt Amdur 
21183b6e0a59SMatt Amdur 	if (argc == 1 && (!(flags & DCMD_TAB_SPACE) || ret == 1))
21193b6e0a59SMatt Amdur 		return (mdb_tab_complete_type(mcp, tn, MDB_TABC_NOPOINT |
21203b6e0a59SMatt Amdur 		    MDB_TABC_NOARRAY));
21213b6e0a59SMatt Amdur 
21223b6e0a59SMatt Amdur 	if (argc == 1 && (flags & DCMD_TAB_SPACE))
21233b6e0a59SMatt Amdur 		return (mdb_tab_complete_member(mcp, tn, NULL));
21243b6e0a59SMatt Amdur 
21253b6e0a59SMatt Amdur 	/*
21263b6e0a59SMatt Amdur 	 * This is the reason that tab completion was created. We're going to go
21273b6e0a59SMatt Amdur 	 * along and walk the delimiters until we find something a member that
21283b6e0a59SMatt Amdur 	 * we don't recognize, at which point we'll try and tab complete it.
21293b6e0a59SMatt Amdur 	 * Note that ::print takes multiple args, so this is going to operate on
21303b6e0a59SMatt Amdur 	 * whatever the last arg that we have is.
21313b6e0a59SMatt Amdur 	 */
21323b6e0a59SMatt Amdur 	if (mdb_ctf_lookup_by_name(tn, &id) != 0)
21333b6e0a59SMatt Amdur 		return (1);
21343b6e0a59SMatt Amdur 
21353b6e0a59SMatt Amdur 	(void) mdb_ctf_type_resolve(id, &rid);
21363b6e0a59SMatt Amdur 	start = (char *)argv[argc-1].a_un.a_str;
21373b6e0a59SMatt Amdur 	delim = parse_delimiter(&start);
21383b6e0a59SMatt Amdur 
21393b6e0a59SMatt Amdur 	/*
21403b6e0a59SMatt Amdur 	 * If we hit the case where we actually have no delimiters, than we need
21413b6e0a59SMatt Amdur 	 * to make sure that we properly set up the fields the loops would.
21423b6e0a59SMatt Amdur 	 */
21433b6e0a59SMatt Amdur 	if (delim == MEMBER_DELIM_DONE)
21443b6e0a59SMatt Amdur 		(void) mdb_snprintf(member, sizeof (member), "%s", start);
21453b6e0a59SMatt Amdur 
21463b6e0a59SMatt Amdur 	while (delim != MEMBER_DELIM_DONE) {
21473b6e0a59SMatt Amdur 		switch (delim) {
21483b6e0a59SMatt Amdur 		case MEMBER_DELIM_PTR:
21493b6e0a59SMatt Amdur 			kind = mdb_ctf_type_kind(rid);
21503b6e0a59SMatt Amdur 			if (kind != CTF_K_POINTER)
21513b6e0a59SMatt Amdur 				return (1);
21523b6e0a59SMatt Amdur 
21533b6e0a59SMatt Amdur 			(void) mdb_ctf_type_reference(rid, &id);
21543b6e0a59SMatt Amdur 			(void) mdb_ctf_type_resolve(id, &rid);
21553b6e0a59SMatt Amdur 			break;
21563b6e0a59SMatt Amdur 		case MEMBER_DELIM_DOT:
21573b6e0a59SMatt Amdur 			kind = mdb_ctf_type_kind(rid);
21583b6e0a59SMatt Amdur 			if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
21593b6e0a59SMatt Amdur 				return (1);
21603b6e0a59SMatt Amdur 			break;
21613b6e0a59SMatt Amdur 		case MEMBER_DELIM_LBR:
21623b6e0a59SMatt Amdur 			end = strchr(start, ']');
21633b6e0a59SMatt Amdur 			/*
21643b6e0a59SMatt Amdur 			 * We're not going to try and tab complete the indexes
21653b6e0a59SMatt Amdur 			 * here. So for now, punt on it. Also, we're not going
21663b6e0a59SMatt Amdur 			 * to try and validate you're within the bounds, just
21673b6e0a59SMatt Amdur 			 * that you get the type you asked for.
21683b6e0a59SMatt Amdur 			 */
21693b6e0a59SMatt Amdur 			if (end == NULL)
21703b6e0a59SMatt Amdur 				return (1);
21713b6e0a59SMatt Amdur 
21723b6e0a59SMatt Amdur 			switch (mdb_ctf_type_kind(rid)) {
21733b6e0a59SMatt Amdur 			case CTF_K_POINTER:
21743b6e0a59SMatt Amdur 				(void) mdb_ctf_type_reference(rid, &id);
21753b6e0a59SMatt Amdur 				(void) mdb_ctf_type_resolve(id, &rid);
21763b6e0a59SMatt Amdur 				break;
21773b6e0a59SMatt Amdur 			case CTF_K_ARRAY:
21783b6e0a59SMatt Amdur 				(void) mdb_ctf_array_info(rid, &ar);
21793b6e0a59SMatt Amdur 				id = ar.mta_contents;
21803b6e0a59SMatt Amdur 				(void) mdb_ctf_type_resolve(id, &rid);
21813b6e0a59SMatt Amdur 				break;
21823b6e0a59SMatt Amdur 			default:
21833b6e0a59SMatt Amdur 				return (1);
21843b6e0a59SMatt Amdur 			}
21853b6e0a59SMatt Amdur 
21863b6e0a59SMatt Amdur 			start = end + 1;
21873b6e0a59SMatt Amdur 			delim = parse_delimiter(&start);
21883b6e0a59SMatt Amdur 			break;
21893b6e0a59SMatt Amdur 		case MEMBER_DELIM_ERR:
21903b6e0a59SMatt Amdur 		default:
21913b6e0a59SMatt Amdur 			break;
21923b6e0a59SMatt Amdur 		}
21933b6e0a59SMatt Amdur 
21943b6e0a59SMatt Amdur 		for (end = start + 1; isalnum(*end) || *end == '_'; end++)
21953b6e0a59SMatt Amdur 			continue;
21963b6e0a59SMatt Amdur 
21973b6e0a59SMatt Amdur 		(void) mdb_snprintf(member, end - start + 1, start);
21983b6e0a59SMatt Amdur 
21993b6e0a59SMatt Amdur 		/*
22003b6e0a59SMatt Amdur 		 * We are going to try to resolve this name as a member. There
22013b6e0a59SMatt Amdur 		 * are a few two different questions that we need to answer. The
22023b6e0a59SMatt Amdur 		 * first is do we recognize this member. The second is are we at
22033b6e0a59SMatt Amdur 		 * the end of the string. If we encounter a member that we don't
22043b6e0a59SMatt Amdur 		 * recognize before the end, then we have to error out and can't
22053b6e0a59SMatt Amdur 		 * complete it. But if there are no more delimiters then we can
22063b6e0a59SMatt Amdur 		 * try and complete it.
22073b6e0a59SMatt Amdur 		 */
22083b6e0a59SMatt Amdur 		ret = mdb_ctf_member_info(rid, member, &dul, &id);
22093b6e0a59SMatt Amdur 		start = end;
22103b6e0a59SMatt Amdur 		delim = parse_delimiter(&start);
22113b6e0a59SMatt Amdur 		if (ret != 0 && errno == EMDB_CTFNOMEMB) {
22123b6e0a59SMatt Amdur 			if (delim != MEMBER_DELIM_DONE)
22133b6e0a59SMatt Amdur 				return (1);
22143b6e0a59SMatt Amdur 			continue;
22153b6e0a59SMatt Amdur 		} else if (ret != 0)
22163b6e0a59SMatt Amdur 			return (1);
22173b6e0a59SMatt Amdur 
22183b6e0a59SMatt Amdur 		if (delim == MEMBER_DELIM_DONE)
22193b6e0a59SMatt Amdur 			return (mdb_tab_complete_member_by_id(mcp, rid,
22203b6e0a59SMatt Amdur 			    member));
22213b6e0a59SMatt Amdur 
22223b6e0a59SMatt Amdur 		(void) mdb_ctf_type_resolve(id, &rid);
22233b6e0a59SMatt Amdur 	}
22243b6e0a59SMatt Amdur 
22253b6e0a59SMatt Amdur 	/*
22263b6e0a59SMatt Amdur 	 * If we've reached here, then we need to try and tab complete the last
22273b6e0a59SMatt Amdur 	 * field, which is currently member, based on the ctf type id that we
22283b6e0a59SMatt Amdur 	 * already have in rid.
22293b6e0a59SMatt Amdur 	 */
22303b6e0a59SMatt Amdur 	return (mdb_tab_complete_member_by_id(mcp, rid, member));
22313b6e0a59SMatt Amdur }
22323b6e0a59SMatt Amdur 
22332f045fd6SRobert Mustacchi int
22342f045fd6SRobert Mustacchi cmd_print_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
22352f045fd6SRobert Mustacchi     const mdb_arg_t *argv)
22362f045fd6SRobert Mustacchi {
22372f045fd6SRobert Mustacchi 	int i, dummy;
22382f045fd6SRobert Mustacchi 
22392f045fd6SRobert Mustacchi 	/*
22402f045fd6SRobert Mustacchi 	 * This getopts is only here to make the tab completion work better when
22412f045fd6SRobert Mustacchi 	 * including options in the ::print arguments. None of the values should
22422f045fd6SRobert Mustacchi 	 * be used. This should only be updated with additional arguments, if
22432f045fd6SRobert Mustacchi 	 * they are added to cmd_print.
22442f045fd6SRobert Mustacchi 	 */
22452f045fd6SRobert Mustacchi 	i = mdb_getopts(argc, argv,
22462f045fd6SRobert Mustacchi 	    'a', MDB_OPT_SETBITS, PA_SHOWADDR, &dummy,
22472f045fd6SRobert Mustacchi 	    'C', MDB_OPT_SETBITS, TRUE, &dummy,
22482f045fd6SRobert Mustacchi 	    'c', MDB_OPT_UINTPTR, &dummy,
22492f045fd6SRobert Mustacchi 	    'd', MDB_OPT_SETBITS, PA_INTDEC, &dummy,
22502f045fd6SRobert Mustacchi 	    'h', MDB_OPT_SETBITS, PA_SHOWHOLES, &dummy,
22512f045fd6SRobert Mustacchi 	    'i', MDB_OPT_SETBITS, TRUE, &dummy,
22522f045fd6SRobert Mustacchi 	    'L', MDB_OPT_SETBITS, TRUE, &dummy,
22532f045fd6SRobert Mustacchi 	    'l', MDB_OPT_UINTPTR, &dummy,
22542f045fd6SRobert Mustacchi 	    'n', MDB_OPT_SETBITS, PA_NOSYMBOLIC, &dummy,
22552f045fd6SRobert Mustacchi 	    'p', MDB_OPT_SETBITS, TRUE, &dummy,
22562f045fd6SRobert Mustacchi 	    's', MDB_OPT_UINTPTR, &dummy,
22572f045fd6SRobert Mustacchi 	    'T', MDB_OPT_SETBITS, PA_SHOWTYPE | PA_SHOWBASETYPE, &dummy,
22582f045fd6SRobert Mustacchi 	    't', MDB_OPT_SETBITS, PA_SHOWTYPE, &dummy,
22592f045fd6SRobert Mustacchi 	    'x', MDB_OPT_SETBITS, PA_INTHEX, &dummy,
22602f045fd6SRobert Mustacchi 	    NULL);
22612f045fd6SRobert Mustacchi 
22622f045fd6SRobert Mustacchi 	argc -= i;
22632f045fd6SRobert Mustacchi 	argv += i;
22642f045fd6SRobert Mustacchi 
22652f045fd6SRobert Mustacchi 	return (cmd_print_tab_common(mcp, flags, argc, argv));
22662f045fd6SRobert Mustacchi }
22672f045fd6SRobert Mustacchi 
22687c478bd9Sstevel@tonic-gate /*
22697c478bd9Sstevel@tonic-gate  * Recursively descend a print a given data structure.  We create a struct of
22707c478bd9Sstevel@tonic-gate  * the relevant print arguments and then call mdb_ctf_type_visit() to do the
22717c478bd9Sstevel@tonic-gate  * traversal, using elt_print() as the callback for each element.
22727c478bd9Sstevel@tonic-gate  */
22737c478bd9Sstevel@tonic-gate /*ARGSUSED*/
22747c478bd9Sstevel@tonic-gate int
22757c478bd9Sstevel@tonic-gate cmd_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
22767c478bd9Sstevel@tonic-gate {
22777c478bd9Sstevel@tonic-gate 	uintptr_t opt_c = MDB_ARR_NOLIMIT, opt_l = MDB_ARR_NOLIMIT;
22787c478bd9Sstevel@tonic-gate 	uint_t opt_C = FALSE, opt_L = FALSE, opt_p = FALSE, opt_i = FALSE;
2279bf5197d8Sjwadams 	uintptr_t opt_s = (uintptr_t)-1ul;
22807c478bd9Sstevel@tonic-gate 	int uflags = (flags & DCMD_ADDRSPEC) ? PA_SHOWVAL : 0;
22817c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
22827c478bd9Sstevel@tonic-gate 	int err = DCMD_OK;
22837c478bd9Sstevel@tonic-gate 
22847c478bd9Sstevel@tonic-gate 	mdb_tgt_t *t = mdb.m_target;
22857c478bd9Sstevel@tonic-gate 	printarg_t pa;
22867c478bd9Sstevel@tonic-gate 	int d, i;
22877c478bd9Sstevel@tonic-gate 
22887c478bd9Sstevel@tonic-gate 	char s_name[MDB_SYM_NAMLEN];
22897c478bd9Sstevel@tonic-gate 	mdb_syminfo_t s_info;
22907c478bd9Sstevel@tonic-gate 	GElf_Sym sym;
22917c478bd9Sstevel@tonic-gate 
22923b6e0a59SMatt Amdur 	/*
22933b6e0a59SMatt Amdur 	 * If a new option is added, make sure the getopts above in
22943b6e0a59SMatt Amdur 	 * cmd_print_tab is also updated.
22953b6e0a59SMatt Amdur 	 */
22967c478bd9Sstevel@tonic-gate 	i = mdb_getopts(argc, argv,
22977c478bd9Sstevel@tonic-gate 	    'a', MDB_OPT_SETBITS, PA_SHOWADDR, &uflags,
22987c478bd9Sstevel@tonic-gate 	    'C', MDB_OPT_SETBITS, TRUE, &opt_C,
2299cce40297SJonathan Adams 	    'c', MDB_OPT_UINTPTR, &opt_c,
23007c478bd9Sstevel@tonic-gate 	    'd', MDB_OPT_SETBITS, PA_INTDEC, &uflags,
23017c478bd9Sstevel@tonic-gate 	    'h', MDB_OPT_SETBITS, PA_SHOWHOLES, &uflags,
2302cce40297SJonathan Adams 	    'i', MDB_OPT_SETBITS, TRUE, &opt_i,
23037c478bd9Sstevel@tonic-gate 	    'L', MDB_OPT_SETBITS, TRUE, &opt_L,
2304cce40297SJonathan Adams 	    'l', MDB_OPT_UINTPTR, &opt_l,
23057c478bd9Sstevel@tonic-gate 	    'n', MDB_OPT_SETBITS, PA_NOSYMBOLIC, &uflags,
23067c478bd9Sstevel@tonic-gate 	    'p', MDB_OPT_SETBITS, TRUE, &opt_p,
2307cce40297SJonathan Adams 	    's', MDB_OPT_UINTPTR, &opt_s,
2308cce40297SJonathan Adams 	    'T', MDB_OPT_SETBITS, PA_SHOWTYPE | PA_SHOWBASETYPE, &uflags,
23097c478bd9Sstevel@tonic-gate 	    't', MDB_OPT_SETBITS, PA_SHOWTYPE, &uflags,
23107c478bd9Sstevel@tonic-gate 	    'x', MDB_OPT_SETBITS, PA_INTHEX, &uflags,
23117c478bd9Sstevel@tonic-gate 	    NULL);
23127c478bd9Sstevel@tonic-gate 
23137c478bd9Sstevel@tonic-gate 	if (uflags & PA_INTHEX)
23147c478bd9Sstevel@tonic-gate 		uflags &= ~PA_INTDEC;	/* -x and -d are mutually exclusive */
23157c478bd9Sstevel@tonic-gate 
2316bf5197d8Sjwadams 	uflags |= PA_SHOWNAME;
23177c478bd9Sstevel@tonic-gate 
23187c478bd9Sstevel@tonic-gate 	if (opt_p && opt_i) {
23197c478bd9Sstevel@tonic-gate 		mdb_warn("-p and -i options are incompatible\n");
23207c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
23217c478bd9Sstevel@tonic-gate 	}
23227c478bd9Sstevel@tonic-gate 
23237c478bd9Sstevel@tonic-gate 	argc -= i;
23247c478bd9Sstevel@tonic-gate 	argv += i;
23257c478bd9Sstevel@tonic-gate 
23267c478bd9Sstevel@tonic-gate 	if (argc != 0 && argv->a_type == MDB_TYPE_STRING) {
23277c478bd9Sstevel@tonic-gate 		const char *t_name = s_name;
23287c478bd9Sstevel@tonic-gate 		int ret;
23297c478bd9Sstevel@tonic-gate 
23307c478bd9Sstevel@tonic-gate 		if (strchr("+-", argv->a_un.a_str[0]) != NULL)
23317c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
23327c478bd9Sstevel@tonic-gate 
23337c478bd9Sstevel@tonic-gate 		if ((ret = args_to_typename(&argc, &argv, s_name,
23347c478bd9Sstevel@tonic-gate 		    sizeof (s_name))) != 0)
23357c478bd9Sstevel@tonic-gate 			return (ret);
23367c478bd9Sstevel@tonic-gate 
23377c478bd9Sstevel@tonic-gate 		if (mdb_ctf_lookup_by_name(t_name, &id) != 0) {
23387c478bd9Sstevel@tonic-gate 			if (!(flags & DCMD_ADDRSPEC) || opt_i ||
23397c478bd9Sstevel@tonic-gate 			    addr_to_sym(t, addr, s_name, sizeof (s_name),
2340cce40297SJonathan Adams 			    &sym, &s_info) == NULL ||
23417c478bd9Sstevel@tonic-gate 			    mdb_ctf_lookup_by_symbol(&sym, &s_info, &id) != 0) {
23427c478bd9Sstevel@tonic-gate 
23437c478bd9Sstevel@tonic-gate 				mdb_warn("failed to look up type %s", t_name);
23447c478bd9Sstevel@tonic-gate 				return (DCMD_ABORT);
23457c478bd9Sstevel@tonic-gate 			}
23467c478bd9Sstevel@tonic-gate 		} else {
23477c478bd9Sstevel@tonic-gate 			argc--;
23487c478bd9Sstevel@tonic-gate 			argv++;
23497c478bd9Sstevel@tonic-gate 		}
23507c478bd9Sstevel@tonic-gate 
23517c478bd9Sstevel@tonic-gate 	} else if (!(flags & DCMD_ADDRSPEC) || opt_i) {
23527c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
23537c478bd9Sstevel@tonic-gate 
23547c478bd9Sstevel@tonic-gate 	} else if (addr_to_sym(t, addr, s_name, sizeof (s_name),
23557c478bd9Sstevel@tonic-gate 	    &sym, &s_info) == NULL) {
23567c478bd9Sstevel@tonic-gate 		mdb_warn("no symbol information for %a", addr);
23577c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
23587c478bd9Sstevel@tonic-gate 
23597c478bd9Sstevel@tonic-gate 	} else if (mdb_ctf_lookup_by_symbol(&sym, &s_info, &id) != 0) {
23607c478bd9Sstevel@tonic-gate 		mdb_warn("no type data available for %a [%u]", addr,
23617c478bd9Sstevel@tonic-gate 		    s_info.sym_id);
23627c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
23637c478bd9Sstevel@tonic-gate 	}
23647c478bd9Sstevel@tonic-gate 
23657c478bd9Sstevel@tonic-gate 	pa.pa_tgt = mdb.m_target;
23667c478bd9Sstevel@tonic-gate 	pa.pa_realtgt = pa.pa_tgt;
23677c478bd9Sstevel@tonic-gate 	pa.pa_immtgt = NULL;
23687c478bd9Sstevel@tonic-gate 	pa.pa_as = opt_p ? MDB_TGT_AS_PHYS : MDB_TGT_AS_VIRT;
23697c478bd9Sstevel@tonic-gate 	pa.pa_armemlim = mdb.m_armemlim;
23707c478bd9Sstevel@tonic-gate 	pa.pa_arstrlim = mdb.m_arstrlim;
23717c478bd9Sstevel@tonic-gate 	pa.pa_delim = "\n";
23727c478bd9Sstevel@tonic-gate 	pa.pa_flags = uflags;
23737c478bd9Sstevel@tonic-gate 	pa.pa_nest = 0;
23747c478bd9Sstevel@tonic-gate 	pa.pa_tab = 4;
23757c478bd9Sstevel@tonic-gate 	pa.pa_prefix = NULL;
23767c478bd9Sstevel@tonic-gate 	pa.pa_suffix = NULL;
23777c478bd9Sstevel@tonic-gate 	pa.pa_holes = NULL;
23787c478bd9Sstevel@tonic-gate 	pa.pa_nholes = 0;
23797c478bd9Sstevel@tonic-gate 	pa.pa_depth = 0;
2380bf5197d8Sjwadams 	pa.pa_maxdepth = opt_s;
2381838d7172SSebastien Roy 	pa.pa_nooutdepth = (uint_t)-1;
23827c478bd9Sstevel@tonic-gate 
23837c478bd9Sstevel@tonic-gate 	if ((flags & DCMD_ADDRSPEC) && !opt_i)
23847c478bd9Sstevel@tonic-gate 		pa.pa_addr = opt_p ? mdb_get_dot() : addr;
23857c478bd9Sstevel@tonic-gate 	else
2386892ad162SToomas Soome 		pa.pa_addr = 0;
23877c478bd9Sstevel@tonic-gate 
23887c478bd9Sstevel@tonic-gate 	if (opt_i) {
23897c478bd9Sstevel@tonic-gate 		const char *vargv[2];
23907c478bd9Sstevel@tonic-gate 		uintmax_t dot = mdb_get_dot();
23917c478bd9Sstevel@tonic-gate 		size_t outsize = mdb_ctf_type_size(id);
23927c478bd9Sstevel@tonic-gate 		vargv[0] = (const char *)&dot;
23937c478bd9Sstevel@tonic-gate 		vargv[1] = (const char *)&outsize;
23947c478bd9Sstevel@tonic-gate 		pa.pa_immtgt = mdb_tgt_create(mdb_value_tgt_create,
2395cce40297SJonathan Adams 		    0, 2, vargv);
23967c478bd9Sstevel@tonic-gate 		pa.pa_tgt = pa.pa_immtgt;
23977c478bd9Sstevel@tonic-gate 	}
23987c478bd9Sstevel@tonic-gate 
23997c478bd9Sstevel@tonic-gate 	if (opt_c != MDB_ARR_NOLIMIT)
24007c478bd9Sstevel@tonic-gate 		pa.pa_arstrlim = opt_c;
24017c478bd9Sstevel@tonic-gate 	if (opt_C)
24027c478bd9Sstevel@tonic-gate 		pa.pa_arstrlim = MDB_ARR_NOLIMIT;
24037c478bd9Sstevel@tonic-gate 	if (opt_l != MDB_ARR_NOLIMIT)
24047c478bd9Sstevel@tonic-gate 		pa.pa_armemlim = opt_l;
24057c478bd9Sstevel@tonic-gate 	if (opt_L)
24067c478bd9Sstevel@tonic-gate 		pa.pa_armemlim = MDB_ARR_NOLIMIT;
24077c478bd9Sstevel@tonic-gate 
24087c478bd9Sstevel@tonic-gate 	if (argc > 0) {
24097c478bd9Sstevel@tonic-gate 		for (i = 0; i < argc; i++) {
24107c478bd9Sstevel@tonic-gate 			mdb_ctf_id_t mid;
24117c478bd9Sstevel@tonic-gate 			int last_deref;
24127c478bd9Sstevel@tonic-gate 			ulong_t off;
24137c478bd9Sstevel@tonic-gate 			int kind;
24147c478bd9Sstevel@tonic-gate 			char buf[MDB_SYM_NAMLEN];
24157c478bd9Sstevel@tonic-gate 
24167c478bd9Sstevel@tonic-gate 			mdb_tgt_t *oldtgt = pa.pa_tgt;
24177c478bd9Sstevel@tonic-gate 			mdb_tgt_as_t oldas = pa.pa_as;
24187c478bd9Sstevel@tonic-gate 			mdb_tgt_addr_t oldaddr = pa.pa_addr;
24197c478bd9Sstevel@tonic-gate 
24207c478bd9Sstevel@tonic-gate 			if (argv->a_type == MDB_TYPE_STRING) {
24217c478bd9Sstevel@tonic-gate 				const char *member = argv[i].a_un.a_str;
24227c478bd9Sstevel@tonic-gate 				mdb_ctf_id_t rid;
24237c478bd9Sstevel@tonic-gate 
24247c478bd9Sstevel@tonic-gate 				if (parse_member(&pa, member, id, &mid,
24257c478bd9Sstevel@tonic-gate 				    &off, &last_deref) != 0) {
24267c478bd9Sstevel@tonic-gate 					err = DCMD_ABORT;
24277c478bd9Sstevel@tonic-gate 					goto out;
24287c478bd9Sstevel@tonic-gate 				}
24297c478bd9Sstevel@tonic-gate 
24307c478bd9Sstevel@tonic-gate 				/*
24317c478bd9Sstevel@tonic-gate 				 * If the member string ends with a "[0]"
24327c478bd9Sstevel@tonic-gate 				 * (last_deref * is true) and the type is a
24337c478bd9Sstevel@tonic-gate 				 * structure or union, * print "->" rather
24347c478bd9Sstevel@tonic-gate 				 * than "[0]." in elt_print.
24357c478bd9Sstevel@tonic-gate 				 */
24367c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_type_resolve(mid, &rid);
24377c478bd9Sstevel@tonic-gate 				kind = mdb_ctf_type_kind(rid);
24387c478bd9Sstevel@tonic-gate 				if (last_deref && IS_SOU(kind)) {
24397c478bd9Sstevel@tonic-gate 					char *end;
24407c478bd9Sstevel@tonic-gate 					(void) mdb_snprintf(buf, sizeof (buf),
24417c478bd9Sstevel@tonic-gate 					    "%s", member);
24427c478bd9Sstevel@tonic-gate 					end = strrchr(buf, '[');
24437c478bd9Sstevel@tonic-gate 					*end = '\0';
24447c478bd9Sstevel@tonic-gate 					pa.pa_suffix = "->";
24457c478bd9Sstevel@tonic-gate 					member = &buf[0];
24467c478bd9Sstevel@tonic-gate 				} else if (IS_SOU(kind)) {
24477c478bd9Sstevel@tonic-gate 					pa.pa_suffix = ".";
24487c478bd9Sstevel@tonic-gate 				} else {
24497c478bd9Sstevel@tonic-gate 					pa.pa_suffix = "";
24507c478bd9Sstevel@tonic-gate 				}
24517c478bd9Sstevel@tonic-gate 
24527c478bd9Sstevel@tonic-gate 				pa.pa_prefix = member;
24537c478bd9Sstevel@tonic-gate 			} else {
24547c478bd9Sstevel@tonic-gate 				ulong_t moff;
24557c478bd9Sstevel@tonic-gate 
24567c478bd9Sstevel@tonic-gate 				moff = (ulong_t)argv[i].a_un.a_val;
24577c478bd9Sstevel@tonic-gate 
24587c478bd9Sstevel@tonic-gate 				if (mdb_ctf_offset_to_name(id, moff * NBBY,
24597c478bd9Sstevel@tonic-gate 				    buf, sizeof (buf), 0, &mid, &off) == -1) {
24607c478bd9Sstevel@tonic-gate 					mdb_warn("invalid offset %lx\n", moff);
24617c478bd9Sstevel@tonic-gate 					err = DCMD_ABORT;
24627c478bd9Sstevel@tonic-gate 					goto out;
24637c478bd9Sstevel@tonic-gate 				}
24647c478bd9Sstevel@tonic-gate 
24657c478bd9Sstevel@tonic-gate 				pa.pa_prefix = buf;
24667c478bd9Sstevel@tonic-gate 				pa.pa_addr += moff - off / NBBY;
24677c478bd9Sstevel@tonic-gate 				pa.pa_suffix = strlen(buf) == 0 ? "" : ".";
24687c478bd9Sstevel@tonic-gate 			}
24697c478bd9Sstevel@tonic-gate 
24707c478bd9Sstevel@tonic-gate 			off %= NBBY;
2471bf5197d8Sjwadams 			if (flags & DCMD_PIPE_OUT) {
2472bf5197d8Sjwadams 				if (pipe_print(mid, off, &pa) != 0) {
2473bf5197d8Sjwadams 					mdb_warn("failed to print type");
2474bf5197d8Sjwadams 					err = DCMD_ERR;
2475bf5197d8Sjwadams 					goto out;
2476bf5197d8Sjwadams 				}
2477bf5197d8Sjwadams 			} else if (off != 0) {
2478cce40297SJonathan Adams 				mdb_ctf_id_t base;
2479cce40297SJonathan Adams 				(void) mdb_ctf_type_resolve(mid, &base);
2480cce40297SJonathan Adams 
2481cce40297SJonathan Adams 				if (elt_print("", mid, base, off, 0,
2482cce40297SJonathan Adams 				    &pa) != 0) {
24837c478bd9Sstevel@tonic-gate 					mdb_warn("failed to print type");
24847c478bd9Sstevel@tonic-gate 					err = DCMD_ERR;
24857c478bd9Sstevel@tonic-gate 					goto out;
24867c478bd9Sstevel@tonic-gate 				}
24877c478bd9Sstevel@tonic-gate 			} else {
24887c478bd9Sstevel@tonic-gate 				if (mdb_ctf_type_visit(mid, elt_print,
24897c478bd9Sstevel@tonic-gate 				    &pa) == -1) {
24907c478bd9Sstevel@tonic-gate 					mdb_warn("failed to print type");
24917c478bd9Sstevel@tonic-gate 					err = DCMD_ERR;
24927c478bd9Sstevel@tonic-gate 					goto out;
24937c478bd9Sstevel@tonic-gate 				}
24947c478bd9Sstevel@tonic-gate 
24957c478bd9Sstevel@tonic-gate 				for (d = pa.pa_depth - 1; d >= 0; d--)
24967c478bd9Sstevel@tonic-gate 					print_close_sou(&pa, d);
24977c478bd9Sstevel@tonic-gate 			}
24987c478bd9Sstevel@tonic-gate 
24997c478bd9Sstevel@tonic-gate 			pa.pa_depth = 0;
25007c478bd9Sstevel@tonic-gate 			pa.pa_tgt = oldtgt;
25017c478bd9Sstevel@tonic-gate 			pa.pa_as = oldas;
25027c478bd9Sstevel@tonic-gate 			pa.pa_addr = oldaddr;
25037c478bd9Sstevel@tonic-gate 			pa.pa_delim = "\n";
25047c478bd9Sstevel@tonic-gate 		}
25057c478bd9Sstevel@tonic-gate 
2506bf5197d8Sjwadams 	} else if (flags & DCMD_PIPE_OUT) {
2507bf5197d8Sjwadams 		if (pipe_print(id, 0, &pa) != 0) {
2508bf5197d8Sjwadams 			mdb_warn("failed to print type");
2509bf5197d8Sjwadams 			err = DCMD_ERR;
2510bf5197d8Sjwadams 			goto out;
2511bf5197d8Sjwadams 		}
25127c478bd9Sstevel@tonic-gate 	} else {
25137c478bd9Sstevel@tonic-gate 		if (mdb_ctf_type_visit(id, elt_print, &pa) == -1) {
25147c478bd9Sstevel@tonic-gate 			mdb_warn("failed to print type");
25157c478bd9Sstevel@tonic-gate 			err = DCMD_ERR;
25167c478bd9Sstevel@tonic-gate 			goto out;
25177c478bd9Sstevel@tonic-gate 		}
25187c478bd9Sstevel@tonic-gate 
25197c478bd9Sstevel@tonic-gate 		for (d = pa.pa_depth - 1; d >= 0; d--)
25207c478bd9Sstevel@tonic-gate 			print_close_sou(&pa, d);
25217c478bd9Sstevel@tonic-gate 	}
25227c478bd9Sstevel@tonic-gate 
25237c478bd9Sstevel@tonic-gate 	mdb_set_dot(addr + mdb_ctf_type_size(id));
25247c478bd9Sstevel@tonic-gate 	err = DCMD_OK;
25257c478bd9Sstevel@tonic-gate out:
25267c478bd9Sstevel@tonic-gate 	if (pa.pa_immtgt)
25277c478bd9Sstevel@tonic-gate 		mdb_tgt_destroy(pa.pa_immtgt);
25287c478bd9Sstevel@tonic-gate 	return (err);
25297c478bd9Sstevel@tonic-gate }
25307c478bd9Sstevel@tonic-gate 
25317c478bd9Sstevel@tonic-gate void
25327c478bd9Sstevel@tonic-gate print_help(void)
25337c478bd9Sstevel@tonic-gate {
2534cce40297SJonathan Adams 	mdb_printf(
2535cce40297SJonathan Adams 	    "-a         show address of object\n"
25367c478bd9Sstevel@tonic-gate 	    "-C         unlimit the length of character arrays\n"
2537cce40297SJonathan Adams 	    "-c limit   limit the length of character arrays\n"
25387c478bd9Sstevel@tonic-gate 	    "-d         output values in decimal\n"
25397c478bd9Sstevel@tonic-gate 	    "-h         print holes in structures\n"
2540cce40297SJonathan Adams 	    "-i         interpret address as data of the given type\n"
25417c478bd9Sstevel@tonic-gate 	    "-L         unlimit the length of standard arrays\n"
2542cce40297SJonathan Adams 	    "-l limit   limit the length of standard arrays\n"
2543cace7e23Seschrock 	    "-n         don't print pointers as symbol offsets\n"
25447c478bd9Sstevel@tonic-gate 	    "-p         interpret address as a physical memory address\n"
2545cce40297SJonathan Adams 	    "-s depth   limit the recursion depth\n"
2546cce40297SJonathan Adams 	    "-T         show type and <<base type>> of object\n"
25477c478bd9Sstevel@tonic-gate 	    "-t         show type of object\n"
25487c478bd9Sstevel@tonic-gate 	    "-x         output values in hexadecimal\n"
25497c478bd9Sstevel@tonic-gate 	    "\n"
25507c478bd9Sstevel@tonic-gate 	    "type may be omitted if the C type of addr can be inferred.\n"
25517c478bd9Sstevel@tonic-gate 	    "\n"
25527c478bd9Sstevel@tonic-gate 	    "Members may be specified with standard C syntax using the\n"
25537c478bd9Sstevel@tonic-gate 	    "array indexing operator \"[index]\", structure member\n"
25547c478bd9Sstevel@tonic-gate 	    "operator \".\", or structure pointer operator \"->\".\n"
25557c478bd9Sstevel@tonic-gate 	    "\n"
25567c478bd9Sstevel@tonic-gate 	    "Offsets must use the $[ expression ] syntax\n");
25577c478bd9Sstevel@tonic-gate }
25583ddcfaddSBryan Cantrill 
25593ddcfaddSBryan Cantrill static int
25603ddcfaddSBryan Cantrill printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt,
25613ddcfaddSBryan Cantrill     boolean_t sign)
25623ddcfaddSBryan Cantrill {
25633ddcfaddSBryan Cantrill 	ssize_t size;
25643ddcfaddSBryan Cantrill 	mdb_ctf_id_t base;
25653ddcfaddSBryan Cantrill 	ctf_encoding_t e;
25663ddcfaddSBryan Cantrill 
25673ddcfaddSBryan Cantrill 	union {
25683ddcfaddSBryan Cantrill 		uint64_t ui8;
25693ddcfaddSBryan Cantrill 		uint32_t ui4;
25703ddcfaddSBryan Cantrill 		uint16_t ui2;
25713ddcfaddSBryan Cantrill 		uint8_t ui1;
25723ddcfaddSBryan Cantrill 		int64_t i8;
25733ddcfaddSBryan Cantrill 		int32_t i4;
25743ddcfaddSBryan Cantrill 		int16_t i2;
25753ddcfaddSBryan Cantrill 		int8_t i1;
25763ddcfaddSBryan Cantrill 	} u;
25773ddcfaddSBryan Cantrill 
25783ddcfaddSBryan Cantrill 	if (mdb_ctf_type_resolve(id, &base) == -1) {
25793ddcfaddSBryan Cantrill 		mdb_warn("could not resolve type");
25803ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
25813ddcfaddSBryan Cantrill 	}
25823ddcfaddSBryan Cantrill 
258338ce19d2SJosef 'Jeff' Sipek 	switch (mdb_ctf_type_kind(base)) {
258438ce19d2SJosef 'Jeff' Sipek 		case CTF_K_ENUM:
258538ce19d2SJosef 'Jeff' Sipek 			e.cte_format = CTF_INT_SIGNED;
258638ce19d2SJosef 'Jeff' Sipek 			e.cte_offset = 0;
258738ce19d2SJosef 'Jeff' Sipek 			e.cte_bits = mdb_ctf_type_size(id) * NBBY;
258838ce19d2SJosef 'Jeff' Sipek 			break;
258938ce19d2SJosef 'Jeff' Sipek 		case CTF_K_INTEGER:
259038ce19d2SJosef 'Jeff' Sipek 			if (mdb_ctf_type_encoding(base, &e) != 0) {
259138ce19d2SJosef 'Jeff' Sipek 				mdb_warn("could not get type encoding");
259238ce19d2SJosef 'Jeff' Sipek 				return (DCMD_ABORT);
259338ce19d2SJosef 'Jeff' Sipek 			}
259438ce19d2SJosef 'Jeff' Sipek 			break;
259538ce19d2SJosef 'Jeff' Sipek 		default:
259638ce19d2SJosef 'Jeff' Sipek 			mdb_warn("expected integer type\n");
259738ce19d2SJosef 'Jeff' Sipek 			return (DCMD_ABORT);
25983ddcfaddSBryan Cantrill 	}
25993ddcfaddSBryan Cantrill 
26003ddcfaddSBryan Cantrill 	if (sign)
26013ddcfaddSBryan Cantrill 		sign = e.cte_format & CTF_INT_SIGNED;
26023ddcfaddSBryan Cantrill 
26033ddcfaddSBryan Cantrill 	size = e.cte_bits / NBBY;
26043ddcfaddSBryan Cantrill 
26053ddcfaddSBryan Cantrill 	/*
26063ddcfaddSBryan Cantrill 	 * Check to see if our life has been complicated by the presence of
26073ddcfaddSBryan Cantrill 	 * a bitfield.  If it has, we will print it using logic that is only
26083ddcfaddSBryan Cantrill 	 * slightly different than that found in print_bitfield(), above.  (In
26093ddcfaddSBryan Cantrill 	 * particular, see the comments there for an explanation of the
26103ddcfaddSBryan Cantrill 	 * endianness differences in this code.)
26113ddcfaddSBryan Cantrill 	 */
26123ddcfaddSBryan Cantrill 	if (size > 8 || (e.cte_bits % NBBY) != 0 ||
26133ddcfaddSBryan Cantrill 	    (size & (size - 1)) != 0) {
26143ddcfaddSBryan Cantrill 		uint64_t mask = (1ULL << e.cte_bits) - 1;
26153ddcfaddSBryan Cantrill 		uint64_t value = 0;
26163ddcfaddSBryan Cantrill 		uint8_t *buf = (uint8_t *)&value;
26173ddcfaddSBryan Cantrill 		uint8_t shift;
26183ddcfaddSBryan Cantrill 
26193ddcfaddSBryan Cantrill 		/*
26203ddcfaddSBryan Cantrill 		 * Round our size up one byte.
26213ddcfaddSBryan Cantrill 		 */
26223ddcfaddSBryan Cantrill 		size = (e.cte_bits + (NBBY - 1)) / NBBY;
26233ddcfaddSBryan Cantrill 
26243ddcfaddSBryan Cantrill 		if (e.cte_bits > sizeof (value) * NBBY - 1) {
26253ddcfaddSBryan Cantrill 			mdb_printf("invalid bitfield size %u", e.cte_bits);
26263ddcfaddSBryan Cantrill 			return (DCMD_ABORT);
26273ddcfaddSBryan Cantrill 		}
26283ddcfaddSBryan Cantrill 
26293ddcfaddSBryan Cantrill #ifdef _BIG_ENDIAN
26303ddcfaddSBryan Cantrill 		buf += sizeof (value) - size;
26313ddcfaddSBryan Cantrill 		off += e.cte_bits;
26323ddcfaddSBryan Cantrill #endif
26333ddcfaddSBryan Cantrill 
26343ddcfaddSBryan Cantrill 		if (mdb_vread(buf, size, addr) == -1) {
26353ddcfaddSBryan Cantrill 			mdb_warn("failed to read %lu bytes at %p", size, addr);
26363ddcfaddSBryan Cantrill 			return (DCMD_ERR);
26373ddcfaddSBryan Cantrill 		}
26383ddcfaddSBryan Cantrill 
26393ddcfaddSBryan Cantrill 		shift = off % NBBY;
26403ddcfaddSBryan Cantrill #ifdef _BIG_ENDIAN
26413ddcfaddSBryan Cantrill 		shift = NBBY - shift;
26423ddcfaddSBryan Cantrill #endif
26433ddcfaddSBryan Cantrill 
26443ddcfaddSBryan Cantrill 		/*
26453ddcfaddSBryan Cantrill 		 * If we have a bit offset within the byte, shift it down.
26463ddcfaddSBryan Cantrill 		 */
26473ddcfaddSBryan Cantrill 		if (off % NBBY != 0)
26483ddcfaddSBryan Cantrill 			value >>= shift;
26493ddcfaddSBryan Cantrill 		value &= mask;
26503ddcfaddSBryan Cantrill 
26513ddcfaddSBryan Cantrill 		if (sign) {
26523ddcfaddSBryan Cantrill 			int sshift = sizeof (value) * NBBY - e.cte_bits;
26533ddcfaddSBryan Cantrill 			value = ((int64_t)value << sshift) >> sshift;
26543ddcfaddSBryan Cantrill 		}
26553ddcfaddSBryan Cantrill 
26563ddcfaddSBryan Cantrill 		mdb_printf(fmt, value);
26573ddcfaddSBryan Cantrill 		return (0);
26583ddcfaddSBryan Cantrill 	}
26593ddcfaddSBryan Cantrill 
26603ddcfaddSBryan Cantrill 	if (mdb_vread(&u.i8, size, addr) == -1) {
26613ddcfaddSBryan Cantrill 		mdb_warn("failed to read %lu bytes at %p", (ulong_t)size, addr);
26623ddcfaddSBryan Cantrill 		return (DCMD_ERR);
26633ddcfaddSBryan Cantrill 	}
26643ddcfaddSBryan Cantrill 
26653ddcfaddSBryan Cantrill 	switch (size) {
26663ddcfaddSBryan Cantrill 	case sizeof (uint8_t):
26673ddcfaddSBryan Cantrill 		mdb_printf(fmt, (uint64_t)(sign ? u.i1 : u.ui1));
26683ddcfaddSBryan Cantrill 		break;
26693ddcfaddSBryan Cantrill 	case sizeof (uint16_t):
26703ddcfaddSBryan Cantrill 		mdb_printf(fmt, (uint64_t)(sign ? u.i2 : u.ui2));
26713ddcfaddSBryan Cantrill 		break;
26723ddcfaddSBryan Cantrill 	case sizeof (uint32_t):
26733ddcfaddSBryan Cantrill 		mdb_printf(fmt, (uint64_t)(sign ? u.i4 : u.ui4));
26743ddcfaddSBryan Cantrill 		break;
26753ddcfaddSBryan Cantrill 	case sizeof (uint64_t):
26763ddcfaddSBryan Cantrill 		mdb_printf(fmt, (uint64_t)(sign ? u.i8 : u.ui8));
26773ddcfaddSBryan Cantrill 		break;
26783ddcfaddSBryan Cantrill 	}
26793ddcfaddSBryan Cantrill 
26803ddcfaddSBryan Cantrill 	return (0);
26813ddcfaddSBryan Cantrill }
26823ddcfaddSBryan Cantrill 
26833ddcfaddSBryan Cantrill static int
26843ddcfaddSBryan Cantrill printf_int(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
26853ddcfaddSBryan Cantrill {
26863ddcfaddSBryan Cantrill 	return (printf_signed(id, addr, off, fmt, B_TRUE));
26873ddcfaddSBryan Cantrill }
26883ddcfaddSBryan Cantrill 
26893ddcfaddSBryan Cantrill static int
26903ddcfaddSBryan Cantrill printf_uint(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
26913ddcfaddSBryan Cantrill {
26923ddcfaddSBryan Cantrill 	return (printf_signed(id, addr, off, fmt, B_FALSE));
26933ddcfaddSBryan Cantrill }
26943ddcfaddSBryan Cantrill 
26953ddcfaddSBryan Cantrill /*ARGSUSED*/
26963ddcfaddSBryan Cantrill static int
26973ddcfaddSBryan Cantrill printf_uint32(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
26983ddcfaddSBryan Cantrill {
26993ddcfaddSBryan Cantrill 	mdb_ctf_id_t base;
27003ddcfaddSBryan Cantrill 	ctf_encoding_t e;
27013ddcfaddSBryan Cantrill 	uint32_t value;
27023ddcfaddSBryan Cantrill 
27033ddcfaddSBryan Cantrill 	if (mdb_ctf_type_resolve(id, &base) == -1) {
27043ddcfaddSBryan Cantrill 		mdb_warn("could not resolve type\n");
27053ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
27063ddcfaddSBryan Cantrill 	}
27073ddcfaddSBryan Cantrill 
27083ddcfaddSBryan Cantrill 	if (mdb_ctf_type_kind(base) != CTF_K_INTEGER ||
27093ddcfaddSBryan Cantrill 	    mdb_ctf_type_encoding(base, &e) != 0 ||
27103ddcfaddSBryan Cantrill 	    e.cte_bits / NBBY != sizeof (value)) {
27113ddcfaddSBryan Cantrill 		mdb_warn("expected 32-bit integer type\n");
27123ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
27133ddcfaddSBryan Cantrill 	}
27143ddcfaddSBryan Cantrill 
27153ddcfaddSBryan Cantrill 	if (mdb_vread(&value, sizeof (value), addr) == -1) {
27163ddcfaddSBryan Cantrill 		mdb_warn("failed to read 32-bit value at %p", addr);
27173ddcfaddSBryan Cantrill 		return (DCMD_ERR);
27183ddcfaddSBryan Cantrill 	}
27193ddcfaddSBryan Cantrill 
27203ddcfaddSBryan Cantrill 	mdb_printf(fmt, value);
27213ddcfaddSBryan Cantrill 
27223ddcfaddSBryan Cantrill 	return (0);
27233ddcfaddSBryan Cantrill }
27243ddcfaddSBryan Cantrill 
27253ddcfaddSBryan Cantrill /*ARGSUSED*/
27263ddcfaddSBryan Cantrill static int
27273ddcfaddSBryan Cantrill printf_ptr(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
27283ddcfaddSBryan Cantrill {
27293ddcfaddSBryan Cantrill 	uintptr_t value;
27303ddcfaddSBryan Cantrill 	mdb_ctf_id_t base;
27313ddcfaddSBryan Cantrill 
27323ddcfaddSBryan Cantrill 	if (mdb_ctf_type_resolve(id, &base) == -1) {
27333ddcfaddSBryan Cantrill 		mdb_warn("could not resolve type\n");
27343ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
27353ddcfaddSBryan Cantrill 	}
27363ddcfaddSBryan Cantrill 
27373ddcfaddSBryan Cantrill 	if (mdb_ctf_type_kind(base) != CTF_K_POINTER) {
27383ddcfaddSBryan Cantrill 		mdb_warn("expected pointer type\n");
27393ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
27403ddcfaddSBryan Cantrill 	}
27413ddcfaddSBryan Cantrill 
27423ddcfaddSBryan Cantrill 	if (mdb_vread(&value, sizeof (value), addr) == -1) {
27433ddcfaddSBryan Cantrill 		mdb_warn("failed to read pointer at %llx", addr);
27443ddcfaddSBryan Cantrill 		return (DCMD_ERR);
27453ddcfaddSBryan Cantrill 	}
27463ddcfaddSBryan Cantrill 
27473ddcfaddSBryan Cantrill 	mdb_printf(fmt, value);
27483ddcfaddSBryan Cantrill 
27493ddcfaddSBryan Cantrill 	return (0);
27503ddcfaddSBryan Cantrill }
27513ddcfaddSBryan Cantrill 
27523ddcfaddSBryan Cantrill /*ARGSUSED*/
27533ddcfaddSBryan Cantrill static int
27543ddcfaddSBryan Cantrill printf_string(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
27553ddcfaddSBryan Cantrill {
27563ddcfaddSBryan Cantrill 	mdb_ctf_id_t base;
27573ddcfaddSBryan Cantrill 	mdb_ctf_arinfo_t r;
27583ddcfaddSBryan Cantrill 	char buf[1024];
27593ddcfaddSBryan Cantrill 	ssize_t size;
27603ddcfaddSBryan Cantrill 
27613ddcfaddSBryan Cantrill 	if (mdb_ctf_type_resolve(id, &base) == -1) {
27623ddcfaddSBryan Cantrill 		mdb_warn("could not resolve type");
27633ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
27643ddcfaddSBryan Cantrill 	}
27653ddcfaddSBryan Cantrill 
27663ddcfaddSBryan Cantrill 	if (mdb_ctf_type_kind(base) == CTF_K_POINTER) {
27673ddcfaddSBryan Cantrill 		uintptr_t value;
27683ddcfaddSBryan Cantrill 
27693ddcfaddSBryan Cantrill 		if (mdb_vread(&value, sizeof (value), addr) == -1) {
27703ddcfaddSBryan Cantrill 			mdb_warn("failed to read pointer at %llx", addr);
27713ddcfaddSBryan Cantrill 			return (DCMD_ERR);
27723ddcfaddSBryan Cantrill 		}
27733ddcfaddSBryan Cantrill 
27743ddcfaddSBryan Cantrill 		if (mdb_readstr(buf, sizeof (buf) - 1, value) < 0) {
27753ddcfaddSBryan Cantrill 			mdb_warn("failed to read string at %llx", value);
27763ddcfaddSBryan Cantrill 			return (DCMD_ERR);
27773ddcfaddSBryan Cantrill 		}
27783ddcfaddSBryan Cantrill 
27793ddcfaddSBryan Cantrill 		mdb_printf(fmt, buf);
27803ddcfaddSBryan Cantrill 		return (0);
27813ddcfaddSBryan Cantrill 	}
27823ddcfaddSBryan Cantrill 
278338ce19d2SJosef 'Jeff' Sipek 	if (mdb_ctf_type_kind(base) == CTF_K_ENUM) {
278438ce19d2SJosef 'Jeff' Sipek 		const char *strval;
278538ce19d2SJosef 'Jeff' Sipek 		int value;
278638ce19d2SJosef 'Jeff' Sipek 
278738ce19d2SJosef 'Jeff' Sipek 		if (mdb_vread(&value, sizeof (value), addr) == -1) {
278838ce19d2SJosef 'Jeff' Sipek 			mdb_warn("failed to read pointer at %llx", addr);
278938ce19d2SJosef 'Jeff' Sipek 			return (DCMD_ERR);
279038ce19d2SJosef 'Jeff' Sipek 		}
279138ce19d2SJosef 'Jeff' Sipek 
279238ce19d2SJosef 'Jeff' Sipek 		if ((strval = mdb_ctf_enum_name(id, value))) {
279338ce19d2SJosef 'Jeff' Sipek 			mdb_printf(fmt, strval);
279438ce19d2SJosef 'Jeff' Sipek 		} else {
279538ce19d2SJosef 'Jeff' Sipek 			(void) mdb_snprintf(buf, sizeof (buf), "<%d>", value);
279638ce19d2SJosef 'Jeff' Sipek 			mdb_printf(fmt, buf);
279738ce19d2SJosef 'Jeff' Sipek 		}
279838ce19d2SJosef 'Jeff' Sipek 
279938ce19d2SJosef 'Jeff' Sipek 		return (0);
280038ce19d2SJosef 'Jeff' Sipek 	}
280138ce19d2SJosef 'Jeff' Sipek 
28023ddcfaddSBryan Cantrill 	if (mdb_ctf_type_kind(base) != CTF_K_ARRAY) {
28033ddcfaddSBryan Cantrill 		mdb_warn("exepected pointer or array type\n");
28043ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
28053ddcfaddSBryan Cantrill 	}
28063ddcfaddSBryan Cantrill 
28073ddcfaddSBryan Cantrill 	if (mdb_ctf_array_info(base, &r) == -1 ||
28083ddcfaddSBryan Cantrill 	    mdb_ctf_type_resolve(r.mta_contents, &base) == -1 ||
28093ddcfaddSBryan Cantrill 	    (size = mdb_ctf_type_size(base)) == -1) {
28103ddcfaddSBryan Cantrill 		mdb_warn("can't determine array type");
28113ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
28123ddcfaddSBryan Cantrill 	}
28133ddcfaddSBryan Cantrill 
28143ddcfaddSBryan Cantrill 	if (size != 1) {
28153ddcfaddSBryan Cantrill 		mdb_warn("string format specifier requires "
28163ddcfaddSBryan Cantrill 		    "an array of characters\n");
28173ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
28183ddcfaddSBryan Cantrill 	}
28193ddcfaddSBryan Cantrill 
28203ddcfaddSBryan Cantrill 	bzero(buf, sizeof (buf));
28213ddcfaddSBryan Cantrill 
28223ddcfaddSBryan Cantrill 	if (mdb_vread(buf, MIN(r.mta_nelems, sizeof (buf) - 1), addr) == -1) {
28233ddcfaddSBryan Cantrill 		mdb_warn("failed to read array at %p", addr);
28243ddcfaddSBryan Cantrill 		return (DCMD_ERR);
28253ddcfaddSBryan Cantrill 	}
28263ddcfaddSBryan Cantrill 
28273ddcfaddSBryan Cantrill 	mdb_printf(fmt, buf);
28283ddcfaddSBryan Cantrill 
28293ddcfaddSBryan Cantrill 	return (0);
28303ddcfaddSBryan Cantrill }
28313ddcfaddSBryan Cantrill 
28323ddcfaddSBryan Cantrill /*ARGSUSED*/
28333ddcfaddSBryan Cantrill static int
28343ddcfaddSBryan Cantrill printf_ipv6(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
28353ddcfaddSBryan Cantrill {
28363ddcfaddSBryan Cantrill 	mdb_ctf_id_t base;
28373ddcfaddSBryan Cantrill 	mdb_ctf_id_t ipv6_type, ipv6_base;
28383ddcfaddSBryan Cantrill 	in6_addr_t ipv6;
28393ddcfaddSBryan Cantrill 
28403ddcfaddSBryan Cantrill 	if (mdb_ctf_lookup_by_name("in6_addr_t", &ipv6_type) == -1) {
28413ddcfaddSBryan Cantrill 		mdb_warn("could not resolve in6_addr_t type\n");
28423ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
28433ddcfaddSBryan Cantrill 	}
28443ddcfaddSBryan Cantrill 
28453ddcfaddSBryan Cantrill 	if (mdb_ctf_type_resolve(id, &base) == -1) {
28463ddcfaddSBryan Cantrill 		mdb_warn("could not resolve type\n");
28473ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
28483ddcfaddSBryan Cantrill 	}
28493ddcfaddSBryan Cantrill 
28503ddcfaddSBryan Cantrill 	if (mdb_ctf_type_resolve(ipv6_type, &ipv6_base) == -1) {
28513ddcfaddSBryan Cantrill 		mdb_warn("could not resolve in6_addr_t type\n");
28523ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
28533ddcfaddSBryan Cantrill 	}
28543ddcfaddSBryan Cantrill 
28553ddcfaddSBryan Cantrill 	if (mdb_ctf_type_cmp(base, ipv6_base) != 0) {
28563ddcfaddSBryan Cantrill 		mdb_warn("requires argument of type in6_addr_t\n");
28573ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
28583ddcfaddSBryan Cantrill 	}
28593ddcfaddSBryan Cantrill 
28603ddcfaddSBryan Cantrill 	if (mdb_vread(&ipv6, sizeof (ipv6), addr) == -1) {
28613ddcfaddSBryan Cantrill 		mdb_warn("couldn't read in6_addr_t at %p", addr);
28623ddcfaddSBryan Cantrill 		return (DCMD_ERR);
28633ddcfaddSBryan Cantrill 	}
28643ddcfaddSBryan Cantrill 
28653ddcfaddSBryan Cantrill 	mdb_printf(fmt, &ipv6);
28663ddcfaddSBryan Cantrill 
28673ddcfaddSBryan Cantrill 	return (0);
28683ddcfaddSBryan Cantrill }
28693ddcfaddSBryan Cantrill 
28703ddcfaddSBryan Cantrill /*
28713ddcfaddSBryan Cantrill  * To validate the format string specified to ::printf, we run the format
28723ddcfaddSBryan Cantrill  * string through a very simple state machine that restricts us to a subset
28733ddcfaddSBryan Cantrill  * of mdb_printf() functionality.
28743ddcfaddSBryan Cantrill  */
28753ddcfaddSBryan Cantrill enum {
28763ddcfaddSBryan Cantrill 	PRINTF_NOFMT = 1,		/* no current format specifier */
28773ddcfaddSBryan Cantrill 	PRINTF_PERC,			/* processed '%' */
28783ddcfaddSBryan Cantrill 	PRINTF_FMT,			/* processing format specifier */
28793ddcfaddSBryan Cantrill 	PRINTF_LEFT,			/* processed '-', expecting width */
28803ddcfaddSBryan Cantrill 	PRINTF_WIDTH,			/* processing width */
28813ddcfaddSBryan Cantrill 	PRINTF_QUES			/* processed '?', expecting format */
28823ddcfaddSBryan Cantrill };
28833ddcfaddSBryan Cantrill 
28842f045fd6SRobert Mustacchi int
28852f045fd6SRobert Mustacchi cmd_printf_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
28862f045fd6SRobert Mustacchi     const mdb_arg_t *argv)
28872f045fd6SRobert Mustacchi {
28882f045fd6SRobert Mustacchi 	int ii;
28892f045fd6SRobert Mustacchi 	char *f;
28902f045fd6SRobert Mustacchi 
28912f045fd6SRobert Mustacchi 	/*
28922f045fd6SRobert Mustacchi 	 * If argc doesn't have more than what should be the format string,
28932f045fd6SRobert Mustacchi 	 * ignore it.
28942f045fd6SRobert Mustacchi 	 */
28952f045fd6SRobert Mustacchi 	if (argc <= 1)
28962f045fd6SRobert Mustacchi 		return (0);
28972f045fd6SRobert Mustacchi 
28982f045fd6SRobert Mustacchi 	/*
28992f045fd6SRobert Mustacchi 	 * Because we aren't leveraging the lex and yacc engine, we have to
29002f045fd6SRobert Mustacchi 	 * manually walk the arguments to find both the first and last
29012f045fd6SRobert Mustacchi 	 * open/close quote of the format string.
29022f045fd6SRobert Mustacchi 	 */
29032f045fd6SRobert Mustacchi 	f = strchr(argv[0].a_un.a_str, '"');
29042f045fd6SRobert Mustacchi 	if (f == NULL)
29052f045fd6SRobert Mustacchi 		return (0);
29062f045fd6SRobert Mustacchi 
29072f045fd6SRobert Mustacchi 	f = strchr(f + 1, '"');
29082f045fd6SRobert Mustacchi 	if (f != NULL) {
29092f045fd6SRobert Mustacchi 		ii = 0;
29102f045fd6SRobert Mustacchi 	} else {
29112f045fd6SRobert Mustacchi 		for (ii = 1; ii < argc; ii++) {
29122f045fd6SRobert Mustacchi 			if (argv[ii].a_type != MDB_TYPE_STRING)
29132f045fd6SRobert Mustacchi 				continue;
29142f045fd6SRobert Mustacchi 			f = strchr(argv[ii].a_un.a_str, '"');
29152f045fd6SRobert Mustacchi 			if (f != NULL)
29162f045fd6SRobert Mustacchi 				break;
29172f045fd6SRobert Mustacchi 		}
29182f045fd6SRobert Mustacchi 		/* Never found */
29192f045fd6SRobert Mustacchi 		if (ii == argc)
29202f045fd6SRobert Mustacchi 			return (0);
29212f045fd6SRobert Mustacchi 	}
29222f045fd6SRobert Mustacchi 
29232f045fd6SRobert Mustacchi 	ii++;
29242f045fd6SRobert Mustacchi 	argc -= ii;
29252f045fd6SRobert Mustacchi 	argv += ii;
29262f045fd6SRobert Mustacchi 
29272f045fd6SRobert Mustacchi 	return (cmd_print_tab_common(mcp, flags, argc, argv));
29282f045fd6SRobert Mustacchi }
29292f045fd6SRobert Mustacchi 
29303ddcfaddSBryan Cantrill int
29313ddcfaddSBryan Cantrill cmd_printf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
29323ddcfaddSBryan Cantrill {
29333ddcfaddSBryan Cantrill 	char type[MDB_SYM_NAMLEN];
29343ddcfaddSBryan Cantrill 	int i, nfmts = 0, ret;
29353ddcfaddSBryan Cantrill 	mdb_ctf_id_t id;
29363ddcfaddSBryan Cantrill 	const char *fmt, *member;
29373ddcfaddSBryan Cantrill 	char **fmts, *last, *dest, f;
29383ddcfaddSBryan Cantrill 	int (**funcs)(mdb_ctf_id_t, uintptr_t, ulong_t, char *);
29393ddcfaddSBryan Cantrill 	int state = PRINTF_NOFMT;
29403ddcfaddSBryan Cantrill 	printarg_t pa;
29413ddcfaddSBryan Cantrill 
29423ddcfaddSBryan Cantrill 	if (!(flags & DCMD_ADDRSPEC))
29433ddcfaddSBryan Cantrill 		return (DCMD_USAGE);
29443ddcfaddSBryan Cantrill 
29453ddcfaddSBryan Cantrill 	bzero(&pa, sizeof (pa));
29463ddcfaddSBryan Cantrill 	pa.pa_as = MDB_TGT_AS_VIRT;
29473ddcfaddSBryan Cantrill 	pa.pa_realtgt = pa.pa_tgt = mdb.m_target;
29483ddcfaddSBryan Cantrill 
29493ddcfaddSBryan Cantrill 	if (argc == 0 || argv[0].a_type != MDB_TYPE_STRING) {
29503ddcfaddSBryan Cantrill 		mdb_warn("expected a format string\n");
29513ddcfaddSBryan Cantrill 		return (DCMD_USAGE);
29523ddcfaddSBryan Cantrill 	}
29533ddcfaddSBryan Cantrill 
29543ddcfaddSBryan Cantrill 	/*
29553ddcfaddSBryan Cantrill 	 * Our first argument is a format string; rip it apart and run it
29563ddcfaddSBryan Cantrill 	 * through our state machine to validate that our input is within the
29573ddcfaddSBryan Cantrill 	 * subset of mdb_printf() format strings that we allow.
29583ddcfaddSBryan Cantrill 	 */
29593ddcfaddSBryan Cantrill 	fmt = argv[0].a_un.a_str;
29603ddcfaddSBryan Cantrill 	/*
29613ddcfaddSBryan Cantrill 	 * 'dest' must be large enough to hold a copy of the format string,
29623ddcfaddSBryan Cantrill 	 * plus a NUL and up to 2 additional characters for each conversion
29633ddcfaddSBryan Cantrill 	 * in the format string.  This gives us a bloat factor of 5/2 ~= 3.
29643ddcfaddSBryan Cantrill 	 *   e.g. "%d" (strlen of 2) --> "%lld\0" (need 5 bytes)
29653ddcfaddSBryan Cantrill 	 */
29663ddcfaddSBryan Cantrill 	dest = mdb_zalloc(strlen(fmt) * 3, UM_SLEEP | UM_GC);
29673ddcfaddSBryan Cantrill 	fmts = mdb_zalloc(strlen(fmt) * sizeof (char *), UM_SLEEP | UM_GC);
29683ddcfaddSBryan Cantrill 	funcs = mdb_zalloc(strlen(fmt) * sizeof (void *), UM_SLEEP | UM_GC);
29693ddcfaddSBryan Cantrill 	last = dest;
29703ddcfaddSBryan Cantrill 
29713ddcfaddSBryan Cantrill 	for (i = 0; fmt[i] != '\0'; i++) {
29723ddcfaddSBryan Cantrill 		*dest++ = f = fmt[i];
29733ddcfaddSBryan Cantrill 
29743ddcfaddSBryan Cantrill 		switch (state) {
29753ddcfaddSBryan Cantrill 		case PRINTF_NOFMT:
29763ddcfaddSBryan Cantrill 			state = f == '%' ? PRINTF_PERC : PRINTF_NOFMT;
29773ddcfaddSBryan Cantrill 			break;
29783ddcfaddSBryan Cantrill 
29793ddcfaddSBryan Cantrill 		case PRINTF_PERC:
29803ddcfaddSBryan Cantrill 			state = f == '-' ? PRINTF_LEFT :
29813ddcfaddSBryan Cantrill 			    f >= '0' && f <= '9' ? PRINTF_WIDTH :
29823ddcfaddSBryan Cantrill 			    f == '?' ? PRINTF_QUES :
29833ddcfaddSBryan Cantrill 			    f == '%' ? PRINTF_NOFMT : PRINTF_FMT;
29843ddcfaddSBryan Cantrill 			break;
29853ddcfaddSBryan Cantrill 
29863ddcfaddSBryan Cantrill 		case PRINTF_LEFT:
29873ddcfaddSBryan Cantrill 			state = f >= '0' && f <= '9' ? PRINTF_WIDTH :
29883ddcfaddSBryan Cantrill 			    f == '?' ? PRINTF_QUES : PRINTF_FMT;
29893ddcfaddSBryan Cantrill 			break;
29903ddcfaddSBryan Cantrill 
29913ddcfaddSBryan Cantrill 		case PRINTF_WIDTH:
29923ddcfaddSBryan Cantrill 			state = f >= '0' && f <= '9' ? PRINTF_WIDTH :
29933ddcfaddSBryan Cantrill 			    PRINTF_FMT;
29943ddcfaddSBryan Cantrill 			break;
29953ddcfaddSBryan Cantrill 
29963ddcfaddSBryan Cantrill 		case PRINTF_QUES:
29973ddcfaddSBryan Cantrill 			state = PRINTF_FMT;
29983ddcfaddSBryan Cantrill 			break;
29993ddcfaddSBryan Cantrill 		}
30003ddcfaddSBryan Cantrill 
30013ddcfaddSBryan Cantrill 		if (state != PRINTF_FMT)
30023ddcfaddSBryan Cantrill 			continue;
30033ddcfaddSBryan Cantrill 
30043ddcfaddSBryan Cantrill 		dest--;
30053ddcfaddSBryan Cantrill 
30063ddcfaddSBryan Cantrill 		/*
30073ddcfaddSBryan Cantrill 		 * Now check that we have one of our valid format characters.
30083ddcfaddSBryan Cantrill 		 */
30093ddcfaddSBryan Cantrill 		switch (f) {
30103ddcfaddSBryan Cantrill 		case 'a':
30113ddcfaddSBryan Cantrill 		case 'A':
30123ddcfaddSBryan Cantrill 		case 'p':
30133ddcfaddSBryan Cantrill 			funcs[nfmts] = printf_ptr;
30143ddcfaddSBryan Cantrill 			break;
30153ddcfaddSBryan Cantrill 
30163ddcfaddSBryan Cantrill 		case 'd':
30173ddcfaddSBryan Cantrill 		case 'q':
30183ddcfaddSBryan Cantrill 		case 'R':
30193ddcfaddSBryan Cantrill 			funcs[nfmts] = printf_int;
30203ddcfaddSBryan Cantrill 			*dest++ = 'l';
30213ddcfaddSBryan Cantrill 			*dest++ = 'l';
30223ddcfaddSBryan Cantrill 			break;
30233ddcfaddSBryan Cantrill 
30243ddcfaddSBryan Cantrill 		case 'I':
30253ddcfaddSBryan Cantrill 			funcs[nfmts] = printf_uint32;
30263ddcfaddSBryan Cantrill 			break;
30273ddcfaddSBryan Cantrill 
30283ddcfaddSBryan Cantrill 		case 'N':
30293ddcfaddSBryan Cantrill 			funcs[nfmts] = printf_ipv6;
30303ddcfaddSBryan Cantrill 			break;
30313ddcfaddSBryan Cantrill 
3032cab8de14SBryan Cantrill 		case 'H':
30333ddcfaddSBryan Cantrill 		case 'o':
30343ddcfaddSBryan Cantrill 		case 'r':
30353ddcfaddSBryan Cantrill 		case 'u':
30363ddcfaddSBryan Cantrill 		case 'x':
30373ddcfaddSBryan Cantrill 		case 'X':
30383ddcfaddSBryan Cantrill 			funcs[nfmts] = printf_uint;
30393ddcfaddSBryan Cantrill 			*dest++ = 'l';
30403ddcfaddSBryan Cantrill 			*dest++ = 'l';
30413ddcfaddSBryan Cantrill 			break;
30423ddcfaddSBryan Cantrill 
30433ddcfaddSBryan Cantrill 		case 's':
30443ddcfaddSBryan Cantrill 			funcs[nfmts] = printf_string;
30453ddcfaddSBryan Cantrill 			break;
30463ddcfaddSBryan Cantrill 
30473ddcfaddSBryan Cantrill 		case 'Y':
30483ddcfaddSBryan Cantrill 			funcs[nfmts] = sizeof (time_t) == sizeof (int) ?
30493ddcfaddSBryan Cantrill 			    printf_uint32 : printf_uint;
30503ddcfaddSBryan Cantrill 			break;
30513ddcfaddSBryan Cantrill 
30523ddcfaddSBryan Cantrill 		default:
30533ddcfaddSBryan Cantrill 			mdb_warn("illegal format string at or near "
30543ddcfaddSBryan Cantrill 			    "'%c' (position %d)\n", f, i + 1);
30553ddcfaddSBryan Cantrill 			return (DCMD_ABORT);
30563ddcfaddSBryan Cantrill 		}
30573ddcfaddSBryan Cantrill 
30583ddcfaddSBryan Cantrill 		*dest++ = f;
30593ddcfaddSBryan Cantrill 		*dest++ = '\0';
30603ddcfaddSBryan Cantrill 		fmts[nfmts++] = last;
30613ddcfaddSBryan Cantrill 		last = dest;
30623ddcfaddSBryan Cantrill 		state = PRINTF_NOFMT;
30633ddcfaddSBryan Cantrill 	}
30643ddcfaddSBryan Cantrill 
30653ddcfaddSBryan Cantrill 	argc--;
30663ddcfaddSBryan Cantrill 	argv++;
30673ddcfaddSBryan Cantrill 
30683ddcfaddSBryan Cantrill 	/*
30693ddcfaddSBryan Cantrill 	 * Now we expect a type name.
30703ddcfaddSBryan Cantrill 	 */
30713ddcfaddSBryan Cantrill 	if ((ret = args_to_typename(&argc, &argv, type, sizeof (type))) != 0)
30723ddcfaddSBryan Cantrill 		return (ret);
30733ddcfaddSBryan Cantrill 
30743ddcfaddSBryan Cantrill 	argv++;
30753ddcfaddSBryan Cantrill 	argc--;
30763ddcfaddSBryan Cantrill 
30773ddcfaddSBryan Cantrill 	if (mdb_ctf_lookup_by_name(type, &id) != 0) {
30783ddcfaddSBryan Cantrill 		mdb_warn("failed to look up type %s", type);
30793ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
30803ddcfaddSBryan Cantrill 	}
30813ddcfaddSBryan Cantrill 
30823ddcfaddSBryan Cantrill 	if (argc == 0) {
30833ddcfaddSBryan Cantrill 		mdb_warn("at least one member must be specified\n");
30843ddcfaddSBryan Cantrill 		return (DCMD_USAGE);
30853ddcfaddSBryan Cantrill 	}
30863ddcfaddSBryan Cantrill 
30873ddcfaddSBryan Cantrill 	if (argc != nfmts) {
30883ddcfaddSBryan Cantrill 		mdb_warn("%s format specifiers (found %d, expected %d)\n",
30893ddcfaddSBryan Cantrill 		    argc > nfmts ? "missing" : "extra", nfmts, argc);
30903ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
30913ddcfaddSBryan Cantrill 	}
30923ddcfaddSBryan Cantrill 
30933ddcfaddSBryan Cantrill 	for (i = 0; i < argc; i++) {
30943ddcfaddSBryan Cantrill 		mdb_ctf_id_t mid;
30953ddcfaddSBryan Cantrill 		ulong_t off;
30963ddcfaddSBryan Cantrill 		int ignored;
30973ddcfaddSBryan Cantrill 
30983ddcfaddSBryan Cantrill 		if (argv[i].a_type != MDB_TYPE_STRING) {
30993ddcfaddSBryan Cantrill 			mdb_warn("expected only type member arguments\n");
31003ddcfaddSBryan Cantrill 			return (DCMD_ABORT);
31013ddcfaddSBryan Cantrill 		}
31023ddcfaddSBryan Cantrill 
31033ddcfaddSBryan Cantrill 		if (strcmp((member = argv[i].a_un.a_str), ".") == 0) {
31043ddcfaddSBryan Cantrill 			/*
31053ddcfaddSBryan Cantrill 			 * We allow "." to be specified to denote the current
31063ddcfaddSBryan Cantrill 			 * value of dot.
31073ddcfaddSBryan Cantrill 			 */
31083ddcfaddSBryan Cantrill 			if (funcs[i] != printf_ptr && funcs[i] != printf_uint &&
31093ddcfaddSBryan Cantrill 			    funcs[i] != printf_int) {
31103ddcfaddSBryan Cantrill 				mdb_warn("expected integer or pointer format "
31113ddcfaddSBryan Cantrill 				    "specifier for '.'\n");
31123ddcfaddSBryan Cantrill 				return (DCMD_ABORT);
31133ddcfaddSBryan Cantrill 			}
31143ddcfaddSBryan Cantrill 
31153ddcfaddSBryan Cantrill 			mdb_printf(fmts[i], mdb_get_dot());
31163ddcfaddSBryan Cantrill 			continue;
31173ddcfaddSBryan Cantrill 		}
31183ddcfaddSBryan Cantrill 
31193ddcfaddSBryan Cantrill 		pa.pa_addr = addr;
31203ddcfaddSBryan Cantrill 
31213ddcfaddSBryan Cantrill 		if (parse_member(&pa, member, id, &mid, &off, &ignored) != 0)
31223ddcfaddSBryan Cantrill 			return (DCMD_ABORT);
31233ddcfaddSBryan Cantrill 
31243ddcfaddSBryan Cantrill 		if ((ret = funcs[i](mid, pa.pa_addr, off, fmts[i])) != 0) {
31253ddcfaddSBryan Cantrill 			mdb_warn("failed to print member '%s'\n", member);
31263ddcfaddSBryan Cantrill 			return (ret);
31273ddcfaddSBryan Cantrill 		}
31283ddcfaddSBryan Cantrill 	}
31293ddcfaddSBryan Cantrill 
31303ddcfaddSBryan Cantrill 	mdb_printf("%s", last);
31316b98d994SRobert Mustacchi 	mdb_set_dot(addr + mdb_ctf_type_size(id));
31323ddcfaddSBryan Cantrill 
31333ddcfaddSBryan Cantrill 	return (DCMD_OK);
31343ddcfaddSBryan Cantrill }
31353ddcfaddSBryan Cantrill 
31363ddcfaddSBryan Cantrill static char _mdb_printf_help[] =
31373ddcfaddSBryan Cantrill "The format string argument is a printf(3C)-like format string that is a\n"
31383ddcfaddSBryan Cantrill "subset of the format strings supported by mdb_printf().  The type argument\n"
31393ddcfaddSBryan Cantrill "is the name of a type to be used to interpret the memory referenced by dot.\n"
31403ddcfaddSBryan Cantrill "The member should either be a field in the specified structure, or the\n"
31413ddcfaddSBryan Cantrill "special member '.', denoting the value of dot (and treated as a pointer).\n"
31423ddcfaddSBryan Cantrill "The number of members must match the number of format specifiers in the\n"
31433ddcfaddSBryan Cantrill "format string.\n"
31443ddcfaddSBryan Cantrill "\n"
31453ddcfaddSBryan Cantrill "The following format specifiers are recognized by ::printf:\n"
31463ddcfaddSBryan Cantrill "\n"
31473ddcfaddSBryan Cantrill "  %%    Prints the '%' symbol.\n"
31483ddcfaddSBryan Cantrill "  %a    Prints the member in symbolic form.\n"
31493ddcfaddSBryan Cantrill "  %d    Prints the member as a decimal integer.  If the member is a signed\n"
31503ddcfaddSBryan Cantrill "        integer type, the output will be signed.\n"
3151cab8de14SBryan Cantrill "  %H    Prints the member as a human-readable size.\n"
3152cab8de14SBryan Cantrill "  %I    Prints the member as an IPv4 address (must be 32-bit integer type).\n"
3153cab8de14SBryan Cantrill "  %N    Prints the member as an IPv6 address (must be of type in6_addr_t).\n"
31543ddcfaddSBryan Cantrill "  %o    Prints the member as an unsigned octal integer.\n"
31553ddcfaddSBryan Cantrill "  %p    Prints the member as a pointer, in hexadecimal.\n"
31563ddcfaddSBryan Cantrill "  %q    Prints the member in signed octal.  Honk if you ever use this!\n"
31573ddcfaddSBryan Cantrill "  %r    Prints the member as an unsigned value in the current output radix.\n"
31583ddcfaddSBryan Cantrill "  %R    Prints the member as a signed value in the current output radix.\n"
31593ddcfaddSBryan Cantrill "  %s    Prints the member as a string (requires a pointer or an array of\n"
31603ddcfaddSBryan Cantrill "        characters).\n"
31613ddcfaddSBryan Cantrill "  %u    Prints the member as an unsigned decimal integer.\n"
31623ddcfaddSBryan Cantrill "  %x    Prints the member in hexadecimal.\n"
31633ddcfaddSBryan Cantrill "  %X    Prints the member in hexadecimal, using the characters A-F as the\n"
31643ddcfaddSBryan Cantrill "        digits for the values 10-15.\n"
31653ddcfaddSBryan Cantrill "  %Y    Prints the member as a time_t as the string "
31663ddcfaddSBryan Cantrill 	    "'year month day HH:MM:SS'.\n"
31673ddcfaddSBryan Cantrill "\n"
31683ddcfaddSBryan Cantrill "The following field width specifiers are recognized by ::printf:\n"
31693ddcfaddSBryan Cantrill "\n"
31703ddcfaddSBryan Cantrill "  %n    Field width is set to the specified decimal value.\n"
31713ddcfaddSBryan Cantrill "  %?    Field width is set to the maximum width of a hexadecimal pointer\n"
31723ddcfaddSBryan Cantrill "        value.  This is 8 in an ILP32 environment, and 16 in an LP64\n"
31733ddcfaddSBryan Cantrill "        environment.\n"
31743ddcfaddSBryan Cantrill "\n"
31753ddcfaddSBryan Cantrill "The following flag specifers are recognized by ::printf:\n"
31763ddcfaddSBryan Cantrill "\n"
31773ddcfaddSBryan Cantrill "  %-    Left-justify the output within the specified field width.  If the\n"
31783ddcfaddSBryan Cantrill "        width of the output is less than the specified field width, the\n"
31793ddcfaddSBryan Cantrill "        output will be padded with blanks on the right-hand side.  Without\n"
31803ddcfaddSBryan Cantrill "        %-, values are right-justified by default.\n"
31813ddcfaddSBryan Cantrill "\n"
31823ddcfaddSBryan Cantrill "  %0    Zero-fill the output field if the output is right-justified and the\n"
31833ddcfaddSBryan Cantrill "        width of the output is less than the specified field width.  Without\n"
31843ddcfaddSBryan Cantrill "        %0, right-justified values are prepended with blanks in order to\n"
31853ddcfaddSBryan Cantrill "        fill the field.\n"
31863ddcfaddSBryan Cantrill "\n"
31873ddcfaddSBryan Cantrill "Examples: \n"
31883ddcfaddSBryan Cantrill "\n"
31893ddcfaddSBryan Cantrill "  ::walk proc | "
31903ddcfaddSBryan Cantrill 	"::printf \"%-6d %s\\n\" proc_t p_pidp->pid_id p_user.u_psargs\n"
31913ddcfaddSBryan Cantrill "  ::walk thread | "
31923ddcfaddSBryan Cantrill 	"::printf \"%?p %3d %a\\n\" kthread_t . t_pri t_startpc\n"
31933ddcfaddSBryan Cantrill "  ::walk zone | "
31943ddcfaddSBryan Cantrill 	"::printf \"%-40s %20s\\n\" zone_t zone_name zone_nodename\n"
31953ddcfaddSBryan Cantrill "  ::walk ire | "
31963ddcfaddSBryan Cantrill 	"::printf \"%Y %I\\n\" ire_t ire_create_time ire_u.ire4_u.ire4_addr\n"
31973ddcfaddSBryan Cantrill "\n";
31983ddcfaddSBryan Cantrill 
31993ddcfaddSBryan Cantrill void
32003ddcfaddSBryan Cantrill printf_help(void)
32013ddcfaddSBryan Cantrill {
32023ddcfaddSBryan Cantrill 	mdb_printf("%s", _mdb_printf_help);
32033ddcfaddSBryan Cantrill }
3204