xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_print.c (revision 7a58f538)
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  */
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*7a58f538SRobert Mustacchi  * Copyright 2021 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>
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>
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;
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;
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 */
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)
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)
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)
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
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 *);
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);
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;
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 */
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 	}
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 */
1387c478bd9Sstevel@tonic-gate 	if (mdb_tgt_lookup_by_name(t, mp->map_name, name, symp, sip) == -1)
1397c478bd9Sstevel@tonic-gate 		return (NULL);
1417c478bd9Sstevel@tonic-gate 	return (name);
1427c478bd9Sstevel@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;
1667c478bd9Sstevel@tonic-gate 	if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
1677c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
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 		}
1777c478bd9Sstevel@tonic-gate 		if (argv[1].a_type != MDB_TYPE_STRING)
1787c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
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);
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 	}
1897c478bd9Sstevel@tonic-gate 	return (0);
1907c478bd9Sstevel@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;
2007c478bd9Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC)
2017c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
2037c478bd9Sstevel@tonic-gate 	if ((ret = args_to_typename(&argc, &argv, tn, sizeof (tn))) != 0)
2047c478bd9Sstevel@tonic-gate 		return (ret);
2067c478bd9Sstevel@tonic-gate 	if (argc != 1)
2077c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
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 	}
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));
2197c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
2207c478bd9Sstevel@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;
2557c478bd9Sstevel@tonic-gate 	if (flags & DCMD_ADDRSPEC)
2567c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
2587c478bd9Sstevel@tonic-gate 	if ((ret = args_to_typename(&argc, &argv, tn, sizeof (tn))) != 0)
2597c478bd9Sstevel@tonic-gate 		return (ret);
2617c478bd9Sstevel@tonic-gate 	if (argc != 2 || argv[1].a_type != MDB_TYPE_STRING)
2627c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
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 	}
2697c478bd9Sstevel@tonic-gate 	member = argv[1].a_un.a_str;
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 	}
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");
2967c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
2977c478bd9Sstevel@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
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;
4927c478bd9Sstevel@tonic-gate 	if (flags & E_SEARCH_STRING) {
4937c478bd9Sstevel@tonic-gate 		if (strcmp(name, info->e_string) != 0)
4947c478bd9Sstevel@tonic-gate 			return (0);
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 	}
501cce40297SJonathan Adams 	enum_print(info, name, value);
5037c478bd9Sstevel@tonic-gate 	info->e_found = 1;
5047c478bd9Sstevel@tonic-gate 	return (0);
5057c478bd9Sstevel@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;
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;
5367c478bd9Sstevel@tonic-gate 	int i;
53724537d3eSToomas Soome 	intmax_t search = 0;
538e0ad97e3SJonathan Adams 	uint_t isp2;
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;
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);
5507c478bd9Sstevel@tonic-gate 	argc -= i;
5517c478bd9Sstevel@tonic-gate 	argv += i;
553cce40297SJonathan Adams 	if ((i = args_to_typename(&argc, &argv, type, MDB_SYM_NAMLEN)) != 0)
5547c478bd9Sstevel@tonic-gate 		return (i);
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);
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 	}
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 	}
588e0ad97e3SJonathan Adams 	info.e_id = idr;
589e0ad97e3SJonathan Adams 
5907c478bd9Sstevel@tonic-gate 	if (argc > 2)
5917c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
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 		}
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 	}
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 	}
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 	}
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 	}
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 	}
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);
6597c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
6607c478bd9Sstevel@tonic-gate 	}
6627c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
6637c478bd9Sstevel@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;
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 		}
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 	}
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);
6957c478bd9Sstevel@tonic-gate 	return (0);
6967c478bd9Sstevel@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;
7067c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC) || argc == 0)
7077c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
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);
7187c478bd9Sstevel@tonic-gate 		offset = argv->a_un.a_val;
7207c478bd9Sstevel@tonic-gate 		argv++;
7217c478bd9Sstevel@tonic-gate 		argc--;
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;
7327c478bd9Sstevel@tonic-gate 		ret = args_to_typename(&argc, &argv, buf, sizeof (buf));
7337c478bd9Sstevel@tonic-gate 		if (ret != 0)
7347c478bd9Sstevel@tonic-gate 			return (ret);
7367c478bd9Sstevel@tonic-gate 		argv++;
7377c478bd9Sstevel@tonic-gate 		argc--;
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);
7527c478bd9Sstevel@tonic-gate 		argv++;
7537c478bd9Sstevel@tonic-gate 		argc--;
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 	}
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);
7687c478bd9Sstevel@tonic-gate 		if ((ret = setup_vcb(argv->a_un.a_str, addr)) != 0)
7697c478bd9Sstevel@tonic-gate 			return (ret);
7717c478bd9Sstevel@tonic-gate 	} else if (argc != 0) {
7727c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
7737c478bd9Sstevel@tonic-gate 	}
7757c478bd9Sstevel@tonic-gate 	a = addr;
7777c478bd9Sstevel@tonic-gate 	do {
7787c478bd9Sstevel@tonic-gate 		mdb_printf("%lr\n", a);
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 		}
7867c478bd9Sstevel@tonic-gate 		a = tmp;
787892ad162SToomas Soome 	} while (a != addr && a != 0);
7897c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
7907c478bd9Sstevel@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;
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;
8057c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC))
8067c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
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);
8137c478bd9Sstevel@tonic-gate 		if (argc == 1)	/* unquoted compound type without count */
8147c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
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 		}
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);
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 	}
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);
8427c478bd9Sstevel@tonic-gate 		if ((ret = setup_vcb(argv[argc - 1].a_un.a_str, addr)) != 0)
8437c478bd9Sstevel@tonic-gate 			return (ret);
8457c478bd9Sstevel@tonic-gate 	} else if (argc > 3) {
8467c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
8477c478bd9Sstevel@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 	}
8547c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
8557c478bd9Sstevel@tonic-gate }
8577c478bd9Sstevel@tonic-gate /*
8587c478bd9Sstevel@tonic-gate  * Print an integer bitfield in hexadecimal by reading the enclosing byte(s)
8597c478bd9Sstevel@tonic-gate  * and then shifting and masking the data in the lower bits of a uint64_t.
8607c478bd9Sstevel@tonic-gate  */
8617c478bd9Sstevel@tonic-gate static int
print_bitfield(ulong_t off,printarg_t * pap,ctf_encoding_t * ep)8627c478bd9Sstevel@tonic-gate print_bitfield(ulong_t off, printarg_t *pap, ctf_encoding_t *ep)
8637c478bd9Sstevel@tonic-gate {
8647c478bd9Sstevel@tonic-gate 	mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY;
8657c478bd9Sstevel@tonic-gate 	size_t size = (ep->cte_bits + (NBBY - 1)) / NBBY;
8667c478bd9Sstevel@tonic-gate 	uint64_t mask = (1ULL << ep->cte_bits) - 1;
8677c478bd9Sstevel@tonic-gate 	uint64_t value = 0;
8687c478bd9Sstevel@tonic-gate 	uint8_t *buf = (uint8_t *)&value;
8697c478bd9Sstevel@tonic-gate 	uint8_t shift;
8717c478bd9Sstevel@tonic-gate 	const char *format;
8737c478bd9Sstevel@tonic-gate 	if (!(pap->pa_flags & PA_SHOWVAL))
8747c478bd9Sstevel@tonic-gate 		return (0);
8767c478bd9Sstevel@tonic-gate 	if (ep->cte_bits > sizeof (value) * NBBY - 1) {
8777c478bd9Sstevel@tonic-gate 		mdb_printf("??? (invalid bitfield size %u)", ep->cte_bits);
8787c478bd9Sstevel@tonic-gate 		return (0);
8797c478bd9Sstevel@tonic-gate 	}
881*7a58f538SRobert Mustacchi 	/*
882*7a58f538SRobert Mustacchi 	 * Our bitfield may stradle a byte boundary, if so, the calculation of
883*7a58f538SRobert Mustacchi 	 * size may not correctly capture that. However, off is relative to the
884*7a58f538SRobert Mustacchi 	 * entire bitfield, so we first have to make that relative to the byte.
885*7a58f538SRobert Mustacchi 	 */
886*7a58f538SRobert Mustacchi 	if ((off % 8) + ep->cte_bits > NBBY * size) {
887*7a58f538SRobert Mustacchi 		size++;
888*7a58f538SRobert Mustacchi 	}
889*7a58f538SRobert Mustacchi 
890*7a58f538SRobert Mustacchi 	if (size > sizeof (value)) {
891*7a58f538SRobert Mustacchi 		mdb_printf("??? (total bitfield too large after alignment");
892*7a58f538SRobert Mustacchi 	}
893*7a58f538SRobert Mustacchi 
8947c478bd9Sstevel@tonic-gate 	/*
8957c478bd9Sstevel@tonic-gate 	 * On big-endian machines, we need to adjust the buf pointer to refer
8967c478bd9Sstevel@tonic-gate 	 * to the lowest 'size' bytes in 'value', and we need shift based on
8977c478bd9Sstevel@tonic-gate 	 * the offset from the end of the data, not the offset of the start.
8987c478bd9Sstevel@tonic-gate 	 */
8997c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN
9007c478bd9Sstevel@tonic-gate 	buf += sizeof (value) - size;
9017c478bd9Sstevel@tonic-gate 	off += ep->cte_bits;
9027c478bd9Sstevel@tonic-gate #endif
903*7a58f538SRobert Mustacchi 
9047c478bd9Sstevel@tonic-gate 	if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, buf, size, addr) != size) {
9057c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read %lu bytes at %llx",
9067c478bd9Sstevel@tonic-gate 		    (ulong_t)size, addr);
9077c478bd9Sstevel@tonic-gate 		return (1);
9087c478bd9Sstevel@tonic-gate 	}
9107c478bd9Sstevel@tonic-gate 	shift = off % NBBY;
9127c478bd9Sstevel@tonic-gate 	/*
9137c478bd9Sstevel@tonic-gate 	 * Offsets are counted from opposite ends on little- and
9147c478bd9Sstevel@tonic-gate 	 * big-endian machines.
9157c478bd9Sstevel@tonic-gate 	 */
9167c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN
9177c478bd9Sstevel@tonic-gate 	shift = NBBY - shift;
9187c478bd9Sstevel@tonic-gate #endif
9207c478bd9Sstevel@tonic-gate 	/*
9217c478bd9Sstevel@tonic-gate 	 * If the bits we want do not begin on a byte boundary, shift the data
9227c478bd9Sstevel@tonic-gate 	 * right so that the value is in the lowest 'cte_bits' of 'value'.
9237c478bd9Sstevel@tonic-gate 	 */