changeset 13688:32dde9989090

2701 Add tab completion support for mdb Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com> Reviewed by: Adam Leventhal <ahl@delphix.com> Reviewed by: Sebastien Roy <sebastien.roy@delphix.com> Reviewed by: Darren Reed <darrenr@fastmail.net> Reviewed by: Gordon Ross <gordon.w.ross@gmail.com> Approved by: Richard Lowe <richlowe@richlowe.net>
author Matt Amdur <matt.amdur@delphix.com>
date Fri, 11 May 2012 22:38:13 -0400
parents 72ce76fa37fb
children 125d1b3a6fa8
files usr/src/cmd/mdb/Makefile.kmdb.files usr/src/cmd/mdb/Makefile.mdb usr/src/cmd/mdb/common/mdb/mdb.c usr/src/cmd/mdb/common/mdb/mdb.h usr/src/cmd/mdb/common/mdb/mdb_cmds.c usr/src/cmd/mdb/common/mdb/mdb_modapi.h usr/src/cmd/mdb/common/mdb/mdb_module.c usr/src/cmd/mdb/common/mdb/mdb_module.h usr/src/cmd/mdb/common/mdb/mdb_module_load.c usr/src/cmd/mdb/common/mdb/mdb_print.c usr/src/cmd/mdb/common/mdb/mdb_print.h usr/src/cmd/mdb/common/mdb/mdb_tab.c usr/src/cmd/mdb/common/mdb/mdb_tab.h usr/src/cmd/mdb/common/mdb/mdb_termio.c usr/src/cmd/mdb/common/mdb/mdb_whatis.c
diffstat 15 files changed, 1201 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/mdb/Makefile.kmdb.files	Thu May 03 05:27:43 2012 -0500
+++ b/usr/src/cmd/mdb/Makefile.kmdb.files	Fri May 11 22:38:13 2012 -0400
@@ -23,6 +23,11 @@
 # Use is subject to license terms.
 #
 
+#
+# Copyright (c) 2012 by Delphix. All rights reserved.
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
+#
+
 KMDBSRCS += \
 	ffs.c \
 	kaif_start.c \
@@ -75,6 +80,7 @@
 	mdb_string.c \
 	mdb_strio.c \
 	kmdb_stubs.c \
+	mdb_tab.c \
 	mdb_target.c \
 	kmdb_terminfo.c \
 	mdb_termio.c \
--- a/usr/src/cmd/mdb/Makefile.mdb	Thu May 03 05:27:43 2012 -0500
+++ b/usr/src/cmd/mdb/Makefile.mdb	Fri May 11 22:38:13 2012 -0400
@@ -19,11 +19,16 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
+#
+# Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+# Copyright (c) 2012 by Delphix. All rights reserved.
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
+#
+
 .KEEP_STATE:
 .SUFFIXES:
 
@@ -76,6 +81,7 @@
 	mdb_stdlib.c \
 	mdb_string.c \
 	mdb_strio.c \
+	mdb_tab.c \
 	mdb_target.c \
 	mdb_tdb.c \
 	mdb_termio.c \
--- a/usr/src/cmd/mdb/common/mdb/mdb.c	Thu May 03 05:27:43 2012 -0500
+++ b/usr/src/cmd/mdb/common/mdb/mdb.c	Fri May 11 22:38:13 2012 -0400
@@ -24,6 +24,11 @@
  */
 
 /*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ */
+
+/*
  * Modular Debugger (MDB)
  *
  * Refer to the white paper "A Modular Debugger for Solaris" for information
@@ -1132,6 +1137,16 @@
 	return (status);
 }
 
+void
+mdb_call_tab(mdb_idcmd_t *idcp, mdb_tab_cookie_t *mcp, uint_t flags,
+    uintmax_t argc, mdb_arg_t *argv)
+{
+	if (idcp->idc_tabp == NULL)
+		return;
+
+	idcp->idc_tabp(mcp, flags, argc, argv);
+}
+
 /*
  * Call an internal dcmd directly: this code is used by module API functions
  * that need to execute dcmds, and by mdb_call() above.
--- a/usr/src/cmd/mdb/common/mdb/mdb.h	Thu May 03 05:27:43 2012 -0500
+++ b/usr/src/cmd/mdb/common/mdb/mdb.h	Fri May 11 22:38:13 2012 -0400
@@ -23,6 +23,11 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ */
+
 #ifndef	_MDB_H
 #define	_MDB_H
 
