summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Amdur <matt.amdur@delphix.com>2012-05-11 22:38:13 -0400
committerRichard Lowe <richlowe@richlowe.net>2012-05-11 22:38:13 -0400
commit3b6e0a598869dfc84461624e8699bf51738f68b3 (patch)
tree1a9ce118585b6079ea60f64e04d68596b17f61d5
parentad8ef92ae01ac09e533731f5a517162c634308b4 (diff)
downloadillumos-joyent-3b6e0a598869dfc84461624e8699bf51738f68b3.tar.gz
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>
-rw-r--r--usr/src/cmd/mdb/Makefile.kmdb.files6
-rw-r--r--usr/src/cmd/mdb/Makefile.mdb8
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb.c15
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb.h8
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_cmds.c38
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_modapi.h38
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_module.c63
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_module.h8
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_module_load.c3
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_print.c201
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_print.h9
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_tab.c652
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_tab.h71
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_termio.c87
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_whatis.c7
15 files changed, 1201 insertions, 13 deletions
diff --git a/usr/src/cmd/mdb/Makefile.kmdb.files b/usr/src/cmd/mdb/Makefile.kmdb.files
index fffc6ff8c3..3c5983de21 100644
--- a/usr/src/cmd/mdb/Makefile.kmdb.files
+++ b/usr/src/cmd/mdb/Makefile.kmdb.files
@@ -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 @@ KMDBSRCS += \
mdb_string.c \
mdb_strio.c \
kmdb_stubs.c \
+ mdb_tab.c \
mdb_target.c \
kmdb_terminfo.c \
mdb_termio.c \
diff --git a/usr/src/cmd/mdb/Makefile.mdb b/usr/src/cmd/mdb/Makefile.mdb
index 5768115849..1d87ac199f 100644
--- a/usr/src/cmd/mdb/Makefile.mdb
+++ b/usr/src/cmd/mdb/Makefile.mdb
@@ -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 @@ SRCS += \
mdb_stdlib.c \
mdb_string.c \
mdb_strio.c \
+ mdb_tab.c \
mdb_target.c \
mdb_tdb.c \
mdb_termio.c \
diff --git a/usr/src/cmd/mdb/common/mdb/mdb.c b/usr/src/cmd/mdb/common/mdb/mdb.c
index 2cc4c8a388..2a42d93f27 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb.c
@@ -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 @@ done:
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.
diff --git a/usr/src/cmd/mdb/common/mdb/mdb.h b/usr/src/cmd/mdb/common/mdb/mdb.h
index 61f299db1f..dc91906295 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb.h
@@ -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 void mdb_destroy(void);
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);
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
index 46b16b820f..568ef116ed 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
@@ -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 @@ cmd_walk(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
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 @@ const mdb_dcmd_t mdb_dcmd_builtins[] = {
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 @@ const mdb_dcmd_t mdb_dcmd_builtins[] = {
{ "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 @@ const mdb_dcmd_t mdb_dcmd_builtins[] = {
{ "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 },
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h
index aa7e8409b6..20fa34fe13 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h
@@ -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 @@ extern "C" {
#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 @@ extern "C" {
#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 @@ typedef struct mdb_arg {
} 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 @@ typedef struct mdb_dcmd {
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 @@ typedef void (*mdb_callback_f)(void *);
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 *);
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_module.c b/usr/src/cmd/mdb/common/mdb/mdb_module.c
index 30b679dcc3..286f9d0074 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_module.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_module.c
@@ -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 @@ mdb_module_create(const char *name, const char *fname, int mode,
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 @@ mdb_module_create(const char *name, const char *fname, int mode,
*/
switch (info->mi_dvers) {
case MDB_API_VERSION:
+ case 3:
case 2:
case 1:
/*
@@ -186,6 +206,35 @@ mdb_module_create(const char *name, const char *fname, int mode,
}
/*
+ * 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_module_unload_common(const char *name)
{
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_module_unload_common(const char *name)
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 @@ mdb_module_add_dcmd(mdb_module_t *mod, const mdb_dcmd_t *dcp, int flags)
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,
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_module.h b/usr/src/cmd/mdb/common/mdb/mdb_module.h
index 30ec3aff83..4c6a54e28b 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_module.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_module.h
@@ -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 @@ typedef struct mdb_idcmd {
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;
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_module_load.c b/usr/src/cmd/mdb/common/mdb/mdb_module_load.c
index 324b43e461..811af3dd47 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_module_load.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_module_load.c
@@ -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_module_load_all(int mode)
mdb_iob_setflags(mdb.m_out, oflag);
}
+/*ARGSUSED*/
int
mdb_module_unload(const char *name, int mode)
{
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_print.c b/usr/src/cmd/mdb/common/mdb/mdb_print.c
index 80a6f09556..c993fdb5f8 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_print.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_print.c
@@ -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 @@ cmd_sizeof(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
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 @@ parse_member(printarg_t *pap, const char *str, mdb_ctf_id_t id,
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 @@ parse_member(printarg_t *pap, const char *str, mdb_ctf_id_t id,
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 @@ parse_member(printarg_t *pap, const char *str, mdb_ctf_id_t id,
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);
+
+ while (delim != MEMBER_DELIM_DONE) {
+ switch (delim) {
+ case MEMBER_DELIM_PTR:
+ kind = mdb_ctf_type_kind(rid);
+ if (kind != CTF_K_POINTER)
+ return (1);
+
+ (void) mdb_ctf_type_reference(rid, &id);
+ (void) mdb_ctf_type_resolve(id, &rid);
+ break;
+ case MEMBER_DELIM_DOT:
+ kind = mdb_ctf_type_kind(rid);
+ if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
+ return (1);
+ break;
+ case MEMBER_DELIM_LBR:
+ end = strchr(start, ']');
+ /*
+ * We're not going to try and tab complete the indexes
+ * here. So for now, punt on it. Also, we're not going
+ * to try and validate you're within the bounds, just
+ * that you get the type you asked for.
+ */
+ if (end == NULL)
+ return (1);
+
+ switch (mdb_ctf_type_kind(rid)) {
+ case CTF_K_POINTER:
+ (void) mdb_ctf_type_reference(rid, &id);
+ (void) mdb_ctf_type_resolve(id, &rid);
+ break;
+ case CTF_K_ARRAY:
+ (void) mdb_ctf_array_info(rid, &ar);
+ id = ar.mta_contents;
+ (void) mdb_ctf_type_resolve(id, &rid);
+ break;
+ default:
+ return (1);
+ }
+
+ start = end + 1;
+ delim = parse_delimiter(&start);
+ break;
+ case MEMBER_DELIM_ERR:
+ default:
+ break;
+ }
+
+ for (end = start + 1; isalnum(*end) || *end == '_'; end++)
+ continue;
+
+ (void) mdb_snprintf(member, end - start + 1, start);
+
+ /*
+ * We are going to try to resolve this name as a member. There
+ * are a few two different questions that we need to answer. The
+ * first is do we recognize this member. The second is are we at
+ * the end of the string. If we encounter a member that we don't
+ * recognize before the end, then we have to error out and can't
+ * complete it. But if there are no more delimiters then we can
+ * try and complete it.
+ */
+ ret = mdb_ctf_member_info(rid, member, &dul, &id);
+ start = end;
+ delim = parse_delimiter(&start);
+ if (ret != 0 && errno == EMDB_CTFNOMEMB) {
+ if (delim != MEMBER_DELIM_DONE)
+ return (1);
+ continue;
+ } else if (ret != 0)
+ return (1);
+
+ if (delim == MEMBER_DELIM_DONE)
+ return (mdb_tab_complete_member_by_id(mcp, rid,
+ member));
+
+ (void) mdb_ctf_type_resolve(id, &rid);
+ }
+
+ /*
+ * If we've reached here, then we need to try and tab complete the last
+ * field, which is currently member, based on the ctf type id that we
+ * already have in rid.
+ */
+ return (mdb_tab_complete_member_by_id(mcp, rid, member));
+}
+
/*
* Recursively descend a print a given data structure. We create a struct of
* the relevant print arguments and then call mdb_ctf_type_visit() to do the
@@ -2005,6 +2198,10 @@ cmd_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
mdb_syminfo_t s_info;
GElf_Sym sym;
+ /*
+ * If a new option is added, make sure the getopts above in
+ * cmd_print_tab is also updated.
+ */
i = mdb_getopts(argc, argv,
'a', MDB_OPT_SETBITS, PA_SHOWADDR, &uflags,
'C', MDB_OPT_SETBITS, TRUE, &opt_C,
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_print.h b/usr/src/cmd/mdb/common/mdb/mdb_print.h
index 2e7a23918c..6c1c1bc4f9 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_print.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_print.h
@@ -23,9 +23,16 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ */
+
#ifndef _MDB_PRINT_H
#define _MDB_PRINT_H
+#include <mdb/mdb_tab.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -35,10 +42,12 @@ extern "C" {
extern int cmd_enum(uintptr_t, uint_t, int, const mdb_arg_t *);
extern void enum_help(void);
extern int cmd_sizeof(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_sizeof_tab(mdb_tab_cookie_t *, uint_t, int, const mdb_arg_t *);
extern int cmd_offsetof(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int cmd_list(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int cmd_array(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int cmd_print(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_print_tab(mdb_tab_cookie_t *, uint_t, int, const mdb_arg_t *);
extern void print_help(void);
#endif /* _MDB */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_tab.c b/usr/src/cmd/mdb/common/mdb/mdb_tab.c
new file mode 100644
index 0000000000..e2694f1d91
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_tab.c
@@ -0,0 +1,652 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ */
+/*
+ * This file contains all of the interfaces for mdb's tab completion engine.
+ * Currently some interfaces are private to mdb and its internal implementation,
+ * those are in mdb_tab.h. Other pieces are public interfaces. Those are in
+ * mdb_modapi.h.
+ *
+ * Memory allocations in tab completion context have to be done very carefully.
+ * We need to think of ourselves as the same as any other command that is being
+ * executed by the user, which means we must use UM_GC to handle being
+ * interrupted.
+ */
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_ctf.h>
+#include <mdb/mdb_ctf_impl.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_module.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_print.h>
+#include <mdb/mdb_nv.h>
+#include <mdb/mdb_tab.h>
+#include <mdb/mdb.h>
+
+#include <ctype.h>
+
+/*
+ * There may be another way to do this, but this works well enough.
+ */
+#define COMMAND_SEPARATOR "::"
+
+/*
+ * find_command_start --
+ *
+ * Given a buffer find the start of the last command.
+ */
+static char *
+tab_find_command_start(char *buf)
+{
+ char *offset = strstr(buf, COMMAND_SEPARATOR);
+
+ if (offset == NULL)
+ return (NULL);
+
+ for (;;) {
+ char *next = strstr(offset + strlen(COMMAND_SEPARATOR),
+ COMMAND_SEPARATOR);
+
+ if (next == NULL) {
+ return (offset);
+ }
+
+ offset = next;
+ }
+}
+
+/*
+ * get_dcmd --
+ *
+ * Given a buffer containing a command and its argument return
+ * the name of the command and the offset in the buffer where
+ * the command arguments start.
+ *
+ * Note: This will modify the buffer.
+ */
+char *
+tab_get_dcmd(char *buf, char **args, uint_t *flags)
+{
+ char *start = buf + strlen(COMMAND_SEPARATOR);
+ char *separator = start;
+ const char *end = buf + strlen(buf);
+ uint_t space = 0;
+
+ while (separator < end && !isspace(*separator))
+ separator++;
+
+ if (separator == end) {
+ *args = NULL;
+ } else {
+ if (isspace(*separator))
+ space = 1;
+
+ *separator++ = '\0';
+ *args = separator;
+ }
+
+ if (space)
+ *flags |= DCMD_TAB_SPACE;
+
+ return (start);
+}
+
+/*
+ * count_args --
+ *
+ * Given a buffer containing dmcd arguments return the total number
+ * of arguments.
+ *
+ * While parsing arguments we need to keep track of whether or not the last
+ * arguments ends with a trailing space.
+ */
+static int
+tab_count_args(const char *input, uint_t *flags)
+{
+ const char *index;
+ int argc = 0;
+ uint_t space = *flags & DCMD_TAB_SPACE;
+ index = input;
+
+ while (*index != '\0') {
+ while (*index != '\0' && isspace(*index)) {
+ index++;
+ space = 1;
+ }
+
+ if (*index != '\0' && !isspace(*index)) {
+ argc++;
+ space = 0;
+ while (*index != '\0' && !isspace (*index)) {
+ index++;
+ }
+ }
+ }
+
+ if (space)
+ *flags |= DCMD_TAB_SPACE;
+ else
+ *flags &= ~DCMD_TAB_SPACE;
+
+ return (argc);
+}
+
+/*
+ * copy_args --
+ *
+ * Given a buffer containing dcmd arguments and an array of mdb_arg_t's
+ * initialize the string value of each mdb_arg_t.
+ *
+ * Note: This will modify the buffer.
+ */
+static int
+tab_copy_args(char *input, int argc, mdb_arg_t *argv)
+{
+ int i = 0;
+ char *index;
+
+ index = input;
+
+ while (*index) {
+ while (*index && isspace(*index)) {
+ index++;
+ }
+
+ if (*index && !isspace(*index)) {
+ char *end = index;
+
+ while (*end && !isspace(*end)) {
+ end++;
+ }
+
+ if (*end) {
+ *end++ = '\0';
+ }
+
+ argv[i].a_type = MDB_TYPE_STRING;
+ argv[i].a_un.a_str = index;
+
+ index = end;
+ i++;
+ }
+ }
+
+ if (i != argc)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * parse-buf --
+ *
+ * Parse the given buffer and return the specified dcmd, the number
+ * of arguments, and array of mdb_arg_t containing the argument
+ * values.
+ *
+ * Note: this will modify the specified buffer. Caller is responisble
+ * for freeing argvp.
+ */
+static int
+tab_parse_buf(char *buf, char **dcmdp, int *argcp, mdb_arg_t **argvp,
+ uint_t *flags)
+{
+ char *data = tab_find_command_start(buf);
+ char *args_data = NULL;
+ char *dcmd = NULL;
+ int argc = 0;
+ mdb_arg_t *argv = NULL;
+
+ if (data == NULL) {
+ return (-1);
+ }
+
+ dcmd = tab_get_dcmd(data, &args_data, flags);
+
+ if (dcmd == NULL) {
+ return (-1);
+ }
+
+ if (args_data != NULL) {
+ argc = tab_count_args(args_data, flags);
+
+ if (argc != 0) {
+ argv = mdb_alloc(sizeof (mdb_arg_t) * argc,
+ UM_SLEEP | UM_GC);
+
+ if (tab_copy_args(args_data, argc, argv) == -1)
+ return (-1);
+ }
+ }
+
+ *dcmdp = dcmd;
+ *argcp = argc;
+ *argvp = argv;
+
+ return (0);
+}
+
+/*
+ * tab_command --
+ *
+ * This function is executed anytime a tab is entered. It checks
+ * the current buffer to determine if there is a valid dmcd,
+ * if that dcmd has a tab completion handler it will invoke it.
+ *
+ * This function returns the string (if any) that should be added to the
+ * existing buffer to complete it.
+ */
+int
+mdb_tab_command(mdb_tab_cookie_t *mcp, const char *buf)
+{
+ char *data;
+ char *dcmd = NULL;
+ int argc = 0;
+ mdb_arg_t *argv = NULL;
+ int ret = 0;
+ mdb_idcmd_t *cp;
+ uint_t flags = 0;
+
+ /*
+ * Parsing the command and arguments will modify the buffer
+ * (replacing spaces with \0), so make a copy of the specified
+ * buffer first.
+ */
+ data = mdb_alloc(strlen(buf) + 1, UM_SLEEP | UM_GC);
+ (void) strcpy(data, buf);
+
+ /*
+ * Get the specified dcmd and arguments from the buffer.
+ */
+ ret = tab_parse_buf(data, &dcmd, &argc, &argv, &flags);
+
+ if (ret != 0) {
+ goto out;
+ }
+
+ /*
+ * Check to see if the buffer contains a valid dcmd
+ */
+ cp = mdb_dcmd_lookup(dcmd);
+
+ /*
+ * When argc is zero it indicates that we are trying to tab complete
+ * a dcmd. Note, that if there isn't the start of a dcmd, i.e. ::, then
+ * we will have already bailed in the call to tab_parse_buf.
+ */
+ if (cp == NULL && argc != 0) {
+ goto out;
+ }
+
+ /*
+ * Invoke the command specific tab completion handler or the built in
+ * dcmd one if there is no dcmd.
+ */
+ if (cp == NULL)
+ (void) mdb_tab_complete_dcmd(mcp, dcmd);
+ else
+ mdb_call_tab(cp, mcp, flags, argc, argv);
+
+out:
+ return (mdb_tab_size(mcp));
+}
+
+static int
+tab_complete_dcmd(mdb_var_t *v, void *arg)
+{
+ mdb_idcmd_t *idcp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
+ mdb_tab_cookie_t *mcp = (mdb_tab_cookie_t *)arg;
+
+ /*
+ * The way that mdb is implemented, even commands like $C will show up
+ * here. As such, we don't want to match anything that doesn't start
+ * with an alpha or number. While nothing currently appears (via a
+ * cursory search with mdb -k) to start with a capital letter or a
+ * number, we'll support them anyways.
+ */
+ if (!isalnum(idcp->idc_name[0]))
+ return (0);
+
+ mdb_tab_insert(mcp, idcp->idc_name);
+ return (0);
+}
+
+int
+mdb_tab_complete_dcmd(mdb_tab_cookie_t *mcp, const char *dcmd)
+{
+ mdb_tab_setmbase(mcp, dcmd);
+ mdb_nv_sort_iter(&mdb.m_dcmds, tab_complete_dcmd, mcp,
+ UM_GC | UM_SLEEP);
+ return (0);
+}
+
+static int
+tab_complete_walker(mdb_var_t *v, void *arg)
+{
+ mdb_iwalker_t *iwp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
+ mdb_tab_cookie_t *mcp = arg;
+
+ mdb_tab_insert(mcp, iwp->iwlk_name);
+ return (0);
+}
+
+int
+mdb_tab_complete_walker(mdb_tab_cookie_t *mcp, const char *walker)
+{
+ if (walker != NULL)
+ mdb_tab_setmbase(mcp, walker);
+ mdb_nv_sort_iter(&mdb.m_walkers, tab_complete_walker, mcp,
+ UM_GC | UM_SLEEP);
+
+ return (0);
+}
+
+mdb_tab_cookie_t *
+mdb_tab_init(void)
+{
+ mdb_tab_cookie_t *mcp;
+
+ mcp = mdb_zalloc(sizeof (mdb_tab_cookie_t), UM_SLEEP | UM_GC);
+ (void) mdb_nv_create(&mcp->mtc_nv, UM_SLEEP | UM_GC);
+
+ return (mcp);
+}
+
+size_t
+mdb_tab_size(mdb_tab_cookie_t *mcp)
+{
+ return (mdb_nv_size(&mcp->mtc_nv));
+}
+
+/*
+ * Determine whether the specified name is a valid tab completion for
+ * the given command. If the name is a valid tab completion then
+ * it will be saved in the mdb_tab_cookie_t.
+ */
+void
+mdb_tab_insert(mdb_tab_cookie_t *mcp, const char *name)
+{
+ size_t len, matches, index;
+ uint_t flags;
+ mdb_var_t *v;
+ char *n;
+ const char *nvn;
+
+ /*
+ * If we have a match set, then we want to verify that we actually match
+ * it.
+ */
+ if (mcp->mtc_base != NULL &&
+ strncmp(name, mcp->mtc_base, strlen(mcp->mtc_base)) != 0)
+ return;
+
+ v = mdb_nv_lookup(&mcp->mtc_nv, name);
+ if (v != NULL)
+ return;
+
+ /*
+ * Names that we get passed in may be longer than MDB_NV_NAMELEN which
+ * is currently 31 including the null terminator. If that is the case,
+ * then we're going to take care of allocating a string and holding it
+ * for our caller. Note that we don't need to free it, because we're
+ * allocating this with UM_GC.
+ */
+ flags = 0;
+ len = strlen(name);
+ if (len > MDB_NV_NAMELEN - 1) {
+ n = mdb_alloc(len + 1, UM_SLEEP | UM_GC);
+ (void) strcpy(n, name);
+ nvn = n;
+ flags |= MDB_NV_EXTNAME;
+ } else {
+ nvn = name;
+ }
+ flags |= MDB_NV_RDONLY;
+
+ (void) mdb_nv_insert(&mcp->mtc_nv, nvn, NULL, 0, flags);
+
+ matches = mdb_tab_size(mcp);
+ if (matches == 1) {
+ (void) strlcpy(mcp->mtc_match, nvn, MDB_SYM_NAMLEN);
+ } else {
+ index = 0;
+ while (mcp->mtc_match[index] &&
+ mcp->mtc_match[index] == nvn[index])
+ index++;
+
+ mcp->mtc_match[index] = '\0';
+ }
+}
+
+/*ARGSUSED*/
+static int
+tab_print_cb(mdb_var_t *v, void *ignored)
+{
+ mdb_printf("%s\n", mdb_nv_get_name(v));
+ return (0);
+}
+
+void
+mdb_tab_print(mdb_tab_cookie_t *mcp)
+{
+ mdb_nv_sort_iter(&mcp->mtc_nv, tab_print_cb, NULL, UM_SLEEP | UM_GC);
+}
+
+const char *
+mdb_tab_match(mdb_tab_cookie_t *mcp)
+{
+ size_t blen;
+
+ if (mcp->mtc_base == NULL)
+ blen = 0;
+ else
+ blen = strlen(mcp->mtc_base);
+ return (mcp->mtc_match + blen);
+}
+
+void
+mdb_tab_setmbase(mdb_tab_cookie_t *mcp, const char *base)
+{
+ (void) strlcpy(mcp->mtc_base, base, MDB_SYM_NAMLEN);
+}
+
+/*
+ * This function is currently a no-op due to the fact that we have to GC because
+ * we're in command context.
+ */
+/*ARGSUSED*/
+void
+mdb_tab_fini(mdb_tab_cookie_t *mcp)
+{
+}
+
+/*
+ * This function takes a ctf id and determines whether or not the associated
+ * type should be considered as a potential match for the given tab
+ * completion command. We verify that the type itself is valid
+ * for completion given the current context of the command, resolve
+ * its actual name, and then pass it off to mdb_tab_insert to determine
+ * if it's an actual match.
+ */
+static int
+tab_complete_type(mdb_ctf_id_t id, void *arg)
+{
+ int rkind;
+ char buf[MDB_SYM_NAMLEN];
+ mdb_ctf_id_t rid;
+ mdb_tab_cookie_t *mcp = arg;
+ uint_t flags = (uint_t)(uintptr_t)mcp->mtc_cba;
+
+ /*
+ * CTF data includes types that mdb commands don't understand. Before
+ * we resolve the actual type prune any entry that is a type we
+ * don't care about.
+ */
+ switch (mdb_ctf_type_kind(id)) {
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ case CTF_K_VOLATILE:
+ return (0);
+ }
+
+ if (mdb_ctf_type_resolve(id, &rid) != 0)
+ return (1);
+
+ rkind = mdb_ctf_type_kind(rid);
+
+ if ((flags & MDB_TABC_MEMBERS) && rkind != CTF_K_STRUCT &&
+ rkind != CTF_K_UNION)
+ return (0);
+
+ if ((flags & MDB_TABC_NOPOINT) && rkind == CTF_K_POINTER)
+ return (0);
+
+ if ((flags & MDB_TABC_NOARRAY) && rkind == CTF_K_ARRAY)
+ return (0);
+
+ (void) mdb_ctf_type_name(id, buf, sizeof (buf));
+
+ mdb_tab_insert(mcp, buf);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+mdb_tab_complete_module(void *data, const mdb_map_t *mp, const char *name)
+{
+ (void) mdb_ctf_type_iter(name, tab_complete_type, data);
+ return (0);
+}
+
+int
+mdb_tab_complete_type(mdb_tab_cookie_t *mcp, const char *name, uint_t flags)
+{
+ mdb_tgt_t *t = mdb.m_target;
+
+ mcp->mtc_cba = (void *)(uintptr_t)flags;
+ if (name != NULL)
+ mdb_tab_setmbase(mcp, name);
+
+ (void) mdb_tgt_object_iter(t, mdb_tab_complete_module, mcp);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+tab_complete_member(const char *name, mdb_ctf_id_t id, ulong_t off, void *arg)
+{
+ mdb_tab_cookie_t *mcp = arg;
+ mdb_tab_insert(mcp, name);
+ return (0);
+}
+
+int
+mdb_tab_complete_member_by_id(mdb_tab_cookie_t *mcp, mdb_ctf_id_t id,
+ const char *member)
+{
+ if (member != NULL)
+ mdb_tab_setmbase(mcp, member);
+ (void) mdb_ctf_member_iter(id, tab_complete_member, mcp);
+ return (0);
+}
+
+int
+mdb_tab_complete_member(mdb_tab_cookie_t *mcp, const char *type,
+ const char *member)
+{
+ mdb_ctf_id_t id;
+
+ if (mdb_ctf_lookup_by_name(type, &id) != 0)
+ return (-1);
+
+ return (mdb_tab_complete_member_by_id(mcp, id, member));
+}
+
+int
+mdb_tab_complete_mt(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)
+ return (mdb_tab_complete_type(mcp, NULL, MDB_TABC_MEMBERS));
+
+ 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_MEMBERS));
+
+ if (argc == 1 && (flags & DCMD_TAB_SPACE))
+ return (mdb_tab_complete_member(mcp, tn, NULL));
+
+ if (argc == 2)
+ return (mdb_tab_complete_member(mcp, tn, argv[1].a_un.a_str));
+
+ return (0);
+}
+
+/*
+ * This is similar to mdb_print.c's args_to_typename, but it has subtle
+ * differences surrounding how the strings of one element are handled that have
+ * 'struct', 'enum', or 'union' in them and instead works with them for tab
+ * completion purposes.
+ */
+int
+mdb_tab_typename(int *argcp, const mdb_arg_t **argvp, char *buf, size_t len)
+{
+ int argc = *argcp;
+ const mdb_arg_t *argv = *argvp;
+
+ if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ if (strcmp(argv->a_un.a_str, "struct") == 0 ||
+ strcmp(argv->a_un.a_str, "enum") == 0 ||
+ strcmp(argv->a_un.a_str, "union") == 0) {
+ if (argc == 1) {
+ (void) mdb_snprintf(buf, len, "%s ",
+ argv[0].a_un.a_str);
+ return (1);
+ }
+
+ if (argv[1].a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ (void) mdb_snprintf(buf, len, "%s %s",
+ argv[0].a_un.a_str, argv[1].a_un.a_str);
+
+ *argcp = argc - 1;
+ *argvp = argv + 1;
+ } else {
+ (void) mdb_snprintf(buf, len, "%s", argv[0].a_un.a_str);
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_tab.h b/usr/src/cmd/mdb/common/mdb/mdb_tab.h
new file mode 100644
index 0000000000..c3a28b2d40
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_tab.h
@@ -0,0 +1,71 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ */
+/*
+ * This file contains mdb private tab completion related functions. Public
+ * functions for modules are put into the module API, see mdb_modapi.h. Note
+ * that the mdb_ctf_id_t value is private to mdb and not a part of the module
+ * api, hence it has to stay in here.
+ */
+
+#ifndef _MDB_TAB_H
+#define _MDB_TAB_H
+
+#include <sys/types.h>
+#include <mdb/mdb_ctf.h>
+#include <mdb/mdb_nv.h>
+#include <mdb/mdb_modapi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MDB
+
+struct mdb_tab_cookie {
+ mdb_nv_t mtc_nv;
+ char mtc_match[MDB_SYM_NAMLEN];
+ char mtc_base[MDB_SYM_NAMLEN];
+ void *mtc_cba;
+};
+
+extern mdb_tab_cookie_t *mdb_tab_init(void);
+extern size_t mdb_tab_size(mdb_tab_cookie_t *);
+extern const char *mdb_tab_match(mdb_tab_cookie_t *);
+extern void mdb_tab_print(mdb_tab_cookie_t *);
+extern void mdb_tab_fini(mdb_tab_cookie_t *);
+
+extern int mdb_tab_complete_dcmd(mdb_tab_cookie_t *, const char *);
+extern int mdb_tab_complete_walker(mdb_tab_cookie_t *, const char *);
+extern int mdb_tab_complete_member_by_id(mdb_tab_cookie_t *, mdb_ctf_id_t,
+ const char *);
+extern int mdb_tab_command(mdb_tab_cookie_t *, const char *);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_TAB_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_termio.c b/usr/src/cmd/mdb/common/mdb/mdb_termio.c
index d2f3d59f83..5e36cda7ad 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_termio.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_termio.c
@@ -25,6 +25,11 @@
*/
/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ */
+
+/*
* Terminal I/O Backend
*
* Terminal editing backend for standard input. The terminal i/o backend is
@@ -76,6 +81,7 @@
#include <mdb/mdb_string.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_frame.h>
+#include <mdb/mdb_tab.h>
#include <mdb/mdb.h>
#ifdef ERR
@@ -174,6 +180,7 @@ typedef void (*putp_t)(struct termio_data *, const char *, uint_t);
#define TIO_TTYWARN 0x20 /* Warnings about tty issued */
#define TIO_CAPWARN 0x40 /* Warnings about terminfo issued */
#define TIO_XTERM 0x80 /* Terminal is xterm compatible */
+#define TIO_TAB 0x100 /* Tab completion mode */
static const mdb_bitmask_t tio_flag_masks[] = {
{ "FINDHIST", TIO_FINDHIST, TIO_FINDHIST },
@@ -184,6 +191,7 @@ static const mdb_bitmask_t tio_flag_masks[] = {
{ "TTYWARN", TIO_TTYWARN, TIO_TTYWARN },
{ "CAPWARN", TIO_CAPWARN, TIO_CAPWARN },
{ "XTERM", TIO_XTERM, TIO_XTERM },
+ { "TAB", TIO_TAB, TIO_TAB},
{ NULL, 0, 0 }
};
@@ -256,6 +264,7 @@ static void termio_clear(termio_data_t *);
static void termio_redraw(termio_data_t *);
static void termio_prompt(termio_data_t *);
+static const char *termio_tab(termio_data_t *, int);
static const char *termio_insert(termio_data_t *, int);
static const char *termio_accept(termio_data_t *, int);
static const char *termio_backspace(termio_data_t *, int);
@@ -398,7 +407,10 @@ termio_read(mdb_io_t *io, void *buf, size_t nbytes)
goto out;
}
- termio_prompt(td);
+ if (td->tio_flags & TIO_TAB)
+ termio_redraw(td);
+ else
+ termio_prompt(td);
/*
* We need to redraw the entire command-line and restart our read loop
@@ -428,6 +440,13 @@ termio_read(mdb_io_t *io, void *buf, size_t nbytes)
td->tio_active = TRUE;
+ /*
+ * We may have had some error while in tab completion mode which sent us
+ * longjmping all over the place. If that's the case, come back here and
+ * make sure the flag is off.
+ */
+ td->tio_flags &= ~TIO_TAB;
+
do {
char_loop:
if ((c = mdb_iob_getc(td->tio_in)) == EOF) {
@@ -1488,6 +1507,11 @@ mdb_termio_create(const char *name, mdb_io_t *rio, mdb_io_t *wio)
td->tio_keymap['['] = termio_accel;
td->tio_keymap[']'] = termio_accel;
+ /*
+ * Grab tabs
+ */
+ td->tio_keymap['\t'] = termio_tab;
+
td->tio_x = 0;
td->tio_y = 0;
td->tio_max_x = 0;
@@ -1590,6 +1614,67 @@ termio_backspace(termio_data_t *td, int c)
return (NULL);
}
+/*
+ * This function may end up calling termio_read recursively as part of invoking
+ * the mdb pager. To work around this fact, we need to go through and make sure
+ * that we change the underlying terminal settings before and after this
+ * function call. If we don't do this, we invoke the pager, and don't abort
+ * (which will longjmp us elsewhere) we're going to return to the read loop with
+ * the wrong termio settings.
+ *
+ * Furthermore, because of the fact that we're being invoked in a user context
+ * that allows us to be interrupted, we need to actually allocate the memory
+ * that we're using with GC so that it gets cleaned up in case of the pager
+ * resetting us and never reaching the end.
+ */
+/*ARGSUSED*/
+static const char *
+termio_tab(termio_data_t *td, int c)
+{
+ char *buf;
+ const char *result;
+ int nres;
+ mdb_tab_cookie_t *mtp;
+
+ if (termio_ctl(td->tio_io, TCSETSW, &td->tio_dtios) == -1)
+ warn("failed to restore terminal attributes");
+
+ buf = mdb_alloc(td->tio_cmdbuf.cmd_bufidx + 1, UM_SLEEP | UM_GC);
+ (void) strncpy(buf, td->tio_cmdbuf.cmd_buf, td->tio_cmdbuf.cmd_bufidx);
+ buf[td->tio_cmdbuf.cmd_bufidx] = '\0';
+ td->tio_flags |= TIO_TAB;
+ mtp = mdb_tab_init();
+ nres = mdb_tab_command(mtp, buf);
+
+ if (nres == 0) {
+ result = NULL;
+ } else {
+ result = mdb_tab_match(mtp);
+ if (nres != 1) {
+ mdb_printf("\n");
+ mdb_tab_print(mtp);
+ }
+ }
+
+ if (result != NULL) {
+ int index = 0;
+
+ while (result[index] != '\0') {
+ (void) termio_insert(td, result[index]);
+ index++;
+ }
+ }
+
+ termio_redraw(td);
+ mdb_tab_fini(mtp);
+ td->tio_flags &= ~TIO_TAB;
+ if (termio_ctl(td->tio_io, TCSETSW, &td->tio_rtios) == -1)
+ warn("failed to set terminal attributes");
+
+
+ return (NULL);
+}
+
static const char *
termio_delchar(termio_data_t *td, int c)
{
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_whatis.c b/usr/src/cmd/mdb/common/mdb/mdb_whatis.c
index b037900ef8..1b53a32d39 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_whatis.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_whatis.c
@@ -23,7 +23,12 @@
* Use is subject to license terms.
*/
-#include <sys/mdb_modapi.h>
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ */
+
+#include <mdb/mdb_modapi.h>
#include <mdb/mdb.h>
#include <mdb/mdb_io.h>
#include <mdb/mdb_module.h>