xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_print.c (revision 60377884)
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.
30*60377884SRobert Mustacchi  * Copyright 2022 Oxide Computer Company
313b6e0a59SMatt Amdur  */
323b6e0a59SMatt Amdur 
337c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
347c478bd9Sstevel@tonic-gate #include <mdb/mdb_target.h>
357c478bd9Sstevel@tonic-gate #include <mdb/mdb_argvec.h>
367c478bd9Sstevel@tonic-gate #include <mdb/mdb_string.h>
377c478bd9Sstevel@tonic-gate #include <mdb/mdb_stdlib.h>
387c478bd9Sstevel@tonic-gate #include <mdb/mdb_err.h>
397c478bd9Sstevel@tonic-gate #include <mdb/mdb_debug.h>
407c478bd9Sstevel@tonic-gate #include <mdb/mdb_fmt.h>
417c478bd9Sstevel@tonic-gate #include <mdb/mdb_ctf.h>
427c478bd9Sstevel@tonic-gate #include <mdb/mdb_ctf_impl.h>
437c478bd9Sstevel@tonic-gate #include <mdb/mdb.h>
443b6e0a59SMatt Amdur #include <mdb/mdb_tab.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h>
477c478bd9Sstevel@tonic-gate #include <sys/param.h>
487c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
493ddcfaddSBryan Cantrill #include <netinet/in.h>
507c478bd9Sstevel@tonic-gate #include <strings.h>
517c478bd9Sstevel@tonic-gate #include <libctf.h>
527c478bd9Sstevel@tonic-gate #include <ctype.h>
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate typedef struct holeinfo {
557c478bd9Sstevel@tonic-gate 	ulong_t hi_offset;		/* expected offset */
567c478bd9Sstevel@tonic-gate 	uchar_t hi_isunion;		/* represents a union */
577c478bd9Sstevel@tonic-gate } holeinfo_t;
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate typedef struct printarg {
607c478bd9Sstevel@tonic-gate 	mdb_tgt_t *pa_tgt;		/* current target */
617c478bd9Sstevel@tonic-gate 	mdb_tgt_t *pa_realtgt;		/* real target (for -i) */
627c478bd9Sstevel@tonic-gate 	mdb_tgt_t *pa_immtgt;		/* immediate target (for -i) */
637c478bd9Sstevel@tonic-gate 	mdb_tgt_as_t pa_as;		/* address space to use for i/o */
647c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t pa_addr;		/* base address for i/o */
657c478bd9Sstevel@tonic-gate 	ulong_t pa_armemlim;		/* limit on array elements to print */
667c478bd9Sstevel@tonic-gate 	ulong_t pa_arstrlim;		/* limit on array chars to print */
677c478bd9Sstevel@tonic-gate 	const char *pa_delim;		/* element delimiter string */
687c478bd9Sstevel@tonic-gate 	const char *pa_prefix;		/* element prefix string */
697c478bd9Sstevel@tonic-gate 	const char *pa_suffix;		/* element suffix string */
707c478bd9Sstevel@tonic-gate 	holeinfo_t *pa_holes;		/* hole detection information */
717c478bd9Sstevel@tonic-gate 	int pa_nholes;			/* size of holes array */
727c478bd9Sstevel@tonic-gate 	int pa_flags;			/* formatting flags (see below) */
737c478bd9Sstevel@tonic-gate 	int pa_depth;			/* previous depth */
747c478bd9Sstevel@tonic-gate 	int pa_nest;			/* array nesting depth */
757c478bd9Sstevel@tonic-gate 	int pa_tab;			/* tabstop width */
76bf5197d8Sjwadams 	uint_t pa_maxdepth;		/* Limit max depth */
77838d7172SSebastien Roy 	uint_t pa_nooutdepth;		/* don't print output past this depth */
787c478bd9Sstevel@tonic-gate } printarg_t;
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate #define	PA_SHOWTYPE	0x001		/* print type name */
81cce40297SJonathan Adams #define	PA_SHOWBASETYPE	0x002		/* print base type name */
82cce40297SJonathan Adams #define	PA_SHOWNAME	0x004		/* print member name */
83cce40297SJonathan Adams #define	PA_SHOWADDR	0x008		/* print address */
84cce40297SJonathan Adams #define	PA_SHOWVAL	0x010		/* print value */
85cce40297SJonathan Adams #define	PA_SHOWHOLES	0x020		/* print holes in structs */
86cce40297SJonathan Adams #define	PA_INTHEX	0x040		/* print integer values in hex */
87cce40297SJonathan Adams #define	PA_INTDEC	0x080		/* print integer values in decimal */
88cce40297SJonathan Adams #define	PA_NOSYMBOLIC	0x100		/* don't print ptrs as func+offset */
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate #define	IS_CHAR(e) \
917c478bd9Sstevel@tonic-gate 	(((e).cte_format & (CTF_INT_CHAR | CTF_INT_SIGNED)) == \
927c478bd9Sstevel@tonic-gate 	(CTF_INT_CHAR | CTF_INT_SIGNED) && (e).cte_bits == NBBY)
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate #define	COMPOSITE_MASK	((1 << CTF_K_STRUCT) | \
957c478bd9Sstevel@tonic-gate 			(1 << CTF_K_UNION) | (1 << CTF_K_ARRAY))
967c478bd9Sstevel@tonic-gate #define	IS_COMPOSITE(k)	(((1 << k) & COMPOSITE_MASK) != 0)
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate #define	SOU_MASK	((1 << CTF_K_STRUCT) | (1 << CTF_K_UNION))
997c478bd9Sstevel@tonic-gate #define	IS_SOU(k)	(((1 << k) & SOU_MASK) != 0)
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_ERR	-1
1027c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_DONE	0
1037c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_PTR	1
1047c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_DOT	2
1057c478bd9Sstevel@tonic-gate #define	MEMBER_DELIM_LBR	3
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate typedef int printarg_f(const char *, const char *,
1087c478bd9Sstevel@tonic-gate     mdb_ctf_id_t, mdb_ctf_id_t, ulong_t, printarg_t *);
1097c478bd9Sstevel@tonic-gate 
110cce40297SJonathan Adams static int elt_print(const char *, mdb_ctf_id_t, mdb_ctf_id_t, ulong_t, int,
111cce40297SJonathan Adams     void *);
1127c478bd9Sstevel@tonic-gate static void print_close_sou(printarg_t *, int);
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate /*
1157c478bd9Sstevel@tonic-gate  * Given an address, look up the symbol ID of the specified symbol in its
1167c478bd9Sstevel@tonic-gate  * containing module.  We only support lookups for exact matches.
1177c478bd9Sstevel@tonic-gate  */
1187c478bd9Sstevel@tonic-gate static const char *
addr_to_sym(mdb_tgt_t * t,uintptr_t addr,char * name,size_t namelen,GElf_Sym * symp,mdb_syminfo_t * sip)1197c478bd9Sstevel@tonic-gate addr_to_sym(mdb_tgt_t *t, uintptr_t addr, char *name, size_t namelen,
1207c478bd9Sstevel@tonic-gate     GElf_Sym *symp, mdb_syminfo_t *sip)
1217c478bd9Sstevel@tonic-gate {
1227c478bd9Sstevel@tonic-gate 	const mdb_map_t *mp;
1237c478bd9Sstevel@tonic-gate 	const char *p;
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate 	if (mdb_tgt_lookup_by_addr(t, addr, MDB_TGT_SYM_EXACT, name,
1267c478bd9Sstevel@tonic-gate 	    namelen, NULL, NULL) == -1)
1277c478bd9Sstevel@tonic-gate 		return (NULL); /* address does not exactly match a symbol */
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	if ((p = strrsplit(name, '`')) != NULL) {
1307c478bd9Sstevel@tonic-gate 		if (mdb_tgt_lookup_by_name(t, name, p, symp, sip) == -1)
1317c478bd9Sstevel@tonic-gate 			return (NULL);
1327c478bd9Sstevel@tonic-gate 		return (p);
1337c478bd9Sstevel@tonic-gate 	}
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	if ((mp = mdb_tgt_addr_to_map(t, addr)) == NULL)
1367c478bd9Sstevel@tonic-gate 		return (NULL); /* address does not fall within a mapping */
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate 	if (mdb_tgt_lookup_by_name(t, mp->map_name, name, symp, sip) == -1)
1397c478bd9Sstevel@tonic-gate 		return (NULL);
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 	return (name);
1427c478bd9Sstevel@tonic-gate }
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate /*
1457c478bd9Sstevel@tonic-gate  * This lets dcmds be a little fancy with their processing of type arguments
1467c478bd9Sstevel@tonic-gate  * while still treating them more or less as a single argument.
1477c478bd9Sstevel@tonic-gate  * For example, if a command is invokes like this:
1487c478bd9Sstevel@tonic-gate  *
1497c478bd9Sstevel@tonic-gate  *   ::<dcmd> proc_t ...
1507c478bd9Sstevel@tonic-gate  *
1517c478bd9Sstevel@tonic-gate  * this function will just copy "proc_t" into the provided buffer. If the
1527c478bd9Sstevel@tonic-gate  * command is instead invoked like this:
1537c478bd9Sstevel@tonic-gate  *
1547c478bd9Sstevel@tonic-gate  *   ::<dcmd> struct proc ...
1557c478bd9Sstevel@tonic-gate  *
1567c478bd9Sstevel@tonic-gate  * this function will place the string "struct proc" into the provided buffer
1577c478bd9Sstevel@tonic-gate  * and increment the caller's argv and argc. This allows the caller to still
1587c478bd9Sstevel@tonic-gate  * treat the type argument logically as it would an other atomic argument.
1597c478bd9Sstevel@tonic-gate  */
1607c478bd9Sstevel@tonic-gate int
args_to_typename(int * argcp,const mdb_arg_t ** argvp,char * buf,size_t len)1617c478bd9Sstevel@tonic-gate args_to_typename(int *argcp, const mdb_arg_t **argvp, char *buf, size_t len)
1627c478bd9Sstevel@tonic-gate {
1637c478bd9Sstevel@tonic-gate 	int argc = *argcp;
1647c478bd9Sstevel@tonic-gate 	const mdb_arg_t *argv = *argvp;
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate 	if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
1677c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 	if (strcmp(argv->a_un.a_str, "struct") == 0 ||
1707c478bd9Sstevel@tonic-gate 	    strcmp(argv->a_un.a_str, "enum") == 0 ||
1717c478bd9Sstevel@tonic-gate 	    strcmp(argv->a_un.a_str, "union") == 0) {
1727c478bd9Sstevel@tonic-gate 		if (argc <= 1) {
1737c478bd9Sstevel@tonic-gate 			mdb_warn("%s is not a valid type\n", argv->a_un.a_str);
1747c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
1757c478bd9Sstevel@tonic-gate 		}
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 		if (argv[1].a_type != MDB_TYPE_STRING)
1787c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(buf, len, "%s %s",
1817c478bd9Sstevel@tonic-gate 		    argv[0].a_un.a_str, argv[1].a_un.a_str);
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 		*argcp = argc - 1;
1847c478bd9Sstevel@tonic-gate 		*argvp = argv + 1;
1857c478bd9Sstevel@tonic-gate 	} else {
1867c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(buf, len, "%s", argv[0].a_un.a_str);
1877c478bd9Sstevel@tonic-gate 	}
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	return (0);
1907c478bd9Sstevel@tonic-gate }
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1937c478bd9Sstevel@tonic-gate int
cmd_sizeof(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1947c478bd9Sstevel@tonic-gate cmd_sizeof(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1957c478bd9Sstevel@tonic-gate {
1967c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
1977c478bd9Sstevel@tonic-gate 	char tn[MDB_SYM_NAMLEN];
1987c478bd9Sstevel@tonic-gate 	int ret;
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC)
2017c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 	if ((ret = args_to_typename(&argc, &argv, tn, sizeof (tn))) != 0)
2047c478bd9Sstevel@tonic-gate 		return (ret);
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	if (argc != 1)
2077c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 	if (mdb_ctf_lookup_by_name(tn, &id) != 0) {
2107c478bd9Sstevel@tonic-gate 		mdb_warn("failed to look up type %s", tn);
2117c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
2127c478bd9Sstevel@tonic-gate 	}
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	if (flags & DCMD_PIPE_OUT)
2157c478bd9Sstevel@tonic-gate 		mdb_printf("%#lr\n", mdb_ctf_type_size(id));
2167c478bd9Sstevel@tonic-gate 	else
2177c478bd9Sstevel@tonic-gate 		mdb_printf("sizeof (%s) = %#lr\n", tn, mdb_ctf_type_size(id));
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
2207c478bd9Sstevel@tonic-gate }
2217c478bd9Sstevel@tonic-gate 
2223b6e0a59SMatt Amdur int
cmd_sizeof_tab(mdb_tab_cookie_t * mcp,uint_t flags,int argc,const mdb_arg_t * argv)2233b6e0a59SMatt Amdur cmd_sizeof_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
2243b6e0a59SMatt Amdur     const mdb_arg_t *argv)
2253b6e0a59SMatt Amdur {
2263b6e0a59SMatt Amdur 	char tn[MDB_SYM_NAMLEN];
2273b6e0a59SMatt Amdur 	int ret;
2283b6e0a59SMatt Amdur 
2293b6e0a59SMatt Amdur 	if (argc == 0 && !(flags & DCMD_TAB_SPACE))
2303b6e0a59SMatt Amdur 		return (0);
2313b6e0a59SMatt Amdur 
2323b6e0a59SMatt Amdur 	if (argc == 0 && (flags & DCMD_TAB_SPACE))
2333b6e0a59SMatt Amdur 		return (mdb_tab_complete_type(mcp, NULL, MDB_TABC_NOPOINT));
2343b6e0a59SMatt Amdur 
2353b6e0a59SMatt Amdur 	if ((ret = mdb_tab_typename(&argc, &argv, tn, sizeof (tn))) < 0)
2363b6e0a59SMatt Amdur 		return (ret);
2373b6e0a59SMatt Amdur 
2383b6e0a59SMatt Amdur 	if (argc == 1)
2393b6e0a59SMatt Amdur 		return (mdb_tab_complete_type(mcp, tn, MDB_TABC_NOPOINT));
2403b6e0a59SMatt Amdur 
2413b6e0a59SMatt Amdur 	return (0);
2423b6e0a59SMatt Amdur }
2433b6e0a59SMatt Amdur 
2447c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2457c478bd9Sstevel@tonic-gate int
cmd_offsetof(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2467c478bd9Sstevel@tonic-gate cmd_offsetof(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2477c478bd9Sstevel@tonic-gate {
2487c478bd9Sstevel@tonic-gate 	const char *member;
2497c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
2507c478bd9Sstevel@tonic-gate 	ulong_t off;
2517c478bd9Sstevel@tonic-gate 	char tn[MDB_SYM_NAMLEN];
252e0ad97e3SJonathan Adams 	ssize_t sz;
2537c478bd9Sstevel@tonic-gate 	int ret;
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC)
2567c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	if ((ret = args_to_typename(&argc, &argv, tn, sizeof (tn))) != 0)
2597c478bd9Sstevel@tonic-gate 		return (ret);
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	if (argc != 2 || argv[1].a_type != MDB_TYPE_STRING)
2627c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	if (mdb_ctf_lookup_by_name(tn, &id) != 0) {
2657c478bd9Sstevel@tonic-gate 		mdb_warn("failed to look up type %s", tn);
2667c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
2677c478bd9Sstevel@tonic-gate 	}
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	member = argv[1].a_un.a_str;
2707c478bd9Sstevel@tonic-gate 
271e0ad97e3SJonathan Adams 	if (mdb_ctf_member_info(id, member, &off, &id) != 0) {
2727c478bd9Sstevel@tonic-gate 		mdb_warn("failed to find member %s of type %s", member, tn);
2737c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
2747c478bd9Sstevel@tonic-gate 	}
2757c478bd9Sstevel@tonic-gate 
276e0ad97e3SJonathan Adams 	if (flags & DCMD_PIPE_OUT) {
277e0ad97e3SJonathan Adams 		if (off % NBBY != 0) {
278e0ad97e3SJonathan Adams 			mdb_warn("member %s of type %s is not byte-aligned\n",
279e0ad97e3SJonathan Adams 			    member, tn);
280e0ad97e3SJonathan Adams 			return (DCMD_ERR);
281e0ad97e3SJonathan Adams 		}
282e0ad97e3SJonathan Adams 		mdb_printf("%#lr", off / NBBY);
283e0ad97e3SJonathan Adams 		return (DCMD_OK);
284e0ad97e3SJonathan Adams 	}
285e0ad97e3SJonathan Adams 
286e0ad97e3SJonathan Adams 	mdb_printf("offsetof (%s, %s) = %#lr",
287e0ad97e3SJonathan Adams 	    tn, member, off / NBBY);
288e0ad97e3SJonathan Adams 	if (off % NBBY != 0)
289e0ad97e3SJonathan Adams 		mdb_printf(".%lr", off % NBBY);
290e0ad97e3SJonathan Adams 
291e0ad97e3SJonathan Adams 	if ((sz = mdb_ctf_type_size(id)) > 0)
292e0ad97e3SJonathan Adams 		mdb_printf(", sizeof (...->%s) = %#lr", member, sz);
293e0ad97e3SJonathan Adams 
294e0ad97e3SJonathan Adams 	mdb_printf("\n");
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
2977c478bd9Sstevel@tonic-gate }
2987c478bd9Sstevel@tonic-gate 
299e0ad97e3SJonathan Adams /*ARGSUSED*/
300e0ad97e3SJonathan Adams static int
enum_prefix_scan_cb(const char * name,int value,void * arg)301e0ad97e3SJonathan Adams enum_prefix_scan_cb(const char *name, int value, void *arg)
302e0ad97e3SJonathan Adams {
303e0ad97e3SJonathan Adams 	char *str = arg;
304e0ad97e3SJonathan Adams 
305e0ad97e3SJonathan Adams 	/*
306e0ad97e3SJonathan Adams 	 * This function is called with every name in the enum.  We make
307e0ad97e3SJonathan Adams 	 * "arg" be the common prefix, if any.
308e0ad97e3SJonathan Adams 	 */
309e0ad97e3SJonathan Adams 	if (str[0] == 0) {
310e0ad97e3SJonathan Adams 		if (strlcpy(arg, name, MDB_SYM_NAMLEN) >= MDB_SYM_NAMLEN)
311e0ad97e3SJonathan Adams 			return (1);
312e0ad97e3SJonathan Adams 		return (0);
313e0ad97e3SJonathan Adams 	}
314e0ad97e3SJonathan Adams 
315e0ad97e3SJonathan Adams 	while (*name == *str) {
316e0ad97e3SJonathan Adams 		if (*str == 0) {
317e0ad97e3SJonathan Adams 			if (str != arg) {
318e0ad97e3SJonathan Adams 				str--;	/* don't smother a name completely */
319e0ad97e3SJonathan Adams 			}
320e0ad97e3SJonathan Adams 			break;
321e0ad97e3SJonathan Adams 		}
322e0ad97e3SJonathan Adams 		name++;
323e0ad97e3SJonathan Adams 		str++;
324e0ad97e3SJonathan Adams 	}
325e0ad97e3SJonathan Adams 	*str = 0;
326e0ad97e3SJonathan Adams 
327e0ad97e3SJonathan Adams 	return (str == arg);	/* only continue if prefix is non-empty */
328e0ad97e3SJonathan Adams }
329e0ad97e3SJonathan Adams 
330cce40297SJonathan Adams struct enum_p2_info {
331e0ad97e3SJonathan Adams 	intmax_t e_value;	/* value we're processing */
332e0ad97e3SJonathan Adams 	char	*e_buf;		/* buffer for holding names */
333e0ad97e3SJonathan Adams 	size_t	e_size;		/* size of buffer */
334e0ad97e3SJonathan Adams 	size_t	e_prefix;	/* length of initial prefix */
335e0ad97e3SJonathan Adams 	uint_t	e_allprefix;	/* apply prefix to first guy, too */
336e0ad97e3SJonathan Adams 	uint_t	e_bits;		/* bits seen */
337e0ad97e3SJonathan Adams 	uint8_t	e_found;	/* have we seen anything? */
338e0ad97e3SJonathan Adams 	uint8_t	e_first;	/* does buf contain the first one? */
339e0ad97e3SJonathan Adams 	uint8_t	e_zero;		/* have we seen a zero value? */
340cce40297SJonathan Adams };
341cce40297SJonathan Adams 
342cce40297SJonathan Adams static int
enum_p2_cb(const char * name,int bit_arg,void * arg)343cce40297SJonathan Adams enum_p2_cb(const char *name, int bit_arg, void *arg)
344cce40297SJonathan Adams {
345cce40297SJonathan Adams 	struct enum_p2_info *eiip = arg;
346e0ad97e3SJonathan Adams 	uintmax_t bit = bit_arg;
347cce40297SJonathan Adams 
348cce40297SJonathan Adams 	if (bit != 0 && !ISP2(bit))
349cce40297SJonathan Adams 		return (1);	/* non-power-of-2; abort processing */
350cce40297SJonathan Adams 
351cce40297SJonathan Adams 	if ((bit == 0 && eiip->e_zero) ||
352cce40297SJonathan Adams 	    (bit != 0 && (eiip->e_bits & bit) != 0)) {
353cce40297SJonathan Adams 		return (0);	/* already seen this value */
354cce40297SJonathan Adams 	}
355cce40297SJonathan Adams 
356cce40297SJonathan Adams 	if (bit == 0)
357cce40297SJonathan Adams 		eiip->e_zero = 1;
358cce40297SJonathan Adams 	else
359cce40297SJonathan Adams 		eiip->e_bits |= bit;
360cce40297SJonathan Adams 
361cce40297SJonathan Adams 	if (eiip->e_buf != NULL && (eiip->e_value & bit) != 0) {
362e0ad97e3SJonathan Adams 		char *buf = eiip->e_buf;
363e0ad97e3SJonathan Adams 		size_t prefix = eiip->e_prefix;
364e0ad97e3SJonathan Adams 
365e0ad97e3SJonathan Adams 		if (eiip->e_found) {
366e0ad97e3SJonathan Adams 			(void) strlcat(buf, "|", eiip->e_size);
367e0ad97e3SJonathan Adams 
368e0ad97e3SJonathan Adams 			if (eiip->e_first && !eiip->e_allprefix && prefix > 0) {
369e0ad97e3SJonathan Adams 				char c1 = buf[prefix];
370e0ad97e3SJonathan Adams 				char c2 = buf[prefix + 1];
371e0ad97e3SJonathan Adams 				buf[prefix] = '{';
372e0ad97e3SJonathan Adams 				buf[prefix + 1] = 0;
373e0ad97e3SJonathan Adams 				mdb_printf("%s", buf);
374e0ad97e3SJonathan Adams 				buf[prefix] = c1;
375e0ad97e3SJonathan Adams 				buf[prefix + 1] = c2;
376e0ad97e3SJonathan Adams 				mdb_printf("%s", buf + prefix);
377e0ad97e3SJonathan Adams 			} else {
378e0ad97e3SJonathan Adams 				mdb_printf("%s", buf);
379e0ad97e3SJonathan Adams 			}
380cce40297SJonathan Adams 
381e0ad97e3SJonathan Adams 		}
382e0ad97e3SJonathan Adams 		/* skip the common prefix as necessary */
383e0ad97e3SJonathan Adams 		if ((eiip->e_found || eiip->e_allprefix) &&
384e0ad97e3SJonathan Adams 		    strlen(name) > prefix)
385e0ad97e3SJonathan Adams 			name += prefix;
386cce40297SJonathan Adams 
387e0ad97e3SJonathan Adams 		(void) strlcpy(eiip->e_buf, name, eiip->e_size);
388e0ad97e3SJonathan Adams 		eiip->e_first = !eiip->e_found;
389cce40297SJonathan Adams 		eiip->e_found = 1;
390cce40297SJonathan Adams 	}
391cce40297SJonathan Adams 	return (0);
392cce40297SJonathan Adams }
393cce40297SJonathan Adams 
394cce40297SJonathan Adams static int
enum_is_p2(mdb_ctf_id_t id)395e0ad97e3SJonathan Adams enum_is_p2(mdb_ctf_id_t id)
396cce40297SJonathan Adams {
397cce40297SJonathan Adams 	struct enum_p2_info eii;
398e0ad97e3SJonathan Adams 	bzero(&eii, sizeof (eii));
399e0ad97e3SJonathan Adams 
400e0ad97e3SJonathan Adams 	return (mdb_ctf_type_kind(id) == CTF_K_ENUM &&
401e0ad97e3SJonathan Adams 	    mdb_ctf_enum_iter(id, enum_p2_cb, &eii) == 0 &&
402e0ad97e3SJonathan Adams 	    eii.e_bits != 0);
403e0ad97e3SJonathan Adams }
404e0ad97e3SJonathan Adams 
405e0ad97e3SJonathan Adams static int
enum_value_print_p2(mdb_ctf_id_t id,intmax_t value,uint_t allprefix)406e0ad97e3SJonathan Adams enum_value_print_p2(mdb_ctf_id_t id, intmax_t value, uint_t allprefix)
407e0ad97e3SJonathan Adams {
408e0ad97e3SJonathan Adams 	struct enum_p2_info eii;
409e0ad97e3SJonathan Adams 	char prefix[MDB_SYM_NAMLEN + 2];
410e0ad97e3SJonathan Adams 	intmax_t missed;
411cce40297SJonathan Adams 
412cce40297SJonathan Adams 	bzero(&eii, sizeof (eii));
413cce40297SJonathan Adams 
414e0ad97e3SJonathan Adams 	eii.e_value = value;
415e0ad97e3SJonathan Adams 	eii.e_buf = prefix;
416e0ad97e3SJonathan Adams 	eii.e_size = sizeof (prefix);
417e0ad97e3SJonathan Adams 	eii.e_allprefix = allprefix;
418cce40297SJonathan Adams 
419e0ad97e3SJonathan Adams 	prefix[0] = 0;
420e0ad97e3SJonathan Adams 	if (mdb_ctf_enum_iter(id, enum_prefix_scan_cb, prefix) == 0)
421e0ad97e3SJonathan Adams 		eii.e_prefix = strlen(prefix);
422cce40297SJonathan Adams 
423e0ad97e3SJonathan Adams 	if (mdb_ctf_enum_iter(id, enum_p2_cb, &eii) != 0 || eii.e_bits == 0)
424cce40297SJonathan Adams 		return (-1);
425cce40297SJonathan Adams 
426e0ad97e3SJonathan Adams 	missed = (value & ~(intmax_t)eii.e_bits);
427cce40297SJonathan Adams 
428e0ad97e3SJonathan Adams 	if (eii.e_found) {
429e0ad97e3SJonathan Adams 		/* push out any final value, with a | if we missed anything */
430e0ad97e3SJonathan Adams 		if (!eii.e_first)
431e0ad97e3SJonathan Adams 			(void) strlcat(prefix, "}", sizeof (prefix));
432e0ad97e3SJonathan Adams 		if (missed != 0)
433e0ad97e3SJonathan Adams 			(void) strlcat(prefix, "|", sizeof (prefix));
434cce40297SJonathan Adams 
435e0ad97e3SJonathan Adams 		mdb_printf("%s", prefix);
436e0ad97e3SJonathan Adams 	}
437e0ad97e3SJonathan Adams 
438e0ad97e3SJonathan Adams 	if (!eii.e_found || missed) {
439e0ad97e3SJonathan Adams 		mdb_printf("%#llx", missed);
440cce40297SJonathan Adams 	}
441cce40297SJonathan Adams 
442cce40297SJonathan Adams 	return (0);
443cce40297SJonathan Adams }
444cce40297SJonathan Adams 
4457c478bd9Sstevel@tonic-gate struct enum_cbinfo {
4467c478bd9Sstevel@tonic-gate 	uint_t		e_flags;
4477c478bd9Sstevel@tonic-gate 	const char	*e_string;	/* NULL for value searches */
448e0ad97e3SJonathan Adams 	size_t		e_prefix;
449e0ad97e3SJonathan Adams 	intmax_t	e_value;
4507c478bd9Sstevel@tonic-gate 	uint_t		e_found;
451e0ad97e3SJonathan Adams 	mdb_ctf_id_t	e_id;
4527c478bd9Sstevel@tonic-gate };
453e0ad97e3SJonathan Adams #define	E_PRETTY		0x01
454e0ad97e3SJonathan Adams #define	E_HEX			0x02
455e0ad97e3SJonathan Adams #define	E_SEARCH_STRING		0x04
456e0ad97e3SJonathan Adams #define	E_SEARCH_VALUE		0x08
457e0ad97e3SJonathan Adams #define	E_ELIDE_PREFIX		0x10
4587c478bd9Sstevel@tonic-gate 
459cce40297SJonathan Adams static void
enum_print(struct enum_cbinfo * info,const char * name,int value)460cce40297SJonathan Adams enum_print(struct enum_cbinfo *info, const char *name, int value)
461cce40297SJonathan Adams {
462cce40297SJonathan Adams 	uint_t flags = info->e_flags;
463e0ad97e3SJonathan Adams 	uint_t elide_prefix = (info->e_flags & E_ELIDE_PREFIX);
464e0ad97e3SJonathan Adams 
465e0ad97e3SJonathan Adams 	if (name != NULL && info->e_prefix && strlen(name) > info->e_prefix)
466e0ad97e3SJonathan Adams 		name += info->e_prefix;
467cce40297SJonathan Adams 
468cce40297SJonathan Adams 	if (flags & E_PRETTY) {
469e0ad97e3SJonathan Adams 		uint_t indent = 5 + ((flags & E_HEX) ? 8 : 11);
470e0ad97e3SJonathan Adams 
471e0ad97e3SJonathan Adams 		mdb_printf((flags & E_HEX)? "%8x " : "%11d ", value);
472e0ad97e3SJonathan Adams 		(void) mdb_inc_indent(indent);
473e0ad97e3SJonathan Adams 		if (name != NULL) {
474e0ad97e3SJonathan Adams 			mdb_iob_puts(mdb.m_out, name);
475e0ad97e3SJonathan Adams 		} else {
476e0ad97e3SJonathan Adams 			(void) enum_value_print_p2(info->e_id, value,
477e0ad97e3SJonathan Adams 			    elide_prefix);
478e0ad97e3SJonathan Adams 		}
479e0ad97e3SJonathan Adams 		(void) mdb_dec_indent(indent);
480e0ad97e3SJonathan Adams 		mdb_printf("\n");
481cce40297SJonathan Adams 	} else {
482cce40297SJonathan Adams 		mdb_printf("%#r\n", value);
483cce40297SJonathan Adams 	}
484cce40297SJonathan Adams }
485cce40297SJonathan Adams 
4867c478bd9Sstevel@tonic-gate static int
enum_cb(const char * name,int value,void * arg)4877c478bd9Sstevel@tonic-gate enum_cb(const char *name, int value, void *arg)
4887c478bd9Sstevel@tonic-gate {
4897c478bd9Sstevel@tonic-gate 	struct enum_cbinfo *info = arg;
4907c478bd9Sstevel@tonic-gate 	uint_t flags = info->e_flags;
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 	if (flags & E_SEARCH_STRING) {
4937c478bd9Sstevel@tonic-gate 		if (strcmp(name, info->e_string) != 0)
4947c478bd9Sstevel@tonic-gate 			return (0);
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 	} else if (flags & E_SEARCH_VALUE) {
4977c478bd9Sstevel@tonic-gate 		if (value != info->e_value)
4987c478bd9Sstevel@tonic-gate 			return (0);
4997c478bd9Sstevel@tonic-gate 	}
5007c478bd9Sstevel@tonic-gate 
501cce40297SJonathan Adams 	enum_print(info, name, value);
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 	info->e_found = 1;
5047c478bd9Sstevel@tonic-gate 	return (0);
5057c478bd9Sstevel@tonic-gate }
5067c478bd9Sstevel@tonic-gate 
507e0ad97e3SJonathan Adams void
enum_help(void)508e0ad97e3SJonathan Adams enum_help(void)
509e0ad97e3SJonathan Adams {
510e0ad97e3SJonathan Adams 	mdb_printf("%s",
511e0ad97e3SJonathan Adams "Without an address and name, print all values for the enumeration \"enum\".\n"
512e0ad97e3SJonathan Adams "With an address, look up a particular value in \"enum\".  With a name, look\n"
513e0ad97e3SJonathan Adams "up a particular name in \"enum\".\n");
514e0ad97e3SJonathan Adams 
515e0ad97e3SJonathan Adams 	(void) mdb_dec_indent(2);
516e0ad97e3SJonathan Adams 	mdb_printf("\n%<b>OPTIONS%</b>\n");
517e0ad97e3SJonathan Adams 	(void) mdb_inc_indent(2);
518e0ad97e3SJonathan Adams 
519e0ad97e3SJonathan Adams 	mdb_printf("%s",
520e0ad97e3SJonathan Adams "   -e    remove common prefixes from enum names\n"
521e0ad97e3SJonathan Adams "   -x    report enum values in hexadecimal\n");
522e0ad97e3SJonathan Adams }
523e0ad97e3SJonathan Adams 
5247c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5257c478bd9Sstevel@tonic-gate int
cmd_enum(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)5267c478bd9Sstevel@tonic-gate cmd_enum(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
5277c478bd9Sstevel@tonic-gate {
5287c478bd9Sstevel@tonic-gate 	struct enum_cbinfo info;
5297c478bd9Sstevel@tonic-gate 
530cce40297SJonathan Adams 	char type[MDB_SYM_NAMLEN + sizeof ("enum ")];
5317c478bd9Sstevel@tonic-gate 	char tn2[MDB_SYM_NAMLEN + sizeof ("enum ")];
532e0ad97e3SJonathan Adams 	char prefix[MDB_SYM_NAMLEN];
5337c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
5347c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t idr;
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	int i;
53724537d3eSToomas Soome 	intmax_t search = 0;
538e0ad97e3SJonathan Adams 	uint_t isp2;
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 	info.e_flags = (flags & DCMD_PIPE_OUT)? 0 : E_PRETTY;
5417c478bd9Sstevel@tonic-gate 	info.e_string = NULL;
5427c478bd9Sstevel@tonic-gate 	info.e_value = 0;
5437c478bd9Sstevel@tonic-gate 	info.e_found = 0;
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 	i = mdb_getopts(argc, argv,
546e0ad97e3SJonathan Adams 	    'e', MDB_OPT_SETBITS, E_ELIDE_PREFIX, &info.e_flags,
5477c478bd9Sstevel@tonic-gate 	    'x', MDB_OPT_SETBITS, E_HEX, &info.e_flags,
5487c478bd9Sstevel@tonic-gate 	    NULL);
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 	argc -= i;
5517c478bd9Sstevel@tonic-gate 	argv += i;
5527c478bd9Sstevel@tonic-gate 
553cce40297SJonathan Adams 	if ((i = args_to_typename(&argc, &argv, type, MDB_SYM_NAMLEN)) != 0)
5547c478bd9Sstevel@tonic-gate 		return (i);
5557c478bd9Sstevel@tonic-gate 
556cce40297SJonathan Adams 	if (strchr(type, ' ') == NULL) {
5577c478bd9Sstevel@tonic-gate 		/*
5587c478bd9Sstevel@tonic-gate 		 * Check as an enumeration tag first, and fall back
5597c478bd9Sstevel@tonic-gate 		 * to checking for a typedef.  Yes, this means that
5607c478bd9Sstevel@tonic-gate 		 * anonymous enumerations whose typedefs conflict with
5617c478bd9Sstevel@tonic-gate 		 * an enum tag can't be accessed.  Don't do that.
5627c478bd9Sstevel@tonic-gate 		 */
563cce40297SJonathan Adams 		(void) mdb_snprintf(tn2, sizeof (tn2), "enum %s", type);
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 		if (mdb_ctf_lookup_by_name(tn2, &id) == 0) {
56680148899SSurya Prakki 			(void) strcpy(type, tn2);
567cce40297SJonathan Adams 		} else if (mdb_ctf_lookup_by_name(type, &id) != 0) {
568cce40297SJonathan Adams 			mdb_warn("types '%s', '%s'", tn2, type);
5697c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
5707c478bd9Sstevel@tonic-gate 		}
5717c478bd9Sstevel@tonic-gate 	} else {
572cce40297SJonathan Adams 		if (mdb_ctf_lookup_by_name(type, &id) != 0) {
573cce40297SJonathan Adams 			mdb_warn("'%s'", type);
5747c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
5757c478bd9Sstevel@tonic-gate 		}
5767c478bd9Sstevel@tonic-gate 	}
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	/* resolve it, and make sure we're looking at an enumeration */
5797c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_resolve(id, &idr) == -1) {
5807c478bd9Sstevel@tonic-gate 		mdb_warn("unable to resolve '%s'", type);
5817c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
5827c478bd9Sstevel@tonic-gate 	}
5837c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_kind(idr) != CTF_K_ENUM) {
5847c478bd9Sstevel@tonic-gate 		mdb_warn("'%s': not an enumeration\n", type);
5857c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
5867c478bd9Sstevel@tonic-gate 	}
5877c478bd9Sstevel@tonic-gate 
588e0ad97e3SJonathan Adams 	info.e_id = idr;
589e0ad97e3SJonathan Adams 
5907c478bd9Sstevel@tonic-gate 	if (argc > 2)
5917c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 	if (argc == 2) {
5947c478bd9Sstevel@tonic-gate 		if (flags & DCMD_ADDRSPEC) {
5957c478bd9Sstevel@tonic-gate 			mdb_warn("may only specify one of: name, address\n");
5967c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
5977c478bd9Sstevel@tonic-gate 		}
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate 		if (argv[1].a_type == MDB_TYPE_STRING) {
6007c478bd9Sstevel@tonic-gate 			info.e_flags |= E_SEARCH_STRING;
6017c478bd9Sstevel@tonic-gate 			info.e_string = argv[1].a_un.a_str;
6027c478bd9Sstevel@tonic-gate 		} else if (argv[1].a_type == MDB_TYPE_IMMEDIATE) {
6037c478bd9Sstevel@tonic-gate 			info.e_flags |= E_SEARCH_VALUE;
6047c478bd9Sstevel@tonic-gate 			search = argv[1].a_un.a_val;
6057c478bd9Sstevel@tonic-gate 		} else {
6067c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
6077c478bd9Sstevel@tonic-gate 		}
6087c478bd9Sstevel@tonic-gate 	}
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC) {
6117c478bd9Sstevel@tonic-gate 		info.e_flags |= E_SEARCH_VALUE;
6127c478bd9Sstevel@tonic-gate 		search = mdb_get_dot();
6137c478bd9Sstevel@tonic-gate 	}
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	if (info.e_flags & E_SEARCH_VALUE) {
6167c478bd9Sstevel@tonic-gate 		if ((int)search != search) {
6177c478bd9Sstevel@tonic-gate 			mdb_warn("value '%lld' out of enumeration range\n",
6187c478bd9Sstevel@tonic-gate 			    search);
6197c478bd9Sstevel@tonic-gate 		}
6207c478bd9Sstevel@tonic-gate 		info.e_value = search;
6217c478bd9Sstevel@tonic-gate 	}
6227c478bd9Sstevel@tonic-gate 
623e0ad97e3SJonathan Adams 	isp2 = enum_is_p2(idr);
624e0ad97e3SJonathan Adams 	if (isp2)
625e0ad97e3SJonathan Adams 		info.e_flags |= E_HEX;
626e0ad97e3SJonathan Adams 
6277c478bd9Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags) && (info.e_flags & E_PRETTY)) {
6287c478bd9Sstevel@tonic-gate 		if (info.e_flags & E_HEX)
629e0ad97e3SJonathan Adams 			mdb_printf("%<u>%8s %-64s%</u>\n", "VALUE", "NAME");
6307c478bd9Sstevel@tonic-gate 		else
631e0ad97e3SJonathan Adams 			mdb_printf("%<u>%11s %-64s%</u>\n", "VALUE", "NAME");
6327c478bd9Sstevel@tonic-gate 	}
6337c478bd9Sstevel@tonic-gate 
634cce40297SJonathan Adams 	/* if the enum is a power-of-two one, process it that way */
635e0ad97e3SJonathan Adams 	if ((info.e_flags & E_SEARCH_VALUE) && isp2) {
636e0ad97e3SJonathan Adams 		enum_print(&info, NULL, info.e_value);
637cce40297SJonathan Adams 		return (DCMD_OK);
638cce40297SJonathan Adams 	}
639cce40297SJonathan Adams 
640e0ad97e3SJonathan Adams 	prefix[0] = 0;
641e0ad97e3SJonathan Adams 	if ((info.e_flags & E_ELIDE_PREFIX) &&
642e0ad97e3SJonathan Adams 	    mdb_ctf_enum_iter(id, enum_prefix_scan_cb, prefix) == 0)
643e0ad97e3SJonathan Adams 		info.e_prefix = strlen(prefix);
644e0ad97e3SJonathan Adams 
6457c478bd9Sstevel@tonic-gate 	if (mdb_ctf_enum_iter(idr, enum_cb, &info) == -1) {
6467c478bd9Sstevel@tonic-gate 		mdb_warn("cannot walk '%s' as enum", type);
6477c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
6487c478bd9Sstevel@tonic-gate 	}
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 	if (info.e_found == 0 &&
6517c478bd9Sstevel@tonic-gate 	    (info.e_flags & (E_SEARCH_STRING | E_SEARCH_VALUE)) != 0) {
6527c478bd9Sstevel@tonic-gate 		if (info.e_flags & E_SEARCH_STRING)
6537c478bd9Sstevel@tonic-gate 			mdb_warn("name \"%s\" not in '%s'\n", info.e_string,
6547c478bd9Sstevel@tonic-gate 			    type);
6557c478bd9Sstevel@tonic-gate 		else
656e0ad97e3SJonathan Adams 			mdb_warn("value %#lld not in '%s'\n", info.e_value,
657e0ad97e3SJonathan Adams 			    type);
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
6607c478bd9Sstevel@tonic-gate 	}
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
6637c478bd9Sstevel@tonic-gate }
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate static int
setup_vcb(const char * name,uintptr_t addr)6667c478bd9Sstevel@tonic-gate setup_vcb(const char *name, uintptr_t addr)
6677c478bd9Sstevel@tonic-gate {
6687c478bd9Sstevel@tonic-gate 	const char *p;
6697c478bd9Sstevel@tonic-gate 	mdb_var_t *v;
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate 	if ((v = mdb_nv_lookup(&mdb.m_nv, name)) == NULL) {
6727c478bd9Sstevel@tonic-gate 		if ((p = strbadid(name)) != NULL) {
6737c478bd9Sstevel@tonic-gate 			mdb_warn("'%c' may not be used in a variable "
6747c478bd9Sstevel@tonic-gate 			    "name\n", *p);
6757c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
6767c478bd9Sstevel@tonic-gate 		}
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 		if ((v = mdb_nv_insert(&mdb.m_nv, name, NULL, addr, 0)) == NULL)
6797c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
6807c478bd9Sstevel@tonic-gate 	} else {
6817c478bd9Sstevel@tonic-gate 		if (v->v_flags & MDB_NV_RDONLY) {
6827c478bd9Sstevel@tonic-gate 			mdb_warn("variable %s is read-only\n", name);
6837c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
6847c478bd9Sstevel@tonic-gate 		}
6857c478bd9Sstevel@tonic-gate 	}
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate 	/*
6887c478bd9Sstevel@tonic-gate 	 * If there already exists a vcb for this variable, we may be
6897c478bd9Sstevel@tonic-gate 	 * calling the dcmd in a loop.  We only create a vcb for this
6907c478bd9Sstevel@tonic-gate 	 * variable on the first invocation.
6917c478bd9Sstevel@tonic-gate 	 */
6927c478bd9Sstevel@tonic-gate 	if (mdb_vcb_find(v, mdb.m_frame) == NULL)
6937c478bd9Sstevel@tonic-gate 		mdb_vcb_insert(mdb_vcb_create(v), mdb.m_frame);
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 	return (0);
6967c478bd9Sstevel@tonic-gate }
6977c478bd9Sstevel@tonic-gate 
6987c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6997c478bd9Sstevel@tonic-gate int
cmd_list(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)7007c478bd9Sstevel@tonic-gate cmd_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
7017c478bd9Sstevel@tonic-gate {
70228e4da25SMatthew Ahrens 	int offset;
7037c478bd9Sstevel@tonic-gate 	uintptr_t a, tmp;
7047c478bd9Sstevel@tonic-gate 	int ret;
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC) || argc == 0)
7077c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
7087c478bd9Sstevel@tonic-gate 
7097c478bd9Sstevel@tonic-gate 	if (argv->a_type != MDB_TYPE_STRING) {
7107c478bd9Sstevel@tonic-gate 		/*
7117c478bd9Sstevel@tonic-gate 		 * We are being given a raw offset in lieu of a type and
71223edb310SMax Grossman 		 * member; confirm the number of arguments and argument
71323edb310SMax Grossman 		 * type.
7147c478bd9Sstevel@tonic-gate 		 */
71523edb310SMax Grossman 		if (argc != 1 || argv->a_type != MDB_TYPE_IMMEDIATE)
7167c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 		offset = argv->a_un.a_val;
7197c478bd9Sstevel@tonic-gate 
7207c478bd9Sstevel@tonic-gate 		argv++;
7217c478bd9Sstevel@tonic-gate 		argc--;
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate 		if (offset % sizeof (uintptr_t)) {
7247c478bd9Sstevel@tonic-gate 			mdb_warn("offset must fall on a word boundary\n");
7257c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
7267c478bd9Sstevel@tonic-gate 		}
7277c478bd9Sstevel@tonic-gate 	} else {
7287c478bd9Sstevel@tonic-gate 		const char *member;
7297c478bd9Sstevel@tonic-gate 		char buf[MDB_SYM_NAMLEN];
7307c478bd9Sstevel@tonic-gate 		int ret;
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate 		ret = args_to_typename(&argc, &argv, buf, sizeof (buf));
7337c478bd9Sstevel@tonic-gate 		if (ret != 0)
7347c478bd9Sstevel@tonic-gate 			return (ret);
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate 		argv++;
7377c478bd9Sstevel@tonic-gate 		argc--;
7387c478bd9Sstevel@tonic-gate 
739b3eeeb30SCody Peter Mello 		/*
740b3eeeb30SCody Peter Mello 		 * If we make it here, we were provided a type name. We should
741b3eeeb30SCody Peter Mello 		 * only continue if we still have arguments left (e.g. member
742b3eeeb30SCody Peter Mello 		 * name and potentially a variable name).
743b3eeeb30SCody Peter Mello 		 */
744b3eeeb30SCody Peter Mello 		if (argc == 0)
745b3eeeb30SCody Peter Mello 			return (DCMD_USAGE);
746b3eeeb30SCody Peter Mello 
7477c478bd9Sstevel@tonic-gate 		member = argv->a_un.a_str;
74828e4da25SMatthew Ahrens 		offset = mdb_ctf_offsetof_by_name(buf, member);
74928e4da25SMatthew Ahrens 		if (offset == -1)
75028e4da25SMatthew Ahrens 			return (DCMD_ABORT);
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 		argv++;
7537c478bd9Sstevel@tonic-gate 		argc--;
7547c478bd9Sstevel@tonic-gate 
75528e4da25SMatthew Ahrens 		if (offset % (sizeof (uintptr_t)) != 0) {
7567c478bd9Sstevel@tonic-gate 			mdb_warn("%s is not a word-aligned member\n", member);
7577c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
7587c478bd9Sstevel@tonic-gate 		}
7597c478bd9Sstevel@tonic-gate 	}
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate 	/*
7627c478bd9Sstevel@tonic-gate 	 * If we have any unchewed arguments, a variable name must be present.
7637c478bd9Sstevel@tonic-gate 	 */
7647c478bd9Sstevel@tonic-gate 	if (argc == 1) {
7657c478bd9Sstevel@tonic-gate 		if (argv->a_type != MDB_TYPE_STRING)
7667c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 		if ((ret = setup_vcb(argv->a_un.a_str, addr)) != 0)
7697c478bd9Sstevel@tonic-gate 			return (ret);
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	} else if (argc != 0) {
7727c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
7737c478bd9Sstevel@tonic-gate 	}
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 	a = addr;
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate 	do {
7787c478bd9Sstevel@tonic-gate 		mdb_printf("%lr\n", a);
7797c478bd9Sstevel@tonic-gate 
7807c478bd9Sstevel@tonic-gate 		if (mdb_vread(&tmp, sizeof (tmp), a + offset) == -1) {
7817c478bd9Sstevel@tonic-gate 			mdb_warn("failed to read next pointer from object %p",
7827c478bd9Sstevel@tonic-gate 			    a);
7837c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
7847c478bd9Sstevel@tonic-gate 		}
7857c478bd9Sstevel@tonic-gate 
7867c478bd9Sstevel@tonic-gate 		a = tmp;
787892ad162SToomas Soome 	} while (a != addr && a != 0);
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
7907c478bd9Sstevel@tonic-gate }
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate int
cmd_array(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)7937c478bd9Sstevel@tonic-gate cmd_array(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
7947c478bd9Sstevel@tonic-gate {
7957c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
7967c478bd9Sstevel@tonic-gate 	ssize_t elemsize = 0;
7977c478bd9Sstevel@tonic-gate 	char tn[MDB_SYM_NAMLEN];
7987c478bd9Sstevel@tonic-gate 	int ret, nelem = -1;
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 	mdb_tgt_t *t = mdb.m_target;
8017c478bd9Sstevel@tonic-gate 	GElf_Sym sym;
8027c478bd9Sstevel@tonic-gate 	mdb_ctf_arinfo_t ar;
8037c478bd9Sstevel@tonic-gate 	mdb_syminfo_t s_info;
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
8067c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate 	if (argc >= 2) {
8097c478bd9Sstevel@tonic-gate 		ret = args_to_typename(&argc, &argv, tn, sizeof (tn));
8107c478bd9Sstevel@tonic-gate 		if (ret != 0)
8117c478bd9Sstevel@tonic-gate 			return (ret);
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate 		if (argc == 1)	/* unquoted compound type without count */
8147c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate 		if (mdb_ctf_lookup_by_name(tn, &id) != 0) {
8177c478bd9Sstevel@tonic-gate 			mdb_warn("failed to look up type %s", tn);
8187c478bd9Sstevel@tonic-gate 			return (DCMD_ABORT);
8197c478bd9Sstevel@tonic-gate 		}
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate 		if (argv[1].a_type == MDB_TYPE_IMMEDIATE)
8227c478bd9Sstevel@tonic-gate 			nelem = argv[1].a_un.a_val;
8237c478bd9Sstevel@tonic-gate 		else
8247c478bd9Sstevel@tonic-gate 			nelem = mdb_strtoull(argv[1].a_un.a_str);
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 		elemsize = mdb_ctf_type_size(id);
8277c478bd9Sstevel@tonic-gate 	} else if (addr_to_sym(t, addr, tn, sizeof (tn), &sym, &s_info)
828cce40297SJonathan Adams 	    != NULL && mdb_ctf_lookup_by_symbol(&sym, &s_info, &id)
829cce40297SJonathan Adams 	    == 0 && mdb_ctf_type_kind(id) == CTF_K_ARRAY &&
830cce40297SJonathan Adams 	    mdb_ctf_array_info(id, &ar) != -1) {
831cce40297SJonathan Adams 		elemsize = mdb_ctf_type_size(id) / ar.mta_nelems;
832cce40297SJonathan Adams 		nelem = ar.mta_nelems;
8337c478bd9Sstevel@tonic-gate 	} else {
8347c478bd9Sstevel@tonic-gate 		mdb_warn("no symbol information for %a", addr);
8357c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
8367c478bd9Sstevel@tonic-gate 	}
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 	if (argc == 3 || argc == 1) {
8397c478bd9Sstevel@tonic-gate 		if (argv[argc - 1].a_type != MDB_TYPE_STRING)
8407c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 		if ((ret = setup_vcb(argv[argc - 1].a_un.a_str, addr)) != 0)
8437c478bd9Sstevel@tonic-gate 			return (ret);
8447c478bd9Sstevel@tonic-gate 
8457c478bd9Sstevel@tonic-gate 	} else if (argc > 3) {
8467c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
8477c478bd9Sstevel@tonic-gate 	}
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 	for (; nelem > 0; nelem--) {
8507c478bd9Sstevel@tonic-gate 		mdb_printf("%lr\n", addr);
8517c478bd9Sstevel@tonic-gate 		addr = addr + elemsize;
8527c478bd9Sstevel@tonic-gate 	}
8537c478bd9Sstevel@tonic-gate 
8547c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
8557c478bd9Sstevel@tonic-gate }
8567c478bd9Sstevel@tonic-gate 
857*60377884SRobert Mustacchi /*
858*60377884SRobert Mustacchi  * This is a shared implementation to determine if we should treat a type as a
859*60377884SRobert Mustacchi  * bitfield. The parameters are the CTF encoding and the bit offset of the
860*60377884SRobert Mustacchi  * integer. This also exists in mdb_print.c. We consider something a bitfield
861*60377884SRobert Mustacchi  * if:
862*60377884SRobert Mustacchi  *
863*60377884SRobert Mustacchi  *  o The type is more than 8 bytes. This is a bit of a historical choice from
864*60377884SRobert Mustacchi  *    mdb and is a stranger one. The normal integer handling code generally
865*60377884SRobert Mustacchi  *    doesn't handle integers more than 64-bits in size. Of course neither does
866*60377884SRobert Mustacchi  *    the bitfield code...
867*60377884SRobert Mustacchi  *  o The bit count is not a multiple of 8.
868*60377884SRobert Mustacchi  *  o The size in bytes is not a power of 2.
869*60377884SRobert Mustacchi  *  o The offset is not a multiple of 8.
870*60377884SRobert Mustacchi  */
871*60377884SRobert Mustacchi boolean_t
is_bitfield(const ctf_encoding_t * ep,ulong_t off)872*60377884SRobert Mustacchi is_bitfield(const ctf_encoding_t *ep, ulong_t off)
873*60377884SRobert Mustacchi {
874*60377884SRobert Mustacchi 	size_t bsize = ep->cte_bits / NBBY;
875*60377884SRobert Mustacchi 	return (bsize > 8 || (ep->cte_bits % NBBY) != 0 ||
876*60377884SRobert Mustacchi 	    (bsize & (bsize - 1)) != 0 || (off % NBBY) != 0);
877*60377884SRobert Mustacchi }
878*60377884SRobert Mustacchi 
8797c478bd9Sstevel@tonic-gate /*
8807c478bd9Sstevel@tonic-gate  * Print an integer bitfield in hexadecimal by reading the enclosing byte(s)
8817c478bd9Sstevel@tonic-gate  * and then shifting and masking the data in the lower bits of a uint64_t.
8827c478bd9Sstevel@tonic-gate  */
8837c478bd9Sstevel@tonic-gate static int
print_bitfield(ulong_t off,printarg_t * pap,ctf_encoding_t * ep)8847c478bd9Sstevel@tonic-gate print_bitfield(ulong_t off, printarg_t *pap, ctf_encoding_t *ep)
8857c478bd9Sstevel@tonic-gate {
8867c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
8877c478bd9Sstevel@tonic-gate 	uint64_t mask = (1ULL << ep->cte_bits) - 1;
8887c478bd9Sstevel@tonic-gate 	uint64_t value = 0;
8897c478bd9Sstevel@tonic-gate 	uint8_t *buf = (uint8_t *)&value;
8907c478bd9Sstevel@tonic-gate 	uint8_t shift;
8917c478bd9Sstevel@tonic-gate 	const char *format;
8927c478bd9Sstevel@tonic-gate 
893*60377884SRobert Mustacchi 	/*
894*60377884SRobert Mustacchi 	 * Our bitfield may straddle a byte boundary. We explicitly take the
895*60377884SRobert Mustacchi 	 * offset of the bitfield within its byte into account when determining
896*60377884SRobert Mustacchi 	 * the overall amount of data to copy and mask off from the underlying
897*60377884SRobert Mustacchi 	 * data.
898*60377884SRobert Mustacchi 	 */
899*60377884SRobert Mustacchi 	uint_t nbits = ep->cte_bits + (off % NBBY);
900*60377884SRobert Mustacchi 	size_t size = P2ROUNDUP(nbits, NBBY) / NBBY;
901*60377884SRobert Mustacchi 
9027c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
9037c478bd9Sstevel@tonic-gate 		return (0);
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 	if (ep->cte_bits > sizeof (value) * NBBY - 1) {
9067c478bd9Sstevel@tonic-gate 		mdb_printf("??? (invalid bitfield size %u)", ep->cte_bits);
9077c478bd9Sstevel@tonic-gate 		return (0);
9087c478bd9Sstevel@tonic-gate 	}
9097c478bd9Sstevel@tonic-gate 
9107a58f538SRobert Mustacchi 	if (size > sizeof (value)) {
9117a58f538SRobert Mustacchi 		mdb_printf("??? (total bitfield too large after alignment");
912*60377884SRobert Mustacchi 		return (0);
9137a58f538SRobert Mustacchi 	}
9147a58f538SRobert Mustacchi 
9157c478bd9Sstevel@tonic-gate 	/*
9167c478bd9Sstevel@tonic-gate 	 * On big-endian machines, we need to adjust the buf pointer to refer
9177c478bd9Sstevel@tonic-gate 	 * to the lowest 'size' bytes in 'value', and we need shift based on
9187c478bd9Sstevel@tonic-gate 	 * the offset from the end of the data, not the offset of the start.
9197c478bd9Sstevel@tonic-gate 	 */
9207c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN
9217c478bd9Sstevel@tonic-gate 	buf += sizeof (value) - size;
9227c478bd9Sstevel@tonic-gate 	off += ep->cte_bits;
9237c478bd9Sstevel@tonic-gate #endif
9247a58f538SRobert Mustacchi 
9257c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, buf, size, addr) != size) {
9267c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %lu bytes at %llx",
9277c478bd9Sstevel@tonic-gate 		    (ulong_t)size, addr);
9287c478bd9Sstevel@tonic-gate 		return (1);
9297c478bd9Sstevel@tonic-gate 	}
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 	shift = off % NBBY;
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate 	/*
9347c478bd9Sstevel@tonic-gate 	 * Offsets are counted from opposite ends on little- and
9357c478bd9Sstevel@tonic-gate 	 * big-endian machines.
9367c478bd9Sstevel@tonic-gate 	 */
9377c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN
9387c478bd9Sstevel@tonic-gate 	shift = NBBY - shift;
9397c478bd9Sstevel@tonic-gate #endif
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 	/*
9427c478bd9Sstevel@tonic-gate 	 * If the bits we want do not begin on a byte boundary, shift the data
9437c478bd9Sstevel@tonic-gate 	 * right so that the value is in the lowest 'cte_bits' of 'value'.
9447c478bd9Sstevel@tonic-gate 	 */
9457c478bd9Sstevel@tonic-gate 	if (off % NBBY != 0)
9467c478bd9Sstevel@tonic-gate 		value >>= shift;
9477c478bd9Sstevel@tonic-gate 	value &= mask;
9487c478bd9Sstevel@tonic-gate 
9497c478bd9Sstevel@tonic-gate 	/*
9507c478bd9Sstevel@tonic-gate 	 * We default to printing signed bitfields as decimals,
9517c478bd9Sstevel@tonic-gate 	 * and unsigned bitfields in hexadecimal.  If they specify
9527c478bd9Sstevel@tonic-gate 	 * hexadecimal, we treat the field as unsigned.
9537c478bd9Sstevel@tonic-gate 	 */
9547c478bd9Sstevel@tonic-gate 	if ((pap->pa_flags & PA_INTHEX) ||
9557c478bd9Sstevel@tonic-gate 	    !(ep->cte_format & CTF_INT_SIGNED)) {
9567c478bd9Sstevel@tonic-gate 		format = (pap->pa_flags & PA_INTDEC)? "%#llu" : "%#llx";
9577c478bd9Sstevel@tonic-gate 	} else {
9587c478bd9Sstevel@tonic-gate 		int sshift = sizeof (value) * NBBY - ep->cte_bits;
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate 		/* sign-extend value, and print as a signed decimal */
9617c478bd9Sstevel@tonic-gate 		value = ((int64_t)value << sshift) >> sshift;
9627c478bd9Sstevel@tonic-gate 		format = "%#lld";
9637c478bd9Sstevel@tonic-gate 	}
9647c478bd9Sstevel@tonic-gate 	mdb_printf(format, value);
9657c478bd9Sstevel@tonic-gate 
9667c478bd9Sstevel@tonic-gate 	return (0);
9677c478bd9Sstevel@tonic-gate }
9687c478bd9Sstevel@tonic-gate 
969f11c6b60SJohn Levon /*
970f11c6b60SJohn Levon  * We want to print an escaped char as e.g. '\0'. We don't use mdb_fmt_print()
971f11c6b60SJohn Levon  * as it won't get auto-wrap right here (although even now, we don't include any
972f11c6b60SJohn Levon  * trailing comma).
973f11c6b60SJohn Levon  */
974f11c6b60SJohn Levon static int
print_char_val(mdb_tgt_addr_t addr,printarg_t * pap)975f11c6b60SJohn Levon print_char_val(mdb_tgt_addr_t addr, printarg_t *pap)
976f11c6b60SJohn Levon {
977f11c6b60SJohn Levon 	char cval;
978f11c6b60SJohn Levon 	char *s;
979f11c6b60SJohn Levon 
980f11c6b60SJohn Levon 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &cval, 1, addr) != 1)
981f11c6b60SJohn Levon 		return (1);
982f11c6b60SJohn Levon 
983f11c6b60SJohn Levon 	if (mdb.m_flags & MDB_FL_ADB)
984f11c6b60SJohn Levon 		s = strchr2adb(&cval, 1);
985f11c6b60SJohn Levon 	else
986f11c6b60SJohn Levon 		s = strchr2esc(&cval, 1);
987f11c6b60SJohn Levon 
988f11c6b60SJohn Levon 	mdb_printf("'%s'", s);
989f11c6b60SJohn Levon 	strfree(s);
990f11c6b60SJohn Levon 	return (0);
991f11c6b60SJohn Levon }
992f11c6b60SJohn Levon 
9937c478bd9Sstevel@tonic-gate /*
9947c478bd9Sstevel@tonic-gate  * Print out a character or integer value.  We use some simple heuristics,
9957c478bd9Sstevel@tonic-gate  * described below, to determine the appropriate radix to use for output.
9967c478bd9Sstevel@tonic-gate  */
9977c478bd9Sstevel@tonic-gate static int
print_int_val(const char * type,ctf_encoding_t * ep,ulong_t off,printarg_t * pap)9987c478bd9Sstevel@tonic-gate print_int_val(const char *type, ctf_encoding_t *ep, ulong_t off,
9997c478bd9Sstevel@tonic-gate     printarg_t *pap)
10007c478bd9Sstevel@tonic-gate {
10017c478bd9Sstevel@tonic-gate 	static const char *const sformat[] = { "%#d", "%#d", "%#d", "%#lld" };
10027c478bd9Sstevel@tonic-gate 	static const char *const uformat[] = { "%#u", "%#u", "%#u", "%#llu" };
10037c478bd9Sstevel@tonic-gate 	static const char *const xformat[] = { "%#x", "%#x", "%#x", "%#llx" };
10047c478bd9Sstevel@tonic-gate 
10057c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
10067c478bd9Sstevel@tonic-gate 	const char *const *fsp;
10077c478bd9Sstevel@tonic-gate 	size_t size;
10087c478bd9Sstevel@tonic-gate 
10097c478bd9Sstevel@tonic-gate 	union {
10107c478bd9Sstevel@tonic-gate 		uint64_t i8;
10117c478bd9Sstevel@tonic-gate 		uint32_t i4;
10127c478bd9Sstevel@tonic-gate 		uint16_t i2;
10137c478bd9Sstevel@tonic-gate 		uint8_t i1;
10147c478bd9Sstevel@tonic-gate 		time_t t;
1015838d7172SSebastien Roy 		ipaddr_t I;
10167c478bd9Sstevel@tonic-gate 	} u;
10177c478bd9Sstevel@tonic-gate 
10187c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
10197c478bd9Sstevel@tonic-gate 		return (0);
10207c478bd9Sstevel@tonic-gate 
10217c478bd9Sstevel@tonic-gate 	if (ep->cte_format & CTF_INT_VARARGS) {
10227c478bd9Sstevel@tonic-gate 		mdb_printf("...\n");
10237c478bd9Sstevel@tonic-gate 		return (0);
10247c478bd9Sstevel@tonic-gate 	}
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 	size = ep->cte_bits / NBBY;
1027*60377884SRobert Mustacchi 	if (is_bitfield(ep, off)) {
10287c478bd9Sstevel@tonic-gate 		return (print_bitfield(off, pap, ep));
10297a58f538SRobert Mustacchi 	}
10307c478bd9Sstevel@tonic-gate 
1031f11c6b60SJohn Levon 	if (IS_CHAR(*ep))
1032f11c6b60SJohn Levon 		return (print_char_val(addr, pap));
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.i8, size, addr) != size) {
10357c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %lu bytes at %llx",
10367c478bd9Sstevel@tonic-gate 		    (ulong_t)size, addr);
10377c478bd9Sstevel@tonic-gate 		return (1);
10387c478bd9Sstevel@tonic-gate 	}
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 	/*
1041838d7172SSebastien Roy 	 * We pretty-print some integer based types.  time_t values are
1042838d7172SSebastien Roy 	 * printed as a calendar date and time, and IPv4 addresses as human
1043838d7172SSebastien Roy 	 * readable dotted quads.
10447c478bd9Sstevel@tonic-gate 	 */
1045838d7172SSebastien Roy 	if (!(pap->pa_flags & (PA_INTHEX | PA_INTDEC))) {
1046838d7172SSebastien Roy 		if (strcmp(type, "time_t") == 0 && u.t != 0) {
1047838d7172SSebastien Roy 			mdb_printf("%Y", u.t);
1048838d7172SSebastien Roy 			return (0);
1049838d7172SSebastien Roy 		}
1050838d7172SSebastien Roy 		if (strcmp(type, "ipaddr_t") == 0 ||
1051838d7172SSebastien Roy 		    strcmp(type, "in_addr_t") == 0) {
1052838d7172SSebastien Roy 			mdb_printf("%I", u.I);
1053838d7172SSebastien Roy 			return (0);
1054838d7172SSebastien Roy 		}
10557c478bd9Sstevel@tonic-gate 	}
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate 	/*
10587c478bd9Sstevel@tonic-gate 	 * The default format is hexadecimal.
10597c478bd9Sstevel@tonic-gate 	 */
10607c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_INTDEC))
10617c478bd9Sstevel@tonic-gate 		fsp = xformat;
10627c478bd9Sstevel@tonic-gate 	else if (ep->cte_format & CTF_INT_SIGNED)
10637c478bd9Sstevel@tonic-gate 		fsp = sformat;
10647c478bd9Sstevel@tonic-gate 	else
10657c478bd9Sstevel@tonic-gate 		fsp = uformat;
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate 	switch (size) {
10687c478bd9Sstevel@tonic-gate 	case sizeof (uint8_t):
10697c478bd9Sstevel@tonic-gate 		mdb_printf(fsp[0], u.i1);
10707c478bd9Sstevel@tonic-gate 		break;
10717c478bd9Sstevel@tonic-gate 	case sizeof (uint16_t):
10727c478bd9Sstevel@tonic-gate 		mdb_printf(fsp[1], u.i2);
10737c478bd9Sstevel@tonic-gate 		break;
10747c478bd9Sstevel@tonic-gate 	case sizeof (uint32_t):
10757c478bd9Sstevel@tonic-gate 		mdb_printf(fsp[2], u.i4);
10767c478bd9Sstevel@tonic-gate 		break;
10777c478bd9Sstevel@tonic-gate 	case sizeof (uint64_t):
10787c478bd9Sstevel@tonic-gate 		mdb_printf(fsp[3], u.i8);
10797c478bd9Sstevel@tonic-gate 		break;
10807c478bd9Sstevel@tonic-gate 	}
10817c478bd9Sstevel@tonic-gate 	return (0);
10827c478bd9Sstevel@tonic-gate }
10837c478bd9Sstevel@tonic-gate 
10847c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10857c478bd9Sstevel@tonic-gate static int
print_int(const char * type,const char * name,mdb_ctf_id_t id,mdb_ctf_id_t base,ulong_t off,printarg_t * pap)10867c478bd9Sstevel@tonic-gate print_int(const char *type, const char *name, mdb_ctf_id_t id,
10877c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
10887c478bd9Sstevel@tonic-gate {
10897c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
10927c478bd9Sstevel@tonic-gate 		return (0);
10937c478bd9Sstevel@tonic-gate 
10947c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_encoding(base, &e) != 0) {
10957c478bd9Sstevel@tonic-gate 		mdb_printf("??? (%s)", mdb_strerror(errno));
10967c478bd9Sstevel@tonic-gate 		return (0);
10977c478bd9Sstevel@tonic-gate 	}
10987c478bd9Sstevel@tonic-gate 
10997c478bd9Sstevel@tonic-gate 	return (print_int_val(type, &e, off, pap));
11007c478bd9Sstevel@tonic-gate }
11017c478bd9Sstevel@tonic-gate 
11027c478bd9Sstevel@tonic-gate /*
11037c478bd9Sstevel@tonic-gate  * Print out a floating point value.  We only provide support for floats in
11047c478bd9Sstevel@tonic-gate  * the ANSI-C float, double, and long double formats.
11057c478bd9Sstevel@tonic-gate  */
11067c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11077c478bd9Sstevel@tonic-gate static int
print_float(const char * type,const char * name,mdb_ctf_id_t id,mdb_ctf_id_t base,ulong_t off,printarg_t * pap)11087c478bd9Sstevel@tonic-gate print_float(const char *type, const char *name, mdb_ctf_id_t id,
11097c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
11107c478bd9Sstevel@tonic-gate {
11117c478bd9Sstevel@tonic-gate #ifndef _KMDB
11127c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
11137c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
11147c478bd9Sstevel@tonic-gate 
11157c478bd9Sstevel@tonic-gate 	union {
11167c478bd9Sstevel@tonic-gate 		float f;
11177c478bd9Sstevel@tonic-gate 		double d;
11187c478bd9Sstevel@tonic-gate 		long double ld;
11197c478bd9Sstevel@tonic-gate 	} u;
11207c478bd9Sstevel@tonic-gate 
11217c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
11227c478bd9Sstevel@tonic-gate 		return (0);
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_encoding(base, &e) == 0) {
11257c478bd9Sstevel@tonic-gate 		if (e.cte_format == CTF_FP_SINGLE &&
11267c478bd9Sstevel@tonic-gate 		    e.cte_bits == sizeof (float) * NBBY) {
11277c478bd9Sstevel@tonic-gate 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.f,
11287c478bd9Sstevel@tonic-gate 			    sizeof (u.f), addr) != sizeof (u.f)) {
11297c478bd9Sstevel@tonic-gate 				mdb_warn("failed to read float at %llx", addr);
11307c478bd9Sstevel@tonic-gate 				return (1);
11317c478bd9Sstevel@tonic-gate 			}
11327c478bd9Sstevel@tonic-gate 			mdb_printf("%s", doubletos(u.f, 7, 'e'));
11337c478bd9Sstevel@tonic-gate 
11347c478bd9Sstevel@tonic-gate 		} else if (e.cte_format == CTF_FP_DOUBLE &&
11357c478bd9Sstevel@tonic-gate 		    e.cte_bits == sizeof (double) * NBBY) {
11367c478bd9Sstevel@tonic-gate 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.d,
11377c478bd9Sstevel@tonic-gate 			    sizeof (u.d), addr) != sizeof (u.d)) {
11387c478bd9Sstevel@tonic-gate 				mdb_warn("failed to read float at %llx", addr);
11397c478bd9Sstevel@tonic-gate 				return (1);
11407c478bd9Sstevel@tonic-gate 			}
11417c478bd9Sstevel@tonic-gate 			mdb_printf("%s", doubletos(u.d, 7, 'e'));
11427c478bd9Sstevel@tonic-gate 
11437c478bd9Sstevel@tonic-gate 		} else if (e.cte_format == CTF_FP_LDOUBLE &&
11447c478bd9Sstevel@tonic-gate 		    e.cte_bits == sizeof (long double) * NBBY) {
11457c478bd9Sstevel@tonic-gate 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.ld,
11467c478bd9Sstevel@tonic-gate 			    sizeof (u.ld), addr) != sizeof (u.ld)) {
11477c478bd9Sstevel@tonic-gate 				mdb_warn("failed to read float at %llx", addr);
11487c478bd9Sstevel@tonic-gate 				return (1);
11497c478bd9Sstevel@tonic-gate 			}
11507c478bd9Sstevel@tonic-gate 			mdb_printf("%s", longdoubletos(&u.ld, 16, 'e'));
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 		} else {
11537c478bd9Sstevel@tonic-gate 			mdb_printf("??? (unsupported FP format %u / %u bits\n",
11547c478bd9Sstevel@tonic-gate 			    e.cte_format, e.cte_bits);
11557c478bd9Sstevel@tonic-gate 		}
11567c478bd9Sstevel@tonic-gate 	} else
11577c478bd9Sstevel@tonic-gate 		mdb_printf("??? (%s)", mdb_strerror(errno));
11587c478bd9Sstevel@tonic-gate #else
11597c478bd9Sstevel@tonic-gate 	mdb_printf("<FLOAT>");
11607c478bd9Sstevel@tonic-gate #endif
11617c478bd9Sstevel@tonic-gate 	return (0);
11627c478bd9Sstevel@tonic-gate }
11637c478bd9Sstevel@tonic-gate 
11647c478bd9Sstevel@tonic-gate 
11657c478bd9Sstevel@tonic-gate /*
11667c478bd9Sstevel@tonic-gate  * Print out a pointer value as a symbol name + offset or a hexadecimal value.
11677c478bd9Sstevel@tonic-gate  * If the pointer itself is a char *, we attempt to read a bit of the data
11687c478bd9Sstevel@tonic-gate  * referenced by the pointer and display it if it is a printable ASCII string.
11697c478bd9Sstevel@tonic-gate  */
11707c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11717c478bd9Sstevel@tonic-gate static int
print_ptr(const char * type,const char * name,mdb_ctf_id_t id,mdb_ctf_id_t base,ulong_t off,printarg_t * pap)11727c478bd9Sstevel@tonic-gate print_ptr(const char *type, const char *name, mdb_ctf_id_t id,
11737c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
11747c478bd9Sstevel@tonic-gate {
11757c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
11767c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
11777c478bd9Sstevel@tonic-gate 	uintptr_t value;
11787c478bd9Sstevel@tonic-gate 	char buf[256];
11797c478bd9Sstevel@tonic-gate 	ssize_t len;
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
11827c478bd9Sstevel@tonic-gate 		return (0);
11837c478bd9Sstevel@tonic-gate 
11847c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as,
11857c478bd9Sstevel@tonic-gate 	    &value, sizeof (value), addr) != sizeof (value)) {
11867c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %s pointer at %llx", name, addr);
11877c478bd9Sstevel@tonic-gate 		return (1);
11887c478bd9Sstevel@tonic-gate 	}
11897c478bd9Sstevel@tonic-gate 
11907c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_NOSYMBOLIC) {
11917c478bd9Sstevel@tonic-gate 		mdb_printf("%#lx", value);
11927c478bd9Sstevel@tonic-gate 		return (0);
11937c478bd9Sstevel@tonic-gate 	}
11947c478bd9Sstevel@tonic-gate 
11957c478bd9Sstevel@tonic-gate 	mdb_printf("%a", value);
11967c478bd9Sstevel@tonic-gate 
1197892ad162SToomas Soome 	if (value == 0 || strcmp(type, "caddr_t") == 0)
11987c478bd9Sstevel@tonic-gate 		return (0);
11997c478bd9Sstevel@tonic-gate 
12007c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_kind(base) == CTF_K_POINTER &&
12017c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_reference(base, &base) != -1 &&
12027c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_resolve(base, &base) != -1 &&
12037c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_encoding(base, &e) == 0 && IS_CHAR(e)) {
12047c478bd9Sstevel@tonic-gate 		if ((len = mdb_tgt_readstr(pap->pa_realtgt, pap->pa_as,
12057c478bd9Sstevel@tonic-gate 		    buf, sizeof (buf), value)) >= 0 && strisprint(buf)) {
12067c478bd9Sstevel@tonic-gate 			if (len == sizeof (buf))
12077c478bd9Sstevel@tonic-gate 				(void) strabbr(buf, sizeof (buf));
12087c478bd9Sstevel@tonic-gate 			mdb_printf(" \"%s\"", buf);
12097c478bd9Sstevel@tonic-gate 		}
12107c478bd9Sstevel@tonic-gate 	}
12117c478bd9Sstevel@tonic-gate 
12127c478bd9Sstevel@tonic-gate 	return (0);
12137c478bd9Sstevel@tonic-gate }
12147c478bd9Sstevel@tonic-gate 
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate /*
12177c478bd9Sstevel@tonic-gate  * Print out a fixed-size array.  We special-case arrays of characters
12187c478bd9Sstevel@tonic-gate  * and attempt to print them out as ASCII strings if possible.  For other
12197c478bd9Sstevel@tonic-gate  * arrays, we iterate over a maximum of pa_armemlim members and call
12207c478bd9Sstevel@tonic-gate  * mdb_ctf_type_visit() again on each element to print its value.
12217c478bd9Sstevel@tonic-gate  */
12227c478bd9Sstevel@tonic-gate /*ARGSUSED*/
12237c478bd9Sstevel@tonic-gate static int
print_array(const char * type,const char * name,mdb_ctf_id_t id,mdb_ctf_id_t base,ulong_t off,printarg_t * pap)12247c478bd9Sstevel@tonic-gate print_array(const char *type, const char *name, mdb_ctf_id_t id,
12257c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
12267c478bd9Sstevel@tonic-gate {
12277c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
12287c478bd9Sstevel@tonic-gate 	printarg_t pa = *pap;
12297c478bd9Sstevel@tonic-gate 	ssize_t eltsize;
12307c478bd9Sstevel@tonic-gate 	mdb_ctf_arinfo_t r;
12317c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
12327c478bd9Sstevel@tonic-gate 	uint_t i, kind, limit;
12337c478bd9Sstevel@tonic-gate 	int d, sou;
12347c478bd9Sstevel@tonic-gate 	char buf[8];
12357c478bd9Sstevel@tonic-gate 	char *str;
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
12387c478bd9Sstevel@tonic-gate 		return (0);
12397c478bd9Sstevel@tonic-gate 
1240bf5197d8Sjwadams 	if (pap->pa_depth == pap->pa_maxdepth) {
1241bf5197d8Sjwadams 		mdb_printf("[ ... ]");
1242bf5197d8Sjwadams 		return (0);
1243bf5197d8Sjwadams 	}
1244bf5197d8Sjwadams 
12457c478bd9Sstevel@tonic-gate 	/*
12467c478bd9Sstevel@tonic-gate 	 * Determine the base type and size of the array's content.  If this
12477c478bd9Sstevel@tonic-gate 	 * fails, we cannot print anything and just give up.
12487c478bd9Sstevel@tonic-gate 	 */
12497c478bd9Sstevel@tonic-gate 	if (mdb_ctf_array_info(base, &r) == -1 ||
12507c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_resolve(r.mta_contents, &base) == -1 ||
12517c478bd9Sstevel@tonic-gate 	    (eltsize = mdb_ctf_type_size(base)) == -1) {
12527c478bd9Sstevel@tonic-gate 		mdb_printf("[ ??? ] (%s)", mdb_strerror(errno));
12537c478bd9Sstevel@tonic-gate 		return (0);
12547c478bd9Sstevel@tonic-gate 	}
12557c478bd9Sstevel@tonic-gate 
12567c478bd9Sstevel@tonic-gate 	/*
12577c478bd9Sstevel@tonic-gate 	 * Read a few bytes and determine if the content appears to be
12587c478bd9Sstevel@tonic-gate 	 * printable ASCII characters.  If so, read the entire array and
12597c478bd9Sstevel@tonic-gate 	 * attempt to display it as a string if it is printable.
12607c478bd9Sstevel@tonic-gate 	 */
12617c478bd9Sstevel@tonic-gate 	if ((pap->pa_arstrlim == MDB_ARR_NOLIMIT ||
12627c478bd9Sstevel@tonic-gate 	    r.mta_nelems <= pap->pa_arstrlim) &&
12637c478bd9Sstevel@tonic-gate 	    mdb_ctf_type_encoding(base, &e) == 0 && IS_CHAR(e) &&
12647c478bd9Sstevel@tonic-gate 	    mdb_tgt_readstr(pap->pa_tgt, pap->pa_as, buf,
12657c478bd9Sstevel@tonic-gate 	    MIN(sizeof (buf), r.mta_nelems), addr) > 0 && strisprint(buf)) {
12667c478bd9Sstevel@tonic-gate 
12677c478bd9Sstevel@tonic-gate 		str = mdb_alloc(r.mta_nelems + 1, UM_SLEEP | UM_GC);
12687c478bd9Sstevel@tonic-gate 		str[r.mta_nelems] = '\0';
12697c478bd9Sstevel@tonic-gate 
12707c478bd9Sstevel@tonic-gate 		if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, str,
12717c478bd9Sstevel@tonic-gate 		    r.mta_nelems, addr) != r.mta_nelems) {
12727c478bd9Sstevel@tonic-gate 			mdb_warn("failed to read char array at %llx", addr);
12737c478bd9Sstevel@tonic-gate 			return (1);
12747c478bd9Sstevel@tonic-gate 		}
12757c478bd9Sstevel@tonic-gate 
12767c478bd9Sstevel@tonic-gate 		if (strisprint(str)) {
12777c478bd9Sstevel@tonic-gate 			mdb_printf("[ \"%s\" ]", str);
12787c478bd9Sstevel@tonic-gate 			return (0);
12797c478bd9Sstevel@tonic-gate 		}
12807c478bd9Sstevel@tonic-gate 	}
12817c478bd9Sstevel@tonic-gate 
12827c478bd9Sstevel@tonic-gate 	if (pap->pa_armemlim != MDB_ARR_NOLIMIT)
12837c478bd9Sstevel@tonic-gate 		limit = MIN(r.mta_nelems, pap->pa_armemlim);
12847c478bd9Sstevel@tonic-gate 	else
12857c478bd9Sstevel@tonic-gate 		limit = r.mta_nelems;
12867c478bd9Sstevel@tonic-gate 
12877c478bd9Sstevel@tonic-gate 	if (limit == 0) {
12887c478bd9Sstevel@tonic-gate 		mdb_printf("[ ... ]");
12897c478bd9Sstevel@tonic-gate 		return (0);
12907c478bd9Sstevel@tonic-gate 	}
12917c478bd9Sstevel@tonic-gate 
12927c478bd9Sstevel@tonic-gate 	kind = mdb_ctf_type_kind(base);
12937c478bd9Sstevel@tonic-gate 	sou = IS_COMPOSITE(kind);
12947c478bd9Sstevel@tonic-gate 
12957c478bd9Sstevel@tonic-gate 	pa.pa_addr = addr;		/* set base address to start of array */
1296cce40297SJonathan Adams 	pa.pa_maxdepth = pa.pa_maxdepth - pa.pa_depth - 1;
12977c478bd9Sstevel@tonic-gate 	pa.pa_nest += pa.pa_depth + 1;	/* nesting level is current depth + 1 */
12987c478bd9Sstevel@tonic-gate 	pa.pa_depth = 0;		/* reset depth to 0 for new scope */
12997c478bd9Sstevel@tonic-gate 	pa.pa_prefix = NULL;
13007c478bd9Sstevel@tonic-gate 
13017c478bd9Sstevel@tonic-gate 	if (sou) {
13027c478bd9Sstevel@tonic-gate 		pa.pa_delim = "\n";
13037c478bd9Sstevel@tonic-gate 		mdb_printf("[\n");
13047c478bd9Sstevel@tonic-gate 	} else {
13057c478bd9Sstevel@tonic-gate 		pa.pa_flags &= ~(PA_SHOWTYPE | PA_SHOWNAME | PA_SHOWADDR);
13067c478bd9Sstevel@tonic-gate 		pa.pa_delim = ", ";
13077c478bd9Sstevel@tonic-gate 		mdb_printf("[ ");
13087c478bd9Sstevel@tonic-gate 	}
13097c478bd9Sstevel@tonic-gate 
13107c478bd9Sstevel@tonic-gate 	for (i = 0; i < limit; i++, pa.pa_addr += eltsize) {
13117c478bd9Sstevel@tonic-gate 		if (i == limit - 1 && !sou) {
13127c478bd9Sstevel@tonic-gate 			if (limit < r.mta_nelems)
13137c478bd9Sstevel@tonic-gate 				pa.pa_delim = ", ... ]";
13147c478bd9Sstevel@tonic-gate 			else
13157c478bd9Sstevel@tonic-gate 				pa.pa_delim = " ]";
13167c478bd9Sstevel@tonic-gate 		}
13177c478bd9Sstevel@tonic-gate 
13187c478bd9Sstevel@tonic-gate 		if (mdb_ctf_type_visit(r.mta_contents, elt_print, &pa) == -1) {
13197c478bd9Sstevel@tonic-gate 			mdb_warn("failed to print array data");
13207c478bd9Sstevel@tonic-gate 			return (1);
13217c478bd9Sstevel@tonic-gate 		}
13227c478bd9Sstevel@tonic-gate 	}
13237c478bd9Sstevel@tonic-gate 
13247c478bd9Sstevel@tonic-gate 	if (sou) {
13257c478bd9Sstevel@tonic-gate 		for (d = pa.pa_depth - 1; d >= 0; d--)
13267c478bd9Sstevel@tonic-gate 			print_close_sou(&pa, d);
13277c478bd9Sstevel@tonic-gate 
13287c478bd9Sstevel@tonic-gate 		if (limit < r.mta_nelems) {
13297c478bd9Sstevel@tonic-gate 			mdb_printf("%*s... ]",
13307c478bd9Sstevel@tonic-gate 			    (pap->pa_depth + pap->pa_nest) * pap->pa_tab, "");
13317c478bd9Sstevel@tonic-gate 		} else {
13327c478bd9Sstevel@tonic-gate 			mdb_printf("%*s]",
13337c478bd9Sstevel@tonic-gate 			    (pap->pa_depth + pap->pa_nest) * pap->pa_tab, "");
13347c478bd9Sstevel@tonic-gate 		}
13357c478bd9Sstevel@tonic-gate 	}
13367c478bd9Sstevel@tonic-gate 
13377c478bd9Sstevel@tonic-gate 	/* copy the hole array info, since it may have been grown */
13387c478bd9Sstevel@tonic-gate 	pap->pa_holes = pa.pa_holes;
13397c478bd9Sstevel@tonic-gate 	pap->pa_nholes = pa.pa_nholes;
13407c478bd9Sstevel@tonic-gate 
13417c478bd9Sstevel@tonic-gate 	return (0);
13427c478bd9Sstevel@tonic-gate }
13437c478bd9Sstevel@tonic-gate 
13447c478bd9Sstevel@tonic-gate /*
13457c478bd9Sstevel@tonic-gate  * Print out a struct or union header.  We need only print the open brace
13467c478bd9Sstevel@tonic-gate  * because mdb_ctf_type_visit() itself will automatically recurse through
13477c478bd9Sstevel@tonic-gate  * all members of the given struct or union.
13487c478bd9Sstevel@tonic-gate  */
13497c478bd9Sstevel@tonic-gate /*ARGSUSED*/
13507c478bd9Sstevel@tonic-gate static int
print_sou(const char * type,const char * name,mdb_ctf_id_t id,mdb_ctf_id_t base,ulong_t off,printarg_t * pap)13517c478bd9Sstevel@tonic-gate print_sou(const char *type, const char *name, mdb_ctf_id_t id,
13527c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
13537c478bd9Sstevel@tonic-gate {
1354838d7172SSebastien Roy 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
1355838d7172SSebastien Roy 
1356838d7172SSebastien Roy 	/*
1357838d7172SSebastien Roy 	 * We have pretty-printing for some structures where displaying
1358838d7172SSebastien Roy 	 * structure contents has no value.
1359838d7172SSebastien Roy 	 */
1360838d7172SSebastien Roy 	if (pap->pa_flags & PA_SHOWVAL) {
1361838d7172SSebastien Roy 		if (strcmp(type, "in6_addr_t") == 0 ||
1362838d7172SSebastien Roy 		    strcmp(type, "struct in6_addr") == 0) {
1363838d7172SSebastien Roy 			in6_addr_t in6addr;
1364838d7172SSebastien Roy 
1365838d7172SSebastien Roy 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &in6addr,
1366838d7172SSebastien Roy 			    sizeof (in6addr), addr) != sizeof (in6addr)) {
1367838d7172SSebastien Roy 				mdb_warn("failed to read %s pointer at %llx",
1368838d7172SSebastien Roy 				    name, addr);
1369838d7172SSebastien Roy 				return (1);
1370838d7172SSebastien Roy 			}
1371838d7172SSebastien Roy 			mdb_printf("%N", &in6addr);
1372838d7172SSebastien Roy 			/*
1373838d7172SSebastien Roy 			 * Don't print anything further down in the
1374838d7172SSebastien Roy 			 * structure.
1375838d7172SSebastien Roy 			 */
1376838d7172SSebastien Roy 			pap->pa_nooutdepth = pap->pa_depth;
1377838d7172SSebastien Roy 			return (0);
1378838d7172SSebastien Roy 		}
1379838d7172SSebastien Roy 		if (strcmp(type, "struct in_addr") == 0) {
1380838d7172SSebastien Roy 			in_addr_t inaddr;
1381838d7172SSebastien Roy 
1382838d7172SSebastien Roy 			if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &inaddr,
1383838d7172SSebastien Roy 			    sizeof (inaddr), addr) != sizeof (inaddr)) {
1384838d7172SSebastien Roy 				mdb_warn("failed to read %s pointer at %llx",
1385838d7172SSebastien Roy 				    name, addr);
1386838d7172SSebastien Roy 				return (1);
1387838d7172SSebastien Roy 			}
1388838d7172SSebastien Roy 			mdb_printf("%I", inaddr);
1389838d7172SSebastien Roy 			pap->pa_nooutdepth = pap->pa_depth;
1390838d7172SSebastien Roy 			return (0);
1391838d7172SSebastien Roy 		}
1392838d7172SSebastien Roy 	}
1393838d7172SSebastien Roy 
1394bf5197d8Sjwadams 	if (pap->pa_depth == pap->pa_maxdepth)
1395bf5197d8Sjwadams 		mdb_printf("{ ... }");
1396bf5197d8Sjwadams 	else
1397bf5197d8Sjwadams 		mdb_printf("{");
13987c478bd9Sstevel@tonic-gate 	pap->pa_delim = "\n";
13997c478bd9Sstevel@tonic-gate 	return (0);
14007c478bd9Sstevel@tonic-gate }
14017c478bd9Sstevel@tonic-gate 
14027c478bd9Sstevel@tonic-gate /*
14037c478bd9Sstevel@tonic-gate  * Print an enum value.  We attempt to convert the value to the corresponding
14047c478bd9Sstevel@tonic-gate  * enum name and print that if possible.
14057c478bd9Sstevel@tonic-gate  */
14067c478bd9Sstevel@tonic-gate /*ARGSUSED*/
14077c478bd9Sstevel@tonic-gate static int
print_enum(const char * type,const char * name,mdb_ctf_id_t id,mdb_ctf_id_t base,ulong_t off,printarg_t * pap)14087c478bd9Sstevel@tonic-gate print_enum(const char *type, const char *name, mdb_ctf_id_t id,
14097c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
14107c478bd9Sstevel@tonic-gate {
14117c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
14127c478bd9Sstevel@tonic-gate 	const char *ename;
14137c478bd9Sstevel@tonic-gate 	int value;
1414e0ad97e3SJonathan Adams 	int isp2 = enum_is_p2(base);
1415e0ad97e3SJonathan Adams 	int flags = pap->pa_flags | (isp2 ? PA_INTHEX : 0);
14167c478bd9Sstevel@tonic-gate 
1417e0ad97e3SJonathan Adams 	if (!(flags & PA_SHOWVAL))
14187c478bd9Sstevel@tonic-gate 		return (0);
14197c478bd9Sstevel@tonic-gate 
14207c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as,
14217c478bd9Sstevel@tonic-gate 	    &value, sizeof (value), addr) != sizeof (value)) {
14227c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %s integer at %llx", name, addr);
14237c478bd9Sstevel@tonic-gate 		return (1);
14247c478bd9Sstevel@tonic-gate 	}
14257c478bd9Sstevel@tonic-gate 
1426e0ad97e3SJonathan Adams 	if (flags & PA_INTHEX)
14277c478bd9Sstevel@tonic-gate 		mdb_printf("%#x", value);
14287c478bd9Sstevel@tonic-gate 	else
14297c478bd9Sstevel@tonic-gate 		mdb_printf("%#d", value);
14307c478bd9Sstevel@tonic-gate 
1431e0ad97e3SJonathan Adams 	(void) mdb_inc_indent(8);
1432e0ad97e3SJonathan Adams 	mdb_printf(" (");
1433cce40297SJonathan Adams 
1434e0ad97e3SJonathan Adams 	if (!isp2 || enum_value_print_p2(base, value, 0) != 0) {
1435e0ad97e3SJonathan Adams 		ename = mdb_ctf_enum_name(base, value);
1436e0ad97e3SJonathan Adams 		if (ename == NULL) {
1437e0ad97e3SJonathan Adams 			ename = "???";
1438e0ad97e3SJonathan Adams 		}
1439e0ad97e3SJonathan Adams 		mdb_printf("%s", ename);
1440e0ad97e3SJonathan Adams 	}
1441e0ad97e3SJonathan Adams 	mdb_printf(")");
1442e0ad97e3SJonathan Adams 	(void) mdb_dec_indent(8);
14437c478bd9Sstevel@tonic-gate 
14447c478bd9Sstevel@tonic-gate 	return (0);
14457c478bd9Sstevel@tonic-gate }
14467c478bd9Sstevel@tonic-gate 
14477c478bd9Sstevel@tonic-gate /*
1448cce40297SJonathan Adams  * This will only get called if the structure isn't found in any available CTF
1449cce40297SJonathan Adams  * data.
14507c478bd9Sstevel@tonic-gate  */
14517c478bd9Sstevel@tonic-gate /*ARGSUSED*/
14527c478bd9Sstevel@tonic-gate static int
print_tag(const char * type,const char * name,mdb_ctf_id_t id,mdb_ctf_id_t base,ulong_t off,printarg_t * pap)14537c478bd9Sstevel@tonic-gate print_tag(const char *type, const char *name, mdb_ctf_id_t id,
14547c478bd9Sstevel@tonic-gate     mdb_ctf_id_t base, ulong_t off, printarg_t *pap)
14557c478bd9Sstevel@tonic-gate {
1456cce40297SJonathan Adams 	char basename[MDB_SYM_NAMLEN];
1457cce40297SJonathan Adams 
14587c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWVAL)
14597c478bd9Sstevel@tonic-gate 		mdb_printf("; ");
14607c478bd9Sstevel@tonic-gate 
1461cce40297SJonathan Adams 	if (mdb_ctf_type_name(base, basename, sizeof (basename)) != NULL)
1462cce40297SJonathan Adams 		mdb_printf("<forward declaration of %s>", basename);
1463cce40297SJonathan Adams 	else
1464cce40297SJonathan Adams 		mdb_printf("<forward declaration of unknown type>");
1465cce40297SJonathan Adams 
14667c478bd9Sstevel@tonic-gate 	return (0);
14677c478bd9Sstevel@tonic-gate }
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate static void
print_hole(printarg_t * pap,int depth,ulong_t off,ulong_t endoff)14707c478bd9Sstevel@tonic-gate print_hole(printarg_t *pap, int depth, ulong_t off, ulong_t endoff)
14717c478bd9Sstevel@tonic-gate {
14727c478bd9Sstevel@tonic-gate 	ulong_t bits = endoff - off;
14737c478bd9Sstevel@tonic-gate 	ulong_t size = bits / NBBY;
14747c478bd9Sstevel@tonic-gate 	ctf_encoding_t e;
14757c478bd9Sstevel@tonic-gate 
14767c478bd9Sstevel@tonic-gate 	static const char *const name = "<<HOLE>>";
14777c478bd9Sstevel@tonic-gate 	char type[MDB_SYM_NAMLEN];
14787c478bd9Sstevel@tonic-gate 
14797c478bd9Sstevel@tonic-gate 	int bitfield =
14807c478bd9Sstevel@tonic-gate 	    (off % NBBY != 0 ||
14817c478bd9Sstevel@tonic-gate 	    bits % NBBY != 0 ||
14827c478bd9Sstevel@tonic-gate 	    size > 8 ||
14837c478bd9Sstevel@tonic-gate 	    (size & (size - 1)) != 0);
14847c478bd9Sstevel@tonic-gate 
14857c478bd9Sstevel@tonic-gate 	ASSERT(off < endoff);
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate 	if (bits > NBBY * sizeof (uint64_t)) {
14887c478bd9Sstevel@tonic-gate 		ulong_t end;
14897c478bd9Sstevel@tonic-gate 
14907c478bd9Sstevel@tonic-gate 		/*
14917c478bd9Sstevel@tonic-gate 		 * The hole is larger than the largest integer type.  To
14927c478bd9Sstevel@tonic-gate 		 * handle this, we split up the hole at 8-byte-aligned
14937c478bd9Sstevel@tonic-gate 		 * boundaries, recursing to print each subsection.  For
14947c478bd9Sstevel@tonic-gate 		 * normal C structures, we'll loop at most twice.
14957c478bd9Sstevel@tonic-gate 		 */
14967c478bd9Sstevel@tonic-gate 		for (; off < endoff; off = end) {
14977c478bd9Sstevel@tonic-gate 			end = P2END(off, NBBY * sizeof (uint64_t));
14987c478bd9Sstevel@tonic-gate 			if (end > endoff)
14997c478bd9Sstevel@tonic-gate 				end = endoff;
15007c478bd9Sstevel@tonic-gate 
15017c478bd9Sstevel@tonic-gate 			ASSERT((end - off) <= NBBY * sizeof (uint64_t));
15027c478bd9Sstevel@tonic-gate 			print_hole(pap, depth, off, end);
15037c478bd9Sstevel@tonic-gate 		}
15047c478bd9Sstevel@tonic-gate 		ASSERT(end == endoff);
15057c478bd9Sstevel@tonic-gate 
15067c478bd9Sstevel@tonic-gate 		return;
15077c478bd9Sstevel@tonic-gate 	}
15087c478bd9Sstevel@tonic-gate 
15097c478bd9Sstevel@tonic-gate 	if (bitfield)
15107c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(type, sizeof (type), "unsigned");
15117c478bd9Sstevel@tonic-gate 	else
15127c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(type, sizeof (type), "uint%d_t", bits);
15137c478bd9Sstevel@tonic-gate 
15147c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & (PA_SHOWTYPE | PA_SHOWNAME | PA_SHOWADDR))
15157c478bd9Sstevel@tonic-gate 		mdb_printf("%*s", (depth + pap->pa_nest) * pap->pa_tab, "");
15167c478bd9Sstevel@tonic-gate 
15177c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWADDR) {
1518bf5197d8Sjwadams 		if (off % NBBY == 0)
15197c478bd9Sstevel@tonic-gate 			mdb_printf("%llx ", pap->pa_addr + off / NBBY);
15207c478bd9Sstevel@tonic-gate 		else
15217c478bd9Sstevel@tonic-gate 			mdb_printf("%llx.%lx ",
15227c478bd9Sstevel@tonic-gate 			    pap->pa_addr + off / NBBY, off % NBBY);
15237c478bd9Sstevel@tonic-gate 	}
15247c478bd9Sstevel@tonic-gate 
15257c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWTYPE)
15267c478bd9Sstevel@tonic-gate 		mdb_printf("%s ", type);
15277c478bd9Sstevel@tonic-gate 
15287c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWNAME)
15297c478bd9Sstevel@tonic-gate 		mdb_printf("%s", name);
15307c478bd9Sstevel@tonic-gate 
15317c478bd9Sstevel@tonic-gate 	if (bitfield && (pap->pa_flags & PA_SHOWTYPE))
15327c478bd9Sstevel@tonic-gate 		mdb_printf(" :%d", bits);
15337c478bd9Sstevel@tonic-gate 
15347c478bd9Sstevel@tonic-gate 	mdb_printf("%s ", (pap->pa_flags & PA_SHOWVAL)? " =" : "");
15357c478bd9Sstevel@tonic-gate 
15367c478bd9Sstevel@tonic-gate 	/*
15377c478bd9Sstevel@tonic-gate 	 * We fake up a ctf_encoding_t, and use print_int_val() to print
15387c478bd9Sstevel@tonic-gate 	 * the value.  Holes are always processed as unsigned integers.
15397c478bd9Sstevel@tonic-gate 	 */
15407c478bd9Sstevel@tonic-gate 	bzero(&e, sizeof (e));
15417c478bd9Sstevel@tonic-gate 	e.cte_format = 0;
15427c478bd9Sstevel@tonic-gate 	e.cte_offset = 0;
15437c478bd9Sstevel@tonic-gate 	e.cte_bits = bits;
15447c478bd9Sstevel@tonic-gate 
15457c478bd9Sstevel@tonic-gate 	if (print_int_val(type, &e, off, pap) != 0)
15467c478bd9Sstevel@tonic-gate 		mdb_iob_discard(mdb.m_out);
15477c478bd9Sstevel@tonic-gate 	else
15487c478bd9Sstevel@tonic-gate 		mdb_iob_puts(mdb.m_out, pap->pa_delim);
15497c478bd9Sstevel@tonic-gate }
15507c478bd9Sstevel@tonic-gate 
15517c478bd9Sstevel@tonic-gate /*
15527c478bd9Sstevel@tonic-gate  * The print_close_sou() function is called for each structure or union
15537c478bd9Sstevel@tonic-gate  * which has been completed.  For structures, we detect and print any holes
15547c478bd9Sstevel@tonic-gate  * before printing the closing brace.
15557c478bd9Sstevel@tonic-gate  */
15567c478bd9Sstevel@tonic-gate static void
print_close_sou(printarg_t * pap,int newdepth)15577c478bd9Sstevel@tonic-gate print_close_sou(printarg_t *pap, int newdepth)
15587c478bd9Sstevel@tonic-gate {
15597c478bd9Sstevel@tonic-gate 	int d = newdepth + pap->pa_nest;
15607c478bd9Sstevel@tonic-gate 
15617c478bd9Sstevel@tonic-gate 	if ((pap->pa_flags & PA_SHOWHOLES) && !pap->pa_holes[d].hi_isunion) {
15627c478bd9Sstevel@tonic-gate 		ulong_t end = pap->pa_holes[d + 1].hi_offset;
15637c478bd9Sstevel@tonic-gate 		ulong_t expected = pap->pa_holes[d].hi_offset;
15647c478bd9Sstevel@tonic-gate 
15657c478bd9Sstevel@tonic-gate 		if (end < expected)
15667c478bd9Sstevel@tonic-gate 			print_hole(pap, newdepth + 1, end, expected);
15677c478bd9Sstevel@tonic-gate 	}
1568cce40297SJonathan Adams 	/* if the struct is an array element, print a comma after the } */
1569cce40297SJonathan Adams 	mdb_printf("%*s}%s\n", d * pap->pa_tab, "",
1570cce40297SJonathan Adams 	    (newdepth == 0 && pap->pa_nest > 0)? "," : "");
15717c478bd9Sstevel@tonic-gate }
15727c478bd9Sstevel@tonic-gate 
15737c478bd9Sstevel@tonic-gate static printarg_f *const printfuncs[] = {
15747c478bd9Sstevel@tonic-gate 	print_int,	/* CTF_K_INTEGER */
15757c478bd9Sstevel@tonic-gate 	print_float,	/* CTF_K_FLOAT */
15767c478bd9Sstevel@tonic-gate 	print_ptr,	/* CTF_K_POINTER */
15777c478bd9Sstevel@tonic-gate 	print_array,	/* CTF_K_ARRAY */
15787c478bd9Sstevel@tonic-gate 	print_ptr,	/* CTF_K_FUNCTION */
15797c478bd9Sstevel@tonic-gate 	print_sou,	/* CTF_K_STRUCT */
15807c478bd9Sstevel@tonic-gate 	print_sou,	/* CTF_K_UNION */
15817c478bd9Sstevel@tonic-gate 	print_enum,	/* CTF_K_ENUM */
15827c478bd9Sstevel@tonic-gate 	print_tag	/* CTF_K_FORWARD */
15837c478bd9Sstevel@tonic-gate };
15847c478bd9Sstevel@tonic-gate 
15857c478bd9Sstevel@tonic-gate /*
15867c478bd9Sstevel@tonic-gate  * The elt_print function is used as the mdb_ctf_type_visit callback.  For
15877c478bd9Sstevel@tonic-gate  * each element, we print an appropriate name prefix and then call the
15887c478bd9Sstevel@tonic-gate  * print subroutine for this type class in the array above.
15897c478bd9Sstevel@tonic-gate  */
15907c478bd9Sstevel@tonic-gate static int
elt_print(const char * name,mdb_ctf_id_t id,mdb_ctf_id_t base,ulong_t off,int depth,void * data)1591cce40297SJonathan Adams elt_print(const char *name, mdb_ctf_id_t id, mdb_ctf_id_t base,
1592cce40297SJonathan Adams     ulong_t off, int depth, void *data)
15937c478bd9Sstevel@tonic-gate {
1594cce40297SJonathan Adams 	char type[MDB_SYM_NAMLEN + sizeof (" <<12345678...>>")];
15957c478bd9Sstevel@tonic-gate 	int kind, rc, d;
15967c478bd9Sstevel@tonic-gate 	printarg_t *pap = data;
15977c478bd9Sstevel@tonic-gate 
1598838d7172SSebastien Roy 	for (d = pap->pa_depth - 1; d >= depth; d--) {
1599838d7172SSebastien Roy 		if (d < pap->pa_nooutdepth)
1600838d7172SSebastien Roy 			print_close_sou(pap, d);
1601838d7172SSebastien Roy 	}
1602838d7172SSebastien Roy 
1603838d7172SSebastien Roy 	/*
1604838d7172SSebastien Roy 	 * Reset pa_nooutdepth if we've come back out of the structure we
1605838d7172SSebastien Roy 	 * didn't want to print.
1606838d7172SSebastien Roy 	 */
1607838d7172SSebastien Roy 	if (depth <= pap->pa_nooutdepth)
1608838d7172SSebastien Roy 		pap->pa_nooutdepth = (uint_t)-1;
16097c478bd9Sstevel@tonic-gate 
1610838d7172SSebastien Roy 	if (depth > pap->pa_maxdepth || depth > pap->pa_nooutdepth)
1611bf5197d8Sjwadams 		return (0);
1612bf5197d8Sjwadams 
1613cce40297SJonathan Adams 	if (!mdb_ctf_type_valid(base) ||
16147c478bd9Sstevel@tonic-gate 	    (kind = mdb_ctf_type_kind(base)) == -1)
16157c478bd9Sstevel@tonic-gate 		return (-1); /* errno is set for us */
16167c478bd9Sstevel@tonic-gate 
1617cce40297SJonathan Adams 	if (mdb_ctf_type_name(id, type, MDB_SYM_NAMLEN) == NULL)
16187c478bd9Sstevel@tonic-gate 		(void) strcpy(type, "(?)");
16197c478bd9Sstevel@tonic-gate 
1620cce40297SJonathan Adams 	if (pap->pa_flags & PA_SHOWBASETYPE) {
1621cce40297SJonathan Adams 		/*
1622cce40297SJonathan Adams 		 * If basetype is different and informative, concatenate
1623cce40297SJonathan Adams 		 * <<basetype>> (or <<baset...>> if it doesn't fit)
1624cce40297SJonathan Adams 		 *
1625cce40297SJonathan Adams 		 * We just use the end of the buffer to store the type name, and
1626cce40297SJonathan Adams 		 * only connect it up if that's necessary.
1627cce40297SJonathan Adams 		 */
1628cce40297SJonathan Adams 
1629cce40297SJonathan Adams 		char *type_end = type + strlen(type);
1630cce40297SJonathan Adams 		char *basetype;
1631cce40297SJonathan Adams 		size_t sz;
1632cce40297SJonathan Adams 
1633cce40297SJonathan Adams 		(void) strlcat(type, " <<", sizeof (type));
1634cce40297SJonathan Adams 
1635cce40297SJonathan Adams 		basetype = type + strlen(type);
1636cce40297SJonathan Adams 		sz = sizeof (type) - (basetype - type);
1637cce40297SJonathan Adams 
1638cce40297SJonathan Adams 		*type_end = '\0'; /* restore the end of type for strcmp() */
1639cce40297SJonathan Adams 
1640cce40297SJonathan Adams 		if (mdb_ctf_type_name(base, basetype, sz) != NULL &&
1641cce40297SJonathan Adams 		    strcmp(basetype, type) != 0 &&
1642cce40297SJonathan Adams 		    strcmp(basetype, "struct ") != 0 &&
1643cce40297SJonathan Adams 		    strcmp(basetype, "enum ") != 0 &&
1644cce40297SJonathan Adams 		    strcmp(basetype, "union ") != 0) {
1645cce40297SJonathan Adams 			type_end[0] = ' ';	/* reconnect */
1646cce40297SJonathan Adams 			if (strlcat(type, ">>", sizeof (type)) >= sizeof (type))
1647cce40297SJonathan Adams 				(void) strlcpy(
1648cce40297SJonathan Adams 				    type + sizeof (type) - 6, "...>>", 6);
1649cce40297SJonathan Adams 		}
1650cce40297SJonathan Adams 	}
1651cce40297SJonathan Adams 
16527c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & PA_SHOWHOLES) {
16537c478bd9Sstevel@tonic-gate 		ctf_encoding_t e;
16547c478bd9Sstevel@tonic-gate 		ssize_t nsize;
16557c478bd9Sstevel@tonic-gate 		ulong_t newoff;
16567c478bd9Sstevel@tonic-gate 		holeinfo_t *hole;
16577c478bd9Sstevel@tonic-gate 		int extra = IS_COMPOSITE(kind)? 1 : 0;
16587c478bd9Sstevel@tonic-gate 
16597c478bd9Sstevel@tonic-gate 		/*
16607c478bd9Sstevel@tonic-gate 		 * grow the hole array, if necessary
16617c478bd9Sstevel@tonic-gate 		 */
16627c478bd9Sstevel@tonic-gate 		if (pap->pa_nest + depth + extra >= pap->pa_nholes) {
16637c478bd9Sstevel@tonic-gate 			int new = MAX(MAX(8, pap->pa_nholes * 2),
16647c478bd9Sstevel@tonic-gate 			    pap->pa_nest + depth + extra + 1);
16657c478bd9Sstevel@tonic-gate 
16667c478bd9Sstevel@tonic-gate 			holeinfo_t *nhi = mdb_zalloc(
16677c478bd9Sstevel@tonic-gate 			    sizeof (*nhi) * new, UM_NOSLEEP | UM_GC);
16687c478bd9Sstevel@tonic-gate 
16697c478bd9Sstevel@tonic-gate 			bcopy(pap->pa_holes, nhi,
16707c478bd9Sstevel@tonic-gate 			    pap->pa_nholes * sizeof (*nhi));
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 			pap->pa_holes = nhi;
16737c478bd9Sstevel@tonic-gate 			pap->pa_nholes = new;
16747c478bd9Sstevel@tonic-gate 		}
16757c478bd9Sstevel@tonic-gate 
16767c478bd9Sstevel@tonic-gate 		hole = &pap->pa_holes[depth + pap->pa_nest];
16777c478bd9Sstevel@tonic-gate 
16787c478bd9Sstevel@tonic-gate 		if (depth != 0 && off > hole->hi_offset)
16797c478bd9Sstevel@tonic-gate 			print_hole(pap, depth, hole->hi_offset, off);
16807c478bd9Sstevel@tonic-gate 
16817c478bd9Sstevel@tonic-gate 		/* compute the next expected offset */
16827c478bd9Sstevel@tonic-gate 		if (kind == CTF_K_INTEGER &&
16837c478bd9Sstevel@tonic-gate 		    mdb_ctf_type_encoding(base, &e) == 0)
16847c478bd9Sstevel@tonic-gate 			newoff = off + e.cte_bits;
16857c478bd9Sstevel@tonic-gate 		else if ((nsize = mdb_ctf_type_size(base)) >= 0)
16867c478bd9Sstevel@tonic-gate 			newoff = off + nsize * NBBY;
16877c478bd9Sstevel@tonic-gate 		else {
16887c478bd9Sstevel@tonic-gate 			/* something bad happened, disable hole checking */
16897c478bd9Sstevel@tonic-gate 			newoff = -1UL;		/* ULONG_MAX */
16907c478bd9Sstevel@tonic-gate 		}
16917c478bd9Sstevel@tonic-gate 
16927c478bd9Sstevel@tonic-gate 		hole->hi_offset = newoff;
16937c478bd9Sstevel@tonic-gate 
16947c478bd9Sstevel@tonic-gate 		if (IS_COMPOSITE(kind)) {
16957c478bd9Sstevel@tonic-gate 			hole->hi_isunion = (kind == CTF_K_UNION);
16967c478bd9Sstevel@tonic-gate 			hole++;
16977c478bd9Sstevel@tonic-gate 			hole->hi_offset = off;
16987c478bd9Sstevel@tonic-gate 		}
16997c478bd9Sstevel@tonic-gate 	}
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate 	if (pap->pa_flags & (PA_SHOWTYPE | PA_SHOWNAME | PA_SHOWADDR))
17027c478bd9Sstevel@tonic-gate 		mdb_printf("%*s", (depth + pap->pa_nest) * pap->pa_tab, "");
17037c478bd9Sstevel@tonic-gate 
1704cce40297SJonathan Adams 	if (pap->pa_flags & PA_SHOWADDR) {
1705cce40297SJonathan Adams 		if (off % NBBY == 0)
1706cce40297SJonathan Adams 			mdb_printf("%llx ", pap->pa_addr + off / NBBY);
1707cce40297SJonathan Adams 		else
1708cce40297SJonathan Adams 			mdb_printf("%llx.%lx ",
1709cce40297SJonathan Adams 			    pap->pa_addr + off / NBBY, off % NBBY);
1710cce40297SJonathan Adams 	}
17117c478bd9Sstevel@tonic-gate 
1712cce40297SJonathan Adams 	if ((pap->pa_flags & PA_SHOWTYPE)) {
1713cce40297SJonathan Adams 		mdb_printf("%s", type);
1714cce40297SJonathan Adams 		/*
1715cce40297SJonathan Adams 		 * We want to avoid printing a trailing space when
1716cce40297SJonathan Adams 		 * dealing with pointers in a structure, so we end
1717cce40297SJonathan Adams 		 * up with:
1718cce40297SJonathan Adams 		 *
1719cce40297SJonathan Adams 		 *	label_t *t_onfault = 0
1720cce40297SJonathan Adams 		 *
1721cce40297SJonathan Adams 		 * If depth is zero, always print the trailing space unless
1722cce40297SJonathan Adams 		 * we also have a prefix.
1723cce40297SJonathan Adams 		 */
1724cce40297SJonathan Adams 		if (type[strlen(type) - 1] != '*' ||
1725cce40297SJonathan Adams 		    (depth == 0 && (!(pap->pa_flags & PA_SHOWNAME) ||
1726cce40297SJonathan Adams 		    pap->pa_prefix == NULL)))
1727cce40297SJonathan Adams 			mdb_printf(" ");
1728cce40297SJonathan Adams 	}
17297c478bd9Sstevel@tonic-gate 
1730cce40297SJonathan Adams 	if (pap->pa_flags & PA_SHOWNAME) {
1731cce40297SJonathan Adams 		if (pap->pa_prefix != NULL && depth <= 1)
1732cce40297SJonathan Adams 			mdb_printf("%s%s", pap->pa_prefix,
1733cce40297SJonathan Adams 			    (depth == 0) ? "" : pap->pa_suffix);
1734cce40297SJonathan Adams 		mdb_printf("%s", name);
1735cce40297SJonathan Adams 	}
17367c478bd9Sstevel@tonic-gate 
1737cce40297SJonathan Adams 	if ((pap->pa_flags & PA_SHOWTYPE) && kind == CTF_K_INTEGER) {
1738cce40297SJonathan Adams 		ctf_encoding_t e;
17397c478bd9Sstevel@tonic-gate 
1740cce40297SJonathan Adams 		if (mdb_ctf_type_encoding(base, &e) == 0) {
1741cce40297SJonathan Adams 			ulong_t bits = e.cte_bits;
1742cce40297SJonathan Adams 			ulong_t size = bits / NBBY;
17437c478bd9Sstevel@tonic-gate 
1744cce40297SJonathan Adams 			if (bits % NBBY != 0 ||
1745cce40297SJonathan Adams 			    off % NBBY != 0 ||
1746cce40297SJonathan Adams 			    size > 8 ||
1747cce40297SJonathan Adams 			    size != mdb_ctf_type_size(base))
1748cce40297SJonathan Adams 				mdb_printf(" :%d", bits);
17497c478bd9Sstevel@tonic-gate 		}
1750cce40297SJonathan Adams 	}
17517c478bd9Sstevel@tonic-gate 
1752cce40297SJonathan Adams 	if (depth != 0 ||
1753cce40297SJonathan Adams 	    ((pap->pa_flags & PA_SHOWNAME) && pap->pa_prefix != NULL))
17547c478bd9Sstevel@tonic-gate 		mdb_printf("%s ", pap->pa_flags & PA_SHOWVAL ? " =" : "");
17557c478bd9Sstevel@tonic-gate 
1756cce40297SJonathan Adams 	if (depth == 0 && pap->pa_prefix != NULL)
1757cce40297SJonathan Adams 		name = pap->pa_prefix;
17587c478bd9Sstevel@tonic-gate 
17597c478bd9Sstevel@tonic-gate 	pap->pa_depth = depth;
17608a6a72fdSaf 	if (kind <= CTF_K_UNKNOWN || kind >= CTF_K_TYPEDEF) {
17618a6a72fdSaf 		mdb_warn("unknown ctf for %s type %s kind %d\n",
1762cce40297SJonathan Adams 		    name, type, kind);
17638a6a72fdSaf 		return (-1);
17648a6a72fdSaf 	}
17657c478bd9Sstevel@tonic-gate 	rc = printfuncs[kind - 1](type, name, id, base, off, pap);
17667c478bd9Sstevel@tonic-gate 
17677c478bd9Sstevel@tonic-gate 	if (rc != 0)
17687c478bd9Sstevel@tonic-gate 		mdb_iob_discard(mdb.m_out);
17697c478bd9Sstevel@tonic-gate 	else
17707c478bd9Sstevel@tonic-gate 		mdb_iob_puts(mdb.m_out, pap->pa_delim);
17717c478bd9Sstevel@tonic-gate 
17727c478bd9Sstevel@tonic-gate 	return (rc);
17737c478bd9Sstevel@tonic-gate }
17747c478bd9Sstevel@tonic-gate 
1775bf5197d8Sjwadams /*
1776bf5197d8Sjwadams  * Special semantics for pipelines.
1777bf5197d8Sjwadams  */
1778bf5197d8Sjwadams static int
pipe_print(mdb_ctf_id_t id,ulong_t off,void * data)1779bf5197d8Sjwadams pipe_print(mdb_ctf_id_t id, ulong_t off, void *data)
1780bf5197d8Sjwadams {
1781bf5197d8Sjwadams 	printarg_t *pap = data;
1782*60377884SRobert Mustacchi 	size_t size;
1783bf5197d8Sjwadams 	static const char *const fsp[] = { "%#r", "%#r", "%#r", "%#llr" };
1784bf5197d8Sjwadams 	uintptr_t value;
1785bf5197d8Sjwadams 	uintptr_t addr = pap->pa_addr + off / NBBY;
1786bf5197d8Sjwadams 	mdb_ctf_id_t base;
1787e9f82d69SAlex Reece 	int enum_value;
1788bf5197d8Sjwadams 	ctf_encoding_t e;
1789bf5197d8Sjwadams 
1790bf5197d8Sjwadams 	union {
1791bf5197d8Sjwadams 		uint64_t i8;
1792bf5197d8Sjwadams 		uint32_t i4;
1793bf5197d8Sjwadams 		uint16_t i2;
1794bf5197d8Sjwadams 		uint8_t i1;
1795bf5197d8Sjwadams 	} u;
1796bf5197d8Sjwadams 
1797bf5197d8Sjwadams 	if (mdb_ctf_type_resolve(id, &base) == -1) {
17983ddcfaddSBryan Cantrill 		mdb_warn("could not resolve type");
1799bf5197d8Sjwadams 		return (-1);
1800bf5197d8Sjwadams 	}
1801bf5197d8Sjwadams 
1802bf5197d8Sjwadams 	/*
1803bf5197d8Sjwadams 	 * If the user gives -a, then always print out the address of the
1804bf5197d8Sjwadams 	 * member.
1805bf5197d8Sjwadams 	 */
1806bf5197d8Sjwadams 	if ((pap->pa_flags & PA_SHOWADDR)) {
1807bf5197d8Sjwadams 		mdb_printf("%#lr\n", addr);
1808bf5197d8Sjwadams 		return (0);
1809bf5197d8Sjwadams 	}
1810bf5197d8Sjwadams 
1811bf5197d8Sjwadams again:
1812bf5197d8Sjwadams 	switch (mdb_ctf_type_kind(base)) {
1813bf5197d8Sjwadams 	case CTF_K_POINTER:
1814bf5197d8Sjwadams 		if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as,
1815bf5197d8Sjwadams 		    &value, sizeof (value), addr) != sizeof (value)) {
1816bf5197d8Sjwadams 			mdb_warn("failed to read pointer at %p", addr);
1817bf5197d8Sjwadams 			return (-1);
1818bf5197d8Sjwadams 		}
1819bf5197d8Sjwadams 		mdb_printf("%#lr\n", value);
1820bf5197d8Sjwadams 		break;
1821bf5197d8Sjwadams 
1822bf5197d8Sjwadams 	case CTF_K_ENUM:
1823e9f82d69SAlex Reece 		if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &enum_value,
1824e9f82d69SAlex Reece 		    sizeof (enum_value), addr) != sizeof (enum_value)) {
1825e9f82d69SAlex Reece 			mdb_warn("failed to read enum at %llx", addr);
1826e9f82d69SAlex Reece 			return (-1);
1827e9f82d69SAlex Reece 		}
1828e9f82d69SAlex Reece 		mdb_printf("%#r\n", enum_value);
1829e9f82d69SAlex Reece 		break;
1830e9f82d69SAlex Reece 
1831e9f82d69SAlex Reece 	case CTF_K_INTEGER:
1832bf5197d8Sjwadams 		if (mdb_ctf_type_encoding(base, &e) != 0) {
1833e9f82d69SAlex Reece 			mdb_warn("could not get type encoding\n");
1834bf5197d8Sjwadams 			return (-1);
1835bf5197d8Sjwadams 		}
1836bf5197d8Sjwadams 
1837bf5197d8Sjwadams 		/*
1838bf5197d8Sjwadams 		 * For immediate values, we just print out the value.
1839bf5197d8Sjwadams 		 */
1840bf5197d8Sjwadams 		size = e.cte_bits / NBBY;
1841*60377884SRobert Mustacchi 		if (is_bitfield(&e, off)) {
1842bf5197d8Sjwadams 			return (print_bitfield(off, pap, &e));
1843bf5197d8Sjwadams 		}
1844bf5197d8Sjwadams 
1845bf5197d8Sjwadams 		if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.i8, size,
1846*60377884SRobert Mustacchi 		    addr) != (size_t)size) {
1847bf5197d8Sjwadams 			mdb_warn("failed to read %lu bytes at %p",
1848bf5197d8Sjwadams 			    (ulong_t)size, pap->pa_addr);
1849bf5197d8Sjwadams 			return (-1);
1850bf5197d8Sjwadams 		}
1851bf5197d8Sjwadams 
1852bf5197d8Sjwadams 		switch (size) {
1853bf5197d8Sjwadams 		case sizeof (uint8_t):
1854bf5197d8Sjwadams 			mdb_printf(fsp[0], u.i1);
1855bf5197d8Sjwadams 			break;
1856bf5197d8Sjwadams 		case sizeof (uint16_t):
1857bf5197d8Sjwadams 			mdb_printf(fsp[1], u.i2);
1858bf5197d8Sjwadams 			break;
1859bf5197d8Sjwadams 		case sizeof (uint32_t):
1860bf5197d8Sjwadams 			mdb_printf(fsp[2], u.i4);
1861bf5197d8Sjwadams 			break;
1862bf5197d8Sjwadams 		case sizeof (uint64_t):
1863bf5197d8Sjwadams 			mdb_printf(fsp[3], u.i8);
1864bf5197d8Sjwadams 			break;
1865bf5197d8Sjwadams 		}
1866bf5197d8Sjwadams 		mdb_printf("\n");
1867bf5197d8Sjwadams 		break;
1868bf5197d8Sjwadams 
1869bf5197d8Sjwadams 	case CTF_K_FUNCTION:
1870bf5197d8Sjwadams 	case CTF_K_FLOAT:
1871bf5197d8Sjwadams 	case CTF_K_ARRAY:
1872bf5197d8Sjwadams 	case CTF_K_UNKNOWN:
1873bf5197d8Sjwadams 	case CTF_K_STRUCT:
1874bf5197d8Sjwadams 	case CTF_K_UNION:
1875bf5197d8Sjwadams 	case CTF_K_FORWARD:
1876bf5197d8Sjwadams 		/*
1877bf5197d8Sjwadams 		 * For these types, always print the address of the member
1878bf5197d8Sjwadams 		 */
1879bf5197d8Sjwadams 		mdb_printf("%#lr\n", addr);
1880bf5197d8Sjwadams 		break;
1881bf5197d8Sjwadams 
1882bf5197d8Sjwadams 	default:
1883bf5197d8Sjwadams 		mdb_warn("unknown type %d", mdb_ctf_type_kind(base));
1884bf5197d8Sjwadams 		break;
1885bf5197d8Sjwadams 	}
1886bf5197d8Sjwadams 
1887bf5197d8Sjwadams 	return (0);
1888bf5197d8Sjwadams }
1889bf5197d8Sjwadams 
18907c478bd9Sstevel@tonic-gate static int
parse_delimiter(char ** strp)18917c478bd9Sstevel@tonic-gate parse_delimiter(char **strp)
18927c478bd9Sstevel@tonic-gate {
18937c478bd9Sstevel@tonic-gate 	switch (**strp) {
18947c478bd9Sstevel@tonic-gate 	case '\0':
18957c478bd9Sstevel@tonic-gate 		return (MEMBER_DELIM_DONE);
18967c478bd9Sstevel@tonic-gate 
18977c478bd9Sstevel@tonic-gate 	case '.':
18987c478bd9Sstevel@tonic-gate 		*strp = *strp + 1;
18997c478bd9Sstevel@tonic-gate 		return (MEMBER_DELIM_DOT);
19007c478bd9Sstevel@tonic-gate 
19017c478bd9Sstevel@tonic-gate 	case '[':
19027c478bd9Sstevel@tonic-gate 		*strp = *strp + 1;
19037c478bd9Sstevel@tonic-gate 		return (MEMBER_DELIM_LBR);
19047c478bd9Sstevel@tonic-gate 
19057c478bd9Sstevel@tonic-gate 	case '-':
19067c478bd9Sstevel@tonic-gate 		*strp = *strp + 1;
19077c478bd9Sstevel@tonic-gate 		if (**strp == '>') {
19087c478bd9Sstevel@tonic-gate 			*strp = *strp + 1;
19097c478bd9Sstevel@tonic-gate 			return (MEMBER_DELIM_PTR);
19107c478bd9Sstevel@tonic-gate 		}
19117c478bd9Sstevel@tonic-gate 		*strp = *strp - 1;
19127c478bd9Sstevel@tonic-gate 		/*FALLTHROUGH*/
19137c478bd9Sstevel@tonic-gate 	default:
19147c478bd9Sstevel@tonic-gate 		return (MEMBER_DELIM_ERR);
19157c478bd9Sstevel@tonic-gate 	}
19167c478bd9Sstevel@tonic-gate }
19177c478bd9Sstevel@tonic-gate 
19187c478bd9Sstevel@tonic-gate static int
deref(printarg_t * pap,size_t size)19197c478bd9Sstevel@tonic-gate deref(printarg_t *pap, size_t size)
19207c478bd9Sstevel@tonic-gate {
19217c478bd9Sstevel@tonic-gate 	uint32_t a32;
19227c478bd9Sstevel@tonic-gate 	mdb_tgt_as_t as = pap->pa_as;
19237c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t *ap = &pap->pa_addr;
19247c478bd9Sstevel@tonic-gate 
19257c478bd9Sstevel@tonic-gate 	if (size == sizeof (mdb_tgt_addr_t)) {
19267c478bd9Sstevel@tonic-gate 		if (mdb_tgt_aread(mdb.m_target, as, ap, size, *ap) == -1) {
19277c478bd9Sstevel@tonic-gate 			mdb_warn("could not dereference pointer %llx\n", *ap);
19287c478bd9Sstevel@tonic-gate 			return (-1);
19297c478bd9Sstevel@tonic-gate 		}
19307c478bd9Sstevel@tonic-gate 	} else {
19317c478bd9Sstevel@tonic-gate 		if (mdb_tgt_aread(mdb.m_target, as, &a32, size, *ap) == -1) {
19327c478bd9Sstevel@tonic-gate 			mdb_warn("could not dereference pointer %x\n", *ap);
19337c478bd9Sstevel@tonic-gate 			return (-1);
19347c478bd9Sstevel@tonic-gate 		}
19357c478bd9Sstevel@tonic-gate 
19367c478bd9Sstevel@tonic-gate 		*ap = (mdb_tgt_addr_t)a32;
19377c478bd9Sstevel@tonic-gate 	}
19387c478bd9Sstevel@tonic-gate 
19397c478bd9Sstevel@tonic-gate 	/*
19407c478bd9Sstevel@tonic-gate 	 * We've dereferenced at least once, we must be on the real
19417c478bd9Sstevel@tonic-gate 	 * target. If we were in the immediate target, reset to the real
19427c478bd9Sstevel@tonic-gate 	 * target; it's reset as needed when we return to the print
19437c478bd9Sstevel@tonic-gate 	 * routines.
19447c478bd9Sstevel@tonic-gate 	 */
19457c478bd9Sstevel@tonic-gate 	if (pap->pa_tgt == pap->pa_immtgt)
19467c478bd9Sstevel@tonic-gate 		pap->pa_tgt = pap->pa_realtgt;
19477c478bd9Sstevel@tonic-gate 
19487c478bd9Sstevel@tonic-gate 	return (0);
19497c478bd9Sstevel@tonic-gate }
19507c478bd9Sstevel@tonic-gate 
19517c478bd9Sstevel@tonic-gate static int
parse_member(printarg_t * pap,const char * str,mdb_ctf_id_t id,mdb_ctf_id_t * idp,ulong_t * offp,int * last_deref)19527c478bd9Sstevel@tonic-gate parse_member(printarg_t *pap, const char *str, mdb_ctf_id_t id,
19537c478bd9Sstevel@tonic-gate     mdb_ctf_id_t *idp, ulong_t *offp, int *last_deref)
19547c478bd9Sstevel@tonic-gate {
19557c478bd9Sstevel@tonic-gate 	int delim;
19567c478bd9Sstevel@tonic-gate 	char member[64];
19577c478bd9Sstevel@tonic-gate 	char buf[128];
19587c478bd9Sstevel@tonic-gate 	uint_t index;
19597c478bd9Sstevel@tonic-gate 	char *start = (char *)str;
19607c478bd9Sstevel@tonic-gate 	char *end;
19617c478bd9Sstevel@tonic-gate 	ulong_t off = 0;
19627c478bd9Sstevel@tonic-gate 	mdb_ctf_arinfo_t ar;
19637c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t rid;
19647c478bd9Sstevel@tonic-gate 	int kind;
19657c478bd9Sstevel@tonic-gate 	ssize_t size;
19667c478bd9Sstevel@tonic-gate 	int non_array = FALSE;
19677c478bd9Sstevel@tonic-gate 
19687c478bd9Sstevel@tonic-gate 	/*
19697c478bd9Sstevel@tonic-gate 	 * id always has the unresolved type for printing error messages
19707c478bd9Sstevel@tonic-gate 	 * that include the type; rid always has the resolved type for
19717c478bd9Sstevel@tonic-gate 	 * use in mdb_ctf_* calls.  It is possible for this command to fail,
19727c478bd9Sstevel@tonic-gate 	 * however, if the resolved type is in the parent and it is currently
19737c478bd9Sstevel@tonic-gate 	 * unavailable.  Note that we also can't print out the name of the
19747c478bd9Sstevel@tonic-gate 	 * type, since that would also rely on looking up the resolved name.
19757c478bd9Sstevel@tonic-gate 	 */
19767c478bd9Sstevel@tonic-gate 	if (mdb_ctf_type_resolve(id, &rid) != 0) {
19777c478bd9Sstevel@tonic-gate 		mdb_warn("failed to resolve type");
19787c478bd9Sstevel@tonic-gate 		return (-1);
19797c478bd9Sstevel@tonic-gate 	}
19807c478bd9Sstevel@tonic-gate 
19817c478bd9Sstevel@tonic-gate 	delim = parse_delimiter(&start);
19827c478bd9Sstevel@tonic-gate 	/*
19837c478bd9Sstevel@tonic-gate 	 * If the user fails to specify an initial delimiter, guess -> for
19847c478bd9Sstevel@tonic-gate 	 * pointer types and . for non-pointer types.
19857c478bd9Sstevel@tonic-gate 	 */
19867c478bd9Sstevel@tonic-gate 	if (delim == MEMBER_DELIM_ERR)
19877c478bd9Sstevel@tonic-gate 		delim = (mdb_ctf_type_kind(rid) == CTF_K_POINTER) ?
19887c478bd9Sstevel@tonic-gate 		    MEMBER_DELIM_PTR : MEMBER_DELIM_DOT;
19897c478bd9Sstevel@tonic-gate 
19907c478bd9Sstevel@tonic-gate 	*last_deref = FALSE;
19917c478bd9Sstevel@tonic-gate 
19927c478bd9Sstevel@tonic-gate 	while (delim != MEMBER_DELIM_DONE) {
19937c478bd9Sstevel@tonic-gate 		switch (delim) {
19947c478bd9Sstevel@tonic-gate 		case MEMBER_DELIM_PTR:
19957c478bd9Sstevel@tonic-gate 			kind = mdb_ctf_type_kind(rid);
19967c478bd9Sstevel@tonic-gate 			if (kind != CTF_K_POINTER) {
19977c478bd9Sstevel@tonic-gate 				mdb_warn("%s is not a pointer type\n",
19987c478bd9Sstevel@tonic-gate 				    mdb_ctf_type_name(id, buf, sizeof (buf)));
19997c478bd9Sstevel@tonic-gate 				return (-1);
20007c478bd9Sstevel@tonic-gate 			}
20017c478bd9Sstevel@tonic-gate 
20027c478bd9Sstevel@tonic-gate 			size = mdb_ctf_type_size(id);
20037c478bd9Sstevel@tonic-gate 			if (deref(pap, size) != 0)
20047c478bd9Sstevel@tonic-gate 				return (-1);
20057c478bd9Sstevel@tonic-gate 
20067c478bd9Sstevel@tonic-gate 			(void) mdb_ctf_type_reference(rid, &id);
20077c478bd9Sstevel@tonic-gate 			(void) mdb_ctf_type_resolve(id, &rid);
20087c478bd9Sstevel@tonic-gate 
20097c478bd9Sstevel@tonic-gate 			off = 0;
20107c478bd9Sstevel@tonic-gate 			break;
20117c478bd9Sstevel@tonic-gate 
20127c478bd9Sstevel@tonic-gate 		case MEMBER_DELIM_DOT:
20137c478bd9Sstevel@tonic-gate 			kind = mdb_ctf_type_kind(rid);
20147c478bd9Sstevel@tonic-gate 			if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) {
20157c478bd9Sstevel@tonic-gate 				mdb_warn("%s is not a struct or union type\n",
20167c478bd9Sstevel@tonic-gate 				    mdb_ctf_type_name(id, buf, sizeof (buf)));
20177c478bd9Sstevel@tonic-gate 				return (-1);
20187c478bd9Sstevel@tonic-gate 			}
20197c478bd9Sstevel@tonic-gate 			break;
20207c478bd9Sstevel@tonic-gate 
20217c478bd9Sstevel@tonic-gate 		case MEMBER_DELIM_LBR:
20227c478bd9Sstevel@tonic-gate 			end = strchr(start, ']');
20237c478bd9Sstevel@tonic-gate 			if (end == NULL) {
20247c478bd9Sstevel@tonic-gate 				mdb_warn("no trailing ']'\n");
20257c478bd9Sstevel@tonic-gate 				return (-1);
20267c478bd9Sstevel@tonic-gate 			}
20277c478bd9Sstevel@tonic-gate 
20283b6e0a59SMatt Amdur 			(void) mdb_snprintf(member, end - start + 1, "%s",
20293b6e0a59SMatt Amdur 			    start);
20307c478bd9Sstevel@tonic-gate 
20317c478bd9Sstevel@tonic-gate 			index = mdb_strtoull(member);
20327c478bd9Sstevel@tonic-gate 
20337c478bd9Sstevel@tonic-gate 			switch (mdb_ctf_type_kind(rid)) {
20347c478bd9Sstevel@tonic-gate 			case CTF_K_POINTER:
20357c478bd9Sstevel@tonic-gate 				size = mdb_ctf_type_size(rid);
20367c478bd9Sstevel@tonic-gate 
20377c478bd9Sstevel@tonic-gate 				if (deref(pap, size) != 0)
20387c478bd9Sstevel@tonic-gate 					return (-1);
20397c478bd9Sstevel@tonic-gate 
20407c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_type_reference(rid, &id);
20417c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_type_resolve(id, &rid);
20427c478bd9Sstevel@tonic-gate 
20437c478bd9Sstevel@tonic-gate 				size = mdb_ctf_type_size(id);
20447c478bd9Sstevel@tonic-gate 				if (size <= 0) {
20457c478bd9Sstevel@tonic-gate 					mdb_warn("cannot dereference void "
20467c478bd9Sstevel@tonic-gate 					    "type\n");
20477c478bd9Sstevel@tonic-gate 					return (-1);
20487c478bd9Sstevel@tonic-gate 				}
20497c478bd9Sstevel@tonic-gate 
20507c478bd9Sstevel@tonic-gate 				pap->pa_addr += index * size;
20517c478bd9Sstevel@tonic-gate 				off = 0;
20527c478bd9Sstevel@tonic-gate 
20537c478bd9Sstevel@tonic-gate 				if (index == 0 && non_array)
20547c478bd9Sstevel@tonic-gate 					*last_deref = TRUE;
20557c478bd9Sstevel@tonic-gate 				break;
20567c478bd9Sstevel@tonic-gate 
20577c478bd9Sstevel@tonic-gate 			case CTF_K_ARRAY:
20587c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_array_info(rid, &ar);
20597c478bd9Sstevel@tonic-gate 
20607c478bd9Sstevel@tonic-gate 				if (index >= ar.mta_nelems) {
20617c478bd9Sstevel@tonic-gate 					mdb_warn("index %r is outside of "
20627c478bd9Sstevel@tonic-gate 					    "array bounds [0 .. %r]\n",
20637c478bd9Sstevel@tonic-gate 					    index, ar.mta_nelems - 1);
20647c478bd9Sstevel@tonic-gate 				}
20657c478bd9Sstevel@tonic-gate 
20667c478bd9Sstevel@tonic-gate 				id = ar.mta_contents;
20677c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_type_resolve(id, &rid);
20687c478bd9Sstevel@tonic-gate 
20697c478bd9Sstevel@tonic-gate 				size = mdb_ctf_type_size(id);
20707c478bd9Sstevel@tonic-gate 				if (size <= 0) {
20717c478bd9Sstevel@tonic-gate 					mdb_warn("cannot dereference void "
20727c478bd9Sstevel@tonic-gate 					    "type\n");
20737c478bd9Sstevel@tonic-gate 					return (-1);
20747c478bd9Sstevel@tonic-gate 				}
20757c478bd9Sstevel@tonic-gate 
20767c478bd9Sstevel@tonic-gate 				pap->pa_addr += index * size;
20777c478bd9Sstevel@tonic-gate 				off = 0;
20787c478bd9Sstevel@tonic-gate 				break;
20797c478bd9Sstevel@tonic-gate 
20807c478bd9Sstevel@tonic-gate 			default:
20817c478bd9Sstevel@tonic-gate 				mdb_warn("cannot index into non-array, "
20827c478bd9Sstevel@tonic-gate 				    "non-pointer type\n");
20837c478bd9Sstevel@tonic-gate 				return (-1);
20847c478bd9Sstevel@tonic-gate 			}
20857c478bd9Sstevel@tonic-gate 
20867c478bd9Sstevel@tonic-gate 			start = end + 1;
20877c478bd9Sstevel@tonic-gate 			delim = parse_delimiter(&start);
20887c478bd9Sstevel@tonic-gate 			continue;
20897c478bd9Sstevel@tonic-gate 
20907c478bd9Sstevel@tonic-gate 		case MEMBER_DELIM_ERR:
20917c478bd9Sstevel@tonic-gate 		default:
20927c478bd9Sstevel@tonic-gate 			mdb_warn("'%c' is not a valid delimiter\n", *start);
20937c478bd9Sstevel@tonic-gate 			return (-1);
20947c478bd9Sstevel@tonic-gate 		}
20957c478bd9Sstevel@tonic-gate 
20967c478bd9Sstevel@tonic-gate 		*last_deref = FALSE;
20977c478bd9Sstevel@tonic-gate 		non_array = TRUE;
20987c478bd9Sstevel@tonic-gate 
20997c478bd9Sstevel@tonic-gate 		/*
21007c478bd9Sstevel@tonic-gate 		 * Find the end of the member name; assume that a member
21017c478bd9Sstevel@tonic-gate 		 * name is at least one character long.
21027c478bd9Sstevel@tonic-gate 		 */
21037c478bd9Sstevel@tonic-gate 		for (end = start + 1; isalnum(*end) || *end == '_'; end++)
21047c478bd9Sstevel@tonic-gate 			continue;
21057c478bd9Sstevel@tonic-gate 
21063b6e0a59SMatt Amdur 		(void) mdb_snprintf(member, end - start + 1, "%s", start);
21077c478bd9Sstevel@tonic-gate 
21087c478bd9Sstevel@tonic-gate 		if (mdb_ctf_member_info(rid, member, &off, &id) != 0) {
21097c478bd9Sstevel@tonic-gate 			mdb_warn("failed to find member %s of %s", member,
21107c478bd9Sstevel@tonic-gate 			    mdb_ctf_type_name(id, buf, sizeof (buf)));
21117c478bd9Sstevel@tonic-gate 			return (-1);
21127c478bd9Sstevel@tonic-gate 		}
21137c478bd9Sstevel@tonic-gate 		(void) mdb_ctf_type_resolve(id, &rid);
21147c478bd9Sstevel@tonic-gate 
21157c478bd9Sstevel@tonic-gate 		pap->pa_addr += off / NBBY;
21167c478bd9Sstevel@tonic-gate 
21177c478bd9Sstevel@tonic-gate 		start = end;
21187c478bd9Sstevel@tonic-gate 		delim = parse_delimiter(&start);
21197c478bd9Sstevel@tonic-gate 	}
21207c478bd9Sstevel@tonic-gate 
21217c478bd9Sstevel@tonic-gate 	*idp = id;
21227c478bd9Sstevel@tonic-gate 	*offp = off;
21237c478bd9Sstevel@tonic-gate 
21247c478bd9Sstevel@tonic-gate 	return (0);
21257c478bd9Sstevel@tonic-gate }
21267c478bd9Sstevel@tonic-gate 
21272f045fd6SRobert Mustacchi static int
cmd_print_tab_common(mdb_tab_cookie_t * mcp,uint_t flags,int argc,const mdb_arg_t * argv)21282f045fd6SRobert Mustacchi cmd_print_tab_common(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
21293b6e0a59SMatt Amdur     const mdb_arg_t *argv)
21303b6e0a59SMatt Amdur {
21313b6e0a59SMatt Amdur 	char tn[MDB_SYM_NAMLEN];
21323b6e0a59SMatt Amdur 	char member[64];
21332f045fd6SRobert Mustacchi 	int delim, kind;
21343b6e0a59SMatt Amdur 	int ret = 0;
21353b6e0a59SMatt Amdur 	mdb_ctf_id_t id, rid;
21363b6e0a59SMatt Amdur 	mdb_ctf_arinfo_t ar;
21373b6e0a59SMatt Amdur 	char *start, *end;
21383b6e0a59SMatt Amdur 	ulong_t dul;
21393b6e0a59SMatt Amdur 
21403b6e0a59SMatt Amdur 	if (argc == 0 && !(flags & DCMD_TAB_SPACE))
21413b6e0a59SMatt Amdur 		return (0);
21423b6e0a59SMatt Amdur 
21433b6e0a59SMatt Amdur 	if (argc == 0 && (flags & DCMD_TAB_SPACE))
21443b6e0a59SMatt Amdur 		return (mdb_tab_complete_type(mcp, NULL, MDB_TABC_NOPOINT |
21453b6e0a59SMatt Amdur 		    MDB_TABC_NOARRAY));
21463b6e0a59SMatt Amdur 
21473b6e0a59SMatt Amdur 	if ((ret = mdb_tab_typename(&argc, &argv, tn, sizeof (tn))) < 0)
21483b6e0a59SMatt Amdur 		return (ret);
21493b6e0a59SMatt Amdur 
21503b6e0a59SMatt Amdur 	if (argc == 1 && (!(flags & DCMD_TAB_SPACE) || ret == 1))
21513b6e0a59SMatt Amdur 		return (mdb_tab_complete_type(mcp, tn, MDB_TABC_NOPOINT |
21523b6e0a59SMatt Amdur 		    MDB_TABC_NOARRAY));
21533b6e0a59SMatt Amdur 
21543b6e0a59SMatt Amdur 	if (argc == 1 && (flags & DCMD_TAB_SPACE))
21553b6e0a59SMatt Amdur 		return (mdb_tab_complete_member(mcp, tn, NULL));
21563b6e0a59SMatt Amdur 
21573b6e0a59SMatt Amdur 	/*
21583b6e0a59SMatt Amdur 	 * This is the reason that tab completion was created. We're going to go
21593b6e0a59SMatt Amdur 	 * along and walk the delimiters until we find something a member that
21603b6e0a59SMatt Amdur 	 * we don't recognize, at which point we'll try and tab complete it.
21613b6e0a59SMatt Amdur 	 * Note that ::print takes multiple args, so this is going to operate on
21623b6e0a59SMatt Amdur 	 * whatever the last arg that we have is.
21633b6e0a59SMatt Amdur 	 */
21643b6e0a59SMatt Amdur 	if (mdb_ctf_lookup_by_name(tn, &id) != 0)
21653b6e0a59SMatt Amdur 		return (1);
21663b6e0a59SMatt Amdur 
21673b6e0a59SMatt Amdur 	(void) mdb_ctf_type_resolve(id, &rid);
21683b6e0a59SMatt Amdur 	start = (char *)argv[argc-1].a_un.a_str;
21693b6e0a59SMatt Amdur 	delim = parse_delimiter(&start);
21703b6e0a59SMatt Amdur 
21713b6e0a59SMatt Amdur 	/*
21723b6e0a59SMatt Amdur 	 * If we hit the case where we actually have no delimiters, than we need
21733b6e0a59SMatt Amdur 	 * to make sure that we properly set up the fields the loops would.
21743b6e0a59SMatt Amdur 	 */
21753b6e0a59SMatt Amdur 	if (delim == MEMBER_DELIM_DONE)
21763b6e0a59SMatt Amdur 		(void) mdb_snprintf(member, sizeof (member), "%s", start);
21773b6e0a59SMatt Amdur 
21783b6e0a59SMatt Amdur 	while (delim != MEMBER_DELIM_DONE) {
21793b6e0a59SMatt Amdur 		switch (delim) {
21803b6e0a59SMatt Amdur 		case MEMBER_DELIM_PTR:
21813b6e0a59SMatt Amdur 			kind = mdb_ctf_type_kind(rid);
21823b6e0a59SMatt Amdur 			if (kind != CTF_K_POINTER)
21833b6e0a59SMatt Amdur 				return (1);
21843b6e0a59SMatt Amdur 
21853b6e0a59SMatt Amdur 			(void) mdb_ctf_type_reference(rid, &id);
21863b6e0a59SMatt Amdur 			(void) mdb_ctf_type_resolve(id, &rid);
21873b6e0a59SMatt Amdur 			break;
21883b6e0a59SMatt Amdur 		case MEMBER_DELIM_DOT:
21893b6e0a59SMatt Amdur 			kind = mdb_ctf_type_kind(rid);
21903b6e0a59SMatt Amdur 			if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
21913b6e0a59SMatt Amdur 				return (1);
21923b6e0a59SMatt Amdur 			break;
21933b6e0a59SMatt Amdur 		case MEMBER_DELIM_LBR:
21943b6e0a59SMatt Amdur 			end = strchr(start, ']');
21953b6e0a59SMatt Amdur 			/*
21963b6e0a59SMatt Amdur 			 * We're not going to try and tab complete the indexes
21973b6e0a59SMatt Amdur 			 * here. So for now, punt on it. Also, we're not going
21983b6e0a59SMatt Amdur 			 * to try and validate you're within the bounds, just
21993b6e0a59SMatt Amdur 			 * that you get the type you asked for.
22003b6e0a59SMatt Amdur 			 */
22013b6e0a59SMatt Amdur 			if (end == NULL)
22023b6e0a59SMatt Amdur 				return (1);
22033b6e0a59SMatt Amdur 
22043b6e0a59SMatt Amdur 			switch (mdb_ctf_type_kind(rid)) {
22053b6e0a59SMatt Amdur 			case CTF_K_POINTER:
22063b6e0a59SMatt Amdur 				(void) mdb_ctf_type_reference(rid, &id);
22073b6e0a59SMatt Amdur 				(void) mdb_ctf_type_resolve(id, &rid);
22083b6e0a59SMatt Amdur 				break;
22093b6e0a59SMatt Amdur 			case CTF_K_ARRAY:
22103b6e0a59SMatt Amdur 				(void) mdb_ctf_array_info(rid, &ar);
22113b6e0a59SMatt Amdur 				id = ar.mta_contents;
22123b6e0a59SMatt Amdur 				(void) mdb_ctf_type_resolve(id, &rid);
22133b6e0a59SMatt Amdur 				break;
22143b6e0a59SMatt Amdur 			default:
22153b6e0a59SMatt Amdur 				return (1);
22163b6e0a59SMatt Amdur 			}
22173b6e0a59SMatt Amdur 
22183b6e0a59SMatt Amdur 			start = end + 1;
22193b6e0a59SMatt Amdur 			delim = parse_delimiter(&start);
22203b6e0a59SMatt Amdur 			break;
22213b6e0a59SMatt Amdur 		case MEMBER_DELIM_ERR:
22223b6e0a59SMatt Amdur 		default:
22233b6e0a59SMatt Amdur 			break;
22243b6e0a59SMatt Amdur 		}
22253b6e0a59SMatt Amdur 
22263b6e0a59SMatt Amdur 		for (end = start + 1; isalnum(*end) || *end == '_'; end++)
22273b6e0a59SMatt Amdur 			continue;
22283b6e0a59SMatt Amdur 
22293b6e0a59SMatt Amdur 		(void) mdb_snprintf(member, end - start + 1, start);
22303b6e0a59SMatt Amdur 
22313b6e0a59SMatt Amdur 		/*
22323b6e0a59SMatt Amdur 		 * We are going to try to resolve this name as a member. There
22333b6e0a59SMatt Amdur 		 * are a few two different questions that we need to answer. The
22343b6e0a59SMatt Amdur 		 * first is do we recognize this member. The second is are we at
22353b6e0a59SMatt Amdur 		 * the end of the string. If we encounter a member that we don't
22363b6e0a59SMatt Amdur 		 * recognize before the end, then we have to error out and can't
22373b6e0a59SMatt Amdur 		 * complete it. But if there are no more delimiters then we can
22383b6e0a59SMatt Amdur 		 * try and complete it.
22393b6e0a59SMatt Amdur 		 */
22403b6e0a59SMatt Amdur 		ret = mdb_ctf_member_info(rid, member, &dul, &id);
22413b6e0a59SMatt Amdur 		start = end;
22423b6e0a59SMatt Amdur 		delim = parse_delimiter(&start);
22433b6e0a59SMatt Amdur 		if (ret != 0 && errno == EMDB_CTFNOMEMB) {
22443b6e0a59SMatt Amdur 			if (delim != MEMBER_DELIM_DONE)
22453b6e0a59SMatt Amdur 				return (1);
22463b6e0a59SMatt Amdur 			continue;
22473b6e0a59SMatt Amdur 		} else if (ret != 0)
22483b6e0a59SMatt Amdur 			return (1);
22493b6e0a59SMatt Amdur 
22503b6e0a59SMatt Amdur 		if (delim == MEMBER_DELIM_DONE)
22513b6e0a59SMatt Amdur 			return (mdb_tab_complete_member_by_id(mcp, rid,
22523b6e0a59SMatt Amdur 			    member));
22533b6e0a59SMatt Amdur 
22543b6e0a59SMatt Amdur 		(void) mdb_ctf_type_resolve(id, &rid);
22553b6e0a59SMatt Amdur 	}
22563b6e0a59SMatt Amdur 
22573b6e0a59SMatt Amdur 	/*
22583b6e0a59SMatt Amdur 	 * If we've reached here, then we need to try and tab complete the last
22593b6e0a59SMatt Amdur 	 * field, which is currently member, based on the ctf type id that we
22603b6e0a59SMatt Amdur 	 * already have in rid.
22613b6e0a59SMatt Amdur 	 */
22623b6e0a59SMatt Amdur 	return (mdb_tab_complete_member_by_id(mcp, rid, member));
22633b6e0a59SMatt Amdur }
22643b6e0a59SMatt Amdur 
22652f045fd6SRobert Mustacchi int
cmd_print_tab(mdb_tab_cookie_t * mcp,uint_t flags,int argc,const mdb_arg_t * argv)22662f045fd6SRobert Mustacchi cmd_print_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
22672f045fd6SRobert Mustacchi     const mdb_arg_t *argv)
22682f045fd6SRobert Mustacchi {
22692f045fd6SRobert Mustacchi 	int i, dummy;
22702f045fd6SRobert Mustacchi 
22712f045fd6SRobert Mustacchi 	/*
22722f045fd6SRobert Mustacchi 	 * This getopts is only here to make the tab completion work better when
22732f045fd6SRobert Mustacchi 	 * including options in the ::print arguments. None of the values should
22742f045fd6SRobert Mustacchi 	 * be used. This should only be updated with additional arguments, if
22752f045fd6SRobert Mustacchi 	 * they are added to cmd_print.
22762f045fd6SRobert Mustacchi 	 */
22772f045fd6SRobert Mustacchi 	i = mdb_getopts(argc, argv,
22782f045fd6SRobert Mustacchi 	    'a', MDB_OPT_SETBITS, PA_SHOWADDR, &dummy,
22792f045fd6SRobert Mustacchi 	    'C', MDB_OPT_SETBITS, TRUE, &dummy,
22802f045fd6SRobert Mustacchi 	    'c', MDB_OPT_UINTPTR, &dummy,
22812f045fd6SRobert Mustacchi 	    'd', MDB_OPT_SETBITS, PA_INTDEC, &dummy,
22822f045fd6SRobert Mustacchi 	    'h', MDB_OPT_SETBITS, PA_SHOWHOLES, &dummy,
22832f045fd6SRobert Mustacchi 	    'i', MDB_OPT_SETBITS, TRUE, &dummy,
22842f045fd6SRobert Mustacchi 	    'L', MDB_OPT_SETBITS, TRUE, &dummy,
22852f045fd6SRobert Mustacchi 	    'l', MDB_OPT_UINTPTR, &dummy,
22862f045fd6SRobert Mustacchi 	    'n', MDB_OPT_SETBITS, PA_NOSYMBOLIC, &dummy,
22872f045fd6SRobert Mustacchi 	    'p', MDB_OPT_SETBITS, TRUE, &dummy,
22882f045fd6SRobert Mustacchi 	    's', MDB_OPT_UINTPTR, &dummy,
22892f045fd6SRobert Mustacchi 	    'T', MDB_OPT_SETBITS, PA_SHOWTYPE | PA_SHOWBASETYPE, &dummy,
22902f045fd6SRobert Mustacchi 	    't', MDB_OPT_SETBITS, PA_SHOWTYPE, &dummy,
22912f045fd6SRobert Mustacchi 	    'x', MDB_OPT_SETBITS, PA_INTHEX, &dummy,
22922f045fd6SRobert Mustacchi 	    NULL);
22932f045fd6SRobert Mustacchi 
22942f045fd6SRobert Mustacchi 	argc -= i;
22952f045fd6SRobert Mustacchi 	argv += i;
22962f045fd6SRobert Mustacchi 
22972f045fd6SRobert Mustacchi 	return (cmd_print_tab_common(mcp, flags, argc, argv));
22982f045fd6SRobert Mustacchi }
22992f045fd6SRobert Mustacchi 
23007c478bd9Sstevel@tonic-gate /*
23017c478bd9Sstevel@tonic-gate  * Recursively descend a print a given data structure.  We create a struct of
23027c478bd9Sstevel@tonic-gate  * the relevant print arguments and then call mdb_ctf_type_visit() to do the
23037c478bd9Sstevel@tonic-gate  * traversal, using elt_print() as the callback for each element.
23047c478bd9Sstevel@tonic-gate  */
23057c478bd9Sstevel@tonic-gate /*ARGSUSED*/
23067c478bd9Sstevel@tonic-gate int
cmd_print(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)23077c478bd9Sstevel@tonic-gate cmd_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
23087c478bd9Sstevel@tonic-gate {
23097c478bd9Sstevel@tonic-gate 	uintptr_t opt_c = MDB_ARR_NOLIMIT, opt_l = MDB_ARR_NOLIMIT;
23107c478bd9Sstevel@tonic-gate 	uint_t opt_C = FALSE, opt_L = FALSE, opt_p = FALSE, opt_i = FALSE;
2311bf5197d8Sjwadams 	uintptr_t opt_s = (uintptr_t)-1ul;
23127c478bd9Sstevel@tonic-gate 	int uflags = (flags & DCMD_ADDRSPEC) ? PA_SHOWVAL : 0;
23137c478bd9Sstevel@tonic-gate 	mdb_ctf_id_t id;
23147c478bd9Sstevel@tonic-gate 	int err = DCMD_OK;
23157c478bd9Sstevel@tonic-gate 
23167c478bd9Sstevel@tonic-gate 	mdb_tgt_t *t = mdb.m_target;
23177c478bd9Sstevel@tonic-gate 	printarg_t pa;
23187c478bd9Sstevel@tonic-gate 	int d, i;
23197c478bd9Sstevel@tonic-gate 
23207c478bd9Sstevel@tonic-gate 	char s_name[MDB_SYM_NAMLEN];
23217c478bd9Sstevel@tonic-gate 	mdb_syminfo_t s_info;
23227c478bd9Sstevel@tonic-gate 	GElf_Sym sym;
23237c478bd9Sstevel@tonic-gate 
23243b6e0a59SMatt Amdur 	/*
23253b6e0a59SMatt Amdur 	 * If a new option is added, make sure the getopts above in
23263b6e0a59SMatt Amdur 	 * cmd_print_tab is also updated.
23273b6e0a59SMatt Amdur 	 */
23287c478bd9Sstevel@tonic-gate 	i = mdb_getopts(argc, argv,
23297c478bd9Sstevel@tonic-gate 	    'a', MDB_OPT_SETBITS, PA_SHOWADDR, &uflags,
23307c478bd9Sstevel@tonic-gate 	    'C', MDB_OPT_SETBITS, TRUE, &opt_C,
2331cce40297SJonathan Adams 	    'c', MDB_OPT_UINTPTR, &opt_c,
23327c478bd9Sstevel@tonic-gate 	    'd', MDB_OPT_SETBITS, PA_INTDEC, &uflags,
23337c478bd9Sstevel@tonic-gate 	    'h', MDB_OPT_SETBITS, PA_SHOWHOLES, &uflags,
2334cce40297SJonathan Adams 	    'i', MDB_OPT_SETBITS, TRUE, &opt_i,
23357c478bd9Sstevel@tonic-gate 	    'L', MDB_OPT_SETBITS, TRUE, &opt_L,
2336cce40297SJonathan Adams 	    'l', MDB_OPT_UINTPTR, &opt_l,
23377c478bd9Sstevel@tonic-gate 	    'n', MDB_OPT_SETBITS, PA_NOSYMBOLIC, &uflags,
23387c478bd9Sstevel@tonic-gate 	    'p', MDB_OPT_SETBITS, TRUE, &opt_p,
2339cce40297SJonathan Adams 	    's', MDB_OPT_UINTPTR, &opt_s,
2340cce40297SJonathan Adams 	    'T', MDB_OPT_SETBITS, PA_SHOWTYPE | PA_SHOWBASETYPE, &uflags,
23417c478bd9Sstevel@tonic-gate 	    't', MDB_OPT_SETBITS, PA_SHOWTYPE, &uflags,
23427c478bd9Sstevel@tonic-gate 	    'x', MDB_OPT_SETBITS, PA_INTHEX, &uflags,
23437c478bd9Sstevel@tonic-gate 	    NULL);
23447c478bd9Sstevel@tonic-gate 
23457c478bd9Sstevel@tonic-gate 	if (uflags & PA_INTHEX)
23467c478bd9Sstevel@tonic-gate 		uflags &= ~PA_INTDEC;	/* -x and -d are mutually exclusive */
23477c478bd9Sstevel@tonic-gate 
2348bf5197d8Sjwadams 	uflags |= PA_SHOWNAME;
23497c478bd9Sstevel@tonic-gate 
23507c478bd9Sstevel@tonic-gate 	if (opt_p && opt_i) {
23517c478bd9Sstevel@tonic-gate 		mdb_warn("-p and -i options are incompatible\n");
23527c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
23537c478bd9Sstevel@tonic-gate 	}
23547c478bd9Sstevel@tonic-gate 
23557c478bd9Sstevel@tonic-gate 	argc -= i;
23567c478bd9Sstevel@tonic-gate 	argv += i;
23577c478bd9Sstevel@tonic-gate 
23587c478bd9Sstevel@tonic-gate 	if (argc != 0 && argv->a_type == MDB_TYPE_STRING) {
23597c478bd9Sstevel@tonic-gate 		const char *t_name = s_name;
23607c478bd9Sstevel@tonic-gate 		int ret;
23617c478bd9Sstevel@tonic-gate 
23627c478bd9Sstevel@tonic-gate 		if (strchr("+-", argv->a_un.a_str[0]) != NULL)
23637c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
23647c478bd9Sstevel@tonic-gate 
23657c478bd9Sstevel@tonic-gate 		if ((ret = args_to_typename(&argc, &argv, s_name,
23667c478bd9Sstevel@tonic-gate 		    sizeof (s_name))) != 0)
23677c478bd9Sstevel@tonic-gate 			return (ret);
23687c478bd9Sstevel@tonic-gate 
23697c478bd9Sstevel@tonic-gate 		if (mdb_ctf_lookup_by_name(t_name, &id) != 0) {
23707c478bd9Sstevel@tonic-gate 			if (!(flags & DCMD_ADDRSPEC) || opt_i ||
23717c478bd9Sstevel@tonic-gate 			    addr_to_sym(t, addr, s_name, sizeof (s_name),
2372cce40297SJonathan Adams 			    &sym, &s_info) == NULL ||
23737c478bd9Sstevel@tonic-gate 			    mdb_ctf_lookup_by_symbol(&sym, &s_info, &id) != 0) {
23747c478bd9Sstevel@tonic-gate 
23757c478bd9Sstevel@tonic-gate 				mdb_warn("failed to look up type %s", t_name);
23767c478bd9Sstevel@tonic-gate 				return (DCMD_ABORT);
23777c478bd9Sstevel@tonic-gate 			}
23787c478bd9Sstevel@tonic-gate 		} else {
23797c478bd9Sstevel@tonic-gate 			argc--;
23807c478bd9Sstevel@tonic-gate 			argv++;
23817c478bd9Sstevel@tonic-gate 		}
23827c478bd9Sstevel@tonic-gate 
23837c478bd9Sstevel@tonic-gate 	} else if (!(flags & DCMD_ADDRSPEC) || opt_i) {
23847c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
23857c478bd9Sstevel@tonic-gate 
23867c478bd9Sstevel@tonic-gate 	} else if (addr_to_sym(t, addr, s_name, sizeof (s_name),
23877c478bd9Sstevel@tonic-gate 	    &sym, &s_info) == NULL) {
23887c478bd9Sstevel@tonic-gate 		mdb_warn("no symbol information for %a", addr);
23897c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
23907c478bd9Sstevel@tonic-gate 
23917c478bd9Sstevel@tonic-gate 	} else if (mdb_ctf_lookup_by_symbol(&sym, &s_info, &id) != 0) {
23927c478bd9Sstevel@tonic-gate 		mdb_warn("no type data available for %a [%u]", addr,
23937c478bd9Sstevel@tonic-gate 		    s_info.sym_id);
23947c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
23957c478bd9Sstevel@tonic-gate 	}
23967c478bd9Sstevel@tonic-gate 
23977c478bd9Sstevel@tonic-gate 	pa.pa_tgt = mdb.m_target;
23987c478bd9Sstevel@tonic-gate 	pa.pa_realtgt = pa.pa_tgt;
23997c478bd9Sstevel@tonic-gate 	pa.pa_immtgt = NULL;
24007c478bd9Sstevel@tonic-gate 	pa.pa_as = opt_p ? MDB_TGT_AS_PHYS : MDB_TGT_AS_VIRT;
24017c478bd9Sstevel@tonic-gate 	pa.pa_armemlim = mdb.m_armemlim;
24027c478bd9Sstevel@tonic-gate 	pa.pa_arstrlim = mdb.m_arstrlim;
24037c478bd9Sstevel@tonic-gate 	pa.pa_delim = "\n";
24047c478bd9Sstevel@tonic-gate 	pa.pa_flags = uflags;
24057c478bd9Sstevel@tonic-gate 	pa.pa_nest = 0;
24067c478bd9Sstevel@tonic-gate 	pa.pa_tab = 4;
24077c478bd9Sstevel@tonic-gate 	pa.pa_prefix = NULL;
24087c478bd9Sstevel@tonic-gate 	pa.pa_suffix = NULL;
24097c478bd9Sstevel@tonic-gate 	pa.pa_holes = NULL;
24107c478bd9Sstevel@tonic-gate 	pa.pa_nholes = 0;
24117c478bd9Sstevel@tonic-gate 	pa.pa_depth = 0;
2412bf5197d8Sjwadams 	pa.pa_maxdepth = opt_s;
2413838d7172SSebastien Roy 	pa.pa_nooutdepth = (uint_t)-1;
24147c478bd9Sstevel@tonic-gate 
24157c478bd9Sstevel@tonic-gate 	if ((flags & DCMD_ADDRSPEC) && !opt_i)
24167c478bd9Sstevel@tonic-gate 		pa.pa_addr = opt_p ? mdb_get_dot() : addr;
24177c478bd9Sstevel@tonic-gate 	else
2418892ad162SToomas Soome 		pa.pa_addr = 0;
24197c478bd9Sstevel@tonic-gate 
24207c478bd9Sstevel@tonic-gate 	if (opt_i) {
24217c478bd9Sstevel@tonic-gate 		const char *vargv[2];
24227c478bd9Sstevel@tonic-gate 		uintmax_t dot = mdb_get_dot();
24237c478bd9Sstevel@tonic-gate 		size_t outsize = mdb_ctf_type_size(id);
24247c478bd9Sstevel@tonic-gate 		vargv[0] = (const char *)&dot;
24257c478bd9Sstevel@tonic-gate 		vargv[1] = (const char *)&outsize;
24267c478bd9Sstevel@tonic-gate 		pa.pa_immtgt = mdb_tgt_create(mdb_value_tgt_create,
2427cce40297SJonathan Adams 		    0, 2, vargv);
24287c478bd9Sstevel@tonic-gate 		pa.pa_tgt = pa.pa_immtgt;
24297c478bd9Sstevel@tonic-gate 	}
24307c478bd9Sstevel@tonic-gate 
24317c478bd9Sstevel@tonic-gate 	if (opt_c != MDB_ARR_NOLIMIT)
24327c478bd9Sstevel@tonic-gate 		pa.pa_arstrlim = opt_c;
24337c478bd9Sstevel@tonic-gate 	if (opt_C)
24347c478bd9Sstevel@tonic-gate 		pa.pa_arstrlim = MDB_ARR_NOLIMIT;
24357c478bd9Sstevel@tonic-gate 	if (opt_l != MDB_ARR_NOLIMIT)
24367c478bd9Sstevel@tonic-gate 		pa.pa_armemlim = opt_l;
24377c478bd9Sstevel@tonic-gate 	if (opt_L)
24387c478bd9Sstevel@tonic-gate 		pa.pa_armemlim = MDB_ARR_NOLIMIT;
24397c478bd9Sstevel@tonic-gate 
24407c478bd9Sstevel@tonic-gate 	if (argc > 0) {
24417c478bd9Sstevel@tonic-gate 		for (i = 0; i < argc; i++) {
24427c478bd9Sstevel@tonic-gate 			mdb_ctf_id_t mid;
24437c478bd9Sstevel@tonic-gate 			int last_deref;
24447c478bd9Sstevel@tonic-gate 			ulong_t off;
24457c478bd9Sstevel@tonic-gate 			int kind;
24467c478bd9Sstevel@tonic-gate 			char buf[MDB_SYM_NAMLEN];
24477c478bd9Sstevel@tonic-gate 
24487c478bd9Sstevel@tonic-gate 			mdb_tgt_t *oldtgt = pa.pa_tgt;
24497c478bd9Sstevel@tonic-gate 			mdb_tgt_as_t oldas = pa.pa_as;
24507c478bd9Sstevel@tonic-gate 			mdb_tgt_addr_t oldaddr = pa.pa_addr;
24517c478bd9Sstevel@tonic-gate 
24527c478bd9Sstevel@tonic-gate 			if (argv->a_type == MDB_TYPE_STRING) {
24537c478bd9Sstevel@tonic-gate 				const char *member = argv[i].a_un.a_str;
24547c478bd9Sstevel@tonic-gate 				mdb_ctf_id_t rid;
24557c478bd9Sstevel@tonic-gate 
24567c478bd9Sstevel@tonic-gate 				if (parse_member(&pa, member, id, &mid,
24577c478bd9Sstevel@tonic-gate 				    &off, &last_deref) != 0) {
24587c478bd9Sstevel@tonic-gate 					err = DCMD_ABORT;
24597c478bd9Sstevel@tonic-gate 					goto out;
24607c478bd9Sstevel@tonic-gate 				}
24617c478bd9Sstevel@tonic-gate 
24627c478bd9Sstevel@tonic-gate 				/*
24637c478bd9Sstevel@tonic-gate 				 * If the member string ends with a "[0]"
24647c478bd9Sstevel@tonic-gate 				 * (last_deref * is true) and the type is a
24657c478bd9Sstevel@tonic-gate 				 * structure or union, * print "->" rather
24667c478bd9Sstevel@tonic-gate 				 * than "[0]." in elt_print.
24677c478bd9Sstevel@tonic-gate 				 */
24687c478bd9Sstevel@tonic-gate 				(void) mdb_ctf_type_resolve(mid, &rid);
24697c478bd9Sstevel@tonic-gate 				kind = mdb_ctf_type_kind(rid);
24707c478bd9Sstevel@tonic-gate 				if (last_deref && IS_SOU(kind)) {
24717c478bd9Sstevel@tonic-gate 					char *end;
24727c478bd9Sstevel@tonic-gate 					(void) mdb_snprintf(buf, sizeof (buf),
24737c478bd9Sstevel@tonic-gate 					    "%s", member);
24747c478bd9Sstevel@tonic-gate 					end = strrchr(buf, '[');
24757c478bd9Sstevel@tonic-gate 					*end = '\0';
24767c478bd9Sstevel@tonic-gate 					pa.pa_suffix = "->";
24777c478bd9Sstevel@tonic-gate 					member = &buf[0];
24787c478bd9Sstevel@tonic-gate 				} else if (IS_SOU(kind)) {
24797c478bd9Sstevel@tonic-gate 					pa.pa_suffix = ".";
24807c478bd9Sstevel@tonic-gate 				} else {
24817c478bd9Sstevel@tonic-gate 					pa.pa_suffix = "";
24827c478bd9Sstevel@tonic-gate 				}
24837c478bd9Sstevel@tonic-gate 
24847c478bd9Sstevel@tonic-gate 				pa.pa_prefix = member;
24857c478bd9Sstevel@tonic-gate 			} else {
24867c478bd9Sstevel@tonic-gate 				ulong_t moff;
24877c478bd9Sstevel@tonic-gate 
24887c478bd9Sstevel@tonic-gate 				moff = (ulong_t)argv[i].a_un.a_val;
24897c478bd9Sstevel@tonic-gate 
24907c478bd9Sstevel@tonic-gate 				if (mdb_ctf_offset_to_name(id, moff * NBBY,
24917c478bd9Sstevel@tonic-gate 				    buf, sizeof (buf), 0, &mid, &off) == -1) {
24927c478bd9Sstevel@tonic-gate 					mdb_warn("invalid offset %lx\n", moff);
24937c478bd9Sstevel@tonic-gate 					err = DCMD_ABORT;
24947c478bd9Sstevel@tonic-gate 					goto out;
24957c478bd9Sstevel@tonic-gate 				}
24967c478bd9Sstevel@tonic-gate 
24977c478bd9Sstevel@tonic-gate 				pa.pa_prefix = buf;
24987c478bd9Sstevel@tonic-gate 				pa.pa_addr += moff - off / NBBY;
24997c478bd9Sstevel@tonic-gate 				pa.pa_suffix = strlen(buf) == 0 ? "" : ".";
25007c478bd9Sstevel@tonic-gate 			}
25017c478bd9Sstevel@tonic-gate 
25027c478bd9Sstevel@tonic-gate 			off %= NBBY;
2503bf5197d8Sjwadams 			if (flags & DCMD_PIPE_OUT) {
2504bf5197d8Sjwadams 				if (pipe_print(mid, off, &pa) != 0) {
2505bf5197d8Sjwadams 					mdb_warn("failed to print type");
2506bf5197d8Sjwadams 					err = DCMD_ERR;
2507bf5197d8Sjwadams 					goto out;
2508bf5197d8Sjwadams 				}
2509bf5197d8Sjwadams 			} else if (off != 0) {
2510cce40297SJonathan Adams 				mdb_ctf_id_t base;
2511cce40297SJonathan Adams 				(void) mdb_ctf_type_resolve(mid, &base);
2512cce40297SJonathan Adams 
2513cce40297SJonathan Adams 				if (elt_print("", mid, base, off, 0,
2514cce40297SJonathan Adams 				    &pa) != 0) {
25157c478bd9Sstevel@tonic-gate 					mdb_warn("failed to print type");
25167c478bd9Sstevel@tonic-gate 					err = DCMD_ERR;
25177c478bd9Sstevel@tonic-gate 					goto out;
25187c478bd9Sstevel@tonic-gate 				}
25197c478bd9Sstevel@tonic-gate 			} else {
25207c478bd9Sstevel@tonic-gate 				if (mdb_ctf_type_visit(mid, elt_print,
25217c478bd9Sstevel@tonic-gate 				    &pa) == -1) {
25227c478bd9Sstevel@tonic-gate 					mdb_warn("failed to print type");
25237c478bd9Sstevel@tonic-gate 					err = DCMD_ERR;
25247c478bd9Sstevel@tonic-gate 					goto out;
25257c478bd9Sstevel@tonic-gate 				}
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate 				for (d = pa.pa_depth - 1; d >= 0; d--)
25287c478bd9Sstevel@tonic-gate 					print_close_sou(&pa, d);
25297c478bd9Sstevel@tonic-gate 			}
25307c478bd9Sstevel@tonic-gate 
25317c478bd9Sstevel@tonic-gate 			pa.pa_depth = 0;
25327c478bd9Sstevel@tonic-gate 			pa.pa_tgt = oldtgt;
25337c478bd9Sstevel@tonic-gate 			pa.pa_as = oldas;
25347c478bd9Sstevel@tonic-gate 			pa.pa_addr = oldaddr;
25357c478bd9Sstevel@tonic-gate 			pa.pa_delim = "\n";
25367c478bd9Sstevel@tonic-gate 		}
25377c478bd9Sstevel@tonic-gate 
2538bf5197d8Sjwadams 	} else if (flags & DCMD_PIPE_OUT) {
2539bf5197d8Sjwadams 		if (pipe_print(id, 0, &pa) != 0) {
2540bf5197d8Sjwadams 			mdb_warn("failed to print type");
2541bf5197d8Sjwadams 			err = DCMD_ERR;
2542bf5197d8Sjwadams 			goto out;
2543bf5197d8Sjwadams 		}
25447c478bd9Sstevel@tonic-gate 	} else {
25457c478bd9Sstevel@tonic-gate 		if (mdb_ctf_type_visit(id, elt_print, &pa) == -1) {
25467c478bd9Sstevel@tonic-gate 			mdb_warn("failed to print type");
25477c478bd9Sstevel@tonic-gate 			err = DCMD_ERR;
25487c478bd9Sstevel@tonic-gate 			goto out;
25497c478bd9Sstevel@tonic-gate 		}
25507c478bd9Sstevel@tonic-gate 
25517c478bd9Sstevel@tonic-gate 		for (d = pa.pa_depth - 1; d >= 0; d--)
25527c478bd9Sstevel@tonic-gate 			print_close_sou(&pa, d);
25537c478bd9Sstevel@tonic-gate 	}
25547c478bd9Sstevel@tonic-gate 
25557c478bd9Sstevel@tonic-gate 	mdb_set_dot(addr + mdb_ctf_type_size(id));
25567c478bd9Sstevel@tonic-gate 	err = DCMD_OK;
25577c478bd9Sstevel@tonic-gate out:
25587c478bd9Sstevel@tonic-gate 	if (pa.pa_immtgt)
25597c478bd9Sstevel@tonic-gate 		mdb_tgt_destroy(pa.pa_immtgt);
25607c478bd9Sstevel@tonic-gate 	return (err);
25617c478bd9Sstevel@tonic-gate }
25627c478bd9Sstevel@tonic-gate 
25637c478bd9Sstevel@tonic-gate void
print_help(void)25647c478bd9Sstevel@tonic-gate print_help(void)
25657c478bd9Sstevel@tonic-gate {
2566cce40297SJonathan Adams 	mdb_printf(
2567cce40297SJonathan Adams 	    "-a         show address of object\n"
25687c478bd9Sstevel@tonic-gate 	    "-C         unlimit the length of character arrays\n"
2569cce40297SJonathan Adams 	    "-c limit   limit the length of character arrays\n"
25707c478bd9Sstevel@tonic-gate 	    "-d         output values in decimal\n"
25717c478bd9Sstevel@tonic-gate 	    "-h         print holes in structures\n"
2572cce40297SJonathan Adams 	    "-i         interpret address as data of the given type\n"
25737c478bd9Sstevel@tonic-gate 	    "-L         unlimit the length of standard arrays\n"
2574cce40297SJonathan Adams 	    "-l limit   limit the length of standard arrays\n"
2575cace7e23Seschrock 	    "-n         don't print pointers as symbol offsets\n"
25767c478bd9Sstevel@tonic-gate 	    "-p         interpret address as a physical memory address\n"
2577cce40297SJonathan Adams 	    "-s depth   limit the recursion depth\n"
2578cce40297SJonathan Adams 	    "-T         show type and <<base type>> of object\n"
25797c478bd9Sstevel@tonic-gate 	    "-t         show type of object\n"
25807c478bd9Sstevel@tonic-gate 	    "-x         output values in hexadecimal\n"
25817c478bd9Sstevel@tonic-gate 	    "\n"
25827c478bd9Sstevel@tonic-gate 	    "type may be omitted if the C type of addr can be inferred.\n"
25837c478bd9Sstevel@tonic-gate 	    "\n"
25847c478bd9Sstevel@tonic-gate 	    "Members may be specified with standard C syntax using the\n"
25857c478bd9Sstevel@tonic-gate 	    "array indexing operator \"[index]\", structure member\n"
25867c478bd9Sstevel@tonic-gate 	    "operator \".\", or structure pointer operator \"->\".\n"
25877c478bd9Sstevel@tonic-gate 	    "\n"
25887c478bd9Sstevel@tonic-gate 	    "Offsets must use the $[ expression ] syntax\n");
25897c478bd9Sstevel@tonic-gate }
25903ddcfaddSBryan Cantrill 
25913ddcfaddSBryan Cantrill static int
printf_signed(mdb_ctf_id_t id,uintptr_t addr,ulong_t off,char * fmt,boolean_t sign)25923ddcfaddSBryan Cantrill printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt,
25933ddcfaddSBryan Cantrill     boolean_t sign)
25943ddcfaddSBryan Cantrill {
2595*60377884SRobert Mustacchi 	size_t size;
25963ddcfaddSBryan Cantrill 	mdb_ctf_id_t base;
25973ddcfaddSBryan Cantrill 	ctf_encoding_t e;
25983ddcfaddSBryan Cantrill 
25993ddcfaddSBryan Cantrill 	union {
26003ddcfaddSBryan Cantrill 		uint64_t ui8;
26013ddcfaddSBryan Cantrill 		uint32_t ui4;
26023ddcfaddSBryan Cantrill 		uint16_t ui2;
26033ddcfaddSBryan Cantrill 		uint8_t ui1;
26043ddcfaddSBryan Cantrill 		int64_t i8;
26053ddcfaddSBryan Cantrill 		int32_t i4;
26063ddcfaddSBryan Cantrill 		int16_t i2;
26073ddcfaddSBryan Cantrill 		int8_t i1;
26083ddcfaddSBryan Cantrill 	} u;
26093ddcfaddSBryan Cantrill 
26103ddcfaddSBryan Cantrill 	if (mdb_ctf_type_resolve(id, &base) == -1) {
26113ddcfaddSBryan Cantrill 		mdb_warn("could not resolve type");
26123ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
26133ddcfaddSBryan Cantrill 	}
26143ddcfaddSBryan Cantrill 
261538ce19d2SJosef 'Jeff' Sipek 	switch (mdb_ctf_type_kind(base)) {
261638ce19d2SJosef 'Jeff' Sipek 		case CTF_K_ENUM:
261738ce19d2SJosef 'Jeff' Sipek 			e.cte_format = CTF_INT_SIGNED;
261838ce19d2SJosef 'Jeff' Sipek 			e.cte_offset = 0;
261938ce19d2SJosef 'Jeff' Sipek 			e.cte_bits = mdb_ctf_type_size(id) * NBBY;
262038ce19d2SJosef 'Jeff' Sipek 			break;
262138ce19d2SJosef 'Jeff' Sipek 		case CTF_K_INTEGER:
262238ce19d2SJosef 'Jeff' Sipek 			if (mdb_ctf_type_encoding(base, &e) != 0) {
262338ce19d2SJosef 'Jeff' Sipek 				mdb_warn("could not get type encoding");
262438ce19d2SJosef 'Jeff' Sipek 				return (DCMD_ABORT);
262538ce19d2SJosef 'Jeff' Sipek 			}
262638ce19d2SJosef 'Jeff' Sipek 			break;
262738ce19d2SJosef 'Jeff' Sipek 		default:
262838ce19d2SJosef 'Jeff' Sipek 			mdb_warn("expected integer type\n");
262938ce19d2SJosef 'Jeff' Sipek 			return (DCMD_ABORT);
26303ddcfaddSBryan Cantrill 	}
26313ddcfaddSBryan Cantrill 
26323ddcfaddSBryan Cantrill 	if (sign)
26333ddcfaddSBryan Cantrill 		sign = e.cte_format & CTF_INT_SIGNED;
26343ddcfaddSBryan Cantrill 
26353ddcfaddSBryan Cantrill 	size = e.cte_bits / NBBY;
26363ddcfaddSBryan Cantrill 
26373ddcfaddSBryan Cantrill 	/*
26383ddcfaddSBryan Cantrill 	 * Check to see if our life has been complicated by the presence of
26393ddcfaddSBryan Cantrill 	 * a bitfield.  If it has, we will print it using logic that is only
26403ddcfaddSBryan Cantrill 	 * slightly different than that found in print_bitfield(), above.  (In
26413ddcfaddSBryan Cantrill 	 * particular, see the comments there for an explanation of the
26423ddcfaddSBryan Cantrill 	 * endianness differences in this code.)
26433ddcfaddSBryan Cantrill 	 */
2644*60377884SRobert Mustacchi 	if (is_bitfield(&e, off)) {
26453ddcfaddSBryan Cantrill 		uint64_t mask = (1ULL << e.cte_bits) - 1;
26463ddcfaddSBryan Cantrill 		uint64_t value = 0;
26473ddcfaddSBryan Cantrill 		uint8_t *buf = (uint8_t *)&value;
26483ddcfaddSBryan Cantrill 		uint8_t shift;
2649*60377884SRobert Mustacchi 		uint_t nbits;
26503ddcfaddSBryan Cantrill 
26513ddcfaddSBryan Cantrill 		/*
2652*60377884SRobert Mustacchi 		 * Our bitfield may straddle a byte boundary. We explicitly take
2653*60377884SRobert Mustacchi 		 * the offset of the bitfield within its byte into account when
2654*60377884SRobert Mustacchi 		 * determining the overall amount of data to copy and mask off
2655*60377884SRobert Mustacchi 		 * from the underlying data.
26563ddcfaddSBryan Cantrill 		 */
2657*60377884SRobert Mustacchi 		nbits = e.cte_bits + (off % NBBY);
2658*60377884SRobert Mustacchi 		size = P2ROUNDUP(nbits, NBBY) / NBBY;
26593ddcfaddSBryan Cantrill 
26603ddcfaddSBryan Cantrill 		if (e.cte_bits > sizeof (value) * NBBY - 1) {
26613ddcfaddSBryan Cantrill 			mdb_printf("invalid bitfield size %u", e.cte_bits);
26623ddcfaddSBryan Cantrill 			return (DCMD_ABORT);
26633ddcfaddSBryan Cantrill 		}
26643ddcfaddSBryan Cantrill 
2665*60377884SRobert Mustacchi 		/*
2666*60377884SRobert Mustacchi 		 * Our bitfield may straddle a byte boundary, if so, the
2667*60377884SRobert Mustacchi 		 * calculation of size may not correctly capture that. However,
2668*60377884SRobert Mustacchi 		 * off is relative to the entire bitfield, so we first have to
2669*60377884SRobert Mustacchi 		 * make that relative to the byte.
2670*60377884SRobert Mustacchi 		 */
2671*60377884SRobert Mustacchi 		if ((off % NBBY) + e.cte_bits > NBBY * size) {
2672*60377884SRobert Mustacchi 			size++;
2673*60377884SRobert Mustacchi 		}
2674*60377884SRobert Mustacchi 
2675*60377884SRobert Mustacchi 		if (size > sizeof (value)) {
2676*60377884SRobert Mustacchi 			mdb_warn("??? (total bitfield too large after "
2677*60377884SRobert Mustacchi 			    "alignment\n");
2678*60377884SRobert Mustacchi 			return (DCMD_ABORT);
2679*60377884SRobert Mustacchi 		}
2680*60377884SRobert Mustacchi 
26813ddcfaddSBryan Cantrill #ifdef _BIG_ENDIAN
26823ddcfaddSBryan Cantrill 		buf += sizeof (value) - size;
26833ddcfaddSBryan Cantrill 		off += e.cte_bits;
26843ddcfaddSBryan Cantrill #endif
26853ddcfaddSBryan Cantrill 
26863ddcfaddSBryan Cantrill 		if (mdb_vread(buf, size, addr) == -1) {
26873ddcfaddSBryan Cantrill 			mdb_warn("failed to read %lu bytes at %p", size, addr);
26883ddcfaddSBryan Cantrill 			return (DCMD_ERR);
26893ddcfaddSBryan Cantrill 		}
26903ddcfaddSBryan Cantrill 
26913ddcfaddSBryan Cantrill 		shift = off % NBBY;
26923ddcfaddSBryan Cantrill #ifdef _BIG_ENDIAN
26933ddcfaddSBryan Cantrill 		shift = NBBY - shift;
26943ddcfaddSBryan Cantrill #endif
26953ddcfaddSBryan Cantrill 
26963ddcfaddSBryan Cantrill 		/*
26973ddcfaddSBryan Cantrill 		 * If we have a bit offset within the byte, shift it down.
26983ddcfaddSBryan Cantrill 		 */
26993ddcfaddSBryan Cantrill 		if (off % NBBY != 0)
27003ddcfaddSBryan Cantrill 			value >>= shift;
27013ddcfaddSBryan Cantrill 		value &= mask;
27023ddcfaddSBryan Cantrill 
27033ddcfaddSBryan Cantrill 		if (sign) {
27043ddcfaddSBryan Cantrill 			int sshift = sizeof (value) * NBBY - e.cte_bits;
27053ddcfaddSBryan Cantrill 			value = ((int64_t)value << sshift) >> sshift;
27063ddcfaddSBryan Cantrill 		}
27073ddcfaddSBryan Cantrill 
27083ddcfaddSBryan Cantrill 		mdb_printf(fmt, value);
27093ddcfaddSBryan Cantrill 		return (0);
27103ddcfaddSBryan Cantrill 	}
27113ddcfaddSBryan Cantrill 
27123ddcfaddSBryan Cantrill 	if (mdb_vread(&u.i8, size, addr) == -1) {
27133ddcfaddSBryan Cantrill 		mdb_warn("failed to read %lu bytes at %p", (ulong_t)size, addr);
27143ddcfaddSBryan Cantrill 		return (DCMD_ERR);
27153ddcfaddSBryan Cantrill 	}
27163ddcfaddSBryan Cantrill 
27173ddcfaddSBryan Cantrill 	switch (size) {
27183ddcfaddSBryan Cantrill 	case sizeof (uint8_t):
27193ddcfaddSBryan Cantrill 		mdb_printf(fmt, (uint64_t)(sign ? u.i1 : u.ui1));
27203ddcfaddSBryan Cantrill 		break;
27213ddcfaddSBryan Cantrill 	case sizeof (uint16_t):
27223ddcfaddSBryan Cantrill 		mdb_printf(fmt, (uint64_t)(sign ? u.i2 : u.ui2));
27233ddcfaddSBryan Cantrill 		break;
27243ddcfaddSBryan Cantrill 	case sizeof (uint32_t):
27253ddcfaddSBryan Cantrill 		mdb_printf(fmt, (uint64_t)(sign ? u.i4 : u.ui4));
27263ddcfaddSBryan Cantrill 		break;
27273ddcfaddSBryan Cantrill 	case sizeof (uint64_t):
27283ddcfaddSBryan Cantrill 		mdb_printf(fmt, (uint64_t)(sign ? u.i8 : u.ui8));
27293ddcfaddSBryan Cantrill 		break;
27303ddcfaddSBryan Cantrill 	}
27313ddcfaddSBryan Cantrill 
27323ddcfaddSBryan Cantrill 	return (0);
27333ddcfaddSBryan Cantrill }
27343ddcfaddSBryan Cantrill 
27353ddcfaddSBryan Cantrill static int
printf_int(mdb_ctf_id_t id,uintptr_t addr,ulong_t off,char * fmt)27363ddcfaddSBryan Cantrill printf_int(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
27373ddcfaddSBryan Cantrill {
27383ddcfaddSBryan Cantrill 	return (printf_signed(id, addr, off, fmt, B_TRUE));
27393ddcfaddSBryan Cantrill }
27403ddcfaddSBryan Cantrill 
27413ddcfaddSBryan Cantrill static int
printf_uint(mdb_ctf_id_t id,uintptr_t addr,ulong_t off,char * fmt)27423ddcfaddSBryan Cantrill printf_uint(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
27433ddcfaddSBryan Cantrill {
27443ddcfaddSBryan Cantrill 	return (printf_signed(id, addr, off, fmt, B_FALSE));
27453ddcfaddSBryan Cantrill }
27463ddcfaddSBryan Cantrill 
27473ddcfaddSBryan Cantrill /*ARGSUSED*/
27483ddcfaddSBryan Cantrill static int
printf_uint32(mdb_ctf_id_t id,uintptr_t addr,ulong_t off,char * fmt)27493ddcfaddSBryan Cantrill printf_uint32(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
27503ddcfaddSBryan Cantrill {
27513ddcfaddSBryan Cantrill 	mdb_ctf_id_t base;
27523ddcfaddSBryan Cantrill 	ctf_encoding_t e;
27533ddcfaddSBryan Cantrill 	uint32_t value;
27543ddcfaddSBryan Cantrill 
27553ddcfaddSBryan Cantrill 	if (mdb_ctf_type_resolve(id, &base) == -1) {
27563ddcfaddSBryan Cantrill 		mdb_warn("could not resolve type\n");
27573ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
27583ddcfaddSBryan Cantrill 	}
27593ddcfaddSBryan Cantrill 
27603ddcfaddSBryan Cantrill 	if (mdb_ctf_type_kind(base) != CTF_K_INTEGER ||
27613ddcfaddSBryan Cantrill 	    mdb_ctf_type_encoding(base, &e) != 0 ||
27623ddcfaddSBryan Cantrill 	    e.cte_bits / NBBY != sizeof (value)) {
27633ddcfaddSBryan Cantrill 		mdb_warn("expected 32-bit integer type\n");
27643ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
27653ddcfaddSBryan Cantrill 	}
27663ddcfaddSBryan Cantrill 
27673ddcfaddSBryan Cantrill 	if (mdb_vread(&value, sizeof (value), addr) == -1) {
27683ddcfaddSBryan Cantrill 		mdb_warn("failed to read 32-bit value at %p", addr);
27693ddcfaddSBryan Cantrill 		return (DCMD_ERR);
27703ddcfaddSBryan Cantrill 	}
27713ddcfaddSBryan Cantrill 
27723ddcfaddSBryan Cantrill 	mdb_printf(fmt, value);
27733ddcfaddSBryan Cantrill 
27743ddcfaddSBryan Cantrill 	return (0);
27753ddcfaddSBryan Cantrill }
27763ddcfaddSBryan Cantrill 
27773ddcfaddSBryan Cantrill /*ARGSUSED*/
27783ddcfaddSBryan Cantrill static int
printf_ptr(mdb_ctf_id_t id,uintptr_t addr,ulong_t off,char * fmt)27793ddcfaddSBryan Cantrill printf_ptr(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
27803ddcfaddSBryan Cantrill {
27813ddcfaddSBryan Cantrill 	uintptr_t value;
27823ddcfaddSBryan Cantrill 	mdb_ctf_id_t base;
27833ddcfaddSBryan Cantrill 
27843ddcfaddSBryan Cantrill 	if (mdb_ctf_type_resolve(id, &base) == -1) {
27853ddcfaddSBryan Cantrill 		mdb_warn("could not resolve type\n");
27863ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
27873ddcfaddSBryan Cantrill 	}
27883ddcfaddSBryan Cantrill 
27893ddcfaddSBryan Cantrill 	if (mdb_ctf_type_kind(base) != CTF_K_POINTER) {
27903ddcfaddSBryan Cantrill 		mdb_warn("expected pointer type\n");
27913ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
27923ddcfaddSBryan Cantrill 	}
27933ddcfaddSBryan Cantrill 
27943ddcfaddSBryan Cantrill 	if (mdb_vread(&value, sizeof (value), addr) == -1) {
27953ddcfaddSBryan Cantrill 		mdb_warn("failed to read pointer at %llx", addr);
27963ddcfaddSBryan Cantrill 		return (DCMD_ERR);
27973ddcfaddSBryan Cantrill 	}
27983ddcfaddSBryan Cantrill 
27993ddcfaddSBryan Cantrill 	mdb_printf(fmt, value);
28003ddcfaddSBryan Cantrill 
28013ddcfaddSBryan Cantrill 	return (0);
28023ddcfaddSBryan Cantrill }
28033ddcfaddSBryan Cantrill 
28043ddcfaddSBryan Cantrill /*ARGSUSED*/
28053ddcfaddSBryan Cantrill static int
printf_string(mdb_ctf_id_t id,uintptr_t addr,ulong_t off,char * fmt)28063ddcfaddSBryan Cantrill printf_string(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
28073ddcfaddSBryan Cantrill {
28083ddcfaddSBryan Cantrill 	mdb_ctf_id_t base;
28093ddcfaddSBryan Cantrill 	mdb_ctf_arinfo_t r;
28103ddcfaddSBryan Cantrill 	char buf[1024];
28113ddcfaddSBryan Cantrill 	ssize_t size;
28123ddcfaddSBryan Cantrill 
28133ddcfaddSBryan Cantrill 	if (mdb_ctf_type_resolve(id, &base) == -1) {
28143ddcfaddSBryan Cantrill 		mdb_warn("could not resolve type");
28153ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
28163ddcfaddSBryan Cantrill 	}
28173ddcfaddSBryan Cantrill 
28183ddcfaddSBryan Cantrill 	if (mdb_ctf_type_kind(base) == CTF_K_POINTER) {
28193ddcfaddSBryan Cantrill 		uintptr_t value;
28203ddcfaddSBryan Cantrill 
28213ddcfaddSBryan Cantrill 		if (mdb_vread(&value, sizeof (value), addr) == -1) {
28223ddcfaddSBryan Cantrill 			mdb_warn("failed to read pointer at %llx", addr);
28233ddcfaddSBryan Cantrill 			return (DCMD_ERR);
28243ddcfaddSBryan Cantrill 		}
28253ddcfaddSBryan Cantrill 
28263ddcfaddSBryan Cantrill 		if (mdb_readstr(buf, sizeof (buf) - 1, value) < 0) {
28273ddcfaddSBryan Cantrill 			mdb_warn("failed to read string at %llx", value);
28283ddcfaddSBryan Cantrill 			return (DCMD_ERR);
28293ddcfaddSBryan Cantrill 		}
28303ddcfaddSBryan Cantrill 
28313ddcfaddSBryan Cantrill 		mdb_printf(fmt, buf);
28323ddcfaddSBryan Cantrill 		return (0);
28333ddcfaddSBryan Cantrill 	}
28343ddcfaddSBryan Cantrill 
283538ce19d2SJosef 'Jeff' Sipek 	if (mdb_ctf_type_kind(base) == CTF_K_ENUM) {
283638ce19d2SJosef 'Jeff' Sipek 		const char *strval;
283738ce19d2SJosef 'Jeff' Sipek 		int value;
283838ce19d2SJosef 'Jeff' Sipek 
283938ce19d2SJosef 'Jeff' Sipek 		if (mdb_vread(&value, sizeof (value), addr) == -1) {
284038ce19d2SJosef 'Jeff' Sipek 			mdb_warn("failed to read pointer at %llx", addr);
284138ce19d2SJosef 'Jeff' Sipek 			return (DCMD_ERR);
284238ce19d2SJosef 'Jeff' Sipek 		}
284338ce19d2SJosef 'Jeff' Sipek 
284438ce19d2SJosef 'Jeff' Sipek 		if ((strval = mdb_ctf_enum_name(id, value))) {
284538ce19d2SJosef 'Jeff' Sipek 			mdb_printf(fmt, strval);
284638ce19d2SJosef 'Jeff' Sipek 		} else {
284738ce19d2SJosef 'Jeff' Sipek 			(void) mdb_snprintf(buf, sizeof (buf), "<%d>", value);
284838ce19d2SJosef 'Jeff' Sipek 			mdb_printf(fmt, buf);
284938ce19d2SJosef 'Jeff' Sipek 		}
285038ce19d2SJosef 'Jeff' Sipek 
285138ce19d2SJosef 'Jeff' Sipek 		return (0);
285238ce19d2SJosef 'Jeff' Sipek 	}
285338ce19d2SJosef 'Jeff' Sipek 
28543ddcfaddSBryan Cantrill 	if (mdb_ctf_type_kind(base) != CTF_K_ARRAY) {
28553ddcfaddSBryan Cantrill 		mdb_warn("exepected pointer or array type\n");
28563ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
28573ddcfaddSBryan Cantrill 	}
28583ddcfaddSBryan Cantrill 
28593ddcfaddSBryan Cantrill 	if (mdb_ctf_array_info(base, &r) == -1 ||
28603ddcfaddSBryan Cantrill 	    mdb_ctf_type_resolve(r.mta_contents, &base) == -1 ||
28613ddcfaddSBryan Cantrill 	    (size = mdb_ctf_type_size(base)) == -1) {
28623ddcfaddSBryan Cantrill 		mdb_warn("can't determine array type");
28633ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
28643ddcfaddSBryan Cantrill 	}
28653ddcfaddSBryan Cantrill 
28663ddcfaddSBryan Cantrill 	if (size != 1) {
28673ddcfaddSBryan Cantrill 		mdb_warn("string format specifier requires "
28683ddcfaddSBryan Cantrill 		    "an array of characters\n");
28693ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
28703ddcfaddSBryan Cantrill 	}
28713ddcfaddSBryan Cantrill 
28723ddcfaddSBryan Cantrill 	bzero(buf, sizeof (buf));
28733ddcfaddSBryan Cantrill 
28743ddcfaddSBryan Cantrill 	if (mdb_vread(buf, MIN(r.mta_nelems, sizeof (buf) - 1), addr) == -1) {
28753ddcfaddSBryan Cantrill 		mdb_warn("failed to read array at %p", addr);
28763ddcfaddSBryan Cantrill 		return (DCMD_ERR);
28773ddcfaddSBryan Cantrill 	}
28783ddcfaddSBryan Cantrill 
28793ddcfaddSBryan Cantrill 	mdb_printf(fmt, buf);
28803ddcfaddSBryan Cantrill 
28813ddcfaddSBryan Cantrill 	return (0);
28823ddcfaddSBryan Cantrill }
28833ddcfaddSBryan Cantrill 
28843ddcfaddSBryan Cantrill /*ARGSUSED*/
28853ddcfaddSBryan Cantrill static int
printf_ipv6(mdb_ctf_id_t id,uintptr_t addr,ulong_t off,char * fmt)28863ddcfaddSBryan Cantrill printf_ipv6(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
28873ddcfaddSBryan Cantrill {
28883ddcfaddSBryan Cantrill 	mdb_ctf_id_t base;
28893ddcfaddSBryan Cantrill 	mdb_ctf_id_t ipv6_type, ipv6_base;
28903ddcfaddSBryan Cantrill 	in6_addr_t ipv6;
28913ddcfaddSBryan Cantrill 
28923ddcfaddSBryan Cantrill 	if (mdb_ctf_lookup_by_name("in6_addr_t", &ipv6_type) == -1) {
28933ddcfaddSBryan Cantrill 		mdb_warn("could not resolve in6_addr_t type\n");
28943ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
28953ddcfaddSBryan Cantrill 	}
28963ddcfaddSBryan Cantrill 
28973ddcfaddSBryan Cantrill 	if (mdb_ctf_type_resolve(id, &base) == -1) {
28983ddcfaddSBryan Cantrill 		mdb_warn("could not resolve type\n");
28993ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
29003ddcfaddSBryan Cantrill 	}
29013ddcfaddSBryan Cantrill 
29023ddcfaddSBryan Cantrill 	if (mdb_ctf_type_resolve(ipv6_type, &ipv6_base) == -1) {
29033ddcfaddSBryan Cantrill 		mdb_warn("could not resolve in6_addr_t type\n");
29043ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
29053ddcfaddSBryan Cantrill 	}
29063ddcfaddSBryan Cantrill 
29073ddcfaddSBryan Cantrill 	if (mdb_ctf_type_cmp(base, ipv6_base) != 0) {
29083ddcfaddSBryan Cantrill 		mdb_warn("requires argument of type in6_addr_t\n");
29093ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
29103ddcfaddSBryan Cantrill 	}
29113ddcfaddSBryan Cantrill 
29123ddcfaddSBryan Cantrill 	if (mdb_vread(&ipv6, sizeof (ipv6), addr) == -1) {
29133ddcfaddSBryan Cantrill 		mdb_warn("couldn't read in6_addr_t at %p", addr);
29143ddcfaddSBryan Cantrill 		return (DCMD_ERR);
29153ddcfaddSBryan Cantrill 	}
29163ddcfaddSBryan Cantrill 
29173ddcfaddSBryan Cantrill 	mdb_printf(fmt, &ipv6);
29183ddcfaddSBryan Cantrill 
29193ddcfaddSBryan Cantrill 	return (0);
29203ddcfaddSBryan Cantrill }
29213ddcfaddSBryan Cantrill 
29223ddcfaddSBryan Cantrill /*
29233ddcfaddSBryan Cantrill  * To validate the format string specified to ::printf, we run the format
29243ddcfaddSBryan Cantrill  * string through a very simple state machine that restricts us to a subset
29253ddcfaddSBryan Cantrill  * of mdb_printf() functionality.
29263ddcfaddSBryan Cantrill  */
29273ddcfaddSBryan Cantrill enum {
29283ddcfaddSBryan Cantrill 	PRINTF_NOFMT = 1,		/* no current format specifier */
29293ddcfaddSBryan Cantrill 	PRINTF_PERC,			/* processed '%' */
29303ddcfaddSBryan Cantrill 	PRINTF_FMT,			/* processing format specifier */
29313ddcfaddSBryan Cantrill 	PRINTF_LEFT,			/* processed '-', expecting width */
29323ddcfaddSBryan Cantrill 	PRINTF_WIDTH,			/* processing width */
29333ddcfaddSBryan Cantrill 	PRINTF_QUES			/* processed '?', expecting format */
29343ddcfaddSBryan Cantrill };
29353ddcfaddSBryan Cantrill 
29362f045fd6SRobert Mustacchi int
cmd_printf_tab(mdb_tab_cookie_t * mcp,uint_t flags,int argc,const mdb_arg_t * argv)29372f045fd6SRobert Mustacchi cmd_printf_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
29382f045fd6SRobert Mustacchi     const mdb_arg_t *argv)
29392f045fd6SRobert Mustacchi {
29402f045fd6SRobert Mustacchi 	int ii;
29412f045fd6SRobert Mustacchi 	char *f;
29422f045fd6SRobert Mustacchi 
29432f045fd6SRobert Mustacchi 	/*
29442f045fd6SRobert Mustacchi 	 * If argc doesn't have more than what should be the format string,
29452f045fd6SRobert Mustacchi 	 * ignore it.
29462f045fd6SRobert Mustacchi 	 */
29472f045fd6SRobert Mustacchi 	if (argc <= 1)
29482f045fd6SRobert Mustacchi 		return (0);
29492f045fd6SRobert Mustacchi 
29502f045fd6SRobert Mustacchi 	/*
29512f045fd6SRobert Mustacchi 	 * Because we aren't leveraging the lex and yacc engine, we have to
29522f045fd6SRobert Mustacchi 	 * manually walk the arguments to find both the first and last
29532f045fd6SRobert Mustacchi 	 * open/close quote of the format string.
29542f045fd6SRobert Mustacchi 	 */
29552f045fd6SRobert Mustacchi 	f = strchr(argv[0].a_un.a_str, '"');
29562f045fd6SRobert Mustacchi 	if (f == NULL)
29572f045fd6SRobert Mustacchi 		return (0);
29582f045fd6SRobert Mustacchi 
29592f045fd6SRobert Mustacchi 	f = strchr(f + 1, '"');
29602f045fd6SRobert Mustacchi 	if (f != NULL) {
29612f045fd6SRobert Mustacchi 		ii = 0;
29622f045fd6SRobert Mustacchi 	} else {
29632f045fd6SRobert Mustacchi 		for (ii = 1; ii < argc; ii++) {
29642f045fd6SRobert Mustacchi 			if (argv[ii].a_type != MDB_TYPE_STRING)
29652f045fd6SRobert Mustacchi 				continue;
29662f045fd6SRobert Mustacchi 			f = strchr(argv[ii].a_un.a_str, '"');
29672f045fd6SRobert Mustacchi 			if (f != NULL)
29682f045fd6SRobert Mustacchi 				break;
29692f045fd6SRobert Mustacchi 		}
29702f045fd6SRobert Mustacchi 		/* Never found */
29712f045fd6SRobert Mustacchi 		if (ii == argc)
29722f045fd6SRobert Mustacchi 			return (0);
29732f045fd6SRobert Mustacchi 	}
29742f045fd6SRobert Mustacchi 
29752f045fd6SRobert Mustacchi 	ii++;
29762f045fd6SRobert Mustacchi 	argc -= ii;
29772f045fd6SRobert Mustacchi 	argv += ii;
29782f045fd6SRobert Mustacchi 
29792f045fd6SRobert Mustacchi 	return (cmd_print_tab_common(mcp, flags, argc, argv));
29802f045fd6SRobert Mustacchi }
29812f045fd6SRobert Mustacchi 
29823ddcfaddSBryan Cantrill int
cmd_printf(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)29833ddcfaddSBryan Cantrill cmd_printf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
29843ddcfaddSBryan Cantrill {
29853ddcfaddSBryan Cantrill 	char type[MDB_SYM_NAMLEN];
29863ddcfaddSBryan Cantrill 	int i, nfmts = 0, ret;
29873ddcfaddSBryan Cantrill 	mdb_ctf_id_t id;
29883ddcfaddSBryan Cantrill 	const char *fmt, *member;
29893ddcfaddSBryan Cantrill 	char **fmts, *last, *dest, f;
29903ddcfaddSBryan Cantrill 	int (**funcs)(mdb_ctf_id_t, uintptr_t, ulong_t, char *);
29913ddcfaddSBryan Cantrill 	int state = PRINTF_NOFMT;
29923ddcfaddSBryan Cantrill 	printarg_t pa;
29933ddcfaddSBryan Cantrill 
29943ddcfaddSBryan Cantrill 	if (!(flags & DCMD_ADDRSPEC))
29953ddcfaddSBryan Cantrill 		return (DCMD_USAGE);
29963ddcfaddSBryan Cantrill 
29973ddcfaddSBryan Cantrill 	bzero(&pa, sizeof (pa));
29983ddcfaddSBryan Cantrill 	pa.pa_as = MDB_TGT_AS_VIRT;
29993ddcfaddSBryan Cantrill 	pa.pa_realtgt = pa.pa_tgt = mdb.m_target;
30003ddcfaddSBryan Cantrill 
30013ddcfaddSBryan Cantrill 	if (argc == 0 || argv[0].a_type != MDB_TYPE_STRING) {
30023ddcfaddSBryan Cantrill 		mdb_warn("expected a format string\n");
30033ddcfaddSBryan Cantrill 		return (DCMD_USAGE);
30043ddcfaddSBryan Cantrill 	}
30053ddcfaddSBryan Cantrill 
30063ddcfaddSBryan Cantrill 	/*
30073ddcfaddSBryan Cantrill 	 * Our first argument is a format string; rip it apart and run it
30083ddcfaddSBryan Cantrill 	 * through our state machine to validate that our input is within the
30093ddcfaddSBryan Cantrill 	 * subset of mdb_printf() format strings that we allow.
30103ddcfaddSBryan Cantrill 	 */
30113ddcfaddSBryan Cantrill 	fmt = argv[0].a_un.a_str;
30123ddcfaddSBryan Cantrill 	/*
30133ddcfaddSBryan Cantrill 	 * 'dest' must be large enough to hold a copy of the format string,
30143ddcfaddSBryan Cantrill 	 * plus a NUL and up to 2 additional characters for each conversion
30153ddcfaddSBryan Cantrill 	 * in the format string.  This gives us a bloat factor of 5/2 ~= 3.
30163ddcfaddSBryan Cantrill 	 *   e.g. "%d" (strlen of 2) --> "%lld\0" (need 5 bytes)
30173ddcfaddSBryan Cantrill 	 */
30183ddcfaddSBryan Cantrill 	dest = mdb_zalloc(strlen(fmt) * 3, UM_SLEEP | UM_GC);
30193ddcfaddSBryan Cantrill 	fmts = mdb_zalloc(strlen(fmt) * sizeof (char *), UM_SLEEP | UM_GC);
30203ddcfaddSBryan Cantrill 	funcs = mdb_zalloc(strlen(fmt) * sizeof (void *), UM_SLEEP | UM_GC);
30213ddcfaddSBryan Cantrill 	last = dest;
30223ddcfaddSBryan Cantrill 
30233ddcfaddSBryan Cantrill 	for (i = 0; fmt[i] != '\0'; i++) {
30243ddcfaddSBryan Cantrill 		*dest++ = f = fmt[i];
30253ddcfaddSBryan Cantrill 
30263ddcfaddSBryan Cantrill 		switch (state) {
30273ddcfaddSBryan Cantrill 		case PRINTF_NOFMT:
30283ddcfaddSBryan Cantrill 			state = f == '%' ? PRINTF_PERC : PRINTF_NOFMT;
30293ddcfaddSBryan Cantrill 			break;
30303ddcfaddSBryan Cantrill 
30313ddcfaddSBryan Cantrill 		case PRINTF_PERC:
30323ddcfaddSBryan Cantrill 			state = f == '-' ? PRINTF_LEFT :
30333ddcfaddSBryan Cantrill 			    f >= '0' && f <= '9' ? PRINTF_WIDTH :
30343ddcfaddSBryan Cantrill 			    f == '?' ? PRINTF_QUES :
30353ddcfaddSBryan Cantrill 			    f == '%' ? PRINTF_NOFMT : PRINTF_FMT;
30363ddcfaddSBryan Cantrill 			break;
30373ddcfaddSBryan Cantrill 
30383ddcfaddSBryan Cantrill 		case PRINTF_LEFT:
30393ddcfaddSBryan Cantrill 			state = f >= '0' && f <= '9' ? PRINTF_WIDTH :
30403ddcfaddSBryan Cantrill 			    f == '?' ? PRINTF_QUES : PRINTF_FMT;
30413ddcfaddSBryan Cantrill 			break;
30423ddcfaddSBryan Cantrill 
30433ddcfaddSBryan Cantrill 		case PRINTF_WIDTH:
30443ddcfaddSBryan Cantrill 			state = f >= '0' && f <= '9' ? PRINTF_WIDTH :
30453ddcfaddSBryan Cantrill 			    PRINTF_FMT;
30463ddcfaddSBryan Cantrill 			break;
30473ddcfaddSBryan Cantrill 
30483ddcfaddSBryan Cantrill 		case PRINTF_QUES:
30493ddcfaddSBryan Cantrill 			state = PRINTF_FMT;
30503ddcfaddSBryan Cantrill 			break;
30513ddcfaddSBryan Cantrill 		}
30523ddcfaddSBryan Cantrill 
30533ddcfaddSBryan Cantrill 		if (state != PRINTF_FMT)
30543ddcfaddSBryan Cantrill 			continue;
30553ddcfaddSBryan Cantrill 
30563ddcfaddSBryan Cantrill 		dest--;
30573ddcfaddSBryan Cantrill 
30583ddcfaddSBryan Cantrill 		/*
30593ddcfaddSBryan Cantrill 		 * Now check that we have one of our valid format characters.
30603ddcfaddSBryan Cantrill 		 */
30613ddcfaddSBryan Cantrill 		switch (f) {
30623ddcfaddSBryan Cantrill 		case 'a':
30633ddcfaddSBryan Cantrill 		case 'A':
30643ddcfaddSBryan Cantrill 		case 'p':
30653ddcfaddSBryan Cantrill 			funcs[nfmts] = printf_ptr;
30663ddcfaddSBryan Cantrill 			break;
30673ddcfaddSBryan Cantrill 
30683ddcfaddSBryan Cantrill 		case 'd':
30693ddcfaddSBryan Cantrill 		case 'q':
30703ddcfaddSBryan Cantrill 		case 'R':
30713ddcfaddSBryan Cantrill 			funcs[nfmts] = printf_int;
30723ddcfaddSBryan Cantrill 			*dest++ = 'l';
30733ddcfaddSBryan Cantrill 			*dest++ = 'l';
30743ddcfaddSBryan Cantrill 			break;
30753ddcfaddSBryan Cantrill 
30763ddcfaddSBryan Cantrill 		case 'I':
30773ddcfaddSBryan Cantrill 			funcs[nfmts] = printf_uint32;
30783ddcfaddSBryan Cantrill 			break;
30793ddcfaddSBryan Cantrill 
30803ddcfaddSBryan Cantrill 		case 'N':
30813ddcfaddSBryan Cantrill 			funcs[nfmts] = printf_ipv6;
30823ddcfaddSBryan Cantrill 			break;
30833ddcfaddSBryan Cantrill 
3084cab8de14SBryan Cantrill 		case 'H':
30853ddcfaddSBryan Cantrill 		case 'o':
30863ddcfaddSBryan Cantrill 		case 'r':
30873ddcfaddSBryan Cantrill 		case 'u':
30883ddcfaddSBryan Cantrill 		case 'x':
30893ddcfaddSBryan Cantrill 		case 'X':
30903ddcfaddSBryan Cantrill 			funcs[nfmts] = printf_uint;
30913ddcfaddSBryan Cantrill 			*dest++ = 'l';
30923ddcfaddSBryan Cantrill 			*dest++ = 'l';
30933ddcfaddSBryan Cantrill 			break;
30943ddcfaddSBryan Cantrill 
30953ddcfaddSBryan Cantrill 		case 's':
30963ddcfaddSBryan Cantrill 			funcs[nfmts] = printf_string;
30973ddcfaddSBryan Cantrill 			break;
30983ddcfaddSBryan Cantrill 
30993ddcfaddSBryan Cantrill 		case 'Y':
31003ddcfaddSBryan Cantrill 			funcs[nfmts] = sizeof (time_t) == sizeof (int) ?
31013ddcfaddSBryan Cantrill 			    printf_uint32 : printf_uint;
31023ddcfaddSBryan Cantrill 			break;
31033ddcfaddSBryan Cantrill 
31043ddcfaddSBryan Cantrill 		default:
31053ddcfaddSBryan Cantrill 			mdb_warn("illegal format string at or near "
31063ddcfaddSBryan Cantrill 			    "'%c' (position %d)\n", f, i + 1);
31073ddcfaddSBryan Cantrill 			return (DCMD_ABORT);
31083ddcfaddSBryan Cantrill 		}
31093ddcfaddSBryan Cantrill 
31103ddcfaddSBryan Cantrill 		*dest++ = f;
31113ddcfaddSBryan Cantrill 		*dest++ = '\0';
31123ddcfaddSBryan Cantrill 		fmts[nfmts++] = last;
31133ddcfaddSBryan Cantrill 		last = dest;
31143ddcfaddSBryan Cantrill 		state = PRINTF_NOFMT;
31153ddcfaddSBryan Cantrill 	}
31163ddcfaddSBryan Cantrill 
31173ddcfaddSBryan Cantrill 	argc--;
31183ddcfaddSBryan Cantrill 	argv++;
31193ddcfaddSBryan Cantrill 
31203ddcfaddSBryan Cantrill 	/*
31213ddcfaddSBryan Cantrill 	 * Now we expect a type name.
31223ddcfaddSBryan Cantrill 	 */
31233ddcfaddSBryan Cantrill 	if ((ret = args_to_typename(&argc, &argv, type, sizeof (type))) != 0)
31243ddcfaddSBryan Cantrill 		return (ret);
31253ddcfaddSBryan Cantrill 
31263ddcfaddSBryan Cantrill 	argv++;
31273ddcfaddSBryan Cantrill 	argc--;
31283ddcfaddSBryan Cantrill 
31293ddcfaddSBryan Cantrill 	if (mdb_ctf_lookup_by_name(type, &id) != 0) {
31303ddcfaddSBryan Cantrill 		mdb_warn("failed to look up type %s", type);
31313ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
31323ddcfaddSBryan Cantrill 	}
31333ddcfaddSBryan Cantrill 
31343ddcfaddSBryan Cantrill 	if (argc == 0) {
31353ddcfaddSBryan Cantrill 		mdb_warn("at least one member must be specified\n");
31363ddcfaddSBryan Cantrill 		return (DCMD_USAGE);
31373ddcfaddSBryan Cantrill 	}
31383ddcfaddSBryan Cantrill 
31393ddcfaddSBryan Cantrill 	if (argc != nfmts) {
31403ddcfaddSBryan Cantrill 		mdb_warn("%s format specifiers (found %d, expected %d)\n",
31413ddcfaddSBryan Cantrill 		    argc > nfmts ? "missing" : "extra", nfmts, argc);
31423ddcfaddSBryan Cantrill 		return (DCMD_ABORT);
31433ddcfaddSBryan Cantrill 	}
31443ddcfaddSBryan Cantrill 
31453ddcfaddSBryan Cantrill 	for (i = 0; i < argc; i++) {
31463ddcfaddSBryan Cantrill 		mdb_ctf_id_t mid;
31473ddcfaddSBryan Cantrill 		ulong_t off;
31483ddcfaddSBryan Cantrill 		int ignored;
31493ddcfaddSBryan Cantrill 
31503ddcfaddSBryan Cantrill 		if (argv[i].a_type != MDB_TYPE_STRING) {
31513ddcfaddSBryan Cantrill 			mdb_warn("expected only type member arguments\n");
31523ddcfaddSBryan Cantrill 			return (DCMD_ABORT);
31533ddcfaddSBryan Cantrill 		}
31543ddcfaddSBryan Cantrill 
31553ddcfaddSBryan Cantrill 		if (strcmp((member = argv[i].a_un.a_str), ".") == 0) {
31563ddcfaddSBryan Cantrill 			/*
31573ddcfaddSBryan Cantrill 			 * We allow "." to be specified to denote the current
31583ddcfaddSBryan Cantrill 			 * value of dot.
31593ddcfaddSBryan Cantrill 			 */
31603ddcfaddSBryan Cantrill 			if (funcs[i] != printf_ptr && funcs[i] != printf_uint &&
31613ddcfaddSBryan Cantrill 			    funcs[i] != printf_int) {
31623ddcfaddSBryan Cantrill 				mdb_warn("expected integer or pointer format "
31633ddcfaddSBryan Cantrill 				    "specifier for '.'\n");
31643ddcfaddSBryan Cantrill 				return (DCMD_ABORT);
31653ddcfaddSBryan Cantrill 			}
31663ddcfaddSBryan Cantrill 
31673ddcfaddSBryan Cantrill 			mdb_printf(fmts[i], mdb_get_dot());
31683ddcfaddSBryan Cantrill 			continue;
31693ddcfaddSBryan Cantrill 		}
31703ddcfaddSBryan Cantrill 
31713ddcfaddSBryan Cantrill 		pa.pa_addr = addr;
31723ddcfaddSBryan Cantrill 
31733ddcfaddSBryan Cantrill 		if (parse_member(&pa, member, id, &mid, &off, &ignored) != 0)
31743ddcfaddSBryan Cantrill 			return (DCMD_ABORT);
31753ddcfaddSBryan Cantrill 
31763ddcfaddSBryan Cantrill 		if ((ret = funcs[i](mid, pa.pa_addr, off, fmts[i])) != 0) {
31773ddcfaddSBryan Cantrill 			mdb_warn("failed to print member '%s'\n", member);
31783ddcfaddSBryan Cantrill 			return (ret);
31793ddcfaddSBryan Cantrill 		}
31803ddcfaddSBryan Cantrill 	}
31813ddcfaddSBryan Cantrill 
31823ddcfaddSBryan Cantrill 	mdb_printf("%s", last);
31836b98d994SRobert Mustacchi 	mdb_set_dot(addr + mdb_ctf_type_size(id));
31843ddcfaddSBryan Cantrill 
31853ddcfaddSBryan Cantrill 	return (DCMD_OK);
31863ddcfaddSBryan Cantrill }
31873ddcfaddSBryan Cantrill 
31883ddcfaddSBryan Cantrill static char _mdb_printf_help[] =
31893ddcfaddSBryan Cantrill "The format string argument is a printf(3C)-like format string that is a\n"
31903ddcfaddSBryan Cantrill "subset of the format strings supported by mdb_printf().  The type argument\n"
31913ddcfaddSBryan Cantrill "is the name of a type to be used to interpret the memory referenced by dot.\n"
31923ddcfaddSBryan Cantrill "The member should either be a field in the specified structure, or the\n"
31933ddcfaddSBryan Cantrill "special member '.', denoting the value of dot (and treated as a pointer).\n"
31943ddcfaddSBryan Cantrill "The number of members must match the number of format specifiers in the\n"
31953ddcfaddSBryan Cantrill "format string.\n"
31963ddcfaddSBryan Cantrill "\n"
31973ddcfaddSBryan Cantrill "The following format specifiers are recognized by ::printf:\n"
31983ddcfaddSBryan Cantrill "\n"
31993ddcfaddSBryan Cantrill "  %%    Prints the '%' symbol.\n"
32003ddcfaddSBryan Cantrill "  %a    Prints the member in symbolic form.\n"
32013ddcfaddSBryan Cantrill "  %d    Prints the member as a decimal integer.  If the member is a signed\n"
32023ddcfaddSBryan Cantrill "        integer type, the output will be signed.\n"
3203cab8de14SBryan Cantrill "  %H    Prints the member as a human-readable size.\n"
3204cab8de14SBryan Cantrill "  %I    Prints the member as an IPv4 address (must be 32-bit integer type).\n"
3205cab8de14SBryan Cantrill "  %N    Prints the member as an IPv6 address (must be of type in6_addr_t).\n"
32063ddcfaddSBryan Cantrill "  %o    Prints the member as an unsigned octal integer.\n"
32073ddcfaddSBryan Cantrill "  %p    Prints the member as a pointer, in hexadecimal.\n"
32083ddcfaddSBryan Cantrill "  %q    Prints the member in signed octal.  Honk if you ever use this!\n"
32093ddcfaddSBryan Cantrill "  %r    Prints the member as an unsigned value in the current output radix.\n"
32103ddcfaddSBryan Cantrill "  %R    Prints the member as a signed value in the current output radix.\n"
32113ddcfaddSBryan Cantrill "  %s    Prints the member as a string (requires a pointer or an array of\n"
32123ddcfaddSBryan Cantrill "        characters).\n"
32133ddcfaddSBryan Cantrill "  %u    Prints the member as an unsigned decimal integer.\n"
32143ddcfaddSBryan Cantrill "  %x    Prints the member in hexadecimal.\n"
32153ddcfaddSBryan Cantrill "  %X    Prints the member in hexadecimal, using the characters A-F as the\n"
32163ddcfaddSBryan Cantrill "        digits for the values 10-15.\n"
32173ddcfaddSBryan Cantrill "  %Y    Prints the member as a time_t as the string "
32183ddcfaddSBryan Cantrill 	    "'year month day HH:MM:SS'.\n"
32193ddcfaddSBryan Cantrill "\n"
32203ddcfaddSBryan Cantrill "The following field width specifiers are recognized by ::printf:\n"
32213ddcfaddSBryan Cantrill "\n"
32223ddcfaddSBryan Cantrill "  %n    Field width is set to the specified decimal value.\n"
32233ddcfaddSBryan Cantrill "  %?    Field width is set to the maximum width of a hexadecimal pointer\n"
32243ddcfaddSBryan Cantrill "        value.  This is 8 in an ILP32 environment, and 16 in an LP64\n"
32253ddcfaddSBryan Cantrill "        environment.\n"
32263ddcfaddSBryan Cantrill "\n"
32273ddcfaddSBryan Cantrill "The following flag specifers are recognized by ::printf:\n"
32283ddcfaddSBryan Cantrill "\n"
32293ddcfaddSBryan Cantrill "  %-    Left-justify the output within the specified field width.  If the\n"
32303ddcfaddSBryan Cantrill "        width of the output is less than the specified field width, the\n"
32313ddcfaddSBryan Cantrill "        output will be padded with blanks on the right-hand side.  Without\n"
32323ddcfaddSBryan Cantrill "        %-, values are right-justified by default.\n"
32333ddcfaddSBryan Cantrill "\n"
32343ddcfaddSBryan Cantrill "  %0    Zero-fill the output field if the output is right-justified and the\n"
32353ddcfaddSBryan Cantrill "        width of the output is less than the specified field width.  Without\n"
32363ddcfaddSBryan Cantrill "        %0, right-justified values are prepended with blanks in order to\n"
32373ddcfaddSBryan Cantrill "        fill the field.\n"
32383ddcfaddSBryan Cantrill "\n"
32393ddcfaddSBryan Cantrill "Examples: \n"
32403ddcfaddSBryan Cantrill "\n"
32413ddcfaddSBryan Cantrill "  ::walk proc | "
32423ddcfaddSBryan Cantrill 	"::printf \"%-6d %s\\n\" proc_t p_pidp->pid_id p_user.u_psargs\n"
32433ddcfaddSBryan Cantrill "  ::walk thread | "
32443ddcfaddSBryan Cantrill 	"::printf \"%?p %3d %a\\n\" kthread_t . t_pri t_startpc\n"
32453ddcfaddSBryan Cantrill "  ::walk zone | "
32463ddcfaddSBryan Cantrill 	"::printf \"%-40s %20s\\n\" zone_t zone_name zone_nodename\n"
32473ddcfaddSBryan Cantrill "  ::walk ire | "
32483ddcfaddSBryan Cantrill 	"::printf \"%Y %I\\n\" ire_t ire_create_time ire_u.ire4_u.ire4_addr\n"
32493ddcfaddSBryan Cantrill "\n";
32503ddcfaddSBryan Cantrill 
32513ddcfaddSBryan Cantrill void
printf_help(void)32523ddcfaddSBryan Cantrill printf_help(void)
32533ddcfaddSBryan Cantrill {
32543ddcfaddSBryan Cantrill 	mdb_printf("%s", _mdb_printf_help);
32553ddcfaddSBryan Cantrill }
3256