@@ -38,6 +43,7 @@
 #include <mdb/mdb_modapi.h>
 #include <mdb/mdb_list.h>
 #include <mdb/mdb_vcb.h>
+#include <mdb/mdb_tab.h>
 #ifdef _KMDB
 #include <kmdb/kmdb_wr.h>
 #endif
@@ -206,6 +212,8 @@
 
 extern int mdb_call_idcmd(mdb_idcmd_t *, uintmax_t, uintmax_t, uint_t,
     mdb_argvec_t *, mdb_addrvec_t *, mdb_vcb_t *);
+extern void mdb_call_tab(mdb_idcmd_t *, mdb_tab_cookie_t *, uint_t, uintmax_t,
+    mdb_arg_t *);
 
 extern int mdb_call(uintmax_t, uintmax_t, uint_t);
 extern int mdb_run(void);
--- a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c	Thu May 03 05:27:43 2012 -0500
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c	Fri May 11 22:38:13 2012 -0400
@@ -24,6 +24,11 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ */
+
 #include <sys/elf.h>
 #include <sys/elf_SPARC.h>
 
@@ -61,6 +66,7 @@
 #include <mdb/mdb_whatis.h>
 #include <mdb/mdb_whatis_impl.h>
 #include <mdb/mdb_macalias.h>
+#include <mdb/mdb_tab.h>
 #ifdef _KMDB
 #include <kmdb/kmdb_kdi.h>
 #endif
@@ -2129,6 +2135,24 @@
 	return (DCMD_OK);
 }
 
+static int
+cmd_walk_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
+    const mdb_arg_t *argv)
+{
+	if (argc > 1)
+		return (1);
+
+	if (argc == 1) {
+		ASSERT(argv[0].a_type == MDB_TYPE_STRING);
+		return (mdb_tab_complete_walker(mcp, argv[0].a_un.a_str));
+	}
+
+	if (argc == 0 && flags & DCMD_TAB_SPACE)
+		return (mdb_tab_complete_walker(mcp, NULL));
+
+	return (1);
+}
+
 static ssize_t
 mdb_partial_xread(void *buf, size_t nbytes, uintptr_t addr, void *arg)
 {
@@ -2924,7 +2948,8 @@
 	    head_help },
 	{ "help", "[cmd]", "list commands/command help", cmd_help },
 	{ "list", "?type member [variable]",
-	    "walk list using member as link pointer", cmd_list },
+	    "walk list using member as link pointer", cmd_list, NULL,
+	    mdb_tab_complete_mt },
 	{ "map", "?expr", "print dot after evaluating expression", cmd_map },
 	{ "mappings", "?[name]", "print address space mappings", cmd_mappings },
 	{ "nm", "?[-DPdghnopuvx] [-f format] [-t types] [object]",
@@ -2935,14 +2960,16 @@
 	{ "obey", NULL, NULL, cmd_obey },
 	{ "objects", "[-v]", "print load objects information", cmd_objects },
 	{ "offsetof", "type member", "print the offset of a given struct "
-	    "or union member", cmd_offsetof },
+	    "or union member", cmd_offsetof, NULL, mdb_tab_complete_mt },
 	{ "print", "?[-aCdhiLptx] [-c lim] [-l lim] [type] [member|offset ...]",
-	    "print the contents of a data structure", cmd_print, print_help },
+	    "print the contents of a data structure", cmd_print, print_help,
+	    cmd_print_tab },
 	{ "regs", NULL, "print general purpose registers", cmd_notsup },
 	{ "set", "[-wF] [+/-o opt] [-s dist] [-I path] [-L path] [-P prompt]",
 	    "get/set debugger properties", cmd_set },
 	{ "showrev", "[-pv]", "print version information", cmd_showrev },
-	{ "sizeof", "type", "print the size of a type", cmd_sizeof },
+	{ "sizeof", "type", "print the size of a type", cmd_sizeof, NULL,
+	    cmd_sizeof_tab },
 	{ "stack", "?[cnt]", "print stack backtrace", cmd_notsup },
 	{ "stackregs", "?", "print stack backtrace and registers",
 	    cmd_notsup },
@@ -2954,7 +2981,8 @@
 	{ "version", NULL, "print debugger version string", cmd_version },
 	{ "vtop", ":[-a as]", "print physical mapping of virtual address",
 	    cmd_vtop },
-	{ "walk", "?name [variable]", "walk data structure", cmd_walk },
+	{ "walk", "?name [variable]", "walk data structure", cmd_walk, NULL,
+	    cmd_walk_tab },
 	{ "walkers", NULL, "list available walkers", cmd_walkers },
 	{ "whatis", ":[-aikqv]", "given an address, return information",
 	    cmd_whatis, whatis_help },
--- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h	Thu May 03 05:27:43 2012 -0500
+++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h	Fri May 11 22:38:13 2012 -0400
@@ -21,6 +21,8 @@
 
 /*
  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
  */
 
 #ifndef	_MDB_MODAPI_H
@@ -69,7 +71,7 @@
 #define	MAX(x, y) ((x) > (y) ? (x) : (y))
 #endif
 
-#define	MDB_API_VERSION	3	/* Current API version number */
+#define	MDB_API_VERSION	4	/* Current API version number */
 
 /*
  * Debugger command function flags:
@@ -83,6 +85,11 @@
 #define	DCMD_HDRSPEC(fl)	(((fl) & DCMD_LOOPFIRST) || !((fl) & DCMD_LOOP))
 
 /*
+ * Debugger tab command function flags
+ */
+#define	DCMD_TAB_SPACE	0x01	/* Tab cb invoked with trailing space */
+
+/*
  * Debugger command function return values:
  */
 #define	DCMD_OK		0	/* Dcmd completed successfully */
@@ -111,7 +118,10 @@
 	} a_un;
 } mdb_arg_t;
 
+typedef struct mdb_tab_cookie mdb_tab_cookie_t;
 typedef int mdb_dcmd_f(uintptr_t, uint_t, int, const mdb_arg_t *);
+typedef int mdb_dcmd_tab_f(mdb_tab_cookie_t *, uint_t, int,
+    const mdb_arg_t *);
 
 typedef struct mdb_dcmd {
 	const char *dc_name;		/* Command name */
@@ -119,6 +129,7 @@
 	const char *dc_descr;		/* Description */
 	mdb_dcmd_f *dc_funcp;		/* Command function */
 	void (*dc_help)(void);		/* Command help function (or NULL) */
+	mdb_dcmd_tab_f *dc_tabp;	/* Tab completion function */
 } mdb_dcmd_t;
 
 #define	WALK_ERR	-1		/* Walk fatal error (terminate walk) */
@@ -302,6 +313,31 @@
 extern void *mdb_callback_add(int, mdb_callback_f, void *);
 extern void mdb_callback_remove(void *);
 
+#define	MDB_TABC_ALL_TYPES	0x1	/* Include array types in type output */
+#define	MDB_TABC_MEMBERS	0x2	/* Tab comp. types with members */
+#define	MDB_TABC_NOPOINT	0x4	/* Tab comp. everything but pointers */
+#define	MDB_TABC_NOARRAY	0x8	/* Don't include array data in output */
+
+/*
+ * Module's interaction path
+ */
+extern void mdb_tab_insert(mdb_tab_cookie_t *, const char *);
+extern void mdb_tab_setmbase(mdb_tab_cookie_t *, const char *);
+
+/*
+ * Tab completion utility functions for modules.
+ */
+extern int mdb_tab_complete_type(mdb_tab_cookie_t *, const char *, uint_t);
+extern int mdb_tab_complete_member(mdb_tab_cookie_t *, const char *,
+    const char *);
+extern int mdb_tab_typename(int *, const mdb_arg_t **, char *buf, size_t len);
+
+/*
+ * Tab completion functions for common signatures.
+ */
+extern int mdb_tab_complete_mt(mdb_tab_cookie_t *, uint_t, int,
+    const mdb_arg_t *);
+
 extern size_t strlcat(char *, const char *, size_t);
 extern char *strcat(char *, const char *);
 extern char *strcpy(char *, const char *);
--- a/usr/src/cmd/mdb/common/mdb/mdb_module.c	Thu May 03 05:27:43 2012 -0500
+++ b/usr/src/cmd/mdb/common/mdb/mdb_module.c	Fri May 11 22:38:13 2012 -0400
@@ -21,6 +21,8 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
  */
 
 #include <sys/param.h>
@@ -43,6 +45,20 @@
 #include <mdb/mdb.h>
 
 /*
+ * The format of an mdb dcmd changed between MDB_API_VERSION 3 and 4, with an
+ * addition of a new field to the public interface. To maintain backwards
+ * compatibility with older versions, we know to keep around the old version of
+ * the structure so we can correctly read the set of dcmds passed in.
+ */
+typedef struct mdb_dcmd_v3 {
+	const char *dco_name;		/* Command name */
+	const char *dco_usage;		/* Usage message (optional) */
+	const char *dco_descr;		/* Description */
+	mdb_dcmd_f *dco_funcp;		/* Command function */
+	void (*dco_help)(void);		/* Command help function (or NULL) */
+} mdb_dcmd_v3_t;
+
+/*
  * For builtin modules, we set mod_init to this function, which just
  * returns a constant modinfo struct with no dcmds and walkers.
  */
@@ -92,6 +108,9 @@
 	const mdb_dcmd_t *dcp;
 	const mdb_walker_t *wp;
 
+	const mdb_dcmd_v3_t *dcop;
+	mdb_dcmd_t *dctp = NULL;
+
 	mdb_module_t *mod;
 
 	mod = mdb_zalloc(sizeof (mdb_module_t), UM_SLEEP);
@@ -164,6 +183,7 @@
 	 */
 	switch (info->mi_dvers) {
 	case MDB_API_VERSION:
+	case 3:
 	case 2:
 	case 1:
 		/*
@@ -186,6 +206,35 @@
 	}
 
 	/*
+	 * In MDB_API_VERSION 4, the size of the mdb_dcmd_t struct changed. If
+	 * our module is from an earlier version, we need to walk it in the old
+	 * structure and convert it to the new one.
+	 *
+	 * Note that we purposefully don't predicate on whether or not we have
+	 * the empty list case and duplicate it anyways. That case is rare and
+	 * it makes our logic simpler when we need to unload the module.
+	 */
+	if (info->mi_dvers < 4) {
+		int ii = 0;
+		for (dcop = (mdb_dcmd_v3_t *)&mod->mod_info->mi_dcmds[0];
+		    dcop->dco_name != NULL; dcop++)
+			ii++;
+		/* Don't forget null terminated one at the end */
+		dctp = mdb_zalloc(sizeof (mdb_dcmd_t) * (ii + 1), UM_SLEEP);
+		ii = 0;
+		for (dcop = (mdb_dcmd_v3_t *)&mod->mod_info->mi_dcmds[0];
+		    dcop->dco_name != NULL; dcop++, ii++) {
+			dctp[ii].dc_name = dcop->dco_name;
+			dctp[ii].dc_usage = dcop->dco_usage;
+			dctp[ii].dc_descr = dcop->dco_descr;
+			dctp[ii].dc_funcp = dcop->dco_funcp;
+			dctp[ii].dc_help = dcop->dco_help;
+			dctp[ii].dc_tabp = NULL;
+		}
+		mod->mod_info->mi_dcmds = dctp;
+	}
+
+	/*
 	 * Before we actually go ahead with the load, we need to check
 	 * each dcmd and walk structure for any invalid values:
 	 */
@@ -300,6 +349,7 @@
 {
 	mdb_var_t *v = mdb_nv_lookup(&mdb.m_modules, name);
 	mdb_module_t *mod;
+	const mdb_dcmd_t *dcp;
 
 	if (v == NULL)
 		return (set_errno(EMDB_NOMOD));
@@ -358,6 +408,18 @@
 	mdb_nv_destroy(&mod->mod_dcmds);
 
 	strfree((char *)mod->mod_name);
+
+	if (mod->mod_info->mi_dvers < 4) {
+		int ii = 0;
+
+		for (dcp = &mod->mod_info->mi_dcmds[0]; dcp->dc_name != NULL;
+		    dcp++)
+			ii++;
+
+		mdb_free((void *)mod->mod_info->mi_dcmds,
+		    sizeof (mdb_dcmd_t) * (ii + 1));
+	}
+
 	mdb_free(mod->mod_info, sizeof (mdb_modinfo_t));
 	mdb_free(mod, sizeof (mdb_module_t));
 
@@ -384,6 +446,7 @@
 	idcp->idc_descr = dcp->dc_descr;
 	idcp->idc_help = dcp->dc_help;
 	idcp->idc_funcp = dcp->dc_funcp;
+	idcp->idc_tabp = dcp->dc_tabp;
 	idcp->idc_modp = mod;
 
 	v = mdb_nv_insert(&mod->mod_dcmds, dcp->dc_name, NULL,
--- a/usr/src/cmd/mdb/common/mdb/mdb_module.h	Thu May 03 05:27:43 2012 -0500
+++ b/usr/src/cmd/mdb/common/mdb/mdb_module.h	Fri May 11 22:38:13 2012 -0400
@@ -24,11 +24,14 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ */
+
 #ifndef	_MDB_MODULE_H
 #define	_MDB_MODULE_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <mdb/mdb_argvec.h>
 #include <mdb/mdb_nv.h>
 #include <mdb/mdb_modapi.h>
@@ -67,6 +70,7 @@
 	const char *idc_descr;		/* Description */
 	mdb_dcmd_f *idc_funcp;		/* Command function */
 	void (*idc_help)(void);		/* Help function */
+	mdb_dcmd_tab_f *idc_tabp;	/* Tab completion pointer */
 	mdb_module_t *idc_modp;		/* Backpointer to module */
 	mdb_var_t *idc_var;		/* Backpointer to global variable */
 } mdb_idcmd_t;
--- a/usr/src/cmd/mdb/common/mdb/mdb_module_load.c	Thu May 03 05:27:43 2012 -0500
+++ b/usr/src/cmd/mdb/common/mdb/mdb_module_load.c	Fri May 11 22:38:13 2012 -0400
@@ -21,6 +21,8 @@
 
 /*
  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
  */
 
 #include <sys/param.h>
@@ -201,6 +203,7 @@
 	mdb_iob_setflags(mdb.m_out, oflag);
 }
 
+/*ARGSUSED*/
 int
 mdb_module_unload(const char *name, int mode)
 {
--- a/usr/src/cmd/mdb/common/mdb/mdb_print.c	Thu May 03 05:27:43 2012 -0500
+++ b/usr/src/cmd/mdb/common/mdb/mdb_print.c	Fri May 11 22:38:13 2012 -0400
@@ -23,6 +23,11 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ */
+
 #include <mdb/mdb_modapi.h>
 #include <mdb/mdb_target.h>
 #include <mdb/mdb_argvec.h>
@@ -34,6 +39,7 @@
 #include <mdb/mdb_ctf.h>
 #include <mdb/mdb_ctf_impl.h>
 #include <mdb/mdb.h>
+#include <mdb/mdb_tab.h>
 
 #include <sys/isa_defs.h>
 #include <sys/param.h>
@@ -209,6 +215,28 @@
 	return (DCMD_OK);
 }
 
+int
+cmd_sizeof_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
+    const mdb_arg_t *argv)
+{
+	char tn[MDB_SYM_NAMLEN];
+	int ret;
+
+	if (argc == 0 && !(flags & DCMD_TAB_SPACE))
+		return (0);
+
+	if (argc == 0 && (flags & DCMD_TAB_SPACE))
+		return (mdb_tab_complete_type(mcp, NULL, MDB_TABC_NOPOINT));
+
+	if ((ret = mdb_tab_typename(&argc, &argv, tn, sizeof (tn))) < 0)
+		return (ret);
+
+	if (argc == 1)
+		return (mdb_tab_complete_type(mcp, tn, MDB_TABC_NOPOINT));
+
+	return (0);
+}
+
 /*ARGSUSED*/
 int
 cmd_offsetof(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
@@ -1882,7 +1910,8 @@
 				return (-1);
 			}
 
-			(void) mdb_snprintf(member, end - start + 1, start);
+			(void) mdb_snprintf(member, end - start + 1, "%s",
+			    start);
 
 			index = mdb_strtoull(member);
 
@@ -1959,7 +1988,7 @@
 		for (end = start + 1; isalnum(*end) || *end == '_'; end++)
 			continue;
 
-		(void) mdb_snprintf(member, end - start + 1, start);
+		(void) mdb_snprintf(member, end - start + 1, "%s", start);
 
 		if (mdb_ctf_member_info(rid, member, &off, &id) != 0) {
 			mdb_warn("failed to find member %s of %s", member,
@@ -1981,6 +2010,170 @@
 	return (0);
 }
 
+int
+cmd_print_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
+    const mdb_arg_t *argv)
+{
+	char tn[MDB_SYM_NAMLEN];
+	char member[64];
+	int i, dummy, delim, kind;
+	int ret = 0;
+	mdb_ctf_id_t id, rid;
+	mdb_ctf_arinfo_t ar;
+	char *start, *end;
+	ulong_t dul;
+
+	/*
+	 * This getopts is only here to make the tab completion work better when
+	 * including options in the ::print arguments. None of the values should
+	 * be used. This should only be updated with additional arguments, if
+	 * they are added to cmd_print.
+	 */
+	i = mdb_getopts(argc, argv,
+	    'a', MDB_OPT_SETBITS, PA_SHOWADDR, &dummy,
+	    'C', MDB_OPT_SETBITS, TRUE, &dummy,
+	    'c', MDB_OPT_UINTPTR, &dummy,
+	    'd', MDB_OPT_SETBITS, PA_INTDEC, &dummy,
+	    'h', MDB_OPT_SETBITS, PA_SHOWHOLES, &dummy,
+	    'i', MDB_OPT_SETBITS, TRUE, &dummy,
+	    'L', MDB_OPT_SETBITS, TRUE, &dummy,
+	    'l', MDB_OPT_UINTPTR, &dummy,
+	    'n', MDB_OPT_SETBITS, PA_NOSYMBOLIC, &dummy,
+	    'p', MDB_OPT_SETBITS, TRUE, &dummy,
+	    's', MDB_OPT_UINTPTR, &dummy,
+	    'T', MDB_OPT_SETBITS, PA_SHOWTYPE | PA_SHOWBASETYPE, &dummy,
+	    't', MDB_OPT_SETBITS, PA_SHOWTYPE, &dummy,
+	    'x', MDB_OPT_SETBITS, PA_INTHEX, &dummy,
+	    NULL);
+
+	argc -= i;
+	argv += i;
+
+	if (argc == 0 && !(flags & DCMD_TAB_SPACE))
+		return (0);
+
+	if (argc == 0 && (flags & DCMD_TAB_SPACE))
+		return (mdb_tab_complete_type(mcp, NULL, MDB_TABC_NOPOINT |
+		    MDB_TABC_NOARRAY));
+
+	if ((ret = mdb_tab_typename(&argc, &argv, tn, sizeof (tn))) < 0)
+		return (ret);
+
+	if (argc == 1 && (!(flags & DCMD_TAB_SPACE) || ret == 1))
+		return (mdb_tab_complete_type(mcp, tn, MDB_TABC_NOPOINT |
+		    MDB_TABC_NOARRAY));
+
+	if (argc == 1 && (flags & DCMD_TAB_SPACE))
+		return (mdb_tab_complete_member(mcp, tn, NULL));
+
+	/*
+	 * This is the reason that tab completion was created. We're going to go
+	 * along and walk the delimiters until we find something a member that
+	 * we don't recognize, at which point we'll try and tab complete it.
+	 * Note that ::print takes multiple args, so this is going to operate on
+	 * whatever the last arg that we have is.
+	 */
+	if (mdb_ctf_lookup_by_name(tn, &id) != 0)
+		return (1);
+
+	(void) mdb_ctf_type_resolve(id, &rid);
+	start = (char *)argv[argc-1].a_un.a_str;
+	delim = parse_delimiter(&start);
+
+	/*
+	 * If we hit the case where we actually have no delimiters, than we need
+	 * to make sure that we properly set up the fields the loops would.
+	 */
+	if (delim == MEMBER_DELIM_DONE)
+		(void) mdb_snprintf(member, sizeof (member), "%s", start);
+