summaryrefslogtreecommitdiff
path: root/usr/src/cmd/mdb
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/mdb')
-rw-r--r--usr/src/cmd/mdb/Makefile.common3
-rw-r--r--usr/src/cmd/mdb/Makefile.kmdb.files1
-rw-r--r--usr/src/cmd/mdb/Makefile.libstandctf2
-rw-r--r--usr/src/cmd/mdb/Makefile.mdb1
-rw-r--r--usr/src/cmd/mdb/common/libstandctf/mapfile7
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb.c18
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb.h5
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_cmds.c16
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_ctf.c594
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_ctf.h27
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_help.c18
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_help.h5
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c7
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_kvm.c17
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_main.c58
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_modapi.c49
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_modapi.h57
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_module.h2
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_print.c161
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_print.h3
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_proc.c71
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_tab.c29
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_target_impl.h9
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_termio.c2
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_typedef.c738
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_typedef.h42
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_whatis.c2
-rw-r--r--usr/src/cmd/mdb/common/modules/conf/mapfile-extern1
-rw-r--r--usr/src/cmd/mdb/common/modules/dtrace/dtrace.c10
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/genunix.c109
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/netstack.c30
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/netstack.h3
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/zone.c67
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/zone.h4
-rw-r--r--usr/src/cmd/mdb/common/modules/libc/libc.c9
-rw-r--r--usr/src/cmd/mdb/common/modules/libumem/libumem.c197
-rw-r--r--usr/src/cmd/mdb/common/modules/libumem/umem.c214
-rw-r--r--usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c6
-rw-r--r--usr/src/cmd/mdb/common/modules/v8/mdb_v8.c4120
-rw-r--r--usr/src/cmd/mdb/common/modules/v8/mdb_v8_cfg.c728
-rw-r--r--usr/src/cmd/mdb/common/modules/v8/v8cfg.h55
-rw-r--r--usr/src/cmd/mdb/common/modules/v8/v8dbg.h81
-rw-r--r--usr/src/cmd/mdb/i86pc/modules/unix/unix.c141
-rw-r--r--usr/src/cmd/mdb/intel/amd64/libumem/Makefile2
-rw-r--r--usr/src/cmd/mdb/intel/amd64/v8/Makefile45
-rw-r--r--usr/src/cmd/mdb/intel/ia32/libpython2.6/Makefile2
-rw-r--r--usr/src/cmd/mdb/intel/ia32/libumem/Makefile2
-rw-r--r--usr/src/cmd/mdb/intel/ia32/v8/Makefile44
-rw-r--r--usr/src/cmd/mdb/intel/mdb/kvm_isadep.c22
-rw-r--r--usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c56
-rw-r--r--usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c22
-rw-r--r--usr/src/cmd/mdb/intel/mdb/proc_amd64dep.c58
-rw-r--r--usr/src/cmd/mdb/intel/mdb/proc_ia32dep.c22
-rw-r--r--usr/src/cmd/mdb/sparc/v7/libumem/Makefile2
-rw-r--r--usr/src/cmd/mdb/sparc/v9/libumem/Makefile2
-rw-r--r--usr/src/cmd/mdb/test/README18
-rw-r--r--usr/src/cmd/mdb/test/exit-e/err.cmdbadopt.ksh2
-rw-r--r--usr/src/cmd/mdb/test/exit-e/err.enocmd.ksh2
-rw-r--r--usr/src/cmd/mdb/test/exit-e/tst.output.ksh1
-rw-r--r--usr/src/cmd/mdb/test/exit-e/tst.output.ksh.out1
-rw-r--r--usr/src/cmd/mdb/test/exit-e/tst.simple.ksh1
-rwxr-xr-xusr/src/cmd/mdb/test/mtest.sh235
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badid-leadnum.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badid-leadschar.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badmodel.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-extrabraces.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-neglenarr.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-noarrayclose.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-noarraylen.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-noarrayopen.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-nobraces.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-noclosebrace.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-nomembers.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-nomemname.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-nomemsemi.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-noopenbrace.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-noquotes.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-repmemname.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-vlaonly.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badstruct-zerolenarr.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.badunion-hasvla.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.extraargs.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.noargs.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.nokeyword.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.nomodel.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.noname.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/err.typeexists.ksh1
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.anonstruct.mdb4
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.anonstruct.mdb.out5
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.anonunion.mdb4
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.anonunion.mdb.out6
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.cleanupstruct.ksh22
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.deftypes32.mdb27
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.deftypes32.mdb.out26
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.deftypes64.mdb27
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.deftypes64.mdb.out26
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.dellist.mdb3
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.dellist.mdb.out0
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.emptylist.mdb1
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.emptylist.mdb.out0
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.libctype.ksh6
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.libctype.ksh.out4
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.models.ksh11
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.struct.mdb6
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.struct.mdb.out10
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.structselfref.mdb4
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.structselfref.mdb.out5
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.structvla.mdb4
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.structvla.mdb.out6
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.union.mdb6
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.union.mdb.out12
111 files changed, 8252 insertions, 256 deletions
diff --git a/usr/src/cmd/mdb/Makefile.common b/usr/src/cmd/mdb/Makefile.common
index 4e282bf001..b78836c741 100644
--- a/usr/src/cmd/mdb/Makefile.common
+++ b/usr/src/cmd/mdb/Makefile.common
@@ -38,7 +38,8 @@ COMMON_MODULES_PROC = \
libuutil \
libzpool \
mdb_ds \
- mdb_test
+ mdb_test \
+ v8
#
# MDB modules used for debugging user processes which are only 32-bit
diff --git a/usr/src/cmd/mdb/Makefile.kmdb.files b/usr/src/cmd/mdb/Makefile.kmdb.files
index 3c5983de21..a82206d42c 100644
--- a/usr/src/cmd/mdb/Makefile.kmdb.files
+++ b/usr/src/cmd/mdb/Makefile.kmdb.files
@@ -84,6 +84,7 @@ KMDBSRCS += \
mdb_target.c \
kmdb_terminfo.c \
mdb_termio.c \
+ mdb_typedef.c \
mdb_umem.c \
kmdb_umemglue.c \
mdb_value.c \
diff --git a/usr/src/cmd/mdb/Makefile.libstandctf b/usr/src/cmd/mdb/Makefile.libstandctf
index 7d4c8fbf0d..b5dce2d926 100644
--- a/usr/src/cmd/mdb/Makefile.libstandctf
+++ b/usr/src/cmd/mdb/Makefile.libstandctf
@@ -43,7 +43,7 @@ MAPFILE = ../../../common/libstandctf/mapfile
$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG
CPPFLAGS += -I$(SRC)/common/ctf -I../../../common -DCTF_OLD_VERSIONS -D_MDB \
- -Dvsnprintf=ctf_vsnprintf
+ -Dvsnprintf=ctf_vsnprintf -Dassfail=kmdb_prom_assfail
#
# kmdb is a kernel module, so we'll use the kernel's build flags.
diff --git a/usr/src/cmd/mdb/Makefile.mdb b/usr/src/cmd/mdb/Makefile.mdb
index f331a43fda..9d1d7b4d6c 100644
--- a/usr/src/cmd/mdb/Makefile.mdb
+++ b/usr/src/cmd/mdb/Makefile.mdb
@@ -85,6 +85,7 @@ SRCS += \
mdb_target.c \
mdb_tdb.c \
mdb_termio.c \
+ mdb_typedef.c \
mdb_umem.c \
mdb_value.c \
mdb_vcb.c \
diff --git a/usr/src/cmd/mdb/common/libstandctf/mapfile b/usr/src/cmd/mdb/common/libstandctf/mapfile
index e72c2ddbcf..63b2ec06ee 100644
--- a/usr/src/cmd/mdb/common/libstandctf/mapfile
+++ b/usr/src/cmd/mdb/common/libstandctf/mapfile
@@ -20,6 +20,9 @@
#
# CDDL HEADER END
#
+#
+# Copyright (c) 2012, Joyent, Inc.
+#
#
# MAPFILE HEADER START
@@ -40,15 +43,19 @@ $mapfile_version 2
SYMBOL_SCOPE {
global:
ctf_add_array;
+ ctf_add_float;
+ ctf_add_integer;
ctf_add_member;
ctf_add_pointer;
ctf_add_struct;
+ ctf_add_type;
ctf_add_typedef;
ctf_add_union;
ctf_array_info;
ctf_bufopen;
ctf_close;
ctf_create;
+ ctf_delete_type;
ctf_discard;
ctf_enum_iter;
ctf_enum_name;
diff --git a/usr/src/cmd/mdb/common/mdb/mdb.c b/usr/src/cmd/mdb/common/mdb/mdb.c
index 5c74c71772..6f2df6f533 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb.c
@@ -21,9 +21,6 @@
/*
* 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.
*/
@@ -72,6 +69,7 @@
#include <mdb/mdb_err.h>
#include <mdb/mdb_lex.h>
#include <mdb/mdb_io.h>
+#include <mdb/mdb_ctf.h>
#ifdef _KMDB
#include <kmdb/kmdb_module.h>
#endif
@@ -555,6 +553,13 @@ mdb_create(const char *execname, const char *arg0)
(void) mdb_callb_add(NULL, MDB_CALLB_PROMPT, (mdb_callb_f)prompt_update,
NULL);
+ /*
+ * The call to ctf_create that this does can in fact fail, but that's
+ * okay. All of the ctf functions that might use the synthetic types
+ * make sure that this is safe.
+ */
+ (void) mdb_ctf_synthetics_init();
+
#ifdef _KMDB
(void) mdb_nv_create(&mdb.m_dmodctl, UM_SLEEP);
#endif
@@ -577,6 +582,8 @@ mdb_destroy(void)
mdb_intr_disable();
+ mdb_ctf_synthetics_fini();
+
mdb_macalias_destroy();
/*
@@ -899,6 +906,8 @@ mdb_call(uintmax_t addr, uintmax_t count, uint_t flags)
flags | DCMD_PIPE_OUT, &cp->c_argv,
&cp->c_addrv, cp->c_vcbs);
+ mdb.m_lastret = status;
+
ASSERT(mdb.m_in == iobs[MDB_IOB_RDIOB]);
ASSERT(mdb.m_out == iobs[MDB_IOB_WRIOB]);
} else {
@@ -943,8 +952,9 @@ mdb_call(uintmax_t addr, uintmax_t count, uint_t flags)
} else {
mdb_intr_enable();
- (void) mdb_call_idcmd(cp->c_dcmd, addr, count, flags,
+ status = mdb_call_idcmd(cp->c_dcmd, addr, count, flags,
&cp->c_argv, &cp->c_addrv, cp->c_vcbs);
+ mdb.m_lastret = status;
mdb_intr_disable();
}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb.h b/usr/src/cmd/mdb/common/mdb/mdb.h
index dc91906295..46ff90eff5 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb.h
@@ -21,6 +21,8 @@
/*
* Copyright 2008 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.
*/
/*
@@ -43,6 +45,7 @@
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_list.h>
#include <mdb/mdb_vcb.h>
+#include <mdb/mdb_ctf.h>
#include <mdb/mdb_tab.h>
#ifdef _KMDB
#include <kmdb/kmdb_wr.h>
@@ -181,6 +184,8 @@ typedef struct mdb {
char **m_env; /* Current environment */
mdb_list_t m_cblist; /* List of callbacks */
mdb_nv_t m_macaliases; /* Name/value hash of ADB macro aliases */
+ ctf_file_t *m_synth; /* Container for synthetic types */
+ int m_lastret; /* Result of running the last command */
#ifdef _KMDB
struct dpi_ops *m_dpi; /* DPI ops vector */
struct kdi *m_kdi; /* KDI ops vector */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
index 4a7b39fd57..2818a35a70 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
@@ -22,11 +22,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.
+ * Copyright (c) 2013 Joyent, Inc. All rights reserved.
*/
#include <sys/elf.h>
@@ -67,6 +64,7 @@
#include <mdb/mdb_whatis_impl.h>
#include <mdb/mdb_macalias.h>
#include <mdb/mdb_tab.h>
+#include <mdb/mdb_typedef.h>
#ifdef _KMDB
#include <kmdb/kmdb_kdi.h>
#endif
@@ -2946,7 +2944,8 @@ const mdb_dcmd_t mdb_dcmd_builtins[] = {
{ "grep", "?expr", "print dot if expression is true", cmd_grep },
{ "head", "-num|-n num", "limit number of elements in pipe", cmd_head,
head_help },
- { "help", "[cmd]", "list commands/command help", cmd_help },
+ { "help", "[cmd]", "list commands/command help", cmd_help, NULL,
+ cmd_help_tab },
{ "list", "?type member [variable]",
"walk list using member as link pointer", cmd_list, NULL,
mdb_tab_complete_mt },
@@ -2964,8 +2963,9 @@ const mdb_dcmd_t mdb_dcmd_builtins[] = {
{ "print", "?[-aCdhiLptx] [-c lim] [-l lim] [type] [member|offset ...]",
"print the contents of a data structure", cmd_print, print_help,
cmd_print_tab },
- { "printf", "?format type member ...", "print and format the "
- "member(s) of a data structure", cmd_printf, printf_help },
+ { "printf", "?[format] [type] [member ... ]", "print and format the "
+ "member(s) of a data structure", cmd_printf, printf_help,
+ cmd_printf_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 },
@@ -2978,6 +2978,8 @@ const mdb_dcmd_t mdb_dcmd_builtins[] = {
{ "status", NULL, "print summary of current target", cmd_notsup },
{ "term", NULL, "display current terminal type", cmd_term },
{ "typeset", "[+/-t] var ...", "set variable attributes", cmd_typeset },
+ { "typedef", "[-c model | -d | -l | -r file ] [type] [name]",
+ "create synthetic types", cmd_typedef, cmd_typedef_help },
{ "unset", "[name ...]", "unset variables", cmd_unset },
{ "vars", "[-npt]", "print listing of variables", cmd_vars },
{ "version", NULL, "print debugger version string", cmd_version },
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
index 3181f8290c..3128f95766 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
@@ -24,6 +24,7 @@
*/
/*
* Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
*/
#include <mdb/mdb_ctf.h>
@@ -71,6 +72,125 @@ typedef struct mbr_info {
mdb_ctf_id_t *mbr_typep;
} mbr_info_t;
+typedef struct synth_intrinsic {
+ const char *syn_name;
+ ctf_encoding_t syn_enc;
+ uint_t syn_kind;
+} synth_intrinsic_t;
+
+typedef struct synth_typedef {
+ const char *syt_src;
+ const char *syt_targ;
+} synth_typedef_t;
+
+/*
+ * As part of our support for synthetic types via ::typedef, we define a core
+ * set of types.
+ */
+static const synth_intrinsic_t synth_builtins32[] = {
+{ "void", { CTF_INT_SIGNED, 0, 0 }, CTF_K_INTEGER },
+{ "signed", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
+{ "unsigned", { 0, 0, 32 }, CTF_K_INTEGER },
+{ "char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER },
+{ "short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER },
+{ "int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
+{ "long", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
+{ "long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER },
+{ "signed char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER },
+{ "signed short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER },
+{ "signed int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
+{ "signed long", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
+{ "signed long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER },
+{ "unsigned char", { CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER },
+{ "unsigned short", { 0, 0, 16 }, CTF_K_INTEGER },
+{ "unsigned int", { 0, 0, 32 }, CTF_K_INTEGER },
+{ "unsigned long", { 0, 0, 32 }, CTF_K_INTEGER },
+{ "unsigned long long", { 0, 0, 64 }, CTF_K_INTEGER },
+{ "_Bool", { CTF_INT_BOOL, 0, 8 }, CTF_K_INTEGER },
+{ "float", { CTF_FP_SINGLE, 0, 32 }, CTF_K_FLOAT },
+{ "double", { CTF_FP_DOUBLE, 0, 64 }, CTF_K_FLOAT },
+{ "long double", { CTF_FP_LDOUBLE, 0, 128 }, CTF_K_FLOAT },
+{ "float imaginary", { CTF_FP_IMAGRY, 0, 32 }, CTF_K_FLOAT },
+{ "double imaginary", { CTF_FP_DIMAGRY, 0, 64 }, CTF_K_FLOAT },
+{ "long double imaginary", { CTF_FP_LDIMAGRY, 0, 128 }, CTF_K_FLOAT },
+{ "float complex", { CTF_FP_CPLX, 0, 64 }, CTF_K_FLOAT },
+{ "double complex", { CTF_FP_DCPLX, 0, 128 }, CTF_K_FLOAT },
+{ "long double complex", { CTF_FP_LDCPLX, 0, 256 }, CTF_K_FLOAT },
+{ NULL, { 0, 0, 0}, 0 }
+};
+
+static const synth_intrinsic_t synth_builtins64[] = {
+{ "void", { CTF_INT_SIGNED, 0, 0 }, CTF_K_INTEGER },
+{ "signed", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
+{ "unsigned", { 0, 0, 32 }, CTF_K_INTEGER },
+{ "char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER },
+{ "short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER },
+{ "int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
+{ "long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER },
+{ "long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER },
+{ "signed char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER },
+{ "signed short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER },
+{ "signed int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
+{ "signed long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER },
+{ "signed long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER },
+{ "unsigned char", { CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER },
+{ "unsigned short", { 0, 0, 16 }, CTF_K_INTEGER },
+{ "unsigned int", { 0, 0, 32 }, CTF_K_INTEGER },
+{ "unsigned long", { 0, 0, 64 }, CTF_K_INTEGER },
+{ "unsigned long long", { 0, 0, 64 }, CTF_K_INTEGER },
+{ "_Bool", { CTF_INT_BOOL, 0, 8 }, CTF_K_INTEGER },
+{ "float", { CTF_FP_SINGLE, 0, 32 }, CTF_K_FLOAT },
+{ "double", { CTF_FP_DOUBLE, 0, 64 }, CTF_K_FLOAT },
+{ "long double", { CTF_FP_LDOUBLE, 0, 128 }, CTF_K_FLOAT },
+{ "float imaginary", { CTF_FP_IMAGRY, 0, 32 }, CTF_K_FLOAT },
+{ "double imaginary", { CTF_FP_DIMAGRY, 0, 64 }, CTF_K_FLOAT },
+{ "long double imaginary", { CTF_FP_LDIMAGRY, 0, 128 }, CTF_K_FLOAT },
+{ "float complex", { CTF_FP_CPLX, 0, 64 }, CTF_K_FLOAT },
+{ "double complex", { CTF_FP_DCPLX, 0, 128 }, CTF_K_FLOAT },
+{ "long double complex", { CTF_FP_LDCPLX, 0, 256 }, CTF_K_FLOAT },
+{ NULL, { 0, 0, 0 }, 0 }
+};
+
+static const synth_typedef_t synth_typedefs32[] = {
+{ "char", "int8_t" },
+{ "short", "int16_t" },
+{ "int", "int32_t" },
+{ "long long", "int64_t" },
+{ "int", "intptr_t" },
+{ "unsigned char", "uint8_t" },
+{ "unsigned short", "uint16_t" },
+{ "unsigned", "uint32_t" },
+{ "unsigned long long", "uint64_t" },
+{ "unsigned char", "uchar_t" },
+{ "unsigned short", "ushort_t" },
+{ "unsigned", "uint_t" },
+{ "unsigned long", "ulong_t" },
+{ "unsigned long long", "u_longlong_t" },
+{ "int", "ptrdiff_t" },
+{ "unsigned", "uintptr_t" },
+{ NULL, NULL }
+};
+
+static const synth_typedef_t synth_typedefs64[] = {
+{ "char", "int8_t" },
+{ "short", "int16_t" },
+{ "int", "int32_t" },
+{ "long", "int64_t" },
+{ "long", "intptr_t" },
+{ "unsigned char", "uint8_t" },
+{ "unsigned short", "uint16_t" },
+{ "unsigned", "uint32_t" },
+{ "unsigned long", "uint64_t" },
+{ "unsigned char", "uchar_t" },
+{ "unsigned short", "ushort_t" },
+{ "unsigned", "uint_t" },
+{ "unsigned long", "ulong_t" },
+{ "unsigned long long", "u_longlong_t" },
+{ "long", "ptrdiff_t" },
+{ "unsigned long", "uintptr_t" },
+{ NULL, NULL }
+};
+
static void
set_ctf_id(mdb_ctf_id_t *p, ctf_file_t *fp, ctf_id_t id)
{
@@ -146,6 +266,7 @@ name_to_type(mdb_tgt_t *t, const char *cname, ctf_id_t *idp)
/*
* Attempt to look up the name in the primary object file. If this
* fails and the name was unscoped, search all remaining object files.
+ * Finally, search the synthetic types.
*/
if (((fp = mdb_tgt_name_to_ctf(t, object)) == NULL ||
(id = ctf_lookup_by_name(fp, name)) == CTF_ERR ||
@@ -162,6 +283,10 @@ name_to_type(mdb_tgt_t *t, const char *cname, ctf_id_t *idp)
if (arg.tn_id != CTF_ERR) {
fp = arg.tn_fp;
id = arg.tn_id;
+ } else if (mdb.m_synth != NULL) {
+ if ((id = ctf_lookup_by_name(mdb.m_synth,
+ name)) != CTF_ERR)
+ fp = mdb.m_synth;
}
}
@@ -675,7 +800,12 @@ mdb_ctf_type_iter(const char *object, mdb_ctf_type_f *cb, void *data)
int ret;
type_iter_t ti;
- if ((fp = mdb_tgt_name_to_ctf(t, object)) == NULL)
+ if (object == MDB_CTF_SYNTHETIC_ITER)
+ fp = mdb.m_synth;
+ else
+ fp = mdb_tgt_name_to_ctf(t, object);
+
+ if (fp == NULL)
return (-1);
ti.ti_cb = cb;
@@ -1553,3 +1683,465 @@ mdb_ctf_bufopen(const void *ctf_va, size_t ctf_size, const void *sym_va,
return (ctf_bufopen(&ctdata, &symtab, &strtab, errp));
}
+
+int
+mdb_ctf_synthetics_init(void)
+{
+ int err;
+
+ if ((mdb.m_synth = ctf_create(&err)) == NULL)
+ return (set_errno(ctf_to_errno(err)));
+
+ return (0);
+}
+
+void
+mdb_ctf_synthetics_fini(void)
+{
+ if (mdb.m_synth == NULL)
+ return;
+
+ ctf_close(mdb.m_synth);
+ mdb.m_synth = NULL;
+}
+
+int
+mdb_ctf_synthetics_create_base(int kind)
+{
+ const synth_intrinsic_t *synp;
+ const synth_typedef_t *sytp;
+ int err;
+ ctf_id_t id;
+ ctf_file_t *cp = mdb.m_synth;
+
+ if (mdb.m_synth == NULL) {
+ mdb_printf("synthetic types disabled: ctf create failed\n");
+ return (DCMD_ERR);
+ }
+
+ switch (kind) {
+ case SYNTHETIC_ILP32:
+ synp = synth_builtins32;
+ sytp = synth_typedefs32;
+ break;
+ case SYNTHETIC_LP64:
+ synp = synth_builtins64;
+ sytp = synth_typedefs64;
+ break;
+ default:
+ mdb_dprintf(MDB_DBG_CTF, "invalid type of intrinsic: %d\n",
+ kind);
+ return (1);
+ }
+
+ err = 0;
+ for (; synp->syn_name != NULL; synp++) {
+ if (synp->syn_kind == CTF_K_INTEGER) {
+ err = ctf_add_integer(cp, CTF_ADD_ROOT, synp->syn_name,
+ &synp->syn_enc);
+ } else {
+ err = ctf_add_float(cp, CTF_ADD_ROOT, synp->syn_name,
+ &synp->syn_enc);
+ }
+
+ if (err == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "couldn't add synthetic "
+ "type: %s\n", synp->syn_name);
+ err = set_errno(ctf_to_errno(ctf_errno(cp)));
+ goto discard;
+ }
+ }
+
+ if (ctf_update(cp) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types\n");
+ err = set_errno(ctf_to_errno(ctf_errno(cp)));
+ goto discard;
+ }
+
+ for (; sytp->syt_src != NULL; sytp++) {
+ id = ctf_lookup_by_name(cp, sytp->syt_src);
+ if (id == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "cailed to lookup %s: %s\n",
+ sytp->syt_src, ctf_errmsg(ctf_errno(cp)));
+ err = set_errno(ctf_to_errno(ctf_errno(cp)));
+ goto discard;
+ }
+ if (ctf_add_typedef(cp, CTF_ADD_ROOT, sytp->syt_targ, id) ==
+ CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "couldn't add typedef %s "
+ "%s: %s\n", sytp->syt_targ, sytp->syt_src,
+ ctf_errmsg(ctf_errno(cp)));
+ err = set_errno(ctf_to_errno(ctf_errno(cp)));
+ goto discard;
+ }
+ }
+
+ if (ctf_update(cp) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types\n");
+ err = set_errno(ctf_to_errno(ctf_errno(cp)));
+ goto discard;
+ }
+
+ return (0);
+
+discard:
+ (void) ctf_discard(cp);
+ return (err);
+}
+
+int
+mdb_ctf_synthetics_reset(void)
+{
+ mdb_ctf_synthetics_fini();
+ return (mdb_ctf_synthetics_init());
+}
+
+int
+mdb_ctf_add_typedef(const char *name, const mdb_ctf_id_t *p, mdb_ctf_id_t *new)
+{
+ ctf_id_t rid;
+ mdb_ctf_id_t tid;
+ mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)p;
+
+ if (mdb.m_synth == NULL) {
+ mdb_printf("synthetic types disabled: ctf create failed\n");
+ return (DCMD_ERR);
+ }
+
+ if (mdb_ctf_lookup_by_name(name, &tid) == 0) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add type %s: a type "
+ "with that name already exists\n", name);
+ return (set_errno(EEXIST));
+ }
+
+ rid = ctf_add_type(mdb.m_synth, mcip->mci_fp, mcip->mci_id);
+ if (rid == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add reference type: %s\n",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+ rid = ctf_add_typedef(mdb.m_synth, CTF_ADD_ROOT, name, rid);
+ if (rid == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add typedef: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (ctf_update(mdb.m_synth) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (new != NULL)
+ set_ctf_id(new, mdb.m_synth, rid);
+
+ return (0);
+}
+
+int
+mdb_ctf_add_struct(const char *name, mdb_ctf_id_t *rid)
+{
+ mdb_ctf_id_t tid;
+ ctf_id_t id;
+
+ if (mdb.m_synth == NULL) {
+ mdb_printf("synthetic types disabled: ctf create failed\n");
+ return (DCMD_ERR);
+ }
+
+ if (name != NULL && mdb_ctf_lookup_by_name(name, &tid) == 0) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add type %s: a type "
+ "with that name already exists\n", name);
+ return (set_errno(EEXIST));
+ }
+
+ if ((id = ctf_add_struct(mdb.m_synth, CTF_ADD_ROOT, name)) ==
+ CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add struct: %s\n",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (ctf_update(mdb.m_synth) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (rid != NULL)
+ set_ctf_id(rid, mdb.m_synth, id);
+
+ return (0);
+}
+
+int
+mdb_ctf_add_union(const char *name, mdb_ctf_id_t *rid)
+{
+ mdb_ctf_id_t tid;
+ ctf_id_t id;
+
+ if (mdb.m_synth == NULL) {
+ mdb_printf("synthetic types disabled: ctf create failed\n");
+ return (DCMD_ERR);
+ }
+
+ if (name != NULL && mdb_ctf_lookup_by_name(name, &tid) == 0) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add type %s: a type "
+ "with that name already exists\n", name);
+ return (set_errno(EEXIST));
+ }
+
+ if ((id = ctf_add_union(mdb.m_synth, CTF_ADD_ROOT, name)) ==
+ CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add union: %s\n",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (ctf_update(mdb.m_synth) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (rid != NULL)
+ set_ctf_id(rid, mdb.m_synth, id);
+
+ return (0);
+}
+
+int
+mdb_ctf_add_member(const mdb_ctf_id_t *p, const char *name,
+ const mdb_ctf_id_t *mtype, mdb_ctf_id_t *rid)
+{
+ ctf_id_t id, mtid;
+ mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)p;
+ mdb_ctf_impl_t *mcim = (mdb_ctf_impl_t *)mtype;
+
+ if (mdb.m_synth == NULL) {
+ mdb_printf("synthetic types disabled: ctf create failed\n");
+ return (DCMD_ERR);
+ }
+
+ if (mcip->mci_fp != mdb.m_synth) {
+ mdb_dprintf(MDB_DBG_CTF, "requested to add member to a type "
+ "that wasn't created from a synthetic\n");
+ return (set_errno(EINVAL));
+ }
+
+ mtid = ctf_add_type(mdb.m_synth, mcim->mci_fp, mcim->mci_id);
+ if (mtid == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add member type: %s\n",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (ctf_update(mdb.m_synth) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ id = ctf_add_member(mdb.m_synth, mcip->mci_id, name, mtid);
+ if (id == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add member %s: %s\n",
+ name, ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (ctf_update(mdb.m_synth) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (rid != NULL)
+ set_ctf_id(rid, mdb.m_synth, id);
+
+ return (0);
+}
+
+int
+mdb_ctf_add_array(const mdb_ctf_arinfo_t *marp, mdb_ctf_id_t *rid)
+{
+ mdb_ctf_impl_t *mcip;
+ ctf_arinfo_t car;
+ ctf_id_t id;
+
+ if (mdb.m_synth == NULL) {
+ mdb_printf("synthetic types disabled: ctf create failed\n");
+ return (DCMD_ERR);
+ }
+
+ car.ctr_nelems = marp->mta_nelems;
+
+ mcip = (mdb_ctf_impl_t *)&marp->mta_contents;
+ id = ctf_add_type(mdb.m_synth, mcip->mci_fp, mcip->mci_id);
+ if (id == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add member type: %s\n",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+ car.ctr_contents = id;
+
+ mcip = (mdb_ctf_impl_t *)&marp->mta_index;
+ id = ctf_add_type(mdb.m_synth, mcip->mci_fp, mcip->mci_id);
+ if (id == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add member type: %s\n",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+ car.ctr_index = id;
+
+ if (ctf_update(mdb.m_synth) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ id = ctf_add_array(mdb.m_synth, CTF_ADD_ROOT, &car);
+ if (id == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (ctf_update(mdb.m_synth) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (rid != NULL)
+ set_ctf_id(rid, mdb.m_synth, id);
+
+ return (0);
+}
+
+int
+mdb_ctf_add_pointer(const mdb_ctf_id_t *p, mdb_ctf_id_t *rid)
+{
+ ctf_id_t id;
+ mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)p;
+
+ if (mdb.m_synth == NULL) {
+ mdb_printf("synthetic types disabled: ctf create failed\n");
+ return (DCMD_ERR);
+ }
+
+ id = ctf_add_type(mdb.m_synth, mcip->mci_fp, mcip->mci_id);
+ if (id == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add pointer type: %s\n",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (ctf_update(mdb.m_synth) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+
+ id = ctf_add_pointer(mdb.m_synth, CTF_ADD_ROOT, id);
+ if (id == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add pointer: %s\n",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (ctf_update(mdb.m_synth) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+
+ if (rid != NULL)
+ set_ctf_id(rid, mdb.m_synth, id);
+
+ return (0);
+}
+
+int
+mdb_ctf_type_delete(const mdb_ctf_id_t *id)
+{
+ int ret;
+
+ mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)id;
+
+ if (mcip->mci_fp != mdb.m_synth) {
+ mdb_warn("bad ctf_file_t, expected synth container\n");
+ return (DCMD_ERR);
+ }
+
+ ret = ctf_delete_type(mcip->mci_fp, mcip->mci_id);
+ if (ret != 0) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to delete synthetic type: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ if (ctf_update(mdb.m_synth) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
+ ctf_errmsg(ctf_errno(mdb.m_synth)));
+ return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
+ }
+
+ return (0);
+}
+
+static int
+mdb_ctf_synthetics_file_cb(mdb_ctf_id_t id, void *arg)
+{
+ ctf_file_t *targ = arg;
+ mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)&id;
+
+ if (ctf_add_type(targ, mcip->mci_fp, mcip->mci_id) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to add type %d: %s\n",
+ mcip->mci_id, ctf_errmsg(ctf_errno(mcip->mci_fp)));
+ return (set_errno(ctf_to_errno(ctf_errno(mcip->mci_fp))));
+ }
+
+ return (0);
+}
+
+int
+mdb_ctf_synthetics_from_file(const char *file)
+{
+ ctf_file_t *fp, *syn = mdb.m_synth;
+ int ret;
+ type_iter_t ti;
+
+ if (syn == NULL) {
+ mdb_warn("synthetic types disabled: ctf create failed\n");
+ return (DCMD_ERR);
+ }
+
+ if ((fp = mdb_ctf_open(file, &ret)) == NULL) {
+ mdb_warn("failed to parse ctf data in %s", file);
+ return (DCMD_ERR);
+ }
+
+ ret = DCMD_OK;
+ ti.ti_fp = fp;
+ ti.ti_arg = syn;
+ ti.ti_cb = mdb_ctf_synthetics_file_cb;
+ if (ctf_type_iter(fp, type_iter_cb, &ti) == CTF_ERR) {
+ ret = set_errno(ctf_to_errno(ctf_errno(fp)));
+ mdb_warn("failed to add types");
+ goto cleanup;
+ }
+
+ if (ctf_update(syn) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types\n");
+ ret = set_errno(ctf_to_errno(ctf_errno(fp)));
+ }
+
+cleanup:
+ ctf_close(fp);
+ if (ret != DCMD_OK)
+ (void) ctf_discard(syn);
+ return (ret);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf.h b/usr/src/cmd/mdb/common/mdb/mdb_ctf.h
index 05b56a381a..2396145299 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_ctf.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.h
@@ -24,6 +24,7 @@
*/
/*
* Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
*/
#ifndef _MDB_CTF_H
@@ -101,6 +102,29 @@ extern const char *mdb_ctf_enum_name(mdb_ctf_id_t, int);
extern int mdb_ctf_member_iter(mdb_ctf_id_t, mdb_ctf_member_f *, void *);
extern int mdb_ctf_enum_iter(mdb_ctf_id_t, mdb_ctf_enum_f *, void *);
extern int mdb_ctf_type_iter(const char *, mdb_ctf_type_f *, void *);
+extern int mdb_ctf_type_delete(const mdb_ctf_id_t *);
+
+/*
+ * Special values for mdb_ctf_type_iter.
+ */
+#define MDB_CTF_SYNTHETIC_ITER (const char *)(-1L)
+
+#define SYNTHETIC_ILP32 1
+#define SYNTHETIC_LP64 2
+extern int mdb_ctf_synthetics_create_base(int);
+extern int mdb_ctf_synthetics_reset(void);
+
+/*
+ * Synthetic creation routines
+ */
+extern int mdb_ctf_add_typedef(const char *, const mdb_ctf_id_t *,
+ mdb_ctf_id_t *);
+extern int mdb_ctf_add_struct(const char *, mdb_ctf_id_t *);
+extern int mdb_ctf_add_union(const char *, mdb_ctf_id_t *);
+extern int mdb_ctf_add_member(const mdb_ctf_id_t *, const char *,
+ const mdb_ctf_id_t *, mdb_ctf_id_t *);
+extern int mdb_ctf_add_array(const mdb_ctf_arinfo_t *, mdb_ctf_id_t *);
+extern int mdb_ctf_add_pointer(const mdb_ctf_id_t *, mdb_ctf_id_t *);
/* utility stuff */
extern ctf_id_t mdb_ctf_type_id(mdb_ctf_id_t);
@@ -128,6 +152,9 @@ extern ctf_file_t *mdb_ctf_open(const char *, int *); /* Internal */
extern ctf_file_t *mdb_ctf_bufopen(const void *, size_t, /* Internal */
const void *, Shdr *, const void *, Shdr *, int *);
extern void mdb_ctf_close(ctf_file_t *fp); /* Internal */
+extern int mdb_ctf_synthetics_init(void); /* Internal */
+extern void mdb_ctf_synthetics_fini(void); /* Internal */
+extern int mdb_ctf_synthetics_from_file(const char *); /* Internal */
#endif
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_help.c b/usr/src/cmd/mdb/common/mdb/mdb_help.c
index 2fe3ad56a1..95d3ec6af3 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_help.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_help.c
@@ -22,6 +22,7 @@
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#include <mdb/mdb_modapi.h>
@@ -252,6 +253,23 @@ cmd_help(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_OK);
}
+int
+cmd_help_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ if (argc == 0 && !(flags & DCMD_TAB_SPACE))
+ return (0);
+
+ if (argc > 1)
+ return (0);
+
+ if (argc == 0)
+ return (mdb_tab_complete_dcmd(mcp, NULL));
+ else
+ return (mdb_tab_complete_dcmd(mcp, argv[0].a_un.a_str));
+}
+
+
static int
print_dcmd_def(mdb_var_t *v, void *private)
{
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_help.h b/usr/src/cmd/mdb/common/mdb/mdb_help.h
index 3c086e4065..92516a43dd 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_help.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_help.h
@@ -22,12 +22,13 @@
/*
* Copyright (c) 1998-1999 by Sun Microsystems, Inc.
* All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#ifndef _MDB_HELP_H
#define _MDB_HELP_H
-#pragma ident "%Z%%M% %I% %E% SMI"
+#include <mdb/mdb_tab.h>
#ifdef __cplusplus
extern "C" {
@@ -35,11 +36,13 @@ extern "C" {
#ifdef _MDB
+
extern int cmd_dmods(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int cmd_dcmds(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int cmd_walkers(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int cmd_formats(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int cmd_help(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_help_tab(mdb_tab_cookie_t *, uint_t, int, const mdb_arg_t *);
extern int cmd_which(uintptr_t, uint_t, int, const mdb_arg_t *);
#endif /* _MDB */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c b/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c
index d3cac91c5c..48b6e1ec93 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_kb_kvm.c
@@ -23,7 +23,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
/*
* The default KVM backend, which simply calls directly into libkvm for all
@@ -39,9 +41,10 @@
/*ARGSUSED*/
static mdb_io_t *
-libkvm_sym_io(void *kvm, const char *symfile)
+libkvm_sym_io(void *kvm, const char *ignored)
{
mdb_io_t *io;
+ const char *symfile = kvm_namelist(kvm);
if ((io = mdb_fdio_create_path(NULL, symfile, O_RDONLY, 0)) == NULL)
mdb_warn("failed to open %s", symfile);
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
index 4b368e357b..8e96c04350 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
@@ -23,6 +23,10 @@
*/
/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
+
+/*
* Libkvm Kernel Target
*
* The libkvm kernel target provides access to both crash dumps and live
@@ -1571,10 +1575,23 @@ err:
}
int
+mdb_kvm_is_dump(mdb_io_t *io)
+{
+ dumphdr_t h;
+
+ (void) IOP_SEEK(io, (off64_t)0L, SEEK_SET);
+
+ return (IOP_READ(io, &h, sizeof (dumphdr_t)) == sizeof (dumphdr_t) &&
+ h.dump_magic == DUMP_MAGIC);
+}
+
+int
mdb_kvm_is_compressed_dump(mdb_io_t *io)
{
dumphdr_t h;
+ (void) IOP_SEEK(io, (off64_t)0L, SEEK_SET);
+
return (IOP_READ(io, &h, sizeof (dumphdr_t)) == sizeof (dumphdr_t) &&
h.dump_magic == DUMP_MAGIC &&
(h.dump_flags & DF_COMPRESSED) != 0);
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_main.c b/usr/src/cmd/mdb/common/mdb/mdb_main.c
index caccef986d..90e048bb82 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_main.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_main.c
@@ -24,6 +24,10 @@
* Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved.
*/
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
+
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/priocntl.h>
@@ -319,10 +323,12 @@ usage(int status)
{
mdb_iob_printf(mdb.m_err, "Usage: %s [-fkmuwyAFKMSUW] [+/-o option] "
"[-p pid] [-s dist] [-I path] [-L path]\n\t[-P prompt] "
- "[-R root] [-V dis-version] [object [core] | core | suffix]\n\n",
+ "[-R root] [-V dis-version] [-e expr] "
+ "[object [core] | core | suffix]\n\n",
mdb.m_pname);
mdb_iob_puts(mdb.m_err,
+ "\t-e evaluate expr and return status\n"
"\t-f force raw file debugging mode\n"
"\t-k force kernel debugging mode\n"
"\t-m disable demand-loading of module symbols\n"
@@ -403,8 +409,9 @@ int
main(int argc, char *argv[], char *envp[])
{
extern int mdb_kvm_is_compressed_dump(mdb_io_t *);
+ extern int mdb_kvm_is_dump(mdb_io_t *);
mdb_tgt_ctor_f *tgt_ctor = NULL;
- const char **tgt_argv = alloca(argc * sizeof (char *));
+ const char **tgt_argv = alloca((argc + 2) * sizeof (char *));
int tgt_argc = 0;
mdb_tgt_t *tgt;
@@ -415,6 +422,7 @@ main(int argc, char *argv[], char *envp[])
char *p;
const char *Iflag = NULL, *Lflag = NULL, *Vflag = NULL, *pidarg = NULL;
+ const char *eflag = NULL;
int fflag = 0, Kflag = 0, Rflag = 0, Sflag = 0, Oflag = 0, Uflag = 0;
int ttylike;
@@ -503,8 +511,15 @@ main(int argc, char *argv[], char *envp[])
while (optind < argc) {
while ((c = getopt(argc, argv,
- "fkmo:p:s:uwyACD:FI:KL:MOP:R:SUV:W")) != (int)EOF) {
+ "e:fkmo:p:s:uwyACD:FI:KL:MOP:R:SUV:W")) != (int)EOF) {
switch (c) {
+ case 'e':
+ if (eflag != NULL) {
+ warn("-e already specified\n");
+ terminate(2);
+ }
+ eflag = optarg;
+ break;
case 'f':
fflag++;
tgt_ctor = mdb_rawfile_tgt_create;
@@ -686,6 +701,12 @@ main(int argc, char *argv[], char *envp[])
/*NOTREACHED*/
}
+ if (eflag != NULL) {
+ IOP_CLOSE(in_io);
+ in_io = mdb_strio_create(eflag);
+ mdb.m_lastret = 0;
+ }
+
/*
* If standard input appears to have tty attributes, attempt to
* initialize a terminal i/o backend on top of stdin and stdout.
@@ -818,8 +839,8 @@ main(int argc, char *argv[], char *envp[])
size_t len = strlen(tgt_argv[0]) + 8;
const char *object = tgt_argv[0];
- tgt_argv[0] = mdb_alloc(len, UM_SLEEP);
- tgt_argv[1] = mdb_alloc(len, UM_SLEEP);
+ tgt_argv[0] = alloca(len);
+ tgt_argv[1] = alloca(len);
(void) strcpy((char *)tgt_argv[0], "unix.");
(void) strcat((char *)tgt_argv[0], object);
@@ -827,6 +848,14 @@ main(int argc, char *argv[], char *envp[])
(void) strcat((char *)tgt_argv[1], object);
if (access(tgt_argv[0], F_OK) == -1 &&
+ access(tgt_argv[1], F_OK) != -1) {
+ /*
+ * If we have a vmcore but not a unix file,
+ * set the symbol table to be the vmcore to
+ * force libkvm to extract it out of the dump.
+ */
+ tgt_argv[0] = tgt_argv[1];
+ } else if (access(tgt_argv[0], F_OK) == -1 &&
access(tgt_argv[1], F_OK) == -1) {
(void) strcpy((char *)tgt_argv[1], "vmdump.");
(void) strcat((char *)tgt_argv[1], object);
@@ -850,17 +879,25 @@ main(int argc, char *argv[], char *envp[])
O_RDONLY, 0)) == NULL)
die("failed to open %s", tgt_argv[0]);
- /*
- * Check for a single vmdump.N compressed dump file,
- * and give a helpful message.
- */
if (tgt_argc == 1) {
if (mdb_kvm_is_compressed_dump(io)) {
+ /*
+ * We have a single vmdump.N compressed dump
+ * file; give a helpful message.
+ */
mdb_iob_printf(mdb.m_err,
"cannot open compressed dump; "
"decompress using savecore -f %s\n",
tgt_argv[0]);
terminate(0);
+ } else if (mdb_kvm_is_dump(io)) {
+ /*
+ * We have an uncompressed dump as our only
+ * argument; specify the dump as the symbol
+ * table to force libkvm to dig it out of the
+ * dump.
+ */
+ tgt_argv[tgt_argc++] = tgt_argv[0];
}
}
@@ -1058,7 +1095,8 @@ tcreate:
continue;
}
- terminate((status == MDB_ERR_QUIT || status == 0) ? 0 : 1);
+ terminate((status == MDB_ERR_QUIT || status == 0) ?
+ (eflag != NULL && mdb.m_lastret != 0 ? 1 : 0) : 1);
/*NOTREACHED*/
return (0);
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_modapi.c b/usr/src/cmd/mdb/common/mdb/mdb_modapi.c
index df954e2a6d..a675398302 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.c
@@ -822,6 +822,55 @@ mdb_object_iter(mdb_object_cb_t cb, void *data)
}
/*
+ * Private callback structure for implementing mdb_symbol_iter, below.
+ */
+typedef struct {
+ mdb_symbol_cb_t si_cb;
+ void *si_arg;
+ int si_rval;
+} symbol_iter_arg_t;
+
+/*ARGSUSED*/
+static int
+mdb_symbol_cb(void *data, const GElf_Sym *gsym, const char *name,
+ const mdb_syminfo_t *sip, const char *obj)
+{
+ symbol_iter_arg_t *arg = data;
+ mdb_symbol_t sym;
+
+ if (arg->si_rval != 0)
+ return (0);
+
+ bzero(&sym, sizeof (sym));
+ sym.sym_name = name;
+ sym.sym_object = obj;
+ sym.sym_sym = gsym;
+ sym.sym_table = sip->sym_table;
+ sym.sym_id = sip->sym_id;
+
+ arg->si_rval = arg->si_cb(&sym, arg->si_arg);
+
+ return (0);
+}
+
+int
+mdb_symbol_iter(const char *obj, uint_t which, uint_t type,
+ mdb_symbol_cb_t cb, void *data)
+{
+ symbol_iter_arg_t arg;
+
+ arg.si_cb = cb;
+ arg.si_arg = data;
+ arg.si_rval = 0;
+
+ if (mdb_tgt_symbol_iter(mdb.m_target, obj, which, type,
+ mdb_symbol_cb, &arg) != 0)
+ return (-1);
+
+ return (arg.si_rval);
+}
+
+/*
* Private structure and function for implementing mdb_dumpptr on top
* of mdb_dump_internal
*/
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h
index 20fa34fe13..bb880f9b11 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h
@@ -22,7 +22,7 @@
/*
* 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.
+ * Copyright (c) 2013 Joyent, Inc. All rights reserved.
*/
#ifndef _MDB_MODAPI_H
@@ -71,7 +71,13 @@ extern "C" {
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif
+#ifdef MDB_API_VERSION
+#if (MDB_API_VERSION != 3 && MDB_API_VERSION != 4)
+#error "Only modapi versions three and four are supported."
+#endif
+#else /* !MDB_API_VERISON */
#define MDB_API_VERSION 4 /* Current API version number */
+#endif /* MDB_API_VERISON */
/*
* Debugger command function flags:
@@ -85,11 +91,6 @@ 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 */
@@ -118,10 +119,18 @@ typedef struct mdb_arg {
} a_un;
} mdb_arg_t;
+#if (MDB_API_VERSION >= 4)
+/*
+ * Debugger tab command function flags
+ */
+#define DCMD_TAB_SPACE 0x01 /* Tab cb invoked with trailing space */
+
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 *);
+#endif /* MDB_API_VERSION >= 4 */
+
+typedef int mdb_dcmd_f(uintptr_t, uint_t, int, const mdb_arg_t *);
typedef struct mdb_dcmd {
const char *dc_name; /* Command name */
@@ -129,7 +138,9 @@ 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) */
+#if (MDB_API_VERSION >= 4)
mdb_dcmd_tab_f *dc_tabp; /* Tab completion function */
+#endif
} mdb_dcmd_t;
#define WALK_ERR -1 /* Walk fatal error (terminate walk) */
@@ -180,6 +191,14 @@ typedef struct mdb_object {
uintptr_t obj_size; /* in memory size of object in bytes */
} mdb_object_t;
+typedef struct mdb_symbol {
+ const char *sym_name; /* name of symbol */
+ const char *sym_object; /* name of containing object */
+ const GElf_Sym *sym_sym; /* ELF symbol information */
+ uint_t sym_table; /* symbol table id */
+ uint_t sym_id; /* symbol identifier */
+} mdb_symbol_t;
+
extern int mdb_pwalk(const char *, mdb_walk_cb_t, void *, uintptr_t);
extern int mdb_walk(const char *, mdb_walk_cb_t, void *);
@@ -296,6 +315,28 @@ extern ssize_t mdb_get_xdata(const char *, void *, size_t);
typedef int (*mdb_object_cb_t)(mdb_object_t *, void *);
extern int mdb_object_iter(mdb_object_cb_t, void *);
+#define MDB_SYMTAB 1 /* Normal symbol table (.symtab) */
+#define MDB_DYNSYM 2 /* Dynamic symbol table (.dynsym) */
+
+#define MDB_BIND_LOCAL 0x0001 /* Local (static-scope) symbols */
+#define MDB_BIND_GLOBAL 0x0002 /* Global symbols */
+#define MDB_BIND_WEAK 0x0004 /* Weak binding symbols */
+#define MDB_BIND_ANY 0x0007 /* Any of the above */
+
+#define MDB_TYPE_NOTYPE 0x0100 /* Symbol has no type */
+#define MDB_TYPE_OBJECT 0x0200 /* Symbol refers to data */
+#define MDB_TYPE_FUNC 0x0400 /* Symbol refers to text */
+#define MDB_TYPE_SECT 0x0800 /* Symbol refers to a section */
+#define MDB_TYPE_FILE 0x1000 /* Symbol refers to a source file */
+#define MDB_TYPE_COMMON 0x2000 /* Symbol refers to a common block */
+#define MDB_TYPE_TLS 0x4000 /* Symbol refers to TLS */
+
+#define MDB_TYPE_ANY 0x7f00 /* Any of the above */
+
+typedef int (*mdb_symbol_cb_t)(mdb_symbol_t *, void *);
+extern int mdb_symbol_iter(const char *, uint_t, uint_t, mdb_symbol_cb_t,
+ void *);
+
#define MDB_STATE_IDLE 0 /* Target is idle (not running yet) */
#define MDB_STATE_RUNNING 1 /* Target is currently executing */
#define MDB_STATE_STOPPED 2 /* Target is stopped */
@@ -313,6 +354,7 @@ typedef void (*mdb_callback_f)(void *);
extern void *mdb_callback_add(int, mdb_callback_f, void *);
extern void mdb_callback_remove(void *);
+#if (MDB_API_VERSION >= 4)
#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 */
@@ -337,6 +379,7 @@ extern int mdb_tab_typename(int *, const mdb_arg_t **, char *buf, size_t len);
*/
extern int mdb_tab_complete_mt(mdb_tab_cookie_t *, uint_t, int,
const mdb_arg_t *);
+#endif /* MDB_API_VERSION >= 4 */
extern size_t strlcat(char *, const char *, size_t);
extern char *strcat(char *, const char *);
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_module.h b/usr/src/cmd/mdb/common/mdb/mdb_module.h
index 4c6a54e28b..04cb3d1cee 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_module.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_module.h
@@ -22,6 +22,8 @@
/*
* Copyright 2004 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.
*/
/*
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_print.c b/usr/src/cmd/mdb/common/mdb/mdb_print.c
index 910cc9ecfe..ee996be8cf 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_print.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_print.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.
*/
/*
@@ -2055,45 +2057,19 @@ 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,
+static int
+cmd_print_tab_common(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 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);
@@ -2113,10 +2089,10 @@ cmd_print_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
/*
* 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.
+ * along and walk the delimiters until we find something in 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);
@@ -2126,11 +2102,11 @@ cmd_print_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
delim = parse_delimiter(&start);
/*
- * If we hit the case where we actually have no delimiters, than we need
+ * If we hit the case where we actually have no delimiters, then 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);
+ (void) mdb_snprintf(member, sizeof (member), start);
while (delim != MEMBER_DELIM_DONE) {
switch (delim) {
@@ -2187,7 +2163,7 @@ cmd_print_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
/*
* 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
+ * are a 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
@@ -2217,6 +2193,42 @@ cmd_print_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
* already have in rid.
*/
return (mdb_tab_complete_member_by_id(mcp, rid, member));
+
+}
+
+int
+cmd_print_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ int i, dummy;
+
+ /*
+ * 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;
+
+ return (cmd_print_tab_common(mcp, flags, argc, argv));
}
/*
@@ -2511,8 +2523,7 @@ print_help(void)
}
static int
-printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt,
- boolean_t sign)
+printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt, int sign)
{
ssize_t size;
mdb_ctf_id_t base;
@@ -2530,7 +2541,7 @@ printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt,
} u;
if (mdb_ctf_type_resolve(id, &base) == -1) {
- mdb_warn("could not resolve type");
+ mdb_warn("could not resolve type\n");
return (DCMD_ABORT);
}
@@ -2540,7 +2551,7 @@ printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt,
}
if (mdb_ctf_type_encoding(base, &e) != 0) {
- mdb_warn("could not get type encoding");
+ mdb_warn("could not get type encoding\n");
return (DCMD_ABORT);
}
@@ -2742,7 +2753,6 @@ printf_string(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt)
if (size != 1) {
mdb_warn("string format specifier requires "
"an array of characters\n");
- return (DCMD_ABORT);
}
bzero(buf, sizeof (buf));
@@ -2810,6 +2820,51 @@ enum {
};
int
+cmd_printf_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ int ii;
+ char *f;
+
+ /*
+ * If argc doesn't have more than what should be the format string,
+ * ignore it.
+ */
+ if (argc <= 1)
+ return (0);
+
+ /*
+ * Because we aren't leveraging the lex and yacc engine, we have to
+ * manually walk the arguments to find both the first and last
+ * open/close quote of the format string.
+ */
+ f = strchr(argv[0].a_un.a_str, '"');
+ if (f == NULL)
+ return (0);
+
+ f = strchr(f + 1, '"');
+ if (f != NULL) {
+ ii = 0;
+ } else {
+ for (ii = 1; ii < argc; ii++) {
+ f = strchr(argv[ii].a_un.a_str, '"');
+ if (f != NULL)
+ break;
+ }
+ /* Never found */
+ if (ii == argc)
+ return (0);
+ }
+
+ ii++;
+ argc -= ii;
+ argv += ii;
+
+
+ return (cmd_print_tab_common(mcp, flags, argc, argv));
+}
+
+int
cmd_printf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
char type[MDB_SYM_NAMLEN];
@@ -2830,7 +2885,7 @@ cmd_printf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
if (argc == 0 || argv[0].a_type != MDB_TYPE_STRING) {
mdb_warn("expected a format string\n");
- return (DCMD_USAGE);
+ return (DCMD_ABORT);
}
/*
@@ -2839,12 +2894,6 @@ cmd_printf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
* subset of mdb_printf() format strings that we allow.
*/
fmt = argv[0].a_un.a_str;
- /*
- * 'dest' must be large enough to hold a copy of the format string,
- * plus a NUL and up to 2 additional characters for each conversion
- * in the format string. This gives us a bloat factor of 5/2 ~= 3.
- * e.g. "%d" (strlen of 2) --> "%lld\0" (need 5 bytes)
- */
dest = mdb_zalloc(strlen(fmt) * 3, UM_SLEEP | UM_GC);
fmts = mdb_zalloc(strlen(fmt) * sizeof (char *), UM_SLEEP | UM_GC);
funcs = mdb_zalloc(strlen(fmt) * sizeof (void *), UM_SLEEP | UM_GC);
@@ -3027,22 +3076,22 @@ static char _mdb_printf_help[] =
"\n"
" %% Prints the '%' symbol.\n"
" %a Prints the member in symbolic form.\n"
-" %d Prints the member as a decimal integer. If the member is a signed\n"
+" %d Prints the member as a decimal integer. If the member is a signed\n"
" integer type, the output will be signed.\n"
" %H Prints the member as a human-readable size.\n"
" %I Prints the member as an IPv4 address (must be 32-bit integer type).\n"
" %N Prints the member as an IPv6 address (must be of type in6_addr_t).\n"
" %o Prints the member as an unsigned octal integer.\n"
" %p Prints the member as a pointer, in hexadecimal.\n"
-" %q Prints the member in signed octal. Honk if you ever use this!\n"
-" %r Prints the member as an unsigned value in the current output radix.\n"
-" %R Prints the member as a signed value in the current output radix.\n"
+" %q Prints the member in signed octal. Honk if you ever use this!\n"
+" %r Prints the member as an unsigned value in the current output radix. \n"
+" %R Prints the member as a signed value in the current output radix. \n"
" %s Prints the member as a string (requires a pointer or an array of\n"
" characters).\n"
" %u Prints the member as an unsigned decimal integer.\n"
" %x Prints the member in hexadecimal.\n"
" %X Prints the member in hexadecimal, using the characters A-F as the\n"
-" digits for the values 10-15.\n"
+" digits for the values 10-15. \n"
" %Y Prints the member as a time_t as the string "
"'year month day HH:MM:SS'.\n"
"\n"
@@ -3055,13 +3104,13 @@ static char _mdb_printf_help[] =
"\n"
"The following flag specifers are recognized by ::printf:\n"
"\n"
-" %- Left-justify the output within the specified field width. If the\n"
+" %- Left-justify the output within the specified field width. If the\n"
" width of the output is less than the specified field width, the\n"
-" output will be padded with blanks on the right-hand side. Without\n"
+" output will be padded with blanks on the right-hand side. Without\n"
" %-, values are right-justified by default.\n"
"\n"
" %0 Zero-fill the output field if the output is right-justified and the\n"
-" width of the output is less than the specified field width. Without\n"
+" width of the output is less than the specified field width. Without\n"
" %0, right-justified values are prepended with blanks in order to\n"
" fill the field.\n"
"\n"
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_print.h b/usr/src/cmd/mdb/common/mdb/mdb_print.h
index 41e6c07dc4..7449e8e71a 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_print.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_print.h
@@ -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.
*/
/*
@@ -50,6 +52,7 @@ 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);
extern int cmd_printf(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_printf_tab(mdb_tab_cookie_t *, uint_t, int, const mdb_arg_t *);
extern void printf_help(void);
#endif /* _MDB */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_proc.c b/usr/src/cmd/mdb/common/mdb/mdb_proc.c
index 7c419d8fcd..e3d9833a6b 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_proc.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_proc.c
@@ -23,6 +23,9 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
/*
* User Process Target
@@ -122,6 +125,13 @@ static int tlsbase(mdb_tgt_t *, mdb_tgt_tid_t, Lmid_t, const char *,
psaddr_t *);
/*
+ * When debugging postmortem, we don't resolve names as we may very well not
+ * be on a system on which those names resolve.
+ */
+#define PT_LIBPROC_RESOLVE(P) \
+ (!(mdb.m_flags & MDB_FL_LMRAW) && Pstate(P) != PS_DEAD)
+
+/*
* The Perror_printf() function interposes on the default, empty libproc
* definition. It will be called to report additional information on complex
* errors, such as a corrupt core file. We just pass the args to vwarn.
@@ -2163,11 +2173,20 @@ static const mdb_walker_t pt_walkers[] = {
{ NULL }
};
+static int
+pt_agent_check(boolean_t *agent, const lwpstatus_t *psp)
+{
+ if (psp->pr_flags & PR_AGENT)
+ *agent = B_TRUE;
+
+ return (0);
+}
static void
pt_activate_common(mdb_tgt_t *t)
{
pt_data_t *pt = t->t_data;
+ boolean_t hasagent = B_FALSE;
GElf_Sym sym;
/*
@@ -2181,13 +2200,23 @@ pt_activate_common(mdb_tgt_t *t)
"library information will not be available\n");
}
- /*
- * If we have a libproc handle and libthread is loaded, attempt to load
- * and initialize the corresponding libthread_db. If this fails, fall
- * back to our native LWP implementation and issue a warning.
- */
- if (t->t_pshandle != NULL && Pstate(t->t_pshandle) != PS_IDLE)
+ if (t->t_pshandle != NULL) {
+ (void) Plwp_iter(t->t_pshandle,
+ (proc_lwp_f *)pt_agent_check, &hasagent);
+ }
+
+ if (hasagent) {
+ mdb_warn("agent lwp detected; forcing "
+ "lwp thread model (use ::tmodel to change)\n");
+ } else if (t->t_pshandle != NULL && Pstate(t->t_pshandle) != PS_IDLE) {
+ /*
+ * If we have a libproc handle and we do not have an agent LWP,
+ * look for the correct thread debugging library. (If we have
+ * an agent LWP, we leave the model as the raw LWP model to
+ * allow the agent LWP to be visible to the debugger.)
+ */
(void) Pobject_iter(t->t_pshandle, (proc_map_f *)thr_check, t);
+ }
/*
* If there's a global object named '_mdb_abort_info', assuming we're
@@ -2805,7 +2834,7 @@ pt_lookup_by_addr(mdb_tgt_t *t, uintptr_t addr, uint_t flags,
* Once we get the closest symbol, we perform the EXACT match or
* smart-mode or absolute distance check ourself:
*/
- if ((mdb.m_flags & MDB_FL_LMRAW) == 0) {
+ if (PT_LIBPROC_RESOLVE(P)) {
rv = Pxlookup_by_addr_resolved(P, addr, buf, nbytes,
symp, &si);
} else {
@@ -2850,7 +2879,7 @@ found:
const char *prefix = pmp->pr_mapname;
Lmid_t lmid;
- if ((mdb.m_flags & MDB_FL_LMRAW) == 0) {
+ if (PT_LIBPROC_RESOLVE(P)) {
if (Pobjname_resolved(P, addr, pt->p_objname,
MDB_TGT_MAPSZ))
prefix = pt->p_objname;
@@ -2952,7 +2981,7 @@ pt_symbol_iter(mdb_tgt_t *t, const char *object, uint_t which,
which, type, pt_symbol_iter_cb, &ps);
return (0);
} else if (Prd_agent(t->t_pshandle) != NULL) {
- if ((mdb.m_flags & MDB_FL_LMRAW) == 0) {
+ if (PT_LIBPROC_RESOLVE(t->t_pshandle)) {
(void) Pobject_iter_resolved(t->t_pshandle,
pt_objsym_iter, &ps);
} else {
@@ -2991,7 +3020,7 @@ pt_prmap_to_mdbmap(mdb_tgt_t *t, const prmap_t *prp, mdb_map_t *mp)
char *rv, name[MAXPATHLEN];
Lmid_t lmid;
- if ((mdb.m_flags & MDB_FL_LMRAW) == 0) {
+ if (PT_LIBPROC_RESOLVE(P)) {
rv = Pobjname_resolved(P, prp->pr_vaddr, name, sizeof (name));
} else {
rv = Pobjname(P, prp->pr_vaddr, name, sizeof (name));
@@ -3057,7 +3086,7 @@ pt_mapping_iter(mdb_tgt_t *t, mdb_tgt_map_f *func, void *private)
pm.pmap_func = func;
pm.pmap_private = private;
- if ((mdb.m_flags & MDB_FL_LMRAW) == 0) {
+ if (PT_LIBPROC_RESOLVE(t->t_pshandle)) {
(void) Pmapping_iter_resolved(t->t_pshandle,
pt_map_apply, &pm);
} else {
@@ -3086,7 +3115,7 @@ pt_object_iter(mdb_tgt_t *t, mdb_tgt_map_f *func, void *private)
pm.pmap_func = func;
pm.pmap_private = private;
- if ((mdb.m_flags & MDB_FL_LMRAW) == 0) {
+ if (PT_LIBPROC_RESOLVE(t->t_pshandle)) {
(void) Pobject_iter_resolved(t->t_pshandle,
pt_map_apply, &pm);
} else {
@@ -4495,6 +4524,14 @@ pt_getareg(mdb_tgt_t *t, mdb_tgt_tid_t tid,
*/
if (PTL_GETREGS(t, tid, grs) == 0) {
*rp = r | (ulong_t)grs[rd_num];
+ if (rd_flags & MDB_TGT_R_32)
+ *rp &= 0xffffffffULL;
+ else if (rd_flags & MDB_TGT_R_16)
+ *rp &= 0xffffULL;
+ else if (rd_flags & MDB_TGT_R_8H)
+ *rp = (*rp & 0xff00ULL) >> 8;
+ else if (rd_flags & MDB_TGT_R_8L)
+ *rp &= 0xffULL;
return (0);
}
return (-1);
@@ -4521,6 +4558,16 @@ pt_putareg(mdb_tgt_t *t, mdb_tgt_tid_t tid, const char *rname, mdb_tgt_reg_t r)
ushort_t rd_flags = MDB_TGT_R_FLAGS(rd_nval);
if (!MDB_TGT_R_IS_FP(rd_flags)) {
+
+ if (rd_flags & MDB_TGT_R_32)
+ r &= 0xffffffffULL;
+ else if (rd_flags & MDB_TGT_R_16)
+ r &= 0xffffULL;
+ else if (rd_flags & MDB_TGT_R_8H)
+ r = (r & 0xffULL) << 8;
+ else if (rd_flags & MDB_TGT_R_8L)
+ r &= 0xffULL;
+
#if defined(__sparc) && defined(_ILP32)
/*
* If we are debugging on 32-bit SPARC, the globals and
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_tab.c b/usr/src/cmd/mdb/common/mdb/mdb_tab.c
index b06b5db680..af5dec943d 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_tab.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_tab.c
@@ -342,7 +342,8 @@ tab_complete_dcmd(mdb_var_t *v, void *arg)
int
mdb_tab_complete_dcmd(mdb_tab_cookie_t *mcp, const char *dcmd)
{
- mdb_tab_setmbase(mcp, dcmd);
+ if (dcmd != NULL)
+ mdb_tab_setmbase(mcp, dcmd);
mdb_nv_sort_iter(&mdb.m_dcmds, tab_complete_dcmd, mcp,
UM_GC | UM_SLEEP);
return (0);
@@ -386,11 +387,6 @@ 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)
{
@@ -528,31 +524,18 @@ tab_complete_type(mdb_ctf_id_t id, void *arg)
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 &&
+ if (flags & MDB_TABC_MEMBERS && rkind != CTF_K_STRUCT &&
rkind != CTF_K_UNION)
return (0);
- if ((flags & MDB_TABC_NOPOINT) && rkind == CTF_K_POINTER)
+ if (flags & MDB_TABC_NOPOINT && rkind == CTF_K_POINTER)
return (0);
- if ((flags & MDB_TABC_NOARRAY) && rkind == CTF_K_ARRAY)
+ if (flags & MDB_TABC_NOARRAY && rkind == CTF_K_ARRAY)
return (0);
(void) mdb_ctf_type_name(id, buf, sizeof (buf));
@@ -579,6 +562,8 @@ mdb_tab_complete_type(mdb_tab_cookie_t *mcp, const char *name, uint_t flags)
mdb_tab_setmbase(mcp, name);
(void) mdb_tgt_object_iter(t, mdb_tab_complete_module, mcp);
+ (void) mdb_ctf_type_iter(MDB_CTF_SYNTHETIC_ITER, tab_complete_type,
+ mcp);
return (0);
}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_target_impl.h b/usr/src/cmd/mdb/common/mdb/mdb_target_impl.h
index ecc5331a1e..7f78bc4879 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_target_impl.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_target_impl.h
@@ -22,12 +22,13 @@
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ */
#ifndef _MDB_TARGET_IMPL_H
#define _MDB_TARGET_IMPL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <mdb/mdb_target.h>
#include <mdb/mdb_module.h>
#include <mdb/mdb_list.h>
@@ -250,6 +251,10 @@ extern long mdb_tgt_nop(); /* Return 0 for success */
#define MDB_TGT_R_FPQ 0x040 /* Quad-precision floating-point */
#define MDB_TGT_R_FPU 0x080 /* FPU control/status register */
#define MDB_TGT_R_RDONLY 0x100 /* Register is read-only */
+#define MDB_TGT_R_32 0x200 /* 32-bit version of register */
+#define MDB_TGT_R_16 0x400 /* 16-bit version of register */
+#define MDB_TGT_R_8H 0x800 /* upper half of a 16-bit reg */
+#define MDB_TGT_R_8L 0x1000 /* lower half of a 16-bit reg */
#define MDB_TGT_R_IS_FP(f) ((f) & 0xf0) /* Test MDB_TGT_R_FP* bits */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_termio.c b/usr/src/cmd/mdb/common/mdb/mdb_termio.c
index 5e36cda7ad..9f9571d925 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_termio.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_termio.c
@@ -22,6 +22,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.
*/
/*
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_typedef.c b/usr/src/cmd/mdb/common/mdb/mdb_typedef.c
new file mode 100644
index 0000000000..e097cf67d5
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_typedef.c
@@ -0,0 +1,738 @@
+/*
+ * 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) 2013 Joyent, Inc. All rights reserved.
+ */
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_ctf.h>
+#include <mdb/mdb_list.h>
+#include <mdb/mdb_nv.h>
+
+struct parse_node;
+
+#define PN_F_POINTER 0x01
+#define PN_F_ARRAY 0x02
+
+typedef struct parse_node {
+ mdb_list_t pn_list; /* list entry, must be first */
+ char *pn_type; /* name of base type */
+ char *pn_name; /* name of the member */
+ int pn_flags; /* flags */
+ int pn_nptrs; /* number of pointers */
+ int pn_asub; /* value of array subscript */
+} parse_node_t;
+
+typedef struct parse_root {
+ mdb_list_t pr_nodes; /* list of members */
+ int pr_kind; /* CTF_K_* */
+ const char *pr_name; /* entity name */
+ const char *pr_tname; /* entity typedef */
+} parse_root_t;
+
+static int
+typedef_valid_identifier(const char *str)
+{
+ /*
+ * We can't use the standard ctype.h functions because those aren't
+ * necessairly available in kmdb. On the flip side, we only care about
+ * ascii characters here so that isn't too bad.
+ *
+ * C Identifiers have to start with a letter or a _. Afterwards they can
+ * be alphanumeric or an _.
+ */
+
+ if (*str == '\0')
+ return (1);
+
+ if (*str != '_' &&
+ (*str < 0x41 || *str > 0x5a) &&
+ (*str < 0x61 || *str > 0x7a))
+ return (1);
+ str++;
+
+ while (*str != '\0') {
+ if (*str != '_' &&
+ (*str < 0x30 || *str > 0x39) &&
+ (*str < 0x41 || *str > 0x5a) &&
+ (*str < 0x61 || *str > 0x7a))
+ return (1);
+ str++;
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+typedef_list_cb(mdb_ctf_id_t id, void *arg)
+{
+ char buf[MDB_SYM_NAMLEN];
+
+ (void) mdb_ctf_type_name(id, buf, sizeof (buf));
+ mdb_printf("%s\n", buf);
+ return (0);
+}
+
+static char *
+typedef_join_strings(int nstr, const mdb_arg_t *args, int flags)
+{
+ int i, size = 0;
+ char *ret, *sptr;
+
+ for (i = 0; i <= nstr; i++) {
+ /* Always account for the space or the null terminator */
+ size += strlen(args[i].a_un.a_str) + 1;
+ }
+ ret = mdb_alloc(sizeof (char) * size, flags);
+ if (ret == NULL)
+ return (NULL);
+ sptr = ret;
+ for (i = 0; i <= nstr; i++) {
+ (void) strcpy(sptr, args[i].a_un.a_str);
+ sptr += strlen(args[i].a_un.a_str);
+ *sptr = ' ';
+ sptr++;
+ }
+ *sptr = '\0';
+
+ return (ret);
+}
+
+static int
+typedef_list(void)
+{
+
+ (void) mdb_ctf_type_iter(MDB_CTF_SYNTHETIC_ITER, typedef_list_cb,
+ NULL);
+ return (DCMD_OK);
+}
+
+static int
+typedef_destroy(void)
+{
+ if (mdb_ctf_synthetics_reset() != 0) {
+ mdb_warn("failed to reset synthetic types");
+ return (DCMD_ERR);
+ }
+ return (DCMD_OK);
+}
+
+/*
+ * We've been asked to create the basic types that exist. We accept the
+ * following strings to indicate what we should create.
+ * - LP32, ILP32 (case insensitive)
+ * - LP64
+ */
+static int
+typedef_create(const char *arg)
+{
+ int kind;
+
+ if (strcasecmp(arg, "LP32") == 0 || strcasecmp(arg, "ILP32") == 0) {
+ kind = SYNTHETIC_ILP32;
+ } else if (strcasecmp(arg, "LP64") == 0) {
+ kind = SYNTHETIC_LP64;
+ } else {
+ mdb_printf("invalid data model: %s\n", arg);
+ return (DCMD_USAGE);
+ }
+
+ if (mdb_ctf_synthetics_create_base(kind) != 0) {
+ mdb_printf("failed to create intrinsic types, maybe "
+ "they already exist\n");
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
+
+/*
+ * Search the current arguments for a complete member declaration. This function
+ * modifies the value of defn based on what's necessary for parsing. It returns
+ * the appropriate parse node in pnp.
+ */
+static int
+typedef_parse_member(char *defn, char **next, parse_node_t **pnp)
+{
+ char *c, *name, *array;
+ int nptrs = 0;
+ parse_node_t *pn;
+
+ c = strchr(defn, ';');
+ if (c == NULL) {
+ mdb_printf("Cannot find semi-colon to delineate the end "
+ "of a member.\n");
+ return (DCMD_ERR);
+ }
+ *c = '\0';
+ *next = c + 1;
+
+ c = strrchr(defn, ' ');
+ if (c == NULL) {
+ mdb_printf("Missing both a name and a type declaration for "
+ "a member. Instead, found '%s'\n", defn);
+ return (DCMD_ERR);
+ }
+ *c = '\0';
+ name = c + 1;
+ c--;
+ while (*c == '*' || *c == ' ') {
+ if (*c == '*')
+ nptrs++;
+ c--;
+ }
+ *(c + 1) = '\0';
+
+ pn = mdb_zalloc(sizeof (parse_node_t), UM_SLEEP | UM_GC);
+ pn->pn_type = defn;
+
+ /*
+ * Go through and prepare the name field. Note that we still have to
+ * check if this is a pointer or an array. We also need to strip the
+ * ending semi-colon.
+ */
+ while (*name == '*') {
+ name++;
+ nptrs++;
+ }
+
+ if ((c = strchr(name, '[')) != NULL) {
+ array = c;
+ if ((c = strchr(array, ']')) == NULL) {
+ mdb_printf("Found the beginning of an array size "
+ "but no closing ']' in %s\n", array);
+ return (DCMD_ERR);
+ }
+ *array = '\0';
+ array++;
+ *c = '\0';
+ pn->pn_flags |= PN_F_ARRAY;
+ pn->pn_asub = mdb_strtoull(array);
+ if (pn->pn_asub < 0) {
+ mdb_printf("Array lengths cannot be negative\n");
+ return (DCMD_ERR);
+ }
+ }
+
+ if (typedef_valid_identifier(name) != 0) {
+ mdb_printf("The name %s is not a valid C identifier.\n",
+ name);
+ return (DCMD_ERR);
+ }
+
+ if (nptrs) {
+ pn->pn_flags |= PN_F_POINTER;
+ pn->pn_nptrs = nptrs;
+ }
+ pn->pn_name = name;
+
+ *pnp = pn;
+ return (DCMD_OK);
+}
+
+/*
+ * We're going to parse out our types here. Note that we are not strictly
+ * speaking a truely ANSI C compliant parser. Currently we support normal
+ * declarations except for the following:
+ * o function pointers
+ * o bit-fields
+ */
+static int
+typedef_parse(char *defn, const char *name, parse_root_t **prp)
+{
+ int len, ret;
+ const char *kind, *basename;
+ char *c, *brace;
+ parse_root_t *pr;
+ parse_node_t *pn;
+ mdb_ctf_id_t id;
+
+ pr = mdb_zalloc(sizeof (parse_root_t), UM_SLEEP | UM_GC);
+ basename = defn;
+
+ c = strchr(defn, ' ');
+ if (c == NULL) {
+ mdb_printf("Invalid structure definition. Structure "
+ "must start with either 'struct {' or 'union {'\n");
+ return (DCMD_ERR);
+ }
+ *c = '\0';
+
+ if (strcmp(defn, "struct") == 0)
+ pr->pr_kind = CTF_K_STRUCT;
+ else if (strcmp(defn, "union") == 0)
+ pr->pr_kind = CTF_K_UNION;
+ else {
+ mdb_printf("Invalid start of definition. "
+ "Expected 'struct' or 'union'. "
+ "Found: '%s'\n", defn);
+ return (DCMD_ERR);
+ }
+
+ /*
+ * We transform this back to a space so we can validate that a
+ * non-anonymous struct or union name is valid.
+ */
+ *c = ' ';
+
+ kind = defn;
+ defn = c + 1;
+ while (*defn == ' ')
+ defn++;
+
+ /* Check whether this is anonymous or not */
+ if (*defn != '{') {
+ brace = strchr(defn, '{');
+ c = brace;
+ if (c == NULL) {
+ mdb_printf("Missing opening brace for %s definition. "
+ "Expected '{'. "
+ "Found: '%c'\n", kind, *defn);
+ return (DCMD_ERR);
+ }
+ *c = '\0';
+ c--;
+ while (*c == ' ')
+ c--;
+ *(c+1) = '\0';
+ if (typedef_valid_identifier(defn) != 0) {
+ mdb_printf("The name %s is not a valid C identifier.\n",
+ defn);
+ return (DCMD_ERR);
+ }
+
+ if (mdb_ctf_lookup_by_name(basename, &id) != CTF_ERR) {
+ mdb_printf("type name %s already in use\n", basename);
+ return (DCMD_ERR);
+ }
+
+ pr->pr_name = defn;
+ defn = brace;
+ } else {
+ pr->pr_name = NULL;
+ }
+
+ defn++;
+ while (*defn == ' ')
+ defn++;
+
+ len = strlen(defn);
+ if (defn[len-1] != '}') {
+ mdb_printf("Missing closing brace for %s declaration. "
+ "Expected '}'.\n");
+ return (DCMD_ERR);
+ }
+ defn[len-1] = '\0';
+
+ /*
+ * Start walking all the arguments, looking for a terminating semicolon
+ * for type definitions.
+ */
+ for (;;) {
+ ret = typedef_parse_member(defn, &c, &pn);
+ if (ret == DCMD_ERR)
+ return (DCMD_ERR);
+
+ mdb_list_append(&pr->pr_nodes, pn);
+
+ while (*c == ' ')
+ c++;
+
+ if (*c == '\0')
+ break;
+
+ defn = c;
+ }
+
+ pr->pr_tname = name;
+ *prp = pr;
+
+ return (DCMD_OK);
+}
+
+/*
+ * Make sure that none of the member names overlap and that the type names don't
+ * already exist. If we have an array entry that is a VLA, make sure it is the
+ * last member and not the only member.
+ */
+static int
+typedef_validate(parse_root_t *pr)
+{
+ mdb_nv_t nv;
+ parse_node_t *pn;
+ mdb_ctf_id_t id;
+ int count = 0;
+
+ (void) mdb_nv_create(&nv, UM_SLEEP | UM_GC);
+ for (pn = mdb_list_next(&pr->pr_nodes); pn != NULL;
+ pn = mdb_list_next(pn)) {
+ count++;
+ if (mdb_nv_lookup(&nv, pn->pn_name) != NULL) {
+ mdb_printf("duplicate name detected: %s\n",
+ pn->pn_name);
+ return (DCMD_ERR);
+ }
+
+ /*
+ * Our parse tree won't go away before the nv, so it's simpler
+ * to just mark everything external.
+ */
+ (void) mdb_nv_insert(&nv, pn->pn_name, NULL, 0, MDB_NV_EXTNAME);
+
+ if (pn->pn_flags & PN_F_ARRAY && pn->pn_asub == 0) {
+ if (pr->pr_kind != CTF_K_STRUCT) {
+ mdb_printf("Flexible array members are only "
+ "valid in structs.\n");
+ return (DCMD_ERR);
+ }
+
+ if (&pn->pn_list != pr->pr_nodes.ml_prev) {
+ mdb_printf("Flexible array entries are only "
+ "allowed to be the last entry in a "
+ "struct\n");
+ return (DCMD_ERR);
+ }
+
+ if (count == 1) {
+ mdb_printf("Structs must have members aside "
+ "from a flexible member\n");
+ return (DCMD_ERR);
+ }
+ }
+ }
+
+ if (mdb_ctf_lookup_by_name(pr->pr_tname, &id) != CTF_ERR) {
+ mdb_printf("typedef name %s already exists\n", pr->pr_tname);
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
+
+static int
+typedef_add(parse_root_t *pr)
+{
+ parse_node_t *pn;
+ mdb_ctf_id_t id, aid, tid;
+ mdb_ctf_arinfo_t ar;
+ int ii;
+
+ /* Pre-flight checks */
+ if (typedef_validate(pr) == DCMD_ERR)
+ return (DCMD_ERR);
+
+ if (pr->pr_kind == CTF_K_STRUCT) {
+ if (mdb_ctf_add_struct(pr->pr_name, &id) != 0) {
+ mdb_printf("failed to create struct for %s\n",
+ pr->pr_tname);
+ return (DCMD_ERR);
+ }
+ } else {
+ if (mdb_ctf_add_union(pr->pr_name, &id) != 0) {
+ mdb_printf("failed to create union for %s\n",
+ pr->pr_tname);
+ return (DCMD_ERR);
+ }
+ }
+
+ for (pn = mdb_list_next(&pr->pr_nodes); pn != NULL;
+ pn = mdb_list_next(pn)) {
+
+ if (mdb_ctf_lookup_by_name(pn->pn_type, &tid) == CTF_ERR) {
+ mdb_printf("failed to add member %s: type %s does "
+ "not exist\n", pn->pn_name, pn->pn_type);
+ goto destroy;
+ }
+
+ if (pn->pn_flags & PN_F_POINTER) {
+ for (ii = 0; ii < pn->pn_nptrs; ii++) {
+ if (mdb_ctf_add_pointer(&tid,
+ &tid) != 0) {
+ mdb_printf("failed to add a pointer "
+ "type as part of member: %s\n",
+ pn->pn_name);
+ goto destroy;
+ }
+ }
+ }
+
+ if (pn->pn_flags & PN_F_ARRAY) {
+ if (mdb_ctf_lookup_by_name("long", &aid) != 0) {
+ mdb_printf("failed to lookup the type 'long' "
+ "for array indexes, are you running mdb "
+ "without a target or using ::typedef -c?");
+ goto destroy;
+ }
+
+ ar.mta_contents = tid;
+ ar.mta_index = aid;
+ ar.mta_nelems = pn->pn_asub;
+
+ if (mdb_ctf_add_array(&ar, &tid) != 0) {
+ mdb_printf("failed to create array type for "
+ "memeber%s\n", pn->pn_name);
+ goto destroy;
+ }
+ }
+
+ if (mdb_ctf_add_member(&id, pn->pn_name, &tid, NULL) ==
+ CTF_ERR) {
+ mdb_printf("failed to create member %s\n",
+ pn->pn_name);
+ goto destroy;
+ }
+ }
+
+ if (mdb_ctf_add_typedef(pr->pr_tname, &id, NULL) != 0) {
+ mdb_printf("failed to add typedef for %s\n",
+ pr->pr_tname);
+ goto destroy;
+ }
+
+ return (DCMD_OK);
+
+destroy:
+ return (mdb_ctf_type_delete(&id));
+}
+
+static int
+typedef_readfile(const char *file)
+{
+ int ret;
+
+ ret = mdb_ctf_synthetics_from_file(file);
+ if (ret != DCMD_OK)
+ mdb_warn("failed to create synthetics from file\n");
+ return (ret);
+}
+
+/* ARGSUSED */
+int
+cmd_typedef(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ mdb_ctf_id_t id;
+ int i;
+ int destroy = 0, list = 0;
+ const char *cmode = NULL, *rfile = NULL;
+ const char *dst, *src;
+ char *dup;
+ parse_root_t *pr;
+
+ if (flags & DCMD_ADDRSPEC)
+ return (DCMD_USAGE);
+
+ i = mdb_getopts(argc, argv,
+ 'd', MDB_OPT_SETBITS, TRUE, &destroy,
+ 'l', MDB_OPT_SETBITS, TRUE, &list,
+ 'c', MDB_OPT_STR, &cmode,
+ 'r', MDB_OPT_STR, &rfile, NULL);
+
+ argc -= i;
+ argv += i;
+
+ /*
+ * All our options are mutually exclusive currently.
+ */
+ i = 0;
+ if (destroy)
+ i++;
+ if (cmode != NULL)
+ i++;
+ if (list)
+ i++;
+ if (rfile != NULL)
+ i++;
+ if (i > 1)
+ return (DCMD_USAGE);
+
+ if ((destroy || cmode != NULL || list || rfile != NULL) && argc != 0)
+ return (DCMD_USAGE);
+
+ if (destroy)
+ return (typedef_destroy());
+
+ if (cmode)
+ return (typedef_create(cmode));
+
+ if (list)
+ return (typedef_list());
+
+ if (rfile)
+ return (typedef_readfile(rfile));
+
+ if (argc < 2)
+ return (DCMD_USAGE);
+
+ /*
+ * Check to see if we are defining a struct or union. Note that we have
+ * to distinguish between struct foo and struct {. All typedef structs
+ * are annonymous structs that are only known by their typedef name. The
+ * same is true with unions. The problem that we have to deal with is
+ * that the ';' character in mdb causes mdb to begin another command. To
+ * work around that fact we require users to put the whole struct
+ * definition in a pair of "" or ''.
+ */
+ if (argc == 2 && strchr(argv[0].a_un.a_str, '{') != NULL) {
+ dup = mdb_alloc(strlen(argv[0].a_un.a_str) + 1,
+ UM_GC | UM_SLEEP);
+ (void) strcpy(dup, argv[0].a_un.a_str);
+ if (typedef_parse(dup, argv[1].a_un.a_str, &pr) == DCMD_ERR)
+ return (DCMD_ERR);
+ if (typedef_add(pr) == DCMD_ERR)
+ return (DCMD_ERR);
+
+ return (DCMD_OK);
+ }
+
+ /*
+ * Someone could give us something like struct foobar or unsigned int or
+ * even long double imaginary. In this case we end up conjoining all
+ * arguments except the last one into one large string that we look up.
+ */
+ if (argc - 1 == 1) {
+ src = argv[0].a_un.a_str;
+ } else {
+ src = typedef_join_strings(argc - 2, argv, UM_GC | UM_SLEEP);
+ }
+
+ dst = argv[argc-1].a_un.a_str;
+
+ if (mdb_ctf_lookup_by_name(dst, &id) != -1) {
+ mdb_printf("%s already exists\n", dst);
+ return (DCMD_ERR);
+ }
+
+ if (mdb_ctf_lookup_by_name(src, &id) != 0) {
+ mdb_printf("%s does not exist\n", src);
+ return (DCMD_ERR);
+ }
+
+ if (mdb_ctf_add_typedef(dst, &id, NULL) != 0) {
+ mdb_printf("failed to create typedef\n");
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
+
+static char typedef_desc[] =
+"::typedef operates like the C typedef keyword and creates a synthetic type\n"
+"that is usable across mdb just like a type that is embedded in CTF data.\n"
+"This includes familiar dcmds like ::print as well as mdb's tab completion\n"
+"engine. The \"type\" argument can either be a named structure or union\n"
+"declaration, like \"struct proc { int p_id; }\" declartion, an anonymous\n"
+"structure or union declaration, like \"struct { int count; }\", or simply\n"
+"the name of an existing type, like \"uint64_t\". Either form may refer to\n"
+"other types already defined in CTF or a previous ::typedef invocation. When\n"
+"debugging binaries without CTF, definitions for intrinsic types may be\n"
+"created using the -c option. See the OPTIONS section for more information.\n"
+"If a named struct or union is used, then a type will be created for it just\n"
+"like in C. This may be used to mimic a forward declaration and an example of\n"
+"this is in the EXAMPLES section. Regardless of whether a struct or union is\n"
+"anonymous or named, the \"name\" argument is always required.\n"
+"\n"
+"When declaring anonymous structures and unions, the entire definition must\n"
+"be enclosed within \"\" or ''. The ';' is used by mdb to separate commands\n"
+"in a similar fashion to the shell. The ';' cannot be escaped, therefore\n"
+"quoting your argument is necessary. See the EXAMPLES sections for examples\n"
+"of what this looks like.\n"
+"\n"
+"All member and type names must be valid C identifiers. They must start with\n"
+"an underscore or a letter. Subsequent characters are allowed to be letters,\n"
+"numbers, or an underscore.\n"
+"\n"
+"Declaring arrays and any number of pointers in anonymous structures is \n"
+"supported. However the following C features are not supported: \n"
+" o function pointers (use a void * instead)\n"
+" o bitfields (use an integer of the appropriate size instead)\n"
+" o packed structures (all structures currently use their natural alignment)\n"
+"\n"
+"::typedef also allows you to read type definitions from a file. Definitions\n"
+"can be read from any ELF file that has a CTF section that libctf can parse.\n"
+"You can check if a file has such a section with elfdump(1). If a binary or\n"
+"core dump does not have any type information, but you do have it elsewhere,\n"
+"then you can use ::typedef -r to read in that type information.\n"
+"\n";
+
+static char typedef_opts[] =
+" -c model create intrinsic types based on the specified data model.\n"
+" The INTRINSICS section lists the built-in types and typedefs.\n"
+" The following data models are supported:\n"
+" o LP64 - Traditional illumos 64-bit program\n"
+" o LP32 - Traditional illumos 32-bit program.\n"
+" o ILP32 - An alternate name for LP32.\n"
+" -d delete all synthetic types\n"
+" -l list all synthetic types\n"
+" -r file import type definitions (CTF) from another ELF file\n"
+"\n";
+
+static char typedef_examps[] =
+" ::typedef -c LP64\n"
+" ::typedef uint64_t bender_t\n"
+" ::typedef struct proc new_proc_t\n"
+" ::typedef \"union { int frodo; char sam; long gandalf; }\" ringbearer_t;\n"
+" ::typedef \"struct { uintptr_t stone[7]; void **white; }\" gift_t\n"
+" ::typedef \"struct list { struct list *l_next; struct list *l_prev; }\" "
+"list_t\n"
+" ::typedef -r /var/tmp/qemu-system-x86_64\n"
+"\n";
+
+static char typedef_intrins[] =
+"The following C types and <stdint.h> typedefs are provided when \n"
+"::typedef -c is used\n"
+"\n"
+" signed unsigned void\n"
+" char short int\n"
+" long long long signed char\n"
+" signed short signed int signed long\n"
+" singed long long unsigned char unsigned short\n"
+" unsigned int unsigned long unsigned long long\n"
+" _Bool float double\n"
+" long double float imaginary double imaginary\n"
+" long double imaginary float complex\n"
+" double complex long double complex\n"
+"\n"
+" int8_t int16_t int32_t\n"
+" int64_t intptr_t uint8_t\n"
+" uint16_t uint32_t uint64_t\n"
+" uchar_t ushort_t uint_t\n"
+" ulong_t u_longlong_t ptrdiff_t\n"
+" uintptr_t\n"
+"\n";
+
+void
+cmd_typedef_help(void)
+{
+ mdb_printf("%s", typedef_desc);
+ (void) mdb_dec_indent(2);
+ mdb_printf("%<b>OPTIONS%</b>\n");
+ (void) mdb_inc_indent(2);
+ mdb_printf("%s", typedef_opts);
+ (void) mdb_dec_indent(2);
+ mdb_printf("%<b>EXAMPLES%</b>\n");
+ (void) mdb_inc_indent(2);
+ mdb_printf("%s", typedef_examps);
+ (void) mdb_dec_indent(2);
+ mdb_printf("%<b>INTRINSICS%</b>\n");
+ (void) mdb_inc_indent(2);
+ mdb_printf("%s", typedef_intrins);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_typedef.h b/usr/src/cmd/mdb/common/mdb/mdb_typedef.h
new file mode 100644
index 0000000000..28f91b7987
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_typedef.h
@@ -0,0 +1,42 @@
+/*
+ * 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 Joyent, Inc. All rights reserved.
+ */
+
+
+#ifndef _MDB_TYPEDEF_H
+#define _MDB_TYPEDEF_H
+
+#include <mdb/mdb_modapi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int cmd_typedef(uintptr_t, uint_t, int, const mdb_arg_t *);
+void cmd_typedef_help(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_TYPEDEF_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_whatis.c b/usr/src/cmd/mdb/common/mdb/mdb_whatis.c
index 1b53a32d39..6a26a7dec9 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_whatis.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_whatis.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.
*/
/*
diff --git a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern
index d1c401975c..5b4b8f2be1 100644
--- a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern
+++ b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern
@@ -148,6 +148,7 @@ SYMBOL_SCOPE {
mdb_set_pipe { FLAGS = EXTERN };
mdb_snprintf { FLAGS = EXTERN };
mdb_strtoull { FLAGS = EXTERN };
+ mdb_symbol_iter { FLAGS = EXTERN };
mdb_tgt_notsup { FLAGS = EXTERN };
mdb_vnode2path { FLAGS = EXTERN };
mdb_vread { FLAGS = EXTERN };
diff --git a/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c b/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c
index f484828b75..50ad2c3497 100644
--- a/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c
+++ b/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
*/
/*
@@ -432,6 +433,7 @@ dtracemdb_bufsnap(dtrace_buffer_t *which, dtrace_bufdesc_t *desc)
desc->dtbd_size = bufsize;
desc->dtbd_drops = buf.dtb_drops;
desc->dtbd_errors = buf.dtb_errors;
+ desc->dtbd_timestamp = gethrtime();
return (0);
}
@@ -1273,19 +1275,19 @@ int
dtrace_helptrace_init(mdb_walk_state_t *wsp)
{
uint32_t next;
- int enabled;
+ uintptr_t buffer;
if (wsp->walk_addr != NULL) {
mdb_warn("dtrace_helptrace only supports global walks\n");
return (WALK_ERR);
}
- if (mdb_readvar(&enabled, "dtrace_helptrace_enabled") == -1) {
- mdb_warn("couldn't read 'dtrace_helptrace_enabled'");
+ if (mdb_readvar(&buffer, "dtrace_helptrace_buffer") == -1) {
+ mdb_warn("couldn't read 'dtrace_helptrace_buffer'");
return (WALK_ERR);
}
- if (!enabled) {
+ if (buffer == NULL) {
mdb_warn("helper tracing is not enabled\n");
return (WALK_ERR);
}
diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
index d54d11e8ce..48578a52f4 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
@@ -21,6 +21,7 @@
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
*/
#include <mdb/mdb_param.h>
@@ -109,10 +110,6 @@
*/
#define NINTR 16
-#define KILOS 10
-#define MEGS 20
-#define GIGS 30
-
#ifndef STACK_BIAS
#define STACK_BIAS 0
#endif
@@ -1900,24 +1897,24 @@ typedef struct datafmt {
} datafmt_t;
static datafmt_t kmemfmt[] = {
- { "cache ", "name ",
- "-------------------------", "%-25s " },
- { " buf", " size", "------", "%6u " },
- { " buf", "in use", "------", "%6u " },
- { " buf", " total", "------", "%6u " },
- { " memory", " in use", "----------", "%10lu%c " },
- { " alloc", " succeed", "---------", "%9u " },
- { "alloc", " fail", "-----", "%5u " },
+ { "cache ", "name ",
+ "------------------------------", "%-30s " },
+ { " buf", " size", "-----", "%5H " },
+ { " buf", " in use", "---------", "%9u " },
+ { " buf", " total", "---------", "%9u " },
+ { "memory", "in use", "------", "%6lH " },
+ { " alloc", " succeed", "----------", "%10u " },
+ { "alloc", " fail", "-----", "%5u" },
{ NULL, NULL, NULL, NULL }
};
static datafmt_t vmemfmt[] = {
- { "vmem ", "name ",
- "-------------------------", "%-*s " },
- { " memory", " in use", "----------", "%9llu%c " },
- { " memory", " total", "-----------", "%10llu%c " },
- { " memory", " import", "----------", "%9llu%c " },
- { " alloc", " succeed", "---------", "%9llu " },
+ { "vmem ", "name ",
+ "------------------------------", "%-*s " },
+ { " memory", " in use", "---------", "%9llH " },
+ { " memory", " total", "----------", "%10llH " },
+ { " memory", " import", "---------", "%9llH " },
+ { " alloc", " succeed", "----------", "%10llu " },
{ "alloc", " fail", "-----", "%5llu " },
{ NULL, NULL, NULL, NULL }
};
@@ -1969,15 +1966,9 @@ typedef struct kmastat_vmem {
int kv_fail;
} kmastat_vmem_t;
-typedef struct kmastat_args {
- kmastat_vmem_t **ka_kvpp;
- uint_t ka_shift;
-} kmastat_args_t;
-
static int
-kmastat_cache(uintptr_t addr, const kmem_cache_t *cp, kmastat_args_t *kap)
+kmastat_cache(uintptr_t addr, const kmem_cache_t *cp, kmastat_vmem_t **kvpp)
{
- kmastat_vmem_t **kvpp = kap->ka_kvpp;
kmastat_vmem_t *kv;
datafmt_t *dfp = kmemfmt;
int magsize;
@@ -2018,9 +2009,7 @@ out:
mdb_printf((dfp++)->fmt, cp->cache_bufsize);
mdb_printf((dfp++)->fmt, total - avail);
mdb_printf((dfp++)->fmt, total);
- mdb_printf((dfp++)->fmt, meminuse >> kap->ka_shift,
- kap->ka_shift == GIGS ? 'G' : kap->ka_shift == MEGS ? 'M' :
- kap->ka_shift == KILOS ? 'K' : 'B');
+ mdb_printf((dfp++)->fmt, meminuse);
mdb_printf((dfp++)->fmt, alloc);
mdb_printf((dfp++)->fmt, cp->cache_alloc_fail);
mdb_printf("\n");
@@ -2029,9 +2018,8 @@ out:
}
static int
-kmastat_vmem_totals(uintptr_t addr, const vmem_t *v, kmastat_args_t *kap)
+kmastat_vmem_totals(uintptr_t addr, const vmem_t *v, kmastat_vmem_t *kv)
{
- kmastat_vmem_t *kv = *kap->ka_kvpp;
size_t len;
while (kv != NULL && kv->kv_addr != addr)
@@ -2040,20 +2028,18 @@ kmastat_vmem_totals(uintptr_t addr, const vmem_t *v, kmastat_args_t *kap)
if (kv == NULL || kv->kv_alloc == 0)
return (WALK_NEXT);
- len = MIN(17, strlen(v->vm_name));
+ len = MIN(22, strlen(v->vm_name));
- mdb_printf("Total [%s]%*s %6s %6s %6s %10lu%c %9u %5u\n", v->vm_name,
- 17 - len, "", "", "", "",
- kv->kv_meminuse >> kap->ka_shift,
- kap->ka_shift == GIGS ? 'G' : kap->ka_shift == MEGS ? 'M' :
- kap->ka_shift == KILOS ? 'K' : 'B', kv->kv_alloc, kv->kv_fail);
+ mdb_printf("Total [%s]%*s %5s %9s %9s %6lH %10u %5u\n", v->vm_name,
+ 22 - len, "", "", "", "",
+ kv->kv_meminuse, kv->kv_alloc, kv->kv_fail);
return (WALK_NEXT);
}
/*ARGSUSED*/
static int
-kmastat_vmem(uintptr_t addr, const vmem_t *v, const uint_t *shiftp)
+kmastat_vmem(uintptr_t addr, const vmem_t *v, const void *ignored)
{
datafmt_t *dfp = vmemfmt;
const vmem_kstat_t *vkp = &v->vm_kstat;
@@ -2071,16 +2057,10 @@ kmastat_vmem(uintptr_t addr, const vmem_t *v, const uint_t *shiftp)
}
mdb_printf("%*s", ident, "");
- mdb_printf((dfp++)->fmt, 25 - ident, v->vm_name);
- mdb_printf((dfp++)->fmt, vkp->vk_mem_inuse.value.ui64 >> *shiftp,
- *shiftp == GIGS ? 'G' : *shiftp == MEGS ? 'M' :
- *shiftp == KILOS ? 'K' : 'B');
- mdb_printf((dfp++)->fmt, vkp->vk_mem_total.value.ui64 >> *shiftp,
- *shiftp == GIGS ? 'G' : *shiftp == MEGS ? 'M' :
- *shiftp == KILOS ? 'K' : 'B');
- mdb_printf((dfp++)->fmt, vkp->vk_mem_import.value.ui64 >> *shiftp,
- *shiftp == GIGS ? 'G' : *shiftp == MEGS ? 'M' :
- *shiftp == KILOS ? 'K' : 'B');
+ mdb_printf((dfp++)->fmt, 30 - ident, v->vm_name);
+ mdb_printf((dfp++)->fmt, vkp->vk_mem_inuse.value.ui64);
+ mdb_printf((dfp++)->fmt, vkp->vk_mem_total.value.ui64);
+ mdb_printf((dfp++)->fmt, vkp->vk_mem_import.value.ui64);
mdb_printf((dfp++)->fmt, vkp->vk_alloc.value.ui64);
mdb_printf((dfp++)->fmt, vkp->vk_fail.value.ui64);
@@ -2095,44 +2075,35 @@ kmastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
kmastat_vmem_t *kv = NULL;
datafmt_t *dfp;
- kmastat_args_t ka;
-
- ka.ka_shift = 0;
- if (mdb_getopts(argc, argv,
- 'k', MDB_OPT_SETBITS, KILOS, &ka.ka_shift,
- 'm', MDB_OPT_SETBITS, MEGS, &ka.ka_shift,
- 'g', MDB_OPT_SETBITS, GIGS, &ka.ka_shift, NULL) != argc)
- return (DCMD_USAGE);
for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++)
- mdb_printf("%s ", dfp->hdr1);
+ mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->hdr1);
mdb_printf("\n");
for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++)
- mdb_printf("%s ", dfp->hdr2);
+ mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->hdr2);
mdb_printf("\n");
for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++)
- mdb_printf("%s ", dfp->dashes);
+ mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->dashes);
mdb_printf("\n");
- ka.ka_kvpp = &kv;
- if (mdb_walk("kmem_cache", (mdb_walk_cb_t)kmastat_cache, &ka) == -1) {
+ if (mdb_walk("kmem_cache", (mdb_walk_cb_t)kmastat_cache, &kv) == -1) {
mdb_warn("can't walk 'kmem_cache'");
return (DCMD_ERR);
}
for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++)
- mdb_printf("%s ", dfp->dashes);
+ mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->dashes);
mdb_printf("\n");
- if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem_totals, &ka) == -1) {
+ if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem_totals, kv) == -1) {
mdb_warn("can't walk 'vmem'");
return (DCMD_ERR);
}
for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++)
- mdb_printf("%s ", dfp->dashes);
+ mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->dashes);
mdb_printf("\n");
mdb_printf("\n");
@@ -2149,7 +2120,7 @@ kmastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
mdb_printf("%s ", dfp->dashes);
mdb_printf("\n");
- if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem, &ka.ka_shift) == -1) {
+ if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem, NULL) == -1) {
mdb_warn("can't walk 'vmem'");
return (DCMD_ERR);
}
@@ -3986,8 +3957,7 @@ static const mdb_dcmd_t dcmds[] = {
{ "freedby", ":", "given a thread, print its freed buffers", freedby },
{ "kmalog", "?[ fail | slab ]",
"display kmem transaction log and stack traces", kmalog },
- { "kmastat", "[-kmg]", "kernel memory allocator stats",
- kmastat },
+ { "kmastat", NULL, "kernel memory allocator stats", kmastat },
{ "kmausers", "?[-ef] [cache ...]", "current medium and large users "
"of the kmem allocator", kmausers, kmausers_help },
{ "kmem_cache", "?[-n name]",
@@ -4089,6 +4059,9 @@ static const mdb_dcmd_t dcmds[] = {
/* from netstack.c */
{ "netstack", "", "show stack instances", netstack },
+ { "netstackid2netstack", ":",
+ "translate a netstack id to its netstack_t",
+ netstackid2netstack },
/* from nvpair.c */
{ NVPAIR_DCMD_NAME, NVPAIR_DCMD_USAGE, NVPAIR_DCMD_DESCR,
@@ -4179,6 +4152,10 @@ static const mdb_dcmd_t dcmds[] = {
pfiles_help },
/* from zone.c */
+ { "zid2zone", ":", "find the zone_t with the given zone id",
+ zid2zone },
+ { "zdid2zone", ":", "find the zone_t with the given zone debug id",
+ zdid2zone },
{ "zone", "?[-r [-v]]", "display kernel zone(s)", zoneprt },
{ "zsd", ":[-v] [zsd_key]", "display zone-specific-data entries for "
"selected zones", zsd },
diff --git a/usr/src/cmd/mdb/common/modules/genunix/netstack.c b/usr/src/cmd/mdb/common/modules/genunix/netstack.c
index 588bd6dbf3..d46bd85d1f 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/netstack.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/netstack.c
@@ -21,10 +21,9 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_ks.h>
#include <mdb/mdb_ctf.h>
@@ -121,3 +120,30 @@ netstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_OK);
}
+
+static int
+netstackid_lookup_cb(uintptr_t addr, const netstack_t *ns, void *arg)
+{
+ netstackid_t nid = *(uintptr_t *)arg;
+ if (ns->netstack_stackid == nid)
+ mdb_printf("%p\n", addr);
+
+ return (WALK_NEXT);
+}
+
+/*ARGSUSED*/
+int
+netstackid2netstack(uintptr_t addr, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ if (!(flags & DCMD_ADDRSPEC) || argc != 0)
+ return (DCMD_USAGE);
+
+ if (mdb_walk("netstack", (mdb_walk_cb_t)netstackid_lookup_cb, &addr) ==
+ -1) {
+ mdb_warn("failed to walk zone");
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
diff --git a/usr/src/cmd/mdb/common/modules/genunix/netstack.h b/usr/src/cmd/mdb/common/modules/genunix/netstack.h
index 392565caca..f5773c36c1 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/netstack.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/netstack.h
@@ -26,8 +26,6 @@
#ifndef _NETSTACK_H
#define _NETSTACK_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <mdb/mdb_modapi.h>
#ifdef __cplusplus
@@ -38,6 +36,7 @@ int netstack_walk_init(mdb_walk_state_t *);
int netstack_walk_step(mdb_walk_state_t *);
int netstack(uintptr_t, uint_t, int, const mdb_arg_t *);
+int netstackid2netstack(uintptr_t, uint_t, int, const mdb_arg_t *);
#ifdef __cplusplus
}
diff --git a/usr/src/cmd/mdb/common/modules/genunix/zone.c b/usr/src/cmd/mdb/common/modules/genunix/zone.c
index 96f6b598ec..77ed2cbc48 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/zone.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/zone.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#include <mdb/mdb_param.h>
@@ -33,9 +34,9 @@
#define ZONE_NAMELEN 20
#ifdef _LP64
-#define ZONE_PATHLEN 32
+#define ZONE_PATHLEN 25
#else
-#define ZONE_PATHLEN 40
+#define ZONE_PATHLEN 33
#endif
/*
@@ -54,6 +55,56 @@ char *zone_status_names[] = {
"dead" /* ZONE_IS_DEAD */
};
+static int
+zid_lookup_cb(uintptr_t addr, const zone_t *zone, void *arg)
+{
+ zoneid_t zid = *(uintptr_t *)arg;
+ if (zone->zone_id == zid)
+ mdb_printf("%p\n", addr);
+
+ return (WALK_NEXT);
+}
+
+/*ARGSUSED*/
+int
+zid2zone(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (!(flags & DCMD_ADDRSPEC) || argc != 0)
+ return (DCMD_USAGE);
+
+ if (mdb_walk("zone", (mdb_walk_cb_t)zid_lookup_cb, &addr) == -1) {
+ mdb_warn("failed to walk zone");
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
+
+static int
+zdid_lookup_cb(uintptr_t addr, const zone_t *zone, void *arg)
+{
+ zoneid_t zdid = *(uintptr_t *)arg;
+ if (zone->zone_did == zdid)
+ mdb_printf("%p\n", addr);
+
+ return (WALK_NEXT);
+}
+
+/*ARGSUSED*/
+int
+zdid2zone(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (!(flags & DCMD_ADDRSPEC) || argc != 0)
+ return (DCMD_USAGE);
+
+ if (mdb_walk("zone", (mdb_walk_cb_t)zdid_lookup_cb, &addr) == -1) {
+ mdb_warn("failed to walk zone");
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
+
int
zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
@@ -96,10 +147,10 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
*/
if (DCMD_HDRSPEC(flags)) {
if (ropt_given == FALSE)
- mdb_printf("%<u>%?s %6s %-13s %-20s %-s%</u>\n",
+ mdb_printf("%<u>%?s %4s %-13s %-19s %-s%</u>\n",
"ADDR", "ID", "STATUS", "NAME", "PATH");
else
- mdb_printf("%<u>%?s %6s %10s %10s %-20s%</u>\n",
+ mdb_printf("%<u>%?s %6s %10s %10s %-19s%</u>\n",
"ADDR", "ID", "REFS", "CREFS", "NAME");
}
@@ -138,7 +189,7 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
statusp = zone_status_names[zn.zone_status];
else
statusp = "???";
- mdb_printf("%0?p %6d %-13s %-20s %s\n", addr, zn.zone_id,
+ mdb_printf("%0?p %4d %-13s %-19s %s\n", addr, zn.zone_id,
statusp, name, path);
} else {
/*
@@ -146,7 +197,7 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
* Display the zone's subsystem-specific reference counts if
* the user specified the '-v' option.
*/
- mdb_printf("%0?p %6d %10u %10u %-20s\n", addr, zn.zone_id,
+ mdb_printf("%0?p %6d %10u %10u %-19s\n", addr, zn.zone_id,
zn.zone_ref, zn.zone_cred_ref, name);
if (vopt_given == TRUE) {
GElf_Sym subsys_names_sym;
@@ -384,7 +435,7 @@ zsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
* Prepare to output the specified zone's ZSD information.
*/
if (DCMD_HDRSPEC(flags))
- mdb_printf("%<u>%-20s %?s %?s %8s%</u>\n", "ZONE", "KEY",
+ mdb_printf("%<u>%-19s %?s %?s %8s%</u>\n", "ZONE", "KEY",
"VALUE", "FLAGS");
len = mdb_readstr(name, ZONE_NAMELEN, (uintptr_t)zone.zone_name);
if (len > 0) {
@@ -393,7 +444,7 @@ zsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
} else {
(void) strcpy(name, "??");
}
- mdb_printf("%-20s ", name);
+ mdb_printf("%-19s ", name);
/*
* Display the requested ZSD entries.
diff --git a/usr/src/cmd/mdb/common/modules/genunix/zone.h b/usr/src/cmd/mdb/common/modules/genunix/zone.h
index e0e5038527..94a383e41c 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/zone.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/zone.h
@@ -27,14 +27,14 @@
#ifndef _ZONE_H
#define _ZONE_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <mdb/mdb_modapi.h>
#ifdef __cplusplus
extern "C" {
#endif
+extern int zid2zone(uintptr_t, uint_t, int argc, const mdb_arg_t *);
+extern int zdid2zone(uintptr_t, uint_t, int argc, const mdb_arg_t *);
extern int zoneprt(uintptr_t, uint_t, int argc, const mdb_arg_t *);
extern int zone_walk_init(mdb_walk_state_t *);
diff --git a/usr/src/cmd/mdb/common/modules/libc/libc.c b/usr/src/cmd/mdb/common/modules/libc/libc.c
index 27dcade228..44e4f49b87 100644
--- a/usr/src/cmd/mdb/common/modules/libc/libc.c
+++ b/usr/src/cmd/mdb/common/modules/libc/libc.c
@@ -23,6 +23,9 @@
* Copyright (c) 2001, 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/mdb_modapi.h>
#include <mdb/mdb_whatis.h>
@@ -681,6 +684,12 @@ d_ulwp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
prt_addr((void *)(addr + OFFSET(ul_spinlock)), 1),
prt_addr((void *)(addr + OFFSET(ul_fpuenv)), 0));
+ HD("tmem.size &tmem.roots");
+ mdb_printf(OFFSTR "%-21H %s\n",
+ OFFSET(ul_tmem),
+ ulwp.ul_tmem.tm_size,
+ prt_addr((void *)(addr + OFFSET(ul_tmem) + sizeof (size_t)), 0));
+
return (DCMD_OK);
}
diff --git a/usr/src/cmd/mdb/common/modules/libumem/libumem.c b/usr/src/cmd/mdb/common/modules/libumem/libumem.c
index 4a77c5aa82..0984edbdf0 100644
--- a/usr/src/cmd/mdb/common/modules/libumem/libumem.c
+++ b/usr/src/cmd/mdb/common/modules/libumem/libumem.c
@@ -23,6 +23,10 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ */
+
#include "umem.h"
#include <libproc.h>
#include <mdb/mdb_modapi.h>
@@ -34,6 +38,8 @@
#include <umem_impl.h>
#include <sys/vmem_impl_user.h>
+#include <thr_uberdata.h>
+#include <stdio.h>
#include "umem_pagesize.h"
@@ -44,24 +50,33 @@ typedef struct datafmt {
char *fmt;
} datafmt_t;
+static datafmt_t ptcfmt[] = {
+ { " ", "tid", "---", "%3u " },
+ { " memory", " cached", "-------", "%7lH " },
+ { " %", "cap", "---", "%3u " },
+ { " %", NULL, "---", "%3u " },
+ { NULL, NULL, NULL, NULL }
+};
+
static datafmt_t umemfmt[] = {
{ "cache ", "name ",
"-------------------------", "%-25s " },
{ " buf", " size", "------", "%6u " },
- { " buf", "in use", "------", "%6u " },
- { " buf", " total", "------", "%6u " },
- { " memory", " in use", "---------", "%9u " },
+ { " buf", " in use", "-------", "%7u " },
+ { " buf", " in ptc", "-------", "%7s " },
+ { " buf", " total", "-------", "%7u " },
+ { " memory", " in use", "-------", "%7H " },
{ " alloc", " succeed", "---------", "%9u " },
- { "alloc", " fail", "-----", "%5llu " },
+ { "alloc", " fail", "-----", "%5llu" },
{ NULL, NULL, NULL, NULL }
};
static datafmt_t vmemfmt[] = {
{ "vmem ", "name ",
"-------------------------", "%-*s " },
- { " memory", " in use", "---------", "%9llu " },
- { " memory", " total", "----------", "%10llu " },
- { " memory", " import", "---------", "%9llu " },
+ { " memory", " in use", "---------", "%9H " },
+ { " memory", " total", "----------", "%10H " },
+ { " memory", " import", "---------", "%9H " },
{ " alloc", " succeed", "---------", "%9llu " },
{ "alloc", " fail", "-----", "%5llu " },
{ NULL, NULL, NULL, NULL }
@@ -105,14 +120,105 @@ typedef struct umastat_vmem {
int kv_fail;
} umastat_vmem_t;
+/*ARGSUSED*/
+static int
+umastat_cache_nptc(uintptr_t addr, const umem_cache_t *cp, int *nptc)
+{
+ if (!(cp->cache_flags & UMF_PTC))
+ return (WALK_NEXT);
+
+ (*nptc)++;
+ return (WALK_NEXT);
+}
+
+/*ARGSUSED*/
+static int
+umastat_cache_hdr(uintptr_t addr, const umem_cache_t *cp, void *ignored)
+{
+ if (!(cp->cache_flags & UMF_PTC))
+ return (WALK_NEXT);
+
+ mdb_printf("%3d ", cp->cache_bufsize);
+ return (WALK_NEXT);
+}
+
+/*ARGSUSED*/
+static int
+umastat_lwp_ptc(uintptr_t addr, void *buf, int *nbufs)
+{
+ (*nbufs)++;
+ return (WALK_NEXT);
+}
+
+/*ARGSUSED*/
+static int
+umastat_lwp_cache(uintptr_t addr, const umem_cache_t *cp, ulwp_t *ulwp)
+{
+ char walk[60];
+ int nbufs = 0;
+
+ if (!(cp->cache_flags & UMF_PTC))
+ return (WALK_NEXT);
+
+ (void) snprintf(walk, sizeof (walk), "umem_ptc_%d", cp->cache_bufsize);
+
+ if (mdb_pwalk(walk, (mdb_walk_cb_t)umastat_lwp_ptc,
+ &nbufs, (uintptr_t)ulwp->ul_self) == -1) {
+ mdb_warn("unable to walk '%s'", walk);
+ return (WALK_ERR);
+ }
+
+ mdb_printf("%3d ", ulwp->ul_tmem.tm_size ?
+ (nbufs * cp->cache_bufsize * 100) / ulwp->ul_tmem.tm_size : 0);
+
+ return (WALK_NEXT);
+}
+
+/*ARGSUSED*/
+static int
+umastat_lwp(uintptr_t addr, const ulwp_t *ulwp, void *ignored)
+{
+ size_t size;
+ datafmt_t *dfp = ptcfmt;
+
+ mdb_printf((dfp++)->fmt, ulwp->ul_lwpid);
+ mdb_printf((dfp++)->fmt, ulwp->ul_tmem.tm_size);
+
+ if (umem_readvar(&size, "umem_ptc_size") == -1) {
+ mdb_warn("unable to read 'umem_ptc_size'");
+ return (WALK_ERR);
+ }
+
+ mdb_printf((dfp++)->fmt, (ulwp->ul_tmem.tm_size * 100) / size);
+
+ if (mdb_walk("umem_cache",
+ (mdb_walk_cb_t)umastat_lwp_cache, (void *)ulwp) == -1) {
+ mdb_warn("can't walk 'umem_cache'");
+ return (WALK_ERR);
+ }
+
+ mdb_printf("\n");
+
+ return (WALK_NEXT);
+}
+
+/*ARGSUSED*/
+static int
+umastat_cache_ptc(uintptr_t addr, const void *ignored, int *nptc)
+{
+ (*nptc)++;
+ return (WALK_NEXT);
+}
+
static int
umastat_cache(uintptr_t addr, const umem_cache_t *cp, umastat_vmem_t **kvp)
{
umastat_vmem_t *kv;
datafmt_t *dfp = umemfmt;
+ char buf[10];
int magsize;
- int avail, alloc, total;
+ int avail, alloc, total, nptc = 0;
size_t meminuse = (cp->cache_slab_create - cp->cache_slab_destroy) *
cp->cache_slabsize;
@@ -130,6 +236,21 @@ umastat_cache(uintptr_t addr, const umem_cache_t *cp, umastat_vmem_t **kvp)
(void) mdb_pwalk("umem_cpu_cache", cpu_avail, &avail, addr);
(void) mdb_pwalk("umem_slab_partial", slab_avail, &avail, addr);
+ if (cp->cache_flags & UMF_PTC) {
+ char walk[60];
+
+ (void) snprintf(walk, sizeof (walk),
+ "umem_ptc_%d", cp->cache_bufsize);
+
+ if (mdb_walk(walk,
+ (mdb_walk_cb_t)umastat_cache_ptc, &nptc) == -1) {
+ mdb_warn("unable to walk '%s'", walk);
+ return (WALK_ERR);
+ }
+
+ (void) snprintf(buf, sizeof (buf), "%d", nptc);
+ }
+
for (kv = *kvp; kv != NULL; kv = kv->kv_next) {
if (kv->kv_addr == (uintptr_t)cp->cache_arena)
goto out;
@@ -147,6 +268,7 @@ out:
mdb_printf((dfp++)->fmt, cp->cache_name);
mdb_printf((dfp++)->fmt, cp->cache_bufsize);
mdb_printf((dfp++)->fmt, total - avail);
+ mdb_printf((dfp++)->fmt, cp->cache_flags & UMF_PTC ? buf : "-");
mdb_printf((dfp++)->fmt, total);
mdb_printf((dfp++)->fmt, meminuse);
mdb_printf((dfp++)->fmt, alloc);
@@ -165,8 +287,8 @@ umastat_vmem_totals(uintptr_t addr, const vmem_t *v, umastat_vmem_t *kv)
if (kv == NULL || kv->kv_alloc == 0)
return (WALK_NEXT);
- mdb_printf("Total [%s]%*s %6s %6s %6s %9u %9u %5u\n", v->vm_name,
- 17 - strlen(v->vm_name), "", "", "", "",
+ mdb_printf("Total [%s]%*s %6s %7s %7s %7s %7H %9u %5u\n", v->vm_name,
+ 17 - strlen(v->vm_name), "", "", "", "", "",
kv->kv_meminuse, kv->kv_alloc, kv->kv_fail);
return (WALK_NEXT);
@@ -209,20 +331,67 @@ umastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
umastat_vmem_t *kv = NULL;
datafmt_t *dfp;
+ int nptc = 0, i;
if (argc != 0)
return (DCMD_USAGE);
+ /*
+ * We need to determine if we have any caches that have per-thread
+ * caching enabled.
+ */
+ if (mdb_walk("umem_cache",
+ (mdb_walk_cb_t)umastat_cache_nptc, &nptc) == -1) {
+ mdb_warn("can't walk 'umem_cache'");
+ return (DCMD_ERR);
+ }
+
+ if (nptc) {
+ for (dfp = ptcfmt; dfp->hdr2 != NULL; dfp++)
+ mdb_printf("%s ", dfp->hdr1);
+
+ for (i = 0; i < nptc; i++)
+ mdb_printf("%s ", dfp->hdr1);
+
+ mdb_printf("\n");
+
+ for (dfp = ptcfmt; dfp->hdr2 != NULL; dfp++)
+ mdb_printf("%s ", dfp->hdr2);
+
+ if (mdb_walk("umem_cache",
+ (mdb_walk_cb_t)umastat_cache_hdr, NULL) == -1) {
+ mdb_warn("can't walk 'umem_cache'");
+ return (DCMD_ERR);
+ }
+
+ mdb_printf("\n");
+
+ for (dfp = ptcfmt; dfp->hdr2 != NULL; dfp++)
+ mdb_printf("%s ", dfp->dashes);
+
+ for (i = 0; i < nptc; i++)
+ mdb_printf("%s ", dfp->dashes);
+
+ mdb_printf("\n");
+
+ if (mdb_walk("ulwp", (mdb_walk_cb_t)umastat_lwp, NULL) == -1) {
+ mdb_warn("can't walk 'ulwp'");
+ return (DCMD_ERR);
+ }
+
+ mdb_printf("\n");
+ }
+
for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
- mdb_printf("%s ", dfp->hdr1);
+ mdb_printf("%s%s", dfp == umemfmt ? "" : " ", dfp->hdr1);
mdb_printf("\n");
for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
- mdb_printf("%s ", dfp->hdr2);
+ mdb_printf("%s%s", dfp == umemfmt ? "" : " ", dfp->hdr2);
mdb_printf("\n");
for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
- mdb_printf("%s ", dfp->dashes);
+ mdb_printf("%s%s", dfp == umemfmt ? "" : " ", dfp->dashes);
mdb_printf("\n");
if (mdb_walk("umem_cache", (mdb_walk_cb_t)umastat_cache, &kv) == -1) {
@@ -231,7 +400,7 @@ umastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
}
for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
- mdb_printf("%s ", dfp->dashes);
+ mdb_printf("%s%s", dfp == umemfmt ? "" : " ", dfp->dashes);
mdb_printf("\n");
if (mdb_walk("vmem", (mdb_walk_cb_t)umastat_vmem_totals, kv) == -1) {
diff --git a/usr/src/cmd/mdb/common/modules/libumem/umem.c b/usr/src/cmd/mdb/common/modules/libumem/umem.c
index 26a62c7b52..69b003cc5c 100644
--- a/usr/src/cmd/mdb/common/modules/libumem/umem.c
+++ b/usr/src/cmd/mdb/common/modules/libumem/umem.c
@@ -24,7 +24,7 @@
*/
/*
- * Copyright 2011 Joyent, Inc. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
*/
@@ -36,6 +36,8 @@
#include <alloca.h>
#include <limits.h>
#include <mdb/mdb_whatis.h>
+#include <thr_uberdata.h>
+#include <stdio.h>
#include "misc.h"
#include "leaky.h"
@@ -104,12 +106,58 @@ umem_update_variables(void)
return (0);
}
+static int
+umem_ptc_walk_init(mdb_walk_state_t *wsp)
+{
+ if (wsp->walk_addr == NULL) {
+ if (mdb_layered_walk("ulwp", wsp) == -1) {
+ mdb_warn("couldn't walk 'ulwp'");
+ return (WALK_ERR);
+ }
+ }
+
+ return (WALK_NEXT);
+}
+
+static int
+umem_ptc_walk_step(mdb_walk_state_t *wsp)
+{
+ uintptr_t this;
+ int rval;
+
+ if (wsp->walk_layer != NULL) {
+ this = (uintptr_t)((ulwp_t *)wsp->walk_layer)->ul_self +
+ (uintptr_t)wsp->walk_arg;
+ } else {
+ this = wsp->walk_addr + (uintptr_t)wsp->walk_arg;
+ }
+
+ for (;;) {
+ if (mdb_vread(&this, sizeof (void *), this) == -1) {
+ mdb_warn("couldn't read ptc buffer at %p", this);
+ return (WALK_ERR);
+ }
+
+ if (this == NULL)
+ break;
+
+ rval = wsp->walk_callback(this, &this, wsp->walk_cbdata);
+
+ if (rval != WALK_NEXT)
+ return (rval);
+ }
+
+ return (wsp->walk_layer != NULL ? WALK_NEXT : WALK_DONE);
+}
+
/*ARGSUSED*/
static int
-umem_init_walkers(uintptr_t addr, const umem_cache_t *c, void *ignored)
+umem_init_walkers(uintptr_t addr, const umem_cache_t *c, int *sizes)
{
mdb_walker_t w;
char descr[64];
+ char name[64];
+ int i;
(void) mdb_snprintf(descr, sizeof (descr),
"walk the %s cache", c->cache_name);
@@ -124,6 +172,45 @@ umem_init_walkers(uintptr_t addr, const umem_cache_t *c, void *ignored)
if (mdb_add_walker(&w) == -1)
mdb_warn("failed to add %s walker", c->cache_name);
+ if (!(c->cache_flags & UMF_PTC))
+ return (WALK_NEXT);
+
+ /*
+ * For the per-thread cache walker, the address is the offset in the
+ * tm_roots[] array of the ulwp_t.
+ */
+ for (i = 0; sizes[i] != 0; i++) {
+ if (sizes[i] == c->cache_bufsize)
+ break;
+ }
+
+ if (sizes[i] == 0) {
+ mdb_warn("cache %s is cached per-thread, but could not find "
+ "size in umem_alloc_sizes\n", c->cache_name);
+ return (WALK_NEXT);
+ }
+
+ if (i >= NTMEMBASE) {
+ mdb_warn("index for %s (%d) exceeds root slots (%d)\n",
+ c->cache_name, i, NTMEMBASE);
+ return (WALK_NEXT);
+ }
+
+ (void) mdb_snprintf(name, sizeof (name),
+ "umem_ptc_%d", c->cache_bufsize);
+ (void) mdb_snprintf(descr, sizeof (descr),
+ "walk the per-thread cache for %s", c->cache_name);
+
+ w.walk_name = name;
+ w.walk_descr = descr;
+ w.walk_init = umem_ptc_walk_init;
+ w.walk_step = umem_ptc_walk_step;
+ w.walk_fini = NULL;
+ w.walk_init_arg = (void *)offsetof(ulwp_t, ul_tmem.tm_roots[i]);
+
+ if (mdb_add_walker(&w) == -1)
+ mdb_warn("failed to add %s walker", w.walk_name);
+
return (WALK_NEXT);
}
@@ -132,6 +219,8 @@ static void
umem_statechange_cb(void *arg)
{
static int been_ready = 0;
+ GElf_Sym sym;
+ int *sizes;
#ifndef _KMDB
leaky_cleanup(1); /* state changes invalidate leaky state */
@@ -147,7 +236,25 @@ umem_statechange_cb(void *arg)
return;
been_ready = 1;
- (void) mdb_walk("umem_cache", (mdb_walk_cb_t)umem_init_walkers, NULL);
+
+ /*
+ * In order to determine the tm_roots offset of any cache that is
+ * cached per-thread, we need to have the umem_alloc_sizes array.
+ * Read this, assuring that it is zero-terminated.
+ */
+ if (umem_lookup_by_name("umem_alloc_sizes", &sym) == -1) {
+ mdb_warn("unable to lookup 'umem_alloc_sizes'");
+ return;
+ }
+
+ sizes = mdb_zalloc(sym.st_size + sizeof (int), UM_SLEEP | UM_GC);
+
+ if (mdb_vread(sizes, sym.st_size, (uintptr_t)sym.st_value) == -1) {
+ mdb_warn("couldn't read 'umem_alloc_sizes'");
+ return;
+ }
+
+ (void) mdb_walk("umem_cache", (mdb_walk_cb_t)umem_init_walkers, sizes);
}
int
@@ -788,9 +895,9 @@ umem_estimate_allocated(uintptr_t addr, const umem_cache_t *cp)
} \
}
-int
+static int
umem_read_magazines(umem_cache_t *cp, uintptr_t addr,
- void ***maglistp, size_t *magcntp, size_t *magmaxp, int alloc_flags)
+ void ***maglistp, size_t *magcntp, size_t *magmaxp)
{
umem_magazine_t *ump, *mp;
void **maglist = NULL;
@@ -807,7 +914,7 @@ umem_read_magazines(umem_cache_t *cp, uintptr_t addr,
*maglistp = NULL;
*magcntp = 0;
*magmaxp = 0;
- return (WALK_NEXT);
+ return (0);
}
/*
@@ -828,11 +935,11 @@ umem_read_magazines(umem_cache_t *cp, uintptr_t addr,
if (magbsize >= PAGESIZE / 2) {
mdb_warn("magazine size for cache %p unreasonable (%x)\n",
addr, magbsize);
- return (WALK_ERR);
+ return (-1);
}
- maglist = mdb_alloc(magmax * sizeof (void *), alloc_flags);
- mp = mdb_alloc(magbsize, alloc_flags);
+ maglist = mdb_alloc(magmax * sizeof (void *), UM_SLEEP);
+ mp = mdb_alloc(magbsize, UM_SLEEP);
if (mp == NULL || maglist == NULL)
goto fail;
@@ -875,23 +982,80 @@ umem_read_magazines(umem_cache_t *cp, uintptr_t addr,
dprintf(("magazine layer: %d buffers\n", magcnt));
- if (!(alloc_flags & UM_GC))
- mdb_free(mp, magbsize);
+ mdb_free(mp, magbsize);
*maglistp = maglist;
*magcntp = magcnt;
*magmaxp = magmax;
- return (WALK_NEXT);
+ return (0);
fail:
- if (!(alloc_flags & UM_GC)) {
- if (mp)
- mdb_free(mp, magbsize);
- if (maglist)
- mdb_free(maglist, magmax * sizeof (void *));
+ if (mp)
+ mdb_free(mp, magbsize);
+ if (maglist)
+ mdb_free(maglist, magmax * sizeof (void *));
+
+ return (-1);
+}
+
+typedef struct umem_read_ptc_walk {
+ void **urpw_buf;
+ size_t urpw_cnt;
+ size_t urpw_max;
+} umem_read_ptc_walk_t;
+
+/*ARGSUSED*/
+static int
+umem_read_ptc_walk_buf(uintptr_t addr,
+ const void *ignored, umem_read_ptc_walk_t *urpw)
+{
+ if (urpw->urpw_cnt == urpw->urpw_max) {
+ size_t nmax = urpw->urpw_max ? (urpw->urpw_max << 1) : 1;
+ void **new = mdb_zalloc(nmax * sizeof (void *), UM_SLEEP);
+
+ if (nmax > 1) {
+ size_t osize = urpw->urpw_max * sizeof (void *);
+ bcopy(urpw->urpw_buf, new, osize);
+ mdb_free(urpw->urpw_buf, osize);
+ }
+
+ urpw->urpw_buf = new;
+ urpw->urpw_max = nmax;
}
- return (WALK_ERR);
+
+ urpw->urpw_buf[urpw->urpw_cnt++] = (void *)addr;
+
+ return (WALK_NEXT);
+}
+
+static int
+umem_read_ptc(umem_cache_t *cp,
+ void ***buflistp, size_t *bufcntp, size_t *bufmaxp)
+{
+ umem_read_ptc_walk_t urpw;
+ char walk[60];
+ int rval;
+
+ if (!(cp->cache_flags & UMF_PTC))
+ return (0);
+
+ (void) snprintf(walk, sizeof (walk), "umem_ptc_%d", cp->cache_bufsize);
+
+ urpw.urpw_buf = *buflistp;
+ urpw.urpw_cnt = *bufcntp;
+ urpw.urpw_max = *bufmaxp;
+
+ if ((rval = mdb_walk(walk,
+ (mdb_walk_cb_t)umem_read_ptc_walk_buf, &urpw)) == -1) {
+ mdb_warn("couldn't walk %s", walk);
+ }
+
+ *buflistp = urpw.urpw_buf;
+ *bufcntp = urpw.urpw_cnt;
+ *bufmaxp = urpw.urpw_max;
+
+ return (rval);
}
static int
@@ -1022,13 +1186,19 @@ umem_walk_init_common(mdb_walk_state_t *wsp, int type)
/*
* Read in the contents of the magazine layer
*/
- if (umem_read_magazines(cp, addr, &maglist, &magcnt, &magmax,
- UM_SLEEP) == WALK_ERR)
+ if (umem_read_magazines(cp, addr, &maglist, &magcnt, &magmax) != 0)
+ goto out2;
+
+ /*
+ * Read in the contents of the per-thread caches, if any
+ */
+ if (umem_read_ptc(cp, &maglist, &magcnt, &magmax) != 0)
goto out2;
/*
- * We have all of the buffers from the magazines; if we are walking
- * allocated buffers, sort them so we can bsearch them later.
+ * We have all of the buffers from the magazines and from the
+ * per-thread cache (if any); if we are walking allocated buffers,
+ * sort them so we can bsearch them later.
*/
if (type & UM_ALLOCATED)
qsort(maglist, magcnt, sizeof (void *), addrcmp);
diff --git a/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c b/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c
index 7c97ec8b42..fa6e1a578c 100644
--- a/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c
+++ b/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c
@@ -286,8 +286,10 @@ display_targets(struct mptsas_slots *s)
mdb_printf("devhdl %x, sasaddress %"PRIx64", phymask %x,"
"devinfo %x\n", ptgt->m_devhdl, ptgt->m_sas_wwn,
ptgt->m_phymask, ptgt->m_deviceinfo);
- mdb_printf("throttle %x, dr_flag %x, m_t_ncmds %x\n",
- ptgt->m_t_throttle, ptgt->m_dr_flag, ptgt->m_t_ncmds);
+ mdb_printf("throttle %x, dr_flag %x, m_t_ncmds %x, "
+ "enclosure %x, slot_num %x\n", ptgt->m_t_throttle,
+ ptgt->m_dr_flag, ptgt->m_t_ncmds, ptgt->m_enclosure,
+ ptgt->m_slot_num);
mdb_free(ptgt, sizeof (mptsas_target_t));
ptgt = (mptsas_target_t *)hash_traverse(
diff --git a/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c b/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c
new file mode 100644
index 0000000000..28ca6db42f
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c
@@ -0,0 +1,4120 @@
+/*
+ * 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) 2013, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * mdb(1M) module for debugging the V8 JavaScript engine. This implementation
+ * makes heavy use of metadata defined in the V8 binary for inspecting in-memory
+ * structures. Canned configurations can be manually loaded for V8 binaries
+ * that predate this metadata. See mdb_v8_cfg.c for details.
+ */
+
+/*
+ * We hard-code our MDB_API_VERSION to be 3 to allow this module to be
+ * compiled on systems with higher version numbers, but still allow the
+ * resulting binary object to be used on older systems. (We do not make use
+ * of functionality present in versions later than 3.) This is particularly
+ * important for mdb_v8 because (1) it's used in particular to debug
+ * application-level software and (2) it has a history of rapid evolution.
+ */
+#define MDB_API_VERSION 3
+
+#include <sys/mdb_modapi.h>
+#include <assert.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <libproc.h>
+#include <sys/avl.h>
+#include <alloca.h>
+
+#include "v8dbg.h"
+#include "v8cfg.h"
+
+#define offsetof(s, m) ((size_t)(&(((s *)0)->m)))
+
+/*
+ * The "v8_class" and "v8_field" structures describe the C++ classes used to
+ * represent V8 heap objects.
+ */
+typedef struct v8_class {
+ struct v8_class *v8c_next; /* list linkage */
+ struct v8_class *v8c_parent; /* parent class (inheritance) */
+ struct v8_field *v8c_fields; /* array of class fields */
+ size_t v8c_start; /* offset of first class field */
+ size_t v8c_end; /* offset of first subclass field */
+ char v8c_name[64]; /* heap object class name */
+} v8_class_t;
+
+typedef struct v8_field {
+ struct v8_field *v8f_next; /* list linkage */
+ ssize_t v8f_offset; /* field offset */
+ char v8f_name[64]; /* field name */
+ boolean_t v8f_isbyte; /* 1-byte int field */
+ boolean_t v8f_isstr; /* NUL-terminated string */
+} v8_field_t;
+
+/*
+ * Similarly, the "v8_enum" structure describes an enum from V8.
+ */
+typedef struct {
+ char v8e_name[64];
+ uint_t v8e_value;
+} v8_enum_t;
+
+/*
+ * During configuration, the dmod updates these globals with the actual set of
+ * classes, types, and frame types based on the debug metadata.
+ */
+static v8_class_t *v8_classes;
+
+static v8_enum_t v8_types[128];
+static int v8_next_type;
+
+static v8_enum_t v8_frametypes[16];
+static int v8_next_frametype;
+
+static int v8_silent;
+
+/*
+ * The following constants describe offsets from the frame pointer that are used
+ * to inspect each stack frame. They're initialized from the debug metadata.
+ */
+static ssize_t V8_OFF_FP_CONTEXT;
+static ssize_t V8_OFF_FP_MARKER;
+static ssize_t V8_OFF_FP_FUNCTION;
+static ssize_t V8_OFF_FP_ARGS;
+
+/*
+ * The following constants are used by macros defined in heap-dbg-common.h to
+ * examine the types of various V8 heap objects. In general, the macros should
+ * be preferred to using the constants directly. The values of these constants
+ * are initialized from the debug metadata.
+ */
+static intptr_t V8_FirstNonstringType;
+static intptr_t V8_IsNotStringMask;
+static intptr_t V8_StringTag;
+static intptr_t V8_NotStringTag;
+static intptr_t V8_StringEncodingMask;
+static intptr_t V8_TwoByteStringTag;
+static intptr_t V8_AsciiStringTag;
+static intptr_t V8_StringRepresentationMask;
+static intptr_t V8_SeqStringTag;
+static intptr_t V8_ConsStringTag;
+static intptr_t V8_ExternalStringTag;
+static intptr_t V8_FailureTag;
+static intptr_t V8_FailureTagMask;
+static intptr_t V8_HeapObjectTag;
+static intptr_t V8_HeapObjectTagMask;
+static intptr_t V8_SmiTag;
+static intptr_t V8_SmiTagMask;
+static intptr_t V8_SmiValueShift;
+static intptr_t V8_SmiShiftSize;
+static intptr_t V8_PointerSizeLog2;
+
+static intptr_t V8_ISSHARED_SHIFT;
+static intptr_t V8_DICT_SHIFT;
+static intptr_t V8_DICT_PREFIX_SIZE;
+static intptr_t V8_DICT_ENTRY_SIZE;
+static intptr_t V8_DICT_START_INDEX;
+static intptr_t V8_PROP_IDX_CONTENT;
+static intptr_t V8_PROP_IDX_FIRST;
+static intptr_t V8_PROP_TYPE_FIELD;
+static intptr_t V8_PROP_FIRST_PHANTOM;
+static intptr_t V8_PROP_TYPE_MASK;
+static intptr_t V8_PROP_DESC_KEY;
+static intptr_t V8_PROP_DESC_DETAILS;
+static intptr_t V8_PROP_DESC_VALUE;
+static intptr_t V8_PROP_DESC_SIZE;
+static intptr_t V8_TRANSITIONS_IDX_DESC;
+
+static intptr_t V8_TYPE_JSOBJECT = -1;
+static intptr_t V8_TYPE_JSARRAY = -1;
+static intptr_t V8_TYPE_FIXEDARRAY = -1;
+
+/*
+ * Although we have this information in v8_classes, the following offsets are
+ * defined explicitly because they're used directly in code below.
+ */
+static ssize_t V8_OFF_CODE_INSTRUCTION_SIZE;
+static ssize_t V8_OFF_CODE_INSTRUCTION_START;
+static ssize_t V8_OFF_CONSSTRING_FIRST;
+static ssize_t V8_OFF_CONSSTRING_SECOND;
+static ssize_t V8_OFF_EXTERNALSTRING_RESOURCE;
+static ssize_t V8_OFF_FIXEDARRAY_DATA;
+static ssize_t V8_OFF_FIXEDARRAY_LENGTH;
+static ssize_t V8_OFF_HEAPNUMBER_VALUE;
+static ssize_t V8_OFF_HEAPOBJECT_MAP;
+static ssize_t V8_OFF_JSARRAY_LENGTH;
+static ssize_t V8_OFF_JSDATE_VALUE;
+static ssize_t V8_OFF_JSFUNCTION_SHARED;
+static ssize_t V8_OFF_JSOBJECT_ELEMENTS;
+static ssize_t V8_OFF_JSOBJECT_PROPERTIES;
+static ssize_t V8_OFF_MAP_CONSTRUCTOR;
+static ssize_t V8_OFF_MAP_INOBJECT_PROPERTIES;
+static ssize_t V8_OFF_MAP_INSTANCE_ATTRIBUTES;
+static ssize_t V8_OFF_MAP_INSTANCE_DESCRIPTORS;
+static ssize_t V8_OFF_MAP_INSTANCE_SIZE;
+static ssize_t V8_OFF_MAP_BIT_FIELD3;
+static ssize_t V8_OFF_MAP_TRANSITIONS;
+static ssize_t V8_OFF_ODDBALL_TO_STRING;
+static ssize_t V8_OFF_SCRIPT_LINE_ENDS;
+static ssize_t V8_OFF_SCRIPT_NAME;
+static ssize_t V8_OFF_SEQASCIISTR_CHARS;
+static ssize_t V8_OFF_SEQONEBYTESTR_CHARS;
+static ssize_t V8_OFF_SHAREDFUNCTIONINFO_CODE;
+static ssize_t V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION;
+static ssize_t V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME;
+static ssize_t V8_OFF_SHAREDFUNCTIONINFO_LENGTH;
+static ssize_t V8_OFF_SHAREDFUNCTIONINFO_SCRIPT;
+static ssize_t V8_OFF_SHAREDFUNCTIONINFO_NAME;
+static ssize_t V8_OFF_STRING_LENGTH;
+
+#define NODE_OFF_EXTSTR_DATA 0x4 /* see node_string.h */
+
+#define V8_CONSTANT_OPTIONAL 1
+#define V8_CONSTANT_HASFALLBACK 2
+
+#define V8_CONSTANT_MAJORSHIFT 3
+#define V8_CONSTANT_MAJORMASK ((1 << 4) - 1)
+#define V8_CONSTANT_MAJOR(flags) \
+ (((flags) >> V8_CONSTANT_MAJORSHIFT) & V8_CONSTANT_MAJORMASK)
+
+#define V8_CONSTANT_MINORSHIFT 7
+#define V8_CONSTANT_MINORMASK ((1 << 9) - 1)
+#define V8_CONSTANT_MINOR(flags) \
+ (((flags) >> V8_CONSTANT_MINORSHIFT) & V8_CONSTANT_MINORMASK)
+
+#define V8_CONSTANT_FALLBACK(maj, min) \
+ (V8_CONSTANT_OPTIONAL | V8_CONSTANT_HASFALLBACK | \
+ ((maj) << V8_CONSTANT_MAJORSHIFT) | ((min) << V8_CONSTANT_MINORSHIFT))
+
+/*
+ * Table of constants used directly by this file.
+ */
+typedef struct v8_constant {
+ intptr_t *v8c_valp;
+ const char *v8c_symbol;
+ uint32_t v8c_flags;
+ intptr_t v8c_fallback;
+} v8_constant_t;
+
+static v8_constant_t v8_constants[] = {
+ { &V8_OFF_FP_CONTEXT, "v8dbg_off_fp_context" },
+ { &V8_OFF_FP_FUNCTION, "v8dbg_off_fp_function" },
+ { &V8_OFF_FP_MARKER, "v8dbg_off_fp_marker" },
+ { &V8_OFF_FP_ARGS, "v8dbg_off_fp_args" },
+
+ { &V8_FirstNonstringType, "v8dbg_FirstNonstringType" },
+ { &V8_IsNotStringMask, "v8dbg_IsNotStringMask" },
+ { &V8_StringTag, "v8dbg_StringTag" },
+ { &V8_NotStringTag, "v8dbg_NotStringTag" },
+ { &V8_StringEncodingMask, "v8dbg_StringEncodingMask" },
+ { &V8_TwoByteStringTag, "v8dbg_TwoByteStringTag" },
+ { &V8_AsciiStringTag, "v8dbg_AsciiStringTag" },
+ { &V8_StringRepresentationMask, "v8dbg_StringRepresentationMask" },
+ { &V8_SeqStringTag, "v8dbg_SeqStringTag" },
+ { &V8_ConsStringTag, "v8dbg_ConsStringTag" },
+ { &V8_ExternalStringTag, "v8dbg_ExternalStringTag" },
+ { &V8_FailureTag, "v8dbg_FailureTag" },
+ { &V8_FailureTagMask, "v8dbg_FailureTagMask" },
+ { &V8_HeapObjectTag, "v8dbg_HeapObjectTag" },
+ { &V8_HeapObjectTagMask, "v8dbg_HeapObjectTagMask" },
+ { &V8_SmiTag, "v8dbg_SmiTag" },
+ { &V8_SmiTagMask, "v8dbg_SmiTagMask" },
+ { &V8_SmiValueShift, "v8dbg_SmiValueShift" },
+ { &V8_SmiShiftSize, "v8dbg_SmiShiftSize",
+#ifdef _LP64
+ V8_CONSTANT_FALLBACK(0, 0), 31 },
+#else
+ V8_CONSTANT_FALLBACK(0, 0), 0 },
+#endif
+ { &V8_PointerSizeLog2, "v8dbg_PointerSizeLog2" },
+
+ { &V8_DICT_SHIFT, "v8dbg_dict_shift",
+ V8_CONSTANT_FALLBACK(3, 13), 24 },
+ { &V8_DICT_PREFIX_SIZE, "v8dbg_dict_prefix_size",
+ V8_CONSTANT_FALLBACK(3, 11), 2 },
+ { &V8_DICT_ENTRY_SIZE, "v8dbg_dict_entry_size",
+ V8_CONSTANT_FALLBACK(3, 11), 3 },
+ { &V8_DICT_START_INDEX, "v8dbg_dict_start_index",
+ V8_CONSTANT_FALLBACK(3, 11), 3 },
+ { &V8_ISSHARED_SHIFT, "v8dbg_isshared_shift",
+ V8_CONSTANT_FALLBACK(3, 11), 0 },
+ { &V8_PROP_IDX_FIRST, "v8dbg_prop_idx_first" },
+ { &V8_PROP_TYPE_FIELD, "v8dbg_prop_type_field" },
+ { &V8_PROP_FIRST_PHANTOM, "v8dbg_prop_type_first_phantom" },
+ { &V8_PROP_TYPE_MASK, "v8dbg_prop_type_mask" },
+ { &V8_PROP_IDX_CONTENT, "v8dbg_prop_idx_content",
+ V8_CONSTANT_OPTIONAL },
+ { &V8_PROP_DESC_KEY, "v8dbg_prop_desc_key",
+ V8_CONSTANT_FALLBACK(0, 0), 0 },
+ { &V8_PROP_DESC_DETAILS, "v8dbg_prop_desc_details",
+ V8_CONSTANT_FALLBACK(0, 0), 1 },
+ { &V8_PROP_DESC_VALUE, "v8dbg_prop_desc_value",
+ V8_CONSTANT_FALLBACK(0, 0), 2 },
+ { &V8_PROP_DESC_SIZE, "v8dbg_prop_desc_size",
+ V8_CONSTANT_FALLBACK(0, 0), 3 },
+ { &V8_TRANSITIONS_IDX_DESC, "v8dbg_transitions_idx_descriptors",
+ V8_CONSTANT_OPTIONAL },
+};
+
+static int v8_nconstants = sizeof (v8_constants) / sizeof (v8_constants[0]);
+
+typedef struct v8_offset {
+ ssize_t *v8o_valp;
+ const char *v8o_class;
+ const char *v8o_member;
+ boolean_t v8o_optional;
+} v8_offset_t;
+
+static v8_offset_t v8_offsets[] = {
+ { &V8_OFF_CODE_INSTRUCTION_SIZE,
+ "Code", "instruction_size" },
+ { &V8_OFF_CODE_INSTRUCTION_START,
+ "Code", "instruction_start" },
+ { &V8_OFF_CONSSTRING_FIRST,
+ "ConsString", "first" },
+ { &V8_OFF_CONSSTRING_SECOND,
+ "ConsString", "second" },
+ { &V8_OFF_EXTERNALSTRING_RESOURCE,
+ "ExternalString", "resource" },
+ { &V8_OFF_FIXEDARRAY_DATA,
+ "FixedArray", "data" },
+ { &V8_OFF_FIXEDARRAY_LENGTH,
+ "FixedArray", "length" },
+ { &V8_OFF_HEAPNUMBER_VALUE,
+ "HeapNumber", "value" },
+ { &V8_OFF_HEAPOBJECT_MAP,
+ "HeapObject", "map" },
+ { &V8_OFF_JSARRAY_LENGTH,
+ "JSArray", "length" },
+ { &V8_OFF_JSDATE_VALUE,
+ "JSDate", "value", B_TRUE },
+ { &V8_OFF_JSFUNCTION_SHARED,
+ "JSFunction", "shared" },
+ { &V8_OFF_JSOBJECT_ELEMENTS,
+ "JSObject", "elements" },
+ { &V8_OFF_JSOBJECT_PROPERTIES,
+ "JSObject", "properties" },
+ { &V8_OFF_MAP_CONSTRUCTOR,
+ "Map", "constructor" },
+ { &V8_OFF_MAP_INOBJECT_PROPERTIES,
+ "Map", "inobject_properties" },
+ { &V8_OFF_MAP_INSTANCE_ATTRIBUTES,
+ "Map", "instance_attributes" },
+ { &V8_OFF_MAP_INSTANCE_DESCRIPTORS,
+ "Map", "instance_descriptors", B_TRUE },
+ { &V8_OFF_MAP_TRANSITIONS,
+ "Map", "transitions", B_TRUE },
+ { &V8_OFF_MAP_INSTANCE_SIZE,
+ "Map", "instance_size" },
+ { &V8_OFF_MAP_BIT_FIELD3,
+ "Map", "bit_field3", B_TRUE },
+ { &V8_OFF_ODDBALL_TO_STRING,
+ "Oddball", "to_string" },
+ { &V8_OFF_SCRIPT_LINE_ENDS,
+ "Script", "line_ends" },
+ { &V8_OFF_SCRIPT_NAME,
+ "Script", "name" },
+ { &V8_OFF_SEQASCIISTR_CHARS,
+ "SeqAsciiString", "chars", B_TRUE },
+ { &V8_OFF_SEQONEBYTESTR_CHARS,
+ "SeqOneByteString", "chars", B_TRUE },
+ { &V8_OFF_SHAREDFUNCTIONINFO_CODE,
+ "SharedFunctionInfo", "code" },
+ { &V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION,
+ "SharedFunctionInfo", "function_token_position" },
+ { &V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME,
+ "SharedFunctionInfo", "inferred_name" },
+ { &V8_OFF_SHAREDFUNCTIONINFO_LENGTH,
+ "SharedFunctionInfo", "length" },
+ { &V8_OFF_SHAREDFUNCTIONINFO_NAME,
+ "SharedFunctionInfo", "name" },
+ { &V8_OFF_SHAREDFUNCTIONINFO_SCRIPT,
+ "SharedFunctionInfo", "script" },
+ { &V8_OFF_STRING_LENGTH,
+ "String", "length" },
+};
+
+static int v8_noffsets = sizeof (v8_offsets) / sizeof (v8_offsets[0]);
+
+static uintptr_t v8_major;
+static uintptr_t v8_minor;
+static uintptr_t v8_build;
+static uintptr_t v8_patch;
+
+static int autoconf_iter_symbol(mdb_symbol_t *, void *);
+static v8_class_t *conf_class_findcreate(const char *);
+static v8_field_t *conf_field_create(v8_class_t *, const char *, size_t);
+static char *conf_next_part(char *, char *);
+static int conf_update_parent(const char *);
+static int conf_update_field(v8_cfg_t *, const char *);
+static int conf_update_enum(v8_cfg_t *, const char *, const char *,
+ v8_enum_t *);
+static int conf_update_type(v8_cfg_t *, const char *);
+static int conf_update_frametype(v8_cfg_t *, const char *);
+static void conf_class_compute_offsets(v8_class_t *);
+
+static int read_typebyte(uint8_t *, uintptr_t);
+static int heap_offset(const char *, const char *, ssize_t *);
+
+/*
+ * Invoked when this dmod is initially loaded to load the set of classes, enums,
+ * and other constants from the metadata in the target binary.
+ */
+static int
+autoconfigure(v8_cfg_t *cfgp)
+{
+ v8_class_t *clp;
+ v8_enum_t *ep;
+ struct v8_constant *cnp;
+ int ii;
+ int failed = 0;
+
+ assert(v8_classes == NULL);
+
+ /*
+ * Iterate all global symbols looking for metadata.
+ */
+ if (cfgp->v8cfg_iter(cfgp, autoconf_iter_symbol, cfgp) != 0) {
+ mdb_warn("failed to autoconfigure V8 support\n");
+ return (-1);
+ }
+
+ /*
+ * By now we've configured all of the classes so we can update the
+ * "start" and "end" fields in each class with information from its
+ * parent class.
+ */
+ for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) {
+ if (clp->v8c_end != (size_t)-1)
+ continue;
+
+ conf_class_compute_offsets(clp);
+ };
+
+ /*
+ * Load various constants used directly in the module.
+ */
+ for (ii = 0; ii < v8_nconstants; ii++) {
+ cnp = &v8_constants[ii];
+
+ if (cfgp->v8cfg_readsym(cfgp,
+ cnp->v8c_symbol, cnp->v8c_valp) != -1) {
+ continue;
+ }
+
+ if (!(cnp->v8c_flags & V8_CONSTANT_OPTIONAL)) {
+ mdb_warn("failed to read \"%s\"", cnp->v8c_symbol);
+ failed++;
+ continue;
+ }
+
+ if (!(cnp->v8c_flags & V8_CONSTANT_HASFALLBACK) ||
+ v8_major < V8_CONSTANT_MAJOR(cnp->v8c_flags) ||
+ (v8_major == V8_CONSTANT_MAJOR(cnp->v8c_flags) &&
+ v8_minor < V8_CONSTANT_MINOR(cnp->v8c_flags))) {
+ *cnp->v8c_valp = -1;
+ continue;
+ }
+
+ /*
+ * We have a fallback -- and we know that the version satisfies
+ * the fallback's version constraints; use the fallback value.
+ */
+ *cnp->v8c_valp = cnp->v8c_fallback;
+ }
+
+ /*
+ * Load type values for well-known classes that we use a lot.
+ */
+ for (ep = v8_types; ep->v8e_name[0] != '\0'; ep++) {
+ if (strcmp(ep->v8e_name, "JSObject") == 0)
+ V8_TYPE_JSOBJECT = ep->v8e_value;
+
+ if (strcmp(ep->v8e_name, "JSArray") == 0)
+ V8_TYPE_JSARRAY = ep->v8e_value;
+
+ if (strcmp(ep->v8e_name, "FixedArray") == 0)
+ V8_TYPE_FIXEDARRAY = ep->v8e_value;
+ }
+
+ if (V8_TYPE_JSOBJECT == -1) {
+ mdb_warn("couldn't find JSObject type\n");
+ failed++;
+ }
+
+ if (V8_TYPE_JSARRAY == -1) {
+ mdb_warn("couldn't find JSArray type\n");
+ failed++;
+ }
+
+ if (V8_TYPE_FIXEDARRAY == -1) {
+ mdb_warn("couldn't find FixedArray type\n");
+ failed++;
+ }
+
+ /*
+ * Finally, load various class offsets.
+ */
+ for (ii = 0; ii < v8_noffsets; ii++) {
+ struct v8_offset *offp = &v8_offsets[ii];
+ const char *klass = offp->v8o_class;
+
+again:
+ if (heap_offset(klass, offp->v8o_member, offp->v8o_valp) == 0)
+ continue;
+
+ if (strcmp(klass, "FixedArray") == 0) {
+ /*
+ * The V8 included in node v0.6 uses a FixedArrayBase
+ * class to contain the "length" field, while the one
+ * in v0.4 has no such base class and stores the field
+ * directly in FixedArray; if we failed to derive
+ * the offset from FixedArray, try FixedArrayBase.
+ */
+ klass = "FixedArrayBase";
+ goto again;
+ }
+
+ if (offp->v8o_optional) {
+ *offp->v8o_valp = -1;
+ continue;
+ }
+
+ mdb_warn("couldn't find class \"%s\", field \"%s\"\n",
+ offp->v8o_class, offp->v8o_member);
+ failed++;
+ }
+
+ if (!((V8_OFF_SEQASCIISTR_CHARS != -1) ^
+ (V8_OFF_SEQONEBYTESTR_CHARS != -1))) {
+ mdb_warn("expected exactly one of SeqAsciiString and "
+ "SeqOneByteString to be defined\n");
+ failed++;
+ }
+
+ if (V8_OFF_SEQONEBYTESTR_CHARS != -1)
+ V8_OFF_SEQASCIISTR_CHARS = V8_OFF_SEQONEBYTESTR_CHARS;
+
+ return (failed ? -1 : 0);
+}
+
+/* ARGSUSED */
+static int
+autoconf_iter_symbol(mdb_symbol_t *symp, void *arg)
+{
+ v8_cfg_t *cfgp = arg;
+
+ if (strncmp(symp->sym_name, "v8dbg_parent_",
+ sizeof ("v8dbg_parent_") - 1) == 0)
+ return (conf_update_parent(symp->sym_name));
+
+ if (strncmp(symp->sym_name, "v8dbg_class_",
+ sizeof ("v8dbg_class_") - 1) == 0)
+ return (conf_update_field(cfgp, symp->sym_name));
+
+ if (strncmp(symp->sym_name, "v8dbg_type_",
+ sizeof ("v8dbg_type_") - 1) == 0)
+ return (conf_update_type(cfgp, symp->sym_name));
+
+ if (strncmp(symp->sym_name, "v8dbg_frametype_",
+ sizeof ("v8dbg_frametype_") - 1) == 0)
+ return (conf_update_frametype(cfgp, symp->sym_name));
+
+ return (0);
+}
+
+/*
+ * Extracts the next field of a string whose fields are separated by "__" (as
+ * the V8 metadata symbols are).
+ */
+static char *
+conf_next_part(char *buf, char *start)
+{
+ char *pp;
+
+ if ((pp = strstr(start, "__")) == NULL) {
+ mdb_warn("malformed symbol name: %s\n", buf);
+ return (NULL);
+ }
+
+ *pp = '\0';
+ return (pp + sizeof ("__") - 1);
+}
+
+static v8_class_t *
+conf_class_findcreate(const char *name)
+{
+ v8_class_t *clp, *iclp, *prev = NULL;
+ int cmp;
+
+ for (iclp = v8_classes; iclp != NULL; iclp = iclp->v8c_next) {
+ if ((cmp = strcmp(iclp->v8c_name, name)) == 0)
+ return (iclp);
+
+ if (cmp > 0)
+ break;
+
+ prev = iclp;
+ }
+
+ if ((clp = mdb_zalloc(sizeof (*clp), UM_NOSLEEP)) == NULL)
+ return (NULL);
+
+ (void) strlcpy(clp->v8c_name, name, sizeof (clp->v8c_name));
+ clp->v8c_end = (size_t)-1;
+ clp->v8c_next = iclp;
+
+ if (prev != NULL) {
+ prev->v8c_next = clp;
+ } else {
+ v8_classes = clp;
+ }
+
+ return (clp);
+}
+
+static v8_field_t *
+conf_field_create(v8_class_t *clp, const char *name, size_t offset)
+{
+ v8_field_t *flp, *iflp;
+
+ if ((flp = mdb_zalloc(sizeof (*flp), UM_NOSLEEP)) == NULL)
+ return (NULL);
+
+ (void) strlcpy(flp->v8f_name, name, sizeof (flp->v8f_name));
+ flp->v8f_offset = offset;
+
+ if (clp->v8c_fields == NULL || clp->v8c_fields->v8f_offset > offset) {
+ flp->v8f_next = clp->v8c_fields;
+ clp->v8c_fields = flp;
+ return (flp);
+ }
+
+ for (iflp = clp->v8c_fields; iflp->v8f_next != NULL;
+ iflp = iflp->v8f_next) {
+ if (iflp->v8f_next->v8f_offset > offset)
+ break;
+ }
+
+ flp->v8f_next = iflp->v8f_next;
+ iflp->v8f_next = flp;
+ return (flp);
+}
+
+/*
+ * Given a "v8dbg_parent_X__Y", symbol, update the parent of class X to class Y.
+ * Note that neither class necessarily exists already.
+ */
+static int
+conf_update_parent(const char *symbol)
+{
+ char *pp, *qq;
+ char buf[128];
+ v8_class_t *clp, *pclp;
+
+ (void) strlcpy(buf, symbol, sizeof (buf));
+ pp = buf + sizeof ("v8dbg_parent_") - 1;
+ qq = conf_next_part(buf, pp);
+
+ if (qq == NULL)
+ return (-1);
+
+ clp = conf_class_findcreate(pp);
+ pclp = conf_class_findcreate(qq);
+
+ if (clp == NULL || pclp == NULL) {
+ mdb_warn("mdb_v8: out of memory\n");
+ return (-1);
+ }
+
+ clp->v8c_parent = pclp;
+ return (0);
+}
+
+/*
+ * Given a "v8dbg_class_CLASS__FIELD__TYPE", symbol, save field "FIELD" into
+ * class CLASS with the offset described by the symbol. Note that CLASS does
+ * not necessarily exist already.
+ */
+static int
+conf_update_field(v8_cfg_t *cfgp, const char *symbol)
+{
+ v8_class_t *clp;
+ v8_field_t *flp;
+ intptr_t offset;
+ char *pp, *qq, *tt;
+ char buf[128];
+
+ (void) strlcpy(buf, symbol, sizeof (buf));
+
+ pp = buf + sizeof ("v8dbg_class_") - 1;
+ qq = conf_next_part(buf, pp);
+
+ if (qq == NULL || (tt = conf_next_part(buf, qq)) == NULL)
+ return (-1);
+
+ if (cfgp->v8cfg_readsym(cfgp, symbol, &offset) == -1) {
+ mdb_warn("failed to read symbol \"%s\"", symbol);
+ return (-1);
+ }
+
+ if ((clp = conf_class_findcreate(pp)) == NULL ||
+ (flp = conf_field_create(clp, qq, (size_t)offset)) == NULL)
+ return (-1);
+
+ if (strcmp(tt, "int") == 0)
+ flp->v8f_isbyte = B_TRUE;
+
+ if (strcmp(tt, "char") == 0)
+ flp->v8f_isstr = B_TRUE;
+
+ return (0);
+}
+
+static int
+conf_update_enum(v8_cfg_t *cfgp, const char *symbol, const char *name,
+ v8_enum_t *enp)
+{
+ intptr_t value;
+
+ if (cfgp->v8cfg_readsym(cfgp, symbol, &value) == -1) {
+ mdb_warn("failed to read symbol \"%s\"", symbol);
+ return (-1);
+ }
+
+ enp->v8e_value = (int)value;
+ (void) strlcpy(enp->v8e_name, name, sizeof (enp->v8e_name));
+ return (0);
+}
+
+/*
+ * Given a "v8dbg_type_TYPENAME" constant, save the type name in v8_types. Note
+ * that this enum has multiple integer values with the same string label.
+ */
+static int
+conf_update_type(v8_cfg_t *cfgp, const char *symbol)
+{
+ char *klass;
+ v8_enum_t *enp;
+ char buf[128];
+
+ if (v8_next_type > sizeof (v8_types) / sizeof (v8_types[0])) {
+ mdb_warn("too many V8 types\n");
+ return (-1);
+ }
+
+ (void) strlcpy(buf, symbol, sizeof (buf));
+
+ klass = buf + sizeof ("v8dbg_type_") - 1;
+ if (conf_next_part(buf, klass) == NULL)
+ return (-1);
+
+ enp = &v8_types[v8_next_type++];
+ return (conf_update_enum(cfgp, symbol, klass, enp));
+}
+
+/*
+ * Given a "v8dbg_frametype_TYPENAME" constant, save the frame type in
+ * v8_frametypes.
+ */
+static int
+conf_update_frametype(v8_cfg_t *cfgp, const char *symbol)
+{
+ const char *frametype;
+ v8_enum_t *enp;
+
+ if (v8_next_frametype >
+ sizeof (v8_frametypes) / sizeof (v8_frametypes[0])) {
+ mdb_warn("too many V8 frame types\n");
+ return (-1);
+ }
+
+ enp = &v8_frametypes[v8_next_frametype++];
+ frametype = symbol + sizeof ("v8dbg_frametype_") - 1;
+ return (conf_update_enum(cfgp, symbol, frametype, enp));
+}
+
+/*
+ * Now that all classes have been loaded, update the "start" and "end" fields of
+ * each class based on the values of its parent class.
+ */
+static void
+conf_class_compute_offsets(v8_class_t *clp)
+{
+ v8_field_t *flp;
+
+ assert(clp->v8c_start == 0);
+ assert(clp->v8c_end == (size_t)-1);
+
+ if (clp->v8c_parent != NULL) {
+ if (clp->v8c_parent->v8c_end == (size_t)-1)
+ conf_class_compute_offsets(clp->v8c_parent);
+
+ clp->v8c_start = clp->v8c_parent->v8c_end;
+ }
+
+ if (clp->v8c_fields == NULL) {
+ clp->v8c_end = clp->v8c_start;
+ return;
+ }
+
+ for (flp = clp->v8c_fields; flp->v8f_next != NULL; flp = flp->v8f_next)
+ ;
+
+ if (flp == NULL)
+ clp->v8c_end = clp->v8c_start;
+ else
+ clp->v8c_end = flp->v8f_offset + sizeof (uintptr_t);
+}
+
+/*
+ * Utility functions
+ */
+#define JSSTR_NONE 0
+#define JSSTR_NUDE JSSTR_NONE
+#define JSSTR_VERBOSE 0x1
+#define JSSTR_QUOTED 0x2
+
+static int jsstr_print(uintptr_t, uint_t, char **, size_t *);
+static boolean_t jsobj_is_undefined(uintptr_t addr);
+
+static const char *
+enum_lookup_str(v8_enum_t *enums, int val, const char *dflt)
+{
+ v8_enum_t *ep;
+
+ for (ep = enums; ep->v8e_name[0] != '\0'; ep++) {
+ if (ep->v8e_value == val)
+ return (ep->v8e_name);
+ }
+
+ return (dflt);
+}
+
+static void
+enum_print(v8_enum_t *enums)
+{
+ v8_enum_t *itp;
+
+ for (itp = enums; itp->v8e_name[0] != '\0'; itp++)
+ mdb_printf("%-30s = 0x%02x\n", itp->v8e_name, itp->v8e_value);
+}
+
+/*
+ * b[v]snprintf behave like [v]snprintf(3c), except that they update the buffer
+ * and length arguments based on how much buffer space is used by the operation.
+ * This makes it much easier to combine multiple calls in sequence without
+ * worrying about buffer overflow.
+ */
+static size_t
+bvsnprintf(char **bufp, size_t *buflenp, const char *format, va_list alist)
+{
+ size_t rv, len;
+
+ if (*buflenp == 0)
+ return (vsnprintf(NULL, 0, format, alist));
+
+ rv = vsnprintf(*bufp, *buflenp, format, alist);
+
+ len = MIN(rv, *buflenp);
+ *buflenp -= len;
+ *bufp += len;
+
+ return (len);
+}
+
+static size_t
+bsnprintf(char **bufp, size_t *buflenp, const char *format, ...)
+{
+ va_list alist;
+ size_t rv;
+
+ va_start(alist, format);
+ rv = bvsnprintf(bufp, buflenp, format, alist);
+ va_end(alist);
+
+ return (rv);
+}
+
+static void
+v8_warn(const char *format, ...)
+{
+ char buf[512];
+ va_list alist;
+ int len;
+
+ if (v8_silent)
+ return;
+
+ va_start(alist, format);
+ (void) vsnprintf(buf, sizeof (buf), format, alist);
+ va_end(alist);
+
+ /*
+ * This is made slightly annoying because we need to effectively
+ * preserve the original format string to allow for mdb to use the
+ * new-line at the end to indicate that strerror should be elided.
+ */
+ if ((len = strlen(format)) > 0 && format[len - 1] == '\n') {
+ buf[strlen(buf) - 1] = '\0';
+ mdb_warn("%s\n", buf);
+ } else {
+ mdb_warn("%s", buf);
+ }
+}
+
+/*
+ * Returns in "offp" the offset of field "field" in C++ class "klass".
+ */
+static int
+heap_offset(const char *klass, const char *field, ssize_t *offp)
+{
+ v8_class_t *clp;
+ v8_field_t *flp;
+
+ for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) {
+ if (strcmp(klass, clp->v8c_name) == 0)
+ break;
+ }
+
+ if (clp == NULL)
+ return (-1);
+
+ for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next) {
+ if (strcmp(field, flp->v8f_name) == 0)
+ break;
+ }
+
+ if (flp == NULL)
+ return (-1);
+
+ *offp = V8_OFF_HEAP(flp->v8f_offset);
+ return (0);
+}
+
+/*
+ * Assuming "addr" is an instance of the C++ heap class "klass", read into *valp
+ * the pointer-sized value of field "field".
+ */
+static int
+read_heap_ptr(uintptr_t *valp, uintptr_t addr, ssize_t off)
+{
+ if (mdb_vread(valp, sizeof (*valp), addr + off) == -1) {
+ v8_warn("failed to read offset %d from %p", off, addr);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Like read_heap_ptr, but assume the field is an SMI and store the actual value
+ * into *valp rather than the encoded representation.
+ */
+static int
+read_heap_smi(uintptr_t *valp, uintptr_t addr, ssize_t off)
+{
+ if (read_heap_ptr(valp, addr, off) != 0)
+ return (-1);
+
+ if (!V8_IS_SMI(*valp)) {
+ v8_warn("expected SMI, got %p\n", *valp);
+ return (-1);
+ }
+
+ *valp = V8_SMI_VALUE(*valp);
+
+ return (0);
+}
+
+static int
+read_heap_double(double *valp, uintptr_t addr, ssize_t off)
+{
+ if (mdb_vread(valp, sizeof (*valp), addr + off) == -1) {
+ v8_warn("failed to read heap value at %p", addr + off);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Assuming "addr" refers to a FixedArray, return a newly-allocated array
+ * representing its contents.
+ */
+static int
+read_heap_array(uintptr_t addr, uintptr_t **retp, size_t *lenp, int flags)
+{
+ uint8_t type;
+ uintptr_t len;
+
+ if (!V8_IS_HEAPOBJECT(addr))
+ return (-1);
+
+ if (read_typebyte(&type, addr) != 0)
+ return (-1);
+
+ if (type != V8_TYPE_FIXEDARRAY)
+ return (-1);
+
+ if (read_heap_smi(&len, addr, V8_OFF_FIXEDARRAY_LENGTH) != 0)
+ return (-1);
+
+ *lenp = len;
+
+ if (len == 0) {
+ *retp = NULL;
+ return (0);
+ }
+
+ if ((*retp = mdb_zalloc(len * sizeof (uintptr_t), flags)) == NULL)
+ return (-1);
+
+ if (mdb_vread(*retp, len * sizeof (uintptr_t),
+ addr + V8_OFF_FIXEDARRAY_DATA) == -1) {
+ if (!(flags & UM_GC))
+ mdb_free(*retp, len * sizeof (uintptr_t));
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+read_heap_byte(uint8_t *valp, uintptr_t addr, ssize_t off)
+{
+ if (mdb_vread(valp, sizeof (*valp), addr + off) == -1) {
+ v8_warn("failed to read heap value at %p", addr + off);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Given a heap object, returns in *valp the byte describing the type of the
+ * object. This is shorthand for first retrieving the Map at the start of the
+ * heap object and then retrieving the type byte from the Map object.
+ */
+static int
+read_typebyte(uint8_t *valp, uintptr_t addr)
+{
+ uintptr_t mapaddr;
+ ssize_t off = V8_OFF_HEAPOBJECT_MAP;
+
+ if (mdb_vread(&mapaddr, sizeof (mapaddr), addr + off) == -1) {
+ v8_warn("failed to read type of %p", addr);
+ return (-1);
+ }
+
+ if (!V8_IS_HEAPOBJECT(mapaddr)) {
+ v8_warn("object map is not a heap object\n");
+ return (-1);
+ }
+
+ if (read_heap_byte(valp, mapaddr, V8_OFF_MAP_INSTANCE_ATTRIBUTES) == -1)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * Given a heap object, returns in *valp the size of the object. For
+ * variable-size objects, returns an undefined value.
+ */
+static int
+read_size(size_t *valp, uintptr_t addr)
+{
+ uintptr_t mapaddr;
+ uint8_t size;
+
+ if (read_heap_ptr(&mapaddr, addr, V8_OFF_HEAPOBJECT_MAP) != 0)
+ return (-1);
+
+ if (!V8_IS_HEAPOBJECT(mapaddr)) {
+ v8_warn("heap object map is not itself a heap object\n");
+ return (-1);
+ }
+
+ if (read_heap_byte(&size, mapaddr, V8_OFF_MAP_INSTANCE_SIZE) != 0)
+ return (-1);
+
+ *valp = size << V8_PointerSizeLog2;
+ return (0);
+}
+
+/*
+ * Assuming "addr" refers to a FixedArray that is implementing a
+ * StringDictionary, iterate over its contents calling the specified function
+ * with key and value.
+ */
+static int
+read_heap_dict(uintptr_t addr,
+ int (*func)(const char *, uintptr_t, void *), void *arg)
+{
+ uint8_t type;
+ uintptr_t len;
+ char buf[512];
+ char *bufp;
+ int rval = -1;
+ uintptr_t *dict, ndict, i;
+ const char *typename;
+
+ if (read_heap_array(addr, &dict, &ndict, UM_SLEEP) != 0)
+ return (-1);
+
+ if (V8_DICT_ENTRY_SIZE < 2) {
+ v8_warn("dictionary entry size (%d) is too small for a "
+ "key and value\n", V8_DICT_ENTRY_SIZE);
+ goto out;
+ }
+
+ for (i = V8_DICT_START_INDEX + V8_DICT_PREFIX_SIZE; i < ndict;
+ i += V8_DICT_ENTRY_SIZE) {
+ /*
+ * The layout here is key, value, details. (This is hardcoded
+ * in Dictionary<Shape, Key>::SetEntry().)
+ */
+ if (jsobj_is_undefined(dict[i]))
+ continue;
+
+ if (read_typebyte(&type, dict[i]) != 0)
+ goto out;
+
+ typename = enum_lookup_str(v8_types, type, NULL);
+
+ if (typename != NULL && strcmp(typename, "Oddball") == 0) {
+ /*
+ * In some cases, the key can (apparently) be a hole;
+ * assume that any Oddball in the key field falls into
+ * this case and skip over it.
+ */
+ continue;
+ }
+
+ if (!V8_TYPE_STRING(type))
+ goto out;
+
+ bufp = buf;
+ len = sizeof (buf);
+
+ if (jsstr_print(dict[i], JSSTR_NUDE, &bufp, &len) != 0)
+ goto out;
+
+ if (func(buf, dict[i + 1], arg) == -1)
+ goto out;
+ }
+
+ rval = 0;
+out:
+ mdb_free(dict, ndict * sizeof (uintptr_t));
+
+ return (rval);
+}
+
+/*
+ * Returns in "buf" a description of the type of "addr" suitable for printing.
+ */
+static int
+obj_jstype(uintptr_t addr, char **bufp, size_t *lenp, uint8_t *typep)
+{
+ uint8_t typebyte;
+ uintptr_t strptr;
+ const char *typename;
+
+ if (V8_IS_FAILURE(addr)) {
+ if (typep)
+ *typep = 0;
+ (void) bsnprintf(bufp, lenp, "'Failure' object");
+ return (0);
+ }
+
+ if (V8_IS_SMI(addr)) {
+ if (typep)
+ *typep = 0;
+ (void) bsnprintf(bufp, lenp, "SMI: value = %d",
+ V8_SMI_VALUE(addr));
+ return (0);
+ }
+
+ if (read_typebyte(&typebyte, addr) != 0)
+ return (-1);
+
+ if (typep)
+ *typep = typebyte;
+
+ typename = enum_lookup_str(v8_types, typebyte, "<unknown>");
+ (void) bsnprintf(bufp, lenp, typename);
+
+ if (strcmp(typename, "Oddball") == 0) {
+ if (read_heap_ptr(&strptr, addr,
+ V8_OFF_ODDBALL_TO_STRING) != -1) {
+ (void) bsnprintf(bufp, lenp, ": \"");
+ (void) jsstr_print(strptr, JSSTR_NUDE, bufp, lenp);
+ (void) bsnprintf(bufp, lenp, "\"");
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Print out the fields of the given object that come from the given class.
+ */
+static int
+obj_print_fields(uintptr_t baddr, v8_class_t *clp)
+{
+ v8_field_t *flp;
+ uintptr_t addr, value;
+ int rv;
+ char *bufp;
+ size_t len;
+ uint8_t type;
+ char buf[256];
+
+ for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next) {
+ bufp = buf;
+ len = sizeof (buf);
+
+ addr = baddr + V8_OFF_HEAP(flp->v8f_offset);
+
+ if (flp->v8f_isstr) {
+ if (mdb_readstr(buf, sizeof (buf), addr) == -1) {
+ mdb_printf("%p %s (unreadable)\n",
+ addr, flp->v8f_name);
+ continue;
+ }
+
+ mdb_printf("%p %s = \"%s\"\n",
+ addr, flp->v8f_name, buf);
+ continue;
+ }
+
+ if (flp->v8f_isbyte) {
+ uint8_t sv;
+ if (mdb_vread(&sv, sizeof (sv), addr) == -1) {
+ mdb_printf("%p %s (unreadable)\n",
+ addr, flp->v8f_name);
+ continue;
+ }
+
+ mdb_printf("%p %s = 0x%x\n", addr, flp->v8f_name, sv);
+ continue;
+ }
+
+ rv = mdb_vread((void *)&value, sizeof (value), addr);
+
+ if (rv != sizeof (value) ||
+ obj_jstype(value, &bufp, &len, &type) != 0) {
+ mdb_printf("%p %s (unreadable)\n", addr, flp->v8f_name);
+ continue;
+ }
+
+ if (type != 0 && V8_TYPE_STRING(type)) {
+ (void) bsnprintf(&bufp, &len, ": ");
+ (void) jsstr_print(value, JSSTR_QUOTED, &bufp, &len);
+ }
+
+ mdb_printf("%p %s = %p (%s)\n", addr, flp->v8f_name, value,
+ buf);
+ }
+
+ return (DCMD_OK);
+}
+
+/*
+ * Print out all fields of the given object, starting with the root of the class
+ * hierarchy and working down the most specific type.
+ */
+static int
+obj_print_class(uintptr_t addr, v8_class_t *clp)
+{
+ int rv = 0;
+
+ /*
+ * If we have no fields, we just print a simple inheritance hierarchy.
+ * If we have fields but our parent doesn't, our header includes the
+ * inheritance hierarchy.
+ */
+ if (clp->v8c_end == 0) {
+ mdb_printf("%s ", clp->v8c_name);
+
+ if (clp->v8c_parent != NULL) {
+ mdb_printf("< ");
+ (void) obj_print_class(addr, clp->v8c_parent);
+ }
+
+ return (0);
+ }
+
+ mdb_printf("%p %s", addr, clp->v8c_name);
+
+ if (clp->v8c_start == 0 && clp->v8c_parent != NULL) {
+ mdb_printf(" < ");
+ (void) obj_print_class(addr, clp->v8c_parent);
+ }
+
+ mdb_printf(" {\n");
+ (void) mdb_inc_indent(4);
+
+ if (clp->v8c_start > 0 && clp->v8c_parent != NULL)
+ rv = obj_print_class(addr, clp->v8c_parent);
+
+ rv |= obj_print_fields(addr, clp);
+ (void) mdb_dec_indent(4);
+ mdb_printf("}\n");
+
+ return (rv);
+}
+
+/*
+ * Print the ASCII string for the given ASCII JS string, expanding ConsStrings
+ * and ExternalStrings as needed.
+ */
+static int jsstr_print_seq(uintptr_t, uint_t, char **, size_t *);
+static int jsstr_print_cons(uintptr_t, uint_t, char **, size_t *);
+static int jsstr_print_external(uintptr_t, uint_t, char **, size_t *);
+
+static int
+jsstr_print(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
+{
+ uint8_t typebyte;
+ int err = 0;
+ char *lbufp;
+ size_t llen;
+ char buf[64];
+ boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE;
+
+ if (read_typebyte(&typebyte, addr) != 0)
+ return (0);
+
+ if (!V8_TYPE_STRING(typebyte)) {
+ (void) bsnprintf(bufp, lenp, "<not a string>");
+ return (0);
+ }
+
+ if (!V8_STRENC_ASCII(typebyte)) {
+ (void) bsnprintf(bufp, lenp, "<two-byte string>");
+ return (0);
+ }
+
+ if (verbose) {
+ lbufp = buf;
+ llen = sizeof (buf);
+ (void) obj_jstype(addr, &lbufp, &llen, NULL);
+ mdb_printf("%s\n", buf);
+ (void) mdb_inc_indent(4);
+ }
+
+ if (V8_STRREP_SEQ(typebyte))
+ err = jsstr_print_seq(addr, flags, bufp, lenp);
+ else if (V8_STRREP_CONS(typebyte))
+ err = jsstr_print_cons(addr, flags, bufp, lenp);
+ else if (V8_STRREP_EXT(typebyte))
+ err = jsstr_print_external(addr, flags, bufp, lenp);
+ else {
+ (void) bsnprintf(bufp, lenp, "<unknown string type>");
+ err = -1;
+ }
+
+ if (verbose)
+ (void) mdb_dec_indent(4);
+
+ return (err);
+}
+
+static int
+jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
+{
+ /*
+ * To allow the caller to allocate a very large buffer for strings,
+ * we'll allocate a buffer sized based on our input, making it at
+ * least enough space for our ellipsis and at most 256K.
+ */
+ uintptr_t len, rlen, blen = *lenp + sizeof ("[...]") + 1;
+ char *buf = alloca(MIN(blen, 256 * 1024));
+ boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE;
+ boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE;
+
+ if (read_heap_smi(&len, addr, V8_OFF_STRING_LENGTH) != 0)
+ return (-1);
+
+ rlen = len <= blen - 1 ? len : blen - sizeof ("[...]");
+
+ if (verbose)
+ mdb_printf("length: %d, will read: %d\n", len, rlen);
+
+ buf[0] = '\0';
+
+ if (rlen > 0 && mdb_readstr(buf, rlen + 1,
+ addr + V8_OFF_SEQASCIISTR_CHARS) == -1) {
+ v8_warn("failed to read SeqString data");
+ return (-1);
+ }
+
+ if (rlen != len)
+ (void) strlcat(buf, "[...]", blen);
+
+ if (verbose)
+ mdb_printf("value: \"%s\"\n", buf);
+
+ (void) bsnprintf(bufp, lenp, "%s%s%s",
+ quoted ? "\"" : "", buf, quoted ? "\"" : "");
+
+ return (0);
+}
+
+static int
+jsstr_print_cons(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
+{
+ boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE;
+ boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE;
+ uintptr_t ptr1, ptr2;
+
+ if (read_heap_ptr(&ptr1, addr, V8_OFF_CONSSTRING_FIRST) != 0 ||
+ read_heap_ptr(&ptr2, addr, V8_OFF_CONSSTRING_SECOND) != 0)
+ return (-1);
+
+ if (verbose) {
+ mdb_printf("ptr1: %p\n", ptr1);
+ mdb_printf("ptr2: %p\n", ptr2);
+ }
+
+ if (quoted)
+ (void) bsnprintf(bufp, lenp, "\"");
+
+ if (jsstr_print(ptr1, verbose, bufp, lenp) != 0)
+ return (-1);
+
+ if (jsstr_print(ptr2, verbose, bufp, lenp) != 0)
+ return (-1);
+
+ if (quoted)
+ (void) bsnprintf(bufp, lenp, "\"");
+
+ return (0);
+}
+
+static int
+jsstr_print_external(uintptr_t addr, uint_t flags, char **bufp,
+ size_t *lenp)
+{
+ uintptr_t ptr1, ptr2;
+ size_t blen = *lenp + 1;
+ char *buf = alloca(blen);
+ boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE;
+
+ if (flags & JSSTR_VERBOSE)
+ mdb_printf("assuming Node.js string\n");
+
+ if (read_heap_ptr(&ptr1, addr, V8_OFF_EXTERNALSTRING_RESOURCE) != 0)
+ return (-1);
+
+ if (mdb_vread(&ptr2, sizeof (ptr2),
+ ptr1 + NODE_OFF_EXTSTR_DATA) == -1) {
+ v8_warn("failed to read node external pointer: %p",
+ ptr1 + NODE_OFF_EXTSTR_DATA);
+ return (-1);
+ }
+
+ if (mdb_readstr(buf, blen, ptr2) == -1) {
+ v8_warn("failed to read ExternalString data");
+ return (-1);
+ }
+
+ if (buf[0] != '\0' && !isascii(buf[0])) {
+ v8_warn("failed to read ExternalString ascii data\n");
+ return (-1);
+ }
+
+ (void) bsnprintf(bufp, lenp, "%s%s%s",
+ quoted ? "\"" : "", buf, quoted ? "\"" : "");
+
+ return (0);
+}
+
+/*
+ * Returns true if the given address refers to the "undefined" object. Returns
+ * false on failure (since we shouldn't fail on the actual "undefined" value).
+ */
+static boolean_t
+jsobj_is_undefined(uintptr_t addr)
+{
+ uint8_t type;
+ uintptr_t strptr;
+ const char *typename;
+ char buf[16];
+ char *bufp = buf;
+ size_t len = sizeof (buf);
+
+ v8_silent++;
+
+ if (read_typebyte(&type, addr) != 0) {
+ v8_silent--;
+ return (B_FALSE);
+ }
+
+ v8_silent--;
+
+ typename = enum_lookup_str(v8_types, type, "<unknown>");
+ if (strcmp(typename, "Oddball") != 0)
+ return (B_FALSE);
+
+ if (read_heap_ptr(&strptr, addr, V8_OFF_ODDBALL_TO_STRING) == -1)
+ return (B_FALSE);
+
+ if (jsstr_print(strptr, JSSTR_NUDE, &bufp, &len) != 0)
+ return (B_FALSE);
+
+ return (strcmp(buf, "undefined") == 0);
+}
+
+static int
+jsobj_properties(uintptr_t addr,
+ int (*func)(const char *, uintptr_t, void *), void *arg)
+{
+ uintptr_t ptr, map;
+ uintptr_t *props = NULL, *descs = NULL, *content = NULL, *trans;
+ size_t size, nprops, ndescs, ncontent, ntrans;
+ ssize_t ii, rndescs;
+ uint8_t type, ninprops;
+ int rval = -1;
+ size_t ps = sizeof (uintptr_t);
+ ssize_t off;
+
+ /*
+ * Objects have either "fast" properties represented with a FixedArray
+ * or slow properties represented with a Dictionary.
+ */
+ if (mdb_vread(&ptr, ps, addr + V8_OFF_JSOBJECT_PROPERTIES) == -1)
+ return (-1);
+
+ if (read_typebyte(&type, ptr) != 0)
+ return (-1);
+
+ if (type != V8_TYPE_FIXEDARRAY) {
+ /*
+ * If our properties aren't a fixed array, we'll emit a member
+ * that contains the type name, but with a NULL value.
+ */
+ char buf[256];
+
+ (void) mdb_snprintf(buf, sizeof (buf), "<%s>",
+ enum_lookup_str(v8_types, type, "unknown"));
+
+ return (func(buf, NULL, arg));
+ }
+
+ /*
+ * To iterate the properties, we need to examine the instance
+ * descriptors of the associated Map object. Depending on the version
+ * of V8, this might be found directly from the map -- or indirectly
+ * via the transitions array.
+ */
+ if (mdb_vread(&map, ps, addr + V8_OFF_HEAPOBJECT_MAP) == -1)
+ goto err;
+
+ if (V8_DICT_SHIFT != -1) {
+ uintptr_t bit_field3;
+
+ if (mdb_vread(&bit_field3, sizeof (bit_field3),
+ map + V8_OFF_MAP_BIT_FIELD3) == -1)
+ goto err;
+
+ if (V8_SMI_VALUE(bit_field3) & (1 << V8_DICT_SHIFT))
+ return (read_heap_dict(ptr, func, arg));
+ } else if (V8_OFF_MAP_INSTANCE_DESCRIPTORS != -1) {
+ uintptr_t bit_field3;
+
+ if (mdb_vread(&bit_field3, sizeof (bit_field3),
+ map + V8_OFF_MAP_INSTANCE_DESCRIPTORS) == -1)
+ goto err;
+
+ if (V8_SMI_VALUE(bit_field3) == (1 << V8_ISSHARED_SHIFT)) {
+ /*
+ * On versions of V8 prior to that used in 0.10,
+ * the instance descriptors were overloaded to also
+ * be bit_field3 -- and there was no way from that
+ * field to infer a dictionary type. Because we
+ * can't determine if the map is actually the
+ * hash_table_map, we assume that if it's an object
+ * that has kIsShared set, that it is in fact a
+ * dictionary -- an assumption that is assuredly in
+ * error in some cases.
+ */
+ return (read_heap_dict(ptr, func, arg));
+ }
+ }
+
+ if (read_heap_array(ptr, &props, &nprops, UM_SLEEP) != 0)
+ goto err;
+
+ if ((off = V8_OFF_MAP_INSTANCE_DESCRIPTORS) == -1) {
+ if (V8_OFF_MAP_TRANSITIONS == -1 ||
+ V8_TRANSITIONS_IDX_DESC == -1 ||
+ V8_PROP_IDX_CONTENT != -1) {
+ mdb_warn("missing instance_descriptors, but did "
+ "not find expected transitions array metadata; "
+ "cannot read properties\n");
+ goto err;
+ }
+
+ off = V8_OFF_MAP_TRANSITIONS;
+ }
+
+ if (mdb_vread(&ptr, ps, map + off) == -1)
+ goto err;
+
+ if (V8_OFF_MAP_TRANSITIONS != -1) {
+ if (read_heap_array(ptr, &trans, &ntrans, UM_SLEEP) != 0)
+ goto err;
+
+ ptr = trans[V8_TRANSITIONS_IDX_DESC];
+ mdb_free(trans, ntrans * sizeof (uintptr_t));
+ }
+
+ if (read_heap_array(ptr, &descs, &ndescs, UM_SLEEP) != 0)
+ goto err;
+
+ if (read_size(&size, addr) != 0)
+ size = 0;
+
+ if (mdb_vread(&ninprops, 1, map + V8_OFF_MAP_INOBJECT_PROPERTIES) == -1)
+ goto err;
+
+ if (V8_PROP_IDX_CONTENT != -1 && V8_PROP_IDX_CONTENT < ndescs &&
+ read_heap_array(descs[V8_PROP_IDX_CONTENT], &content,
+ &ncontent, UM_SLEEP) != 0)
+ goto err;
+
+ if (V8_PROP_IDX_CONTENT == -1) {
+ /*
+ * On node v0.8 and later, the content is not stored in an
+ * orthogonal FixedArray, but rather with the descriptors.
+ */
+ content = descs;
+ ncontent = ndescs;
+ rndescs = ndescs > V8_PROP_IDX_FIRST ?
+ (ndescs - V8_PROP_IDX_FIRST) / V8_PROP_DESC_SIZE : 0;
+ } else {
+ rndescs = ndescs - V8_PROP_IDX_FIRST;
+ }
+
+ for (ii = 0; ii < rndescs; ii++) {
+ uintptr_t keyidx, validx, detidx, baseidx;
+ char buf[1024];
+ intptr_t val;
+ size_t len = sizeof (buf);
+ char *c = buf;
+
+ if (V8_PROP_IDX_CONTENT != -1) {
+ /*
+ * In node versions prior to v0.8, this was hardcoded
+ * in the V8 implementation, so we hardcode it here
+ * as well.
+ */
+ keyidx = ii + V8_PROP_IDX_FIRST;
+ validx = ii << 1;
+ detidx = (ii << 1) + 1;
+ } else {
+ baseidx = V8_PROP_IDX_FIRST + (ii * V8_PROP_DESC_SIZE);
+ keyidx = baseidx + V8_PROP_DESC_KEY;
+ validx = baseidx + V8_PROP_DESC_VALUE;
+ detidx = baseidx + V8_PROP_DESC_DETAILS;
+ }
+
+ if (detidx >= ncontent) {
+ v8_warn("property descriptor %d: detidx (%d) "
+ "out of bounds for content array (length %d)\n",
+ ii, detidx, ncontent);
+ continue;
+ }
+
+ if (!V8_DESC_ISFIELD(content[detidx]))
+ continue;
+
+ if (keyidx >= ndescs) {
+ v8_warn("property descriptor %d: keyidx (%d) "
+ "out of bounds for descriptor array (length %d)\n",
+ ii, keyidx, ndescs);
+ continue;
+ }
+
+ if (jsstr_print(descs[keyidx], JSSTR_NUDE, &c, &len) != 0)
+ continue;
+
+ val = (intptr_t)content[validx];
+
+ if (!V8_IS_SMI(val)) {
+ v8_warn("object %p: property descriptor %d: value "
+ "index value is not an SMI: %p\n", addr, ii, val);
+ continue;
+ }
+
+ val = V8_SMI_VALUE(val) - ninprops;
+
+ if (val < 0) {
+ /* property is stored directly in the object */
+ if (mdb_vread(&ptr, sizeof (ptr), addr + V8_OFF_HEAP(
+ size + val * sizeof (uintptr_t))) == -1) {
+ v8_warn("failed to read in-object "
+ "property at %p\n", addr + V8_OFF_HEAP(
+ size + val * sizeof (uintptr_t)));
+ continue;
+ }
+ } else {
+ /* property should be in "props" array */
+ if (val >= nprops) {
+ v8_warn("property descriptor %d: value index "
+ "value (%d) out of bounds (%d)\n", ii, val,
+ nprops);
+ continue;
+ }
+
+ ptr = props[val];
+ }
+
+ if (func(buf, ptr, arg) != 0)
+ goto err;
+ }
+
+ rval = 0;
+err:
+ if (props != NULL)
+ mdb_free(props, nprops * sizeof (uintptr_t));
+
+ if (descs != NULL)
+ mdb_free(descs, ndescs * sizeof (uintptr_t));
+
+ if (content != NULL && V8_PROP_IDX_CONTENT != -1)
+ mdb_free(content, ncontent * sizeof (uintptr_t));
+
+ return (rval);
+}
+
+/*
+ * Given the line endings table in "lendsp", computes the line number for the
+ * given token position and print the result into "buf". If "lendsp" is
+ * undefined, prints the token position instead.
+ */
+static int
+jsfunc_lineno(uintptr_t lendsp, uintptr_t tokpos, char *buf, size_t buflen)
+{
+ uintptr_t size, bufsz, lower, upper, ii = 0;
+ uintptr_t *data;
+
+ if (jsobj_is_undefined(lendsp)) {
+ /*
+ * The token position is an SMI, but it comes in as its raw
+ * value so we can more easily compare it to values in the line
+ * endings table. If we're just printing the position directly,
+ * we must convert it here.
+ */
+ mdb_snprintf(buf, buflen, "position %d", V8_SMI_VALUE(tokpos));
+ return (0);
+ }
+
+ if (read_heap_smi(&size, lendsp, V8_OFF_FIXEDARRAY_LENGTH) != 0)
+ return (-1);
+
+ bufsz = size * sizeof (data[0]);
+
+ if ((data = mdb_alloc(bufsz, UM_NOSLEEP)) == NULL) {
+ v8_warn("failed to alloc %d bytes for FixedArray data", bufsz);
+ return (-1);
+ }
+
+ if (mdb_vread(data, bufsz, lendsp + V8_OFF_FIXEDARRAY_DATA) != bufsz) {
+ v8_warn("failed to read FixedArray data");
+ mdb_free(data, bufsz);
+ return (-1);
+ }
+
+ lower = 0;
+ upper = size - 1;
+
+ if (tokpos > data[upper]) {
+ (void) strlcpy(buf, "position out of range", buflen);
+ mdb_free(data, bufsz);
+ return (0);
+ }
+
+ if (tokpos <= data[0]) {
+ (void) strlcpy(buf, "line 1", buflen);
+ mdb_free(data, bufsz);
+ return (0);
+ }
+
+ while (upper >= 1) {
+ ii = (lower + upper) >> 1;
+ if (tokpos > data[ii])
+ lower = ii + 1;
+ else if (tokpos <= data[ii - 1])
+ upper = ii - 1;
+ else
+ break;
+ }
+
+ (void) mdb_snprintf(buf, buflen, "line %d", ii + 1);
+ mdb_free(data, bufsz);
+ return (0);
+}
+
+/*
+ * Given a SharedFunctionInfo object, prints into bufp a name of the function
+ * suitable for printing. This function attempts to infer a name for anonymous
+ * functions.
+ */
+static int
+jsfunc_name(uintptr_t funcinfop, char **bufp, size_t *lenp)
+{
+ uintptr_t ptrp;
+ char *bufs = *bufp;
+
+ if (read_heap_ptr(&ptrp, funcinfop,
+ V8_OFF_SHAREDFUNCTIONINFO_NAME) != 0 ||
+ jsstr_print(ptrp, JSSTR_NUDE, bufp, lenp) != 0)
+ return (-1);
+
+ if (*bufp != bufs)
+ return (0);
+
+ if (read_heap_ptr(&ptrp, funcinfop,
+ V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME) != 0) {
+ (void) bsnprintf(bufp, lenp, "<anonymous>");
+ return (0);
+ }
+
+ (void) bsnprintf(bufp, lenp, "<anonymous> (as ");
+ bufs = *bufp;
+
+ if (jsstr_print(ptrp, JSSTR_NUDE, bufp, lenp) != 0)
+ return (-1);
+
+ if (*bufp == bufs)
+ (void) bsnprintf(bufp, lenp, "<anon>");
+
+ (void) bsnprintf(bufp, lenp, ")");
+
+ return (0);
+}
+
+/*
+ * JavaScript-level object printing
+ */
+typedef struct jsobj_print {
+ char **jsop_bufp;
+ size_t *jsop_lenp;
+ int jsop_indent;
+ uint64_t jsop_depth;
+ boolean_t jsop_printaddr;
+ uintptr_t jsop_baseaddr;
+ int jsop_nprops;
+ const char *jsop_member;
+ boolean_t jsop_found;
+} jsobj_print_t;
+
+static int jsobj_print_number(uintptr_t, jsobj_print_t *);
+static int jsobj_print_oddball(uintptr_t, jsobj_print_t *);
+static int jsobj_print_jsobject(uintptr_t, jsobj_print_t *);
+static int jsobj_print_jsarray(uintptr_t, jsobj_print_t *);
+static int jsobj_print_jsfunction(uintptr_t, jsobj_print_t *);
+static int jsobj_print_jsdate(uintptr_t, jsobj_print_t *);
+
+static int
+jsobj_print(uintptr_t addr, jsobj_print_t *jsop)
+{
+ uint8_t type;
+ const char *klass;
+ char **bufp = jsop->jsop_bufp;
+ size_t *lenp = jsop->jsop_lenp;
+
+ const struct {
+ char *name;
+ int (*func)(uintptr_t, jsobj_print_t *);
+ } table[] = {
+ { "HeapNumber", jsobj_print_number },
+ { "Oddball", jsobj_print_oddball },
+ { "JSObject", jsobj_print_jsobject },
+ { "JSArray", jsobj_print_jsarray },
+ { "JSFunction", jsobj_print_jsfunction },
+ { "JSDate", jsobj_print_jsdate },
+ { NULL }
+ }, *ent;
+
+ if (jsop->jsop_baseaddr != NULL && jsop->jsop_member == NULL)
+ (void) bsnprintf(bufp, lenp, "%p: ", jsop->jsop_baseaddr);
+
+ if (jsop->jsop_printaddr && jsop->jsop_member == NULL)
+ (void) bsnprintf(bufp, lenp, "%p: ", addr);
+
+ if (V8_IS_SMI(addr)) {
+ (void) bsnprintf(bufp, lenp, "%d", V8_SMI_VALUE(addr));
+ return (0);
+ }
+
+ if (!V8_IS_HEAPOBJECT(addr)) {
+ v8_warn("not a heap object: %p\n", addr);
+ return (-1);
+ }
+
+ if (read_typebyte(&type, addr) != 0)
+ return (-1);
+
+ if (V8_TYPE_STRING(type)) {
+ if (jsstr_print(addr, JSSTR_QUOTED, bufp, lenp) == -1)
+ return (-1);
+
+ return (0);
+ }
+
+ klass = enum_lookup_str(v8_types, type, "<unknown>");
+
+ for (ent = &table[0]; ent->name != NULL; ent++) {
+ if (strcmp(klass, ent->name) == 0)
+ return (ent->func(addr, jsop));
+ }
+
+ v8_warn("%p: unknown JavaScript object type \"%s\"\n", addr, klass);
+ return (-1);
+}
+
+static int
+jsobj_print_number(uintptr_t addr, jsobj_print_t *jsop)
+{
+ char **bufp = jsop->jsop_bufp;
+ size_t *lenp = jsop->jsop_lenp;
+ double numval;
+
+ if (read_heap_double(&numval, addr, V8_OFF_HEAPNUMBER_VALUE) == -1)
+ return (-1);
+
+ if (numval == (long long)numval)
+ (void) bsnprintf(bufp, lenp, "%lld", (long long)numval);
+ else
+ (void) bsnprintf(bufp, lenp, "%e", numval);
+
+ return (0);
+}
+
+static int
+jsobj_print_oddball(uintptr_t addr, jsobj_print_t *jsop)
+{
+ char **bufp = jsop->jsop_bufp;
+ size_t *lenp = jsop->jsop_lenp;
+ uintptr_t strptr;
+
+ if (read_heap_ptr(&strptr, addr, V8_OFF_ODDBALL_TO_STRING) != 0)
+ return (-1);
+
+ return (jsstr_print(strptr, JSSTR_NUDE, bufp, lenp));
+}
+
+static int
+jsobj_print_prop(const char *desc, uintptr_t val, void *arg)
+{
+ jsobj_print_t *jsop = arg, descend;
+ char **bufp = jsop->jsop_bufp;
+ size_t *lenp = jsop->jsop_lenp;
+
+ (void) bsnprintf(bufp, lenp, "%s\n%*s%s: ", jsop->jsop_nprops == 0 ?
+ "{" : "", jsop->jsop_indent + 4, "", desc);
+
+ descend = *jsop;
+ descend.jsop_depth--;
+ descend.jsop_indent += 4;
+
+ (void) jsobj_print(val, &descend);
+ (void) bsnprintf(bufp, lenp, ",");
+
+ jsop->jsop_nprops++;
+
+ return (0);
+}
+
+static int
+jsobj_print_prop_member(const char *desc, uintptr_t val, void *arg)
+{
+ jsobj_print_t *jsop = arg, descend;
+ const char *member = jsop->jsop_member, *next = member;
+ int rv;
+
+ for (; *next != '\0' && *next != '.' && *next != '['; next++)
+ continue;
+
+ if (*member == '[') {
+ mdb_warn("cannot use array indexing on an object\n");
+ return (-1);
+ }
+
+ if (strncmp(member, desc, next - member) != 0)
+ return (0);
+
+ if (desc[next - member] != '\0')
+ return (0);
+
+ /*
+ * This property matches the desired member; descend.
+ */
+ descend = *jsop;
+
+ if (*next == '\0') {
+ descend.jsop_member = NULL;
+ descend.jsop_found = B_TRUE;
+ } else {
+ descend.jsop_member = *next == '.' ? next + 1 : next;
+ }
+
+ rv = jsobj_print(val, &descend);
+ jsop->jsop_found = descend.jsop_found;
+
+ return (rv);
+}
+
+static int
+jsobj_print_jsobject(uintptr_t addr, jsobj_print_t *jsop)
+{
+ char **bufp = jsop->jsop_bufp;
+ size_t *lenp = jsop->jsop_lenp;
+
+ if (jsop->jsop_member != NULL)
+ return (jsobj_properties(addr, jsobj_print_prop_member, jsop));
+
+ if (jsop->jsop_depth == 0) {
+ (void) bsnprintf(bufp, lenp, "[...]");
+ return (0);
+ }
+
+ jsop->jsop_nprops = 0;
+
+ if (jsobj_properties(addr, jsobj_print_prop, jsop) != 0)
+ return (-1);
+
+ if (jsop->jsop_nprops > 0) {
+ (void) bsnprintf(bufp, lenp, "\n%*s", jsop->jsop_indent, "");
+ } else if (jsop->jsop_nprops == 0) {
+ (void) bsnprintf(bufp, lenp, "{");
+ } else {
+ (void) bsnprintf(bufp, lenp, "{ /* unknown property */ ");
+ }
+
+ (void) bsnprintf(bufp, lenp, "}");
+
+ return (0);
+}
+
+static int
+jsobj_print_jsarray_member(uintptr_t addr, jsobj_print_t *jsop)
+{
+ uintptr_t *elts;
+ jsobj_print_t descend;
+ uintptr_t ptr;
+ const char *member = jsop->jsop_member, *end, *p;
+ size_t elt = 0, place = 1, len, rv;
+
+ if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0 ||
+ read_heap_array(ptr, &elts, &len, UM_SLEEP | UM_GC) != 0)
+ return (-1);
+
+ if (*member != '[') {
+ mdb_warn("expected bracketed array index; "
+ "found '%s'\n", member);
+ return (-1);
+ }
+
+ if ((end = strchr(member, ']')) == NULL) {
+ mdb_warn("missing array index terminator\n");
+ return (-1);
+ }
+
+ /*
+ * We know where our array index ends; convert it to an integer
+ * by stepping through it from least significant digit to most.
+ */
+ for (p = end - 1; p > member; p--) {
+ if (*p < '0' || *p > '9') {
+ mdb_warn("illegal array index at '%c'\n", *p);
+ return (-1);
+ }
+
+ elt += (*p - '0') * place;
+ place *= 10;
+ }
+
+ if (place == 1) {
+ mdb_warn("missing array index\n");
+ return (-1);
+ }
+
+ if (elt >= len) {
+ mdb_warn("array index %d exceeds size of %d\n", elt, len);
+ return (-1);
+ }
+
+ descend = *jsop;
+
+ switch (*(++end)) {
+ case '\0':
+ descend.jsop_member = NULL;
+ descend.jsop_found = B_TRUE;
+ break;
+
+ case '.':
+ descend.jsop_member = end + 1;
+ break;
+
+ case '[':
+ descend.jsop_member = end;
+ break;
+
+ default:
+ mdb_warn("illegal character '%c' following "
+ "array index terminator\n", *end);
+ return (-1);
+ }
+
+ rv = jsobj_print(elts[elt], &descend);
+ jsop->jsop_found = descend.jsop_found;
+
+ return (rv);
+}
+
+static int
+jsobj_print_jsarray(uintptr_t addr, jsobj_print_t *jsop)
+{
+ char **bufp = jsop->jsop_bufp;
+ size_t *lenp = jsop->jsop_lenp;
+ int indent = jsop->jsop_indent;
+ jsobj_print_t descend;
+ uintptr_t ptr;
+ uintptr_t *elts;
+ size_t ii, len;
+
+ if (jsop->jsop_member != NULL)
+ return (jsobj_print_jsarray_member(addr, jsop));
+
+ if (jsop->jsop_depth == 0) {
+ (void) bsnprintf(bufp, lenp, "[...]");
+ return (0);
+ }
+
+ if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0 ||
+ read_heap_array(ptr, &elts, &len, UM_SLEEP | UM_GC) != 0)
+ return (-1);
+
+ if (len == 0) {
+ (void) bsnprintf(bufp, lenp, "[]");
+ return (0);
+ }
+
+ descend = *jsop;
+ descend.jsop_depth--;
+ descend.jsop_indent += 4;
+
+ if (len == 1) {
+ (void) bsnprintf(bufp, lenp, "[ ");
+ (void) jsobj_print(elts[0], &descend);
+ (void) bsnprintf(bufp, lenp, " ]");
+ return (0);
+ }
+
+ (void) bsnprintf(bufp, lenp, "[\n");
+
+ for (ii = 0; ii < len; ii++) {
+ (void) bsnprintf(bufp, lenp, "%*s", indent + 4, "");
+ (void) jsobj_print(elts[ii], &descend);
+ (void) bsnprintf(bufp, lenp, ",\n");
+ }
+
+ (void) bsnprintf(bufp, lenp, "%*s", indent, "");
+ (void) bsnprintf(bufp, lenp, "]");
+
+ return (0);
+}
+
+static int
+jsobj_print_jsfunction(uintptr_t addr, jsobj_print_t *jsop)
+{
+ char **bufp = jsop->jsop_bufp;
+ size_t *lenp = jsop->jsop_lenp;
+ uintptr_t shared;
+
+ if (read_heap_ptr(&shared, addr, V8_OFF_JSFUNCTION_SHARED) != 0)
+ return (-1);
+
+ (void) bsnprintf(bufp, lenp, "function ");
+ return (jsfunc_name(shared, bufp, lenp) != 0);
+}
+
+static int
+jsobj_print_jsdate(uintptr_t addr, jsobj_print_t *jsop)
+{
+ char **bufp = jsop->jsop_bufp;
+ size_t *lenp = jsop->jsop_lenp;
+ char buf[128];
+ uintptr_t value;
+ uint8_t type;
+ double numval;
+
+ if (V8_OFF_JSDATE_VALUE == -1) {
+ (void) bsnprintf(bufp, lenp, "<JSDate>", buf);
+ return (0);
+ }
+
+ if (read_heap_ptr(&value, addr, V8_OFF_JSDATE_VALUE) != 0)
+ return (-1);
+
+ if (read_typebyte(&type, value) != 0)
+ return (-1);
+
+ if (strcmp(enum_lookup_str(v8_types, type, ""), "HeapNumber") != 0)
+ return (-1);
+
+ if (read_heap_double(&numval, value, V8_OFF_HEAPNUMBER_VALUE) == -1)
+ return (-1);
+
+ mdb_snprintf(buf, sizeof (buf), "%Y",
+ (time_t)((long long)numval / MILLISEC));
+ (void) bsnprintf(bufp, lenp, "%lld (%s)", (long long)numval, buf);
+
+ return (0);
+}
+
+/*
+ * dcmd implementations
+ */
+
+/* ARGSUSED */
+static int
+dcmd_v8classes(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ v8_class_t *clp;
+
+ for (clp = v8_classes; clp != NULL; clp = clp->v8c_next)
+ mdb_printf("%s\n", clp->v8c_name);
+
+ return (DCMD_OK);
+}
+
+static int
+do_v8code(uintptr_t addr, boolean_t opt_d)
+{
+ uintptr_t instrlen;
+ ssize_t instroff = V8_OFF_CODE_INSTRUCTION_START;
+
+ if (read_heap_ptr(&instrlen, addr, V8_OFF_CODE_INSTRUCTION_SIZE) != 0)
+ return (DCMD_ERR);
+
+ mdb_printf("code: %p\n", addr);
+ mdb_printf("instructions: [%p, %p)\n", addr + instroff,
+ addr + instroff + instrlen);
+
+ if (!opt_d)
+ return (DCMD_OK);
+
+ mdb_set_dot(addr + instroff);
+
+ do {
+ (void) mdb_inc_indent(8); /* gets reset by mdb_eval() */
+
+ /*
+ * This is absolutely awful. We want to disassemble the above
+ * range of instructions. Because we don't know how many there
+ * are, we can't use "::dis". We resort to evaluating "./i",
+ * but then we need to advance "." by the size of the
+ * instruction just printed. The only way to do that is by
+ * printing out "+", but we don't want that to show up, so we
+ * redirect it to /dev/null.
+ */
+ if (mdb_eval("/i") != 0 ||
+ mdb_eval("+=p ! cat > /dev/null") != 0) {
+ (void) mdb_dec_indent(8);
+ v8_warn("failed to disassemble at %p", mdb_get_dot());
+ return (DCMD_ERR);
+ }
+ } while (mdb_get_dot() < addr + instroff + instrlen);
+
+ (void) mdb_dec_indent(8);
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+dcmd_v8code(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ boolean_t opt_d = B_FALSE;
+
+ if (mdb_getopts(argc, argv, 'd', MDB_OPT_SETBITS, B_TRUE, &opt_d,
+ NULL) != argc)
+ return (DCMD_USAGE);
+
+ return (do_v8code(addr, opt_d));
+}
+
+/* ARGSUSED */
+static int
+dcmd_v8function(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint8_t type;
+ uintptr_t funcinfop, scriptp, lendsp, tokpos, namep, codep;
+ char *bufp;
+ size_t len;
+ boolean_t opt_d = B_FALSE;
+ char buf[512];
+
+ if (mdb_getopts(argc, argv, 'd', MDB_OPT_SETBITS, B_TRUE, &opt_d,
+ NULL) != argc)
+ return (DCMD_USAGE);
+
+ if (read_typebyte(&type, addr) != 0)
+ return (DCMD_ERR);
+
+ if (strcmp(enum_lookup_str(v8_types, type, ""), "JSFunction") != 0) {
+ v8_warn("%p is not an instance of JSFunction\n", addr);
+ return (DCMD_ERR);
+ }
+
+ if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0 ||
+ read_heap_ptr(&tokpos, funcinfop,
+ V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0 ||
+ read_heap_ptr(&scriptp, funcinfop,
+ V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 ||
+ read_heap_ptr(&namep, scriptp, V8_OFF_SCRIPT_NAME) != 0 ||
+ read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0)
+ return (DCMD_ERR);
+
+ bufp = buf;
+ len = sizeof (buf);
+ if (jsfunc_name(funcinfop, &bufp, &len) != 0)
+ return (DCMD_ERR);
+
+ mdb_printf("%p: JSFunction: %s\n", addr, buf);
+
+ bufp = buf;
+ len = sizeof (buf);
+ mdb_printf("defined at ");
+
+ if (jsstr_print(namep, JSSTR_NUDE, &bufp, &len) == 0)
+ mdb_printf("%s ", buf);
+
+ if (jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf)) == 0)
+ mdb_printf("%s", buf);
+
+ mdb_printf("\n");
+
+ if (read_heap_ptr(&codep,
+ funcinfop, V8_OFF_SHAREDFUNCTIONINFO_CODE) != 0)
+ return (DCMD_ERR);
+
+ return (do_v8code(codep, opt_d));
+}
+
+/* ARGSUSED */
+static int
+dcmd_v8frametypes(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ enum_print(v8_frametypes);
+ return (DCMD_OK);
+}
+
+static void
+dcmd_v8print_help(void)
+{
+ mdb_printf(
+ "Prints out \".\" (a V8 heap object) as an instance of its C++\n"
+ "class. With no arguments, the appropriate class is detected\n"
+ "automatically. The 'class' argument overrides this to print an\n"
+ "object as an instance of the given class. The list of known\n"
+ "classes can be viewed with ::jsclasses.");
+}
+
+/* ARGSUSED */
+static int
+dcmd_v8print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ const char *rqclass;
+ v8_class_t *clp;
+ char *bufp;
+ size_t len;
+ uint8_t type;
+ char buf[256];
+
+ if (argc < 1) {
+ /*
+ * If no type was specified, determine it automatically.
+ */
+ bufp = buf;
+ len = sizeof (buf);
+ if (obj_jstype(addr, &bufp, &len, &type) != 0)
+ return (DCMD_ERR);
+
+ if (type == 0) {
+ /* For SMI or Failure, just print out the type. */
+ mdb_printf("%s\n", buf);
+ return (DCMD_OK);
+ }
+
+ if ((rqclass = enum_lookup_str(v8_types, type, NULL)) == NULL) {
+ v8_warn("object has unknown type\n");
+ return (DCMD_ERR);
+ }
+ } else {
+ if (argv[0].a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ rqclass = argv[0].a_un.a_str;
+ }
+
+ for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) {
+ if (strcmp(rqclass, clp->v8c_name) == 0)
+ break;
+ }
+
+ if (clp == NULL) {
+ v8_warn("unknown class '%s'\n", rqclass);
+ return (DCMD_USAGE);
+ }
+
+ return (obj_print_class(addr, clp));
+}
+
+/* ARGSUSED */
+static int
+dcmd_v8type(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ char buf[64];
+ char *bufp = buf;
+ size_t len = sizeof (buf);
+
+ if (obj_jstype(addr, &bufp, &len, NULL) != 0)
+ return (DCMD_ERR);
+
+ mdb_printf("0x%p: %s\n", addr, buf);
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+dcmd_v8types(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ enum_print(v8_types);
+ return (DCMD_OK);
+}
+
+static int
+load_current_context(uintptr_t *fpp, uintptr_t *raddrp)
+{
+ mdb_reg_t regfp, regip;
+
+#ifdef __amd64
+ if (mdb_getareg(1, "rbp", &regfp) != 0 ||
+ mdb_getareg(1, "rip", &regip) != 0) {
+#else
+#ifdef __i386
+ if (mdb_getareg(1, "ebp", &regfp) != 0 ||
+ mdb_getareg(1, "eip", &regip) != 0) {
+#else
+#error Unrecognized microprocessor
+#endif
+#endif
+ v8_warn("failed to load current context");
+ return (-1);
+ }
+
+ if (fpp != NULL)
+ *fpp = (uintptr_t)regfp;
+
+ if (raddrp != NULL)
+ *raddrp = (uintptr_t)regip;
+
+ return (0);
+}
+
+static int
+do_jsframe_special(uintptr_t fptr, uintptr_t raddr, char *prop)
+{
+ uintptr_t ftype;
+ const char *ftypename;
+
+ /*
+ * First see if this looks like a native frame rather than a JavaScript
+ * frame. We check this by asking MDB to print the return address
+ * symbolically. If that works, we assume this was NOT a V8 frame,
+ * since those are never in the symbol table.
+ */
+ if (mdb_snprintf(NULL, 0, "%A", raddr) > 1) {
+ if (prop != NULL)
+ return (0);
+
+ mdb_printf("%p %a\n", fptr, raddr);
+ return (0);
+ }
+
+ /*
+ * Figure out what kind of frame this is using the same algorithm as
+ * V8's ComputeType function. First, look for an ArgumentsAdaptorFrame.
+ */
+ if (mdb_vread(&ftype, sizeof (ftype), fptr + V8_OFF_FP_CONTEXT) != -1 &&
+ V8_IS_SMI(ftype) &&
+ (ftypename = enum_lookup_str(v8_frametypes, V8_SMI_VALUE(ftype),
+ NULL)) != NULL && strstr(ftypename, "ArgumentsAdaptor") != NULL) {
+ if (prop != NULL)
+ return (0);
+
+ mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename);
+ return (0);
+ }
+
+ /*
+ * Other special frame types are indicated by a marker.
+ */
+ if (mdb_vread(&ftype, sizeof (ftype), fptr + V8_OFF_FP_MARKER) != -1 &&
+ V8_IS_SMI(ftype)) {
+ if (prop != NULL)
+ return (0);
+
+ ftypename = enum_lookup_str(v8_frametypes, V8_SMI_VALUE(ftype),
+ NULL);
+
+ if (ftypename != NULL)
+ mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename);
+ else
+ mdb_printf("%p %a\n", fptr, raddr);
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+static int
+do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose,
+ char *func, char *prop)
+{
+ uintptr_t funcp, funcinfop, tokpos, scriptp, lendsp, ptrp;
+ uintptr_t ii, nargs;
+ const char *typename;
+ char *bufp;
+ size_t len;
+ uint8_t type;
+ char buf[256];
+
+ /*
+ * Check for non-JavaScript frames first.
+ */
+ if (func == NULL && do_jsframe_special(fptr, raddr, prop) == 0)
+ return (DCMD_OK);
+
+ /*
+ * At this point we assume we're looking at a JavaScript frame. As with
+ * native frames, fish the address out of the parent frame.
+ */
+ if (mdb_vread(&funcp, sizeof (funcp),
+ fptr + V8_OFF_FP_FUNCTION) == -1) {
+ v8_warn("failed to read stack at %p",
+ fptr + V8_OFF_FP_FUNCTION);
+ return (DCMD_ERR);
+ }
+
+ /*
+ * Check if this thing is really a JSFunction at all. For some frames,
+ * it's a Code object, presumably indicating some internal frame.
+ */
+ v8_silent++;
+
+ if (read_typebyte(&type, funcp) != 0 ||
+ (typename = enum_lookup_str(v8_types, type, NULL)) == NULL) {
+ v8_silent--;
+
+ if (func != NULL || prop != NULL)
+ return (DCMD_OK);
+
+ mdb_printf("%p %a\n", fptr, raddr);
+ return (DCMD_OK);
+ }
+
+ v8_silent--;
+
+ if (strcmp("Code", typename) == 0) {
+ if (func != NULL || prop != NULL)
+ return (DCMD_OK);
+
+ mdb_printf("%p %a internal (Code: %p)\n", fptr, raddr, funcp);
+ return (DCMD_OK);
+ }
+
+ if (strcmp("JSFunction", typename) != 0) {
+ if (func != NULL || prop != NULL)
+ return (DCMD_OK);
+
+ mdb_printf("%p %a unknown (%s: %p)", fptr, raddr, typename,
+ funcp);
+ return (DCMD_OK);
+ }
+
+ if (read_heap_ptr(&funcinfop, funcp, V8_OFF_JSFUNCTION_SHARED) != 0)
+ return (DCMD_ERR);
+
+ bufp = buf;
+ len = sizeof (buf);
+ if (jsfunc_name(funcinfop, &bufp, &len) != 0)
+ return (DCMD_ERR);
+
+ if (func != NULL && strcmp(buf, func) != 0)
+ return (DCMD_OK);
+
+ if (prop == NULL)
+ mdb_printf("%p %a %s (%p)\n", fptr, raddr, buf, funcp);
+
+ if (!verbose && prop == NULL)
+ return (DCMD_OK);
+
+ /*
+ * Although the token position is technically an SMI, we're going to
+ * byte-compare it to other SMI values so we don't want decode it here.
+ */
+ if (read_heap_ptr(&tokpos, funcinfop,
+ V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0)
+ return (DCMD_ERR);
+
+ if (read_heap_ptr(&scriptp, funcinfop,
+ V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0)
+ return (DCMD_ERR);
+
+ if (read_heap_ptr(&ptrp, scriptp, V8_OFF_SCRIPT_NAME) != 0)
+ return (DCMD_ERR);
+
+ bufp = buf;
+ len = sizeof (buf);
+ (void) jsstr_print(ptrp, JSSTR_NUDE, &bufp, &len);
+
+ if (prop != NULL && strcmp(prop, "file") == 0) {
+ mdb_printf("%s\n", buf);
+ return (DCMD_OK);
+ }
+
+ if (prop == NULL) {
+ (void) mdb_inc_indent(4);
+ mdb_printf("file: %s\n", buf);
+ }
+
+ if (read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0)
+ return (DCMD_ERR);
+
+ (void) jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf));
+
+ if (prop != NULL && strcmp(prop, "posn") == 0) {
+ mdb_printf("%s\n", buf);
+ return (DCMD_OK);
+ }
+
+ if (prop == NULL)
+ mdb_printf("posn: %s\n", buf);
+
+ if (read_heap_smi(&nargs, funcinfop,
+ V8_OFF_SHAREDFUNCTIONINFO_LENGTH) == 0) {
+ for (ii = 0; ii < nargs; ii++) {
+ uintptr_t argptr;
+ char arg[10];
+
+ if (mdb_vread(&argptr, sizeof (argptr),
+ fptr + V8_OFF_FP_ARGS + (nargs - ii - 1) *
+ sizeof (uintptr_t)) == -1)
+ continue;
+
+ (void) snprintf(arg, sizeof (arg), "arg%d", ii + 1);
+
+ if (prop != NULL) {
+ if (strcmp(arg, prop) != 0)
+ continue;
+
+ mdb_printf("%p\n", argptr);
+ return (DCMD_OK);
+ }
+
+ bufp = buf;
+ len = sizeof (buf);
+ (void) obj_jstype(argptr, &bufp, &len, NULL);
+
+ mdb_printf("arg%d: %p (%s)\n", (ii + 1), argptr, buf);
+ }
+ }
+
+ if (prop != NULL) {
+ mdb_warn("unknown frame property '%s'\n", prop);
+ return (DCMD_ERR);
+ }
+
+ (void) mdb_dec_indent(4);
+
+ return (DCMD_OK);
+}
+
+typedef struct findjsobjects_prop {
+ struct findjsobjects_prop *fjsp_next;
+ char fjsp_desc[1];
+} findjsobjects_prop_t;
+
+typedef struct findjsobjects_instance {
+ uintptr_t fjsi_addr;
+ struct findjsobjects_instance *fjsi_next;
+} findjsobjects_instance_t;
+
+typedef struct findjsobjects_obj {
+ findjsobjects_prop_t *fjso_props;
+ findjsobjects_prop_t *fjso_last;
+ size_t fjso_nprops;
+ findjsobjects_instance_t fjso_instances;
+ int fjso_ninstances;
+ avl_node_t fjso_node;
+ struct findjsobjects_obj *fjso_next;
+ boolean_t fjso_malformed;
+ char fjso_constructor[80];
+} findjsobjects_obj_t;
+
+typedef struct findjsobjects_stats {
+ int fjss_heapobjs;
+ int fjss_cached;
+ int fjss_typereads;
+ int fjss_jsobjs;
+ int fjss_objects;
+ int fjss_arrays;
+ int fjss_uniques;
+} findjsobjects_stats_t;
+
+typedef struct findjsobjects_reference {
+ uintptr_t fjsrf_addr;
+ char *fjsrf_desc;
+ size_t fjsrf_index;
+ struct findjsobjects_reference *fjsrf_next;
+} findjsobjects_reference_t;
+
+typedef struct findjsobjects_referent {
+ avl_node_t fjsr_node;
+ uintptr_t fjsr_addr;
+ findjsobjects_reference_t *fjsr_head;
+ findjsobjects_reference_t *fjsr_tail;
+ struct findjsobjects_referent *fjsr_next;
+} findjsobjects_referent_t;
+
+typedef struct findjsobjects_state {
+ uintptr_t fjs_addr;
+ uintptr_t fjs_size;
+ boolean_t fjs_verbose;
+ boolean_t fjs_brk;
+ boolean_t fjs_allobjs;
+ boolean_t fjs_initialized;
+ boolean_t fjs_marking;
+ boolean_t fjs_referred;
+ avl_tree_t fjs_tree;
+ avl_tree_t fjs_referents;
+ findjsobjects_referent_t *fjs_head;
+ findjsobjects_referent_t *fjs_tail;
+ findjsobjects_obj_t *fjs_current;
+ findjsobjects_obj_t *fjs_objects;
+ findjsobjects_stats_t fjs_stats;
+} findjsobjects_state_t;
+
+findjsobjects_obj_t *
+findjsobjects_alloc(uintptr_t addr)
+{
+ findjsobjects_obj_t *obj;
+
+ obj = mdb_zalloc(sizeof (findjsobjects_obj_t), UM_SLEEP);
+ obj->fjso_instances.fjsi_addr = addr;
+ obj->fjso_ninstances = 1;
+
+ return (obj);
+}
+
+void
+findjsobjects_free(findjsobjects_obj_t *obj)
+{
+ findjsobjects_prop_t *prop, *next;
+
+ for (prop = obj->fjso_props; prop != NULL; prop = next) {
+ next = prop->fjsp_next;
+ mdb_free(prop, sizeof (findjsobjects_prop_t) +
+ strlen(prop->fjsp_desc));
+ }
+
+ mdb_free(obj, sizeof (findjsobjects_obj_t));
+}
+
+int
+findjsobjects_cmp(findjsobjects_obj_t *lhs, findjsobjects_obj_t *rhs)
+{
+ findjsobjects_prop_t *lprop, *rprop;
+ int rv;
+
+ lprop = lhs->fjso_props;
+ rprop = rhs->fjso_props;
+
+ while (lprop != NULL && rprop != NULL) {
+ if ((rv = strcmp(lprop->fjsp_desc, rprop->fjsp_desc)) != 0)
+ return (rv > 0 ? 1 : -1);
+
+ lprop = lprop->fjsp_next;
+ rprop = rprop->fjsp_next;
+ }
+
+ if (lprop != NULL)
+ return (1);
+
+ if (rprop != NULL)
+ return (-1);
+
+ if (lhs->fjso_nprops > rhs->fjso_nprops)
+ return (1);
+
+ if (lhs->fjso_nprops < rhs->fjso_nprops)
+ return (-1);
+
+ rv = strcmp(lhs->fjso_constructor, rhs->fjso_constructor);
+
+ return (rv < 0 ? -1 : rv > 0 ? 1 : 0);
+}
+
+int
+findjsobjects_cmp_referents(findjsobjects_referent_t *lhs,
+ findjsobjects_referent_t *rhs)
+{
+ if (lhs->fjsr_addr < rhs->fjsr_addr)
+ return (-1);
+
+ if (lhs->fjsr_addr > rhs->fjsr_addr)
+ return (1);
+
+ return (0);
+}
+
+int
+findjsobjects_cmp_ninstances(const void *l, const void *r)
+{
+ findjsobjects_obj_t *lhs = *((findjsobjects_obj_t **)l);
+ findjsobjects_obj_t *rhs = *((findjsobjects_obj_t **)r);
+ size_t lprod = lhs->fjso_ninstances * lhs->fjso_nprops;
+ size_t rprod = rhs->fjso_ninstances * rhs->fjso_nprops;
+
+ if (lprod < rprod)
+ return (-1);
+
+ if (lprod > rprod)
+ return (1);
+
+ if (lhs->fjso_ninstances < rhs->fjso_ninstances)
+ return (-1);
+
+ if (lhs->fjso_ninstances > rhs->fjso_ninstances)
+ return (1);
+
+ if (lhs->fjso_nprops < rhs->fjso_nprops)
+ return (-1);
+
+ if (lhs->fjso_nprops > rhs->fjso_nprops)
+ return (1);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+findjsobjects_prop(const char *desc, uintptr_t val, void *arg)
+{
+ findjsobjects_state_t *fjs = arg;
+ findjsobjects_obj_t *current = fjs->fjs_current;
+ findjsobjects_prop_t *prop;
+
+ if (desc == NULL)
+ desc = "<unknown>";
+
+ prop = mdb_zalloc(sizeof (findjsobjects_prop_t) +
+ strlen(desc), UM_SLEEP);
+
+ strcpy(prop->fjsp_desc, desc);
+
+ if (current->fjso_last != NULL) {
+ current->fjso_last->fjsp_next = prop;
+ } else {
+ current->fjso_props = prop;
+ }
+
+ current->fjso_last = prop;
+ current->fjso_nprops++;
+ current->fjso_malformed =
+ val == NULL && current->fjso_nprops == 1 && desc[0] == '<';
+
+ return (0);
+}
+
+static void
+findjsobjects_constructor(findjsobjects_obj_t *obj)
+{
+ char *bufp = obj->fjso_constructor;
+ size_t len = sizeof (obj->fjso_constructor);
+ uintptr_t map, funcinfop;
+ uintptr_t addr = obj->fjso_instances.fjsi_addr;
+ uint8_t type;
+
+ v8_silent++;
+
+ if (read_heap_ptr(&map, addr, V8_OFF_HEAPOBJECT_MAP) != 0 ||
+ read_heap_ptr(&addr, map, V8_OFF_MAP_CONSTRUCTOR) != 0)
+ goto out;
+
+ if (read_typebyte(&type, addr) != 0)
+ goto out;
+
+ if (strcmp(enum_lookup_str(v8_types, type, ""), "JSFunction") != 0)
+ goto out;
+
+ if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0)
+ goto out;
+
+ if (jsfunc_name(funcinfop, &bufp, &len) != 0)
+ goto out;
+out:
+ v8_silent--;
+}
+
+int
+findjsobjects_range(findjsobjects_state_t *fjs, uintptr_t addr, uintptr_t size)
+{
+ uintptr_t limit;
+ findjsobjects_stats_t *stats = &fjs->fjs_stats;
+ uint8_t type;
+ int jsobject = V8_TYPE_JSOBJECT, jsarray = V8_TYPE_JSARRAY;
+ caddr_t range = mdb_alloc(size, UM_SLEEP);
+ uintptr_t base = addr, mapaddr;
+
+ if (mdb_vread(range, size, addr) == -1)
+ return (0);
+
+ for (limit = addr + size; addr < limit; addr++) {
+ findjsobjects_instance_t *inst;
+ findjsobjects_obj_t *obj;
+ avl_index_t where;
+
+ if (V8_IS_SMI(addr))
+ continue;
+
+ if (!V8_IS_HEAPOBJECT(addr))
+ continue;
+
+ stats->fjss_heapobjs++;
+
+ mapaddr = *((uintptr_t *)((uintptr_t)range +
+ (addr - base) + V8_OFF_HEAPOBJECT_MAP));
+
+ if (!V8_IS_HEAPOBJECT(mapaddr))
+ continue;
+
+ mapaddr += V8_OFF_MAP_INSTANCE_ATTRIBUTES;
+ stats->fjss_typereads++;
+
+ if (mapaddr >= base && mapaddr < base + size) {
+ stats->fjss_cached++;
+
+ type = *((uint8_t *)((uintptr_t)range +
+ (mapaddr - base)));
+ } else {
+ if (mdb_vread(&type, sizeof (uint8_t), mapaddr) == -1)
+ continue;
+ }
+
+ if (type != jsobject && type != jsarray)
+ continue;
+
+ stats->fjss_jsobjs++;
+
+ fjs->fjs_current = findjsobjects_alloc(addr);
+
+ if (type == jsobject) {
+ if (jsobj_properties(addr,
+ findjsobjects_prop, fjs) != 0) {
+ findjsobjects_free(fjs->fjs_current);
+ fjs->fjs_current = NULL;
+ continue;
+ }
+
+ findjsobjects_constructor(fjs->fjs_current);
+ stats->fjss_objects++;
+ } else {
+ uintptr_t ptr;
+ size_t *nprops = &fjs->fjs_current->fjso_nprops;
+ ssize_t len = V8_OFF_JSARRAY_LENGTH;
+ ssize_t elems = V8_OFF_JSOBJECT_ELEMENTS;
+ ssize_t flen = V8_OFF_FIXEDARRAY_LENGTH;
+ uintptr_t nelems;
+ uint8_t t;
+
+ if (read_heap_smi(nprops, addr, len) != 0 ||
+ read_heap_ptr(&ptr, addr, elems) != 0 ||
+ !V8_IS_HEAPOBJECT(ptr) ||
+ read_typebyte(&t, ptr) != 0 ||
+ t != V8_TYPE_FIXEDARRAY ||
+ read_heap_smi(&nelems, ptr, flen) != 0 ||
+ nelems < *nprops) {
+ findjsobjects_free(fjs->fjs_current);
+ fjs->fjs_current = NULL;
+ continue;
+ }
+
+ strcpy(fjs->fjs_current->fjso_constructor, "Array");
+ stats->fjss_arrays++;
+ }
+
+ /*
+ * Now determine if we already have an object matching our
+ * properties. If we don't, we'll add our new object; if we
+ * do we'll merely enqueue our instance.
+ */
+ obj = avl_find(&fjs->fjs_tree, fjs->fjs_current, &where);
+
+ if (obj == NULL) {
+ avl_add(&fjs->fjs_tree, fjs->fjs_current);
+ fjs->fjs_current->fjso_next = fjs->fjs_objects;
+ fjs->fjs_objects = fjs->fjs_current;
+ fjs->fjs_current = NULL;
+ stats->fjss_uniques++;
+ continue;
+ }
+
+ findjsobjects_free(fjs->fjs_current);
+ fjs->fjs_current = NULL;
+
+ inst = mdb_alloc(sizeof (findjsobjects_instance_t), UM_SLEEP);
+ inst->fjsi_addr = addr;
+ inst->fjsi_next = obj->fjso_instances.fjsi_next;
+ obj->fjso_instances.fjsi_next = inst;
+ obj->fjso_ninstances++;
+ }
+
+ mdb_free(range, size);
+
+ return (0);
+}
+
+static int
+findjsobjects_mapping(findjsobjects_state_t *fjs, const prmap_t *pmp,
+ const char *name)
+{
+ if (name != NULL && !(fjs->fjs_brk && (pmp->pr_mflags & MA_BREAK)))
+ return (0);
+
+ if (fjs->fjs_addr != NULL && (fjs->fjs_addr < pmp->pr_vaddr ||
+ fjs->fjs_addr >= pmp->pr_vaddr + pmp->pr_size))
+ return (0);
+
+ return (findjsobjects_range(fjs, pmp->pr_vaddr, pmp->pr_size));
+}
+
+static void
+findjsobjects_references_add(findjsobjects_state_t *fjs, uintptr_t val,
+ const char *desc, size_t index)
+{
+ findjsobjects_referent_t search, *referent;
+ findjsobjects_reference_t *reference;
+
+ search.fjsr_addr = val;
+
+ if ((referent = avl_find(&fjs->fjs_referents, &search, NULL)) == NULL)
+ return;
+
+ reference = mdb_zalloc(sizeof (*reference), UM_SLEEP | UM_GC);
+ reference->fjsrf_addr = fjs->fjs_addr;
+
+ if (desc != NULL) {
+ reference->fjsrf_desc =
+ mdb_alloc(strlen(desc) + 1, UM_SLEEP | UM_GC);
+ (void) strcpy(reference->fjsrf_desc, desc);
+ } else {
+ reference->fjsrf_index = index;
+ }
+
+ if (referent->fjsr_head == NULL) {
+ referent->fjsr_head = reference;
+ } else {
+ referent->fjsr_tail->fjsrf_next = reference;
+ }
+
+ referent->fjsr_tail = reference;
+}
+
+static int
+findjsobjects_references_prop(const char *desc, uintptr_t val, void *arg)
+{
+ findjsobjects_references_add(arg, val, desc, -1);
+
+ return (0);
+}
+
+static void
+findjsobjects_references_array(findjsobjects_state_t *fjs,
+ findjsobjects_obj_t *obj)
+{
+ findjsobjects_instance_t *inst = &obj->fjso_instances;
+ uintptr_t *elts;
+ size_t i, len;
+
+ for (; inst != NULL; inst = inst->fjsi_next) {
+ uintptr_t addr = inst->fjsi_addr, ptr;
+
+ if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0 ||
+ read_heap_array(ptr, &elts, &len, UM_SLEEP) != 0)
+ continue;
+
+ fjs->fjs_addr = addr;
+
+ for (i = 0; i < len; i++)
+ findjsobjects_references_add(fjs, elts[i], NULL, i);
+
+ mdb_free(elts, len * sizeof (uintptr_t));
+ }
+}
+
+static void
+findjsobjects_referent(findjsobjects_state_t *fjs, uintptr_t addr)
+{
+ findjsobjects_referent_t search, *referent;
+
+ search.fjsr_addr = addr;
+
+ if (avl_find(&fjs->fjs_referents, &search, NULL) != NULL) {
+ assert(fjs->fjs_marking);
+ mdb_warn("%p is already marked; ignoring\n", addr);
+ return;
+ }
+
+ referent = mdb_zalloc(sizeof (findjsobjects_referent_t), UM_SLEEP);
+ referent->fjsr_addr = addr;
+
+ avl_add(&fjs->fjs_referents, referent);
+
+ if (fjs->fjs_tail != NULL) {
+ fjs->fjs_tail->fjsr_next = referent;
+ } else {
+ fjs->fjs_head = referent;
+ }
+
+ fjs->fjs_tail = referent;
+
+ if (fjs->fjs_marking)
+ mdb_printf("findjsobjects: marked %p\n", addr);
+}
+
+static void
+findjsobjects_references(findjsobjects_state_t *fjs)
+{
+ findjsobjects_reference_t *reference;
+ findjsobjects_referent_t *referent;
+ avl_tree_t *referents = &fjs->fjs_referents;
+ findjsobjects_obj_t *obj;
+ void *cookie = NULL;
+ uintptr_t addr;
+
+ fjs->fjs_referred = B_FALSE;
+
+ v8_silent++;
+
+ /*
+ * First traverse over all objects and arrays, looking for references
+ * to our designated referent(s).
+ */
+ for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) {
+ findjsobjects_instance_t *head = &obj->fjso_instances, *inst;
+
+ if (obj->fjso_nprops != 0 && obj->fjso_props == NULL) {
+ findjsobjects_references_array(fjs, obj);
+ continue;
+ }
+
+ for (inst = head; inst != NULL; inst = inst->fjsi_next) {
+ fjs->fjs_addr = inst->fjsi_addr;
+
+ (void) jsobj_properties(inst->fjsi_addr,
+ findjsobjects_references_prop, fjs);
+ }
+ }
+
+ v8_silent--;
+ fjs->fjs_addr = NULL;
+
+ /*
+ * Now go over our referent(s), reporting any references that we have
+ * accumulated.
+ */
+ for (referent = fjs->fjs_head; referent != NULL;
+ referent = referent->fjsr_next) {
+ addr = referent->fjsr_addr;
+
+ if ((reference = referent->fjsr_head) == NULL) {
+ mdb_printf("%p is not referred to by a "
+ "known object.\n", addr);
+ continue;
+ }
+
+ for (; reference != NULL; reference = reference->fjsrf_next) {
+ mdb_printf("%p referred to by %p",
+ addr, reference->fjsrf_addr);
+
+ if (reference->fjsrf_desc == NULL) {
+ mdb_printf("[%d]\n", reference->fjsrf_index);
+ } else {
+ mdb_printf(".%s\n", reference->fjsrf_desc);
+ }
+ }
+ }
+
+ /*
+ * Finally, destroy our referent nodes.
+ */
+ while ((referent = avl_destroy_nodes(referents, &cookie)) != NULL)
+ mdb_free(referent, sizeof (findjsobjects_referent_t));
+
+ fjs->fjs_head = NULL;
+ fjs->fjs_tail = NULL;
+}
+
+static findjsobjects_instance_t *
+findjsobjects_instance(findjsobjects_state_t *fjs, uintptr_t addr,
+ findjsobjects_instance_t **headp)
+{
+ findjsobjects_obj_t *obj;
+
+ for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) {
+ findjsobjects_instance_t *head = &obj->fjso_instances, *inst;
+
+ for (inst = head; inst != NULL; inst = inst->fjsi_next) {
+ if (inst->fjsi_addr == addr) {
+ *headp = head;
+ return (inst);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+/*ARGSUSED*/
+static void
+findjsobjects_match_all(findjsobjects_obj_t *obj, const char *ignored)
+{
+ mdb_printf("%p\n", obj->fjso_instances.fjsi_addr);
+}
+
+static void
+findjsobjects_match_propname(findjsobjects_obj_t *obj, const char *propname)
+{
+ findjsobjects_prop_t *prop;
+
+ for (prop = obj->fjso_props; prop != NULL; prop = prop->fjsp_next) {
+ if (strcmp(prop->fjsp_desc, propname) == 0) {
+ mdb_printf("%p\n", obj->fjso_instances.fjsi_addr);
+ return;
+ }
+ }
+}
+
+static void
+findjsobjects_match_constructor(findjsobjects_obj_t *obj,
+ const char *constructor)
+{
+ if (strcmp(constructor, obj->fjso_constructor) == 0)
+ mdb_printf("%p\n", obj->fjso_instances.fjsi_addr);
+}
+
+static int
+findjsobjects_match(findjsobjects_state_t *fjs, uintptr_t addr,
+ uint_t flags, void (*func)(findjsobjects_obj_t *, const char *),
+ const char *match)
+{
+ findjsobjects_obj_t *obj;
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ for (obj = fjs->fjs_objects; obj != NULL;
+ obj = obj->fjso_next) {
+ if (obj->fjso_malformed && !fjs->fjs_allobjs)
+ continue;
+
+ func(obj, match);
+ }
+
+ return (DCMD_OK);
+ }
+
+ /*
+ * First, look for the specified address among the representative
+ * objects.
+ */
+ for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) {
+ if (obj->fjso_instances.fjsi_addr == addr) {
+ func(obj, match);
+ return (DCMD_OK);
+ }
+ }
+
+ /*
+ * We didn't find it among the representative objects; iterate over
+ * all objects.
+ */
+ for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) {
+ findjsobjects_instance_t *head = &obj->fjso_instances, *inst;
+
+ for (inst = head; inst != NULL; inst = inst->fjsi_next) {
+ if (inst->fjsi_addr == addr) {
+ func(obj, match);
+ return (DCMD_OK);
+ }
+ }
+ }
+
+ mdb_warn("%p does not correspond to a known object\n", addr);
+ return (DCMD_ERR);
+}
+
+static void
+findjsobjects_print(findjsobjects_obj_t *obj)
+{
+ int col = 19 + (sizeof (uintptr_t) * 2) + strlen("..."), len;
+ uintptr_t addr = obj->fjso_instances.fjsi_addr;
+ findjsobjects_prop_t *prop;
+
+ mdb_printf("%?p %8d %8d ",
+ addr, obj->fjso_ninstances, obj->fjso_nprops);
+
+ if (obj->fjso_constructor[0] != '\0') {
+ mdb_printf("%s%s", obj->fjso_constructor,
+ obj->fjso_props != NULL ? ": " : "");
+ col += strlen(obj->fjso_constructor) + 2;
+ }
+
+ for (prop = obj->fjso_props; prop != NULL; prop = prop->fjsp_next) {
+ if (col + (len = strlen(prop->fjsp_desc) + 2) < 80) {
+ mdb_printf("%s%s", prop->fjsp_desc,
+ prop->fjsp_next != NULL ? ", " : "");
+ col += len;
+ } else {
+ mdb_printf("...");
+ break;
+ }
+ }
+
+ mdb_printf("\n", col);
+}
+
+static void
+dcmd_findjsobjects_help(void)
+{
+ mdb_printf("%s\n\n",
+"Finds all JavaScript objects in the V8 heap via brute force iteration over\n"
+"all mapped anonymous memory. (This can take up to several minutes on large\n"
+"dumps.) The output consists of representative objects, the number of\n"
+"instances of that object and the number of properties on the object --\n"
+"followed by the constructor and first few properties of the objects. Once\n"
+"run, subsequent calls to ::findjsobjects use cached data. If provided an\n"
+"address (and in the absence of -r, described below), ::findjsobjects treats\n"
+"the address as that of a representative object, and lists all instances of\n"
+"that object (that is, all objects that have a matching property signature).");
+
+ mdb_dec_indent(2);
+ mdb_printf("%<b>OPTIONS%</b>\n");
+ mdb_inc_indent(2);
+
+ mdb_printf("%s\n",
+" -b Include the heap denoted by the brk(2) (normally excluded)\n"
+" -c cons Display representative objects with the specified constructor\n"
+" -p prop Display representative objects that have the specified property\n"
+" -l List all objects that match the representative object\n"
+" -m Mark specified object for later reference determination via -r\n"
+" -r Find references to the specified and/or marked object(s)\n"
+" -v Provide verbose statistics\n");
+}
+
+static int
+dcmd_findjsobjects(uintptr_t addr,
+ uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ static findjsobjects_state_t fjs;
+ static findjsobjects_stats_t *stats = &fjs.fjs_stats;
+ findjsobjects_obj_t *obj;
+ struct ps_prochandle *Pr;
+ boolean_t references = B_FALSE, listlike = B_FALSE;
+ const char *propname = NULL;
+ const char *constructor = NULL;
+
+ fjs.fjs_verbose = B_FALSE;
+ fjs.fjs_brk = B_FALSE;
+ fjs.fjs_marking = B_FALSE;
+ fjs.fjs_allobjs = B_FALSE;
+
+ if (mdb_getopts(argc, argv,
+ 'a', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_allobjs,
+ 'b', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_brk,
+ 'c', MDB_OPT_STR, &constructor,
+ 'l', MDB_OPT_SETBITS, B_TRUE, &listlike,
+ 'm', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_marking,
+ 'p', MDB_OPT_STR, &propname,
+ 'r', MDB_OPT_SETBITS, B_TRUE, &references,
+ 'v', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_verbose,
+ NULL) != argc)
+ return (DCMD_USAGE);
+
+ if (!fjs.fjs_initialized) {
+ avl_create(&fjs.fjs_tree,
+ (int(*)(const void *, const void *))findjsobjects_cmp,
+ sizeof (findjsobjects_obj_t),
+ offsetof(findjsobjects_obj_t, fjso_node));
+
+ avl_create(&fjs.fjs_referents,
+ (int(*)(const void *, const void *))
+ findjsobjects_cmp_referents,
+ sizeof (findjsobjects_referent_t),
+ offsetof(findjsobjects_referent_t, fjsr_node));
+
+ fjs.fjs_initialized = B_TRUE;
+ }
+
+ if (avl_is_empty(&fjs.fjs_tree)) {
+ findjsobjects_obj_t **sorted;
+ int nobjs, i;
+ hrtime_t start = gethrtime();
+
+ if (mdb_get_xdata("pshandle", &Pr, sizeof (Pr)) == -1) {
+ mdb_warn("couldn't read pshandle xdata");
+ return (DCMD_ERR);
+ }
+
+ v8_silent++;
+
+ if (Pmapping_iter(Pr,
+ (proc_map_f *)findjsobjects_mapping, &fjs) != 0) {
+ v8_silent--;
+ return (DCMD_ERR);
+ }
+
+ if ((nobjs = avl_numnodes(&fjs.fjs_tree)) != 0) {
+ /*
+ * We have the objects -- now sort them.
+ */
+ sorted = mdb_alloc(nobjs * sizeof (void *),
+ UM_SLEEP | UM_GC);
+
+ for (obj = fjs.fjs_objects, i = 0; obj != NULL;
+ obj = obj->fjso_next, i++) {
+ sorted[i] = obj;
+ }
+
+ qsort(sorted, avl_numnodes(&fjs.fjs_tree),
+ sizeof (void *), findjsobjects_cmp_ninstances);
+
+ for (i = 1, fjs.fjs_objects = sorted[0]; i < nobjs; i++)
+ sorted[i - 1]->fjso_next = sorted[i];
+
+ sorted[nobjs - 1]->fjso_next = NULL;
+ }
+
+ v8_silent--;
+
+ if (fjs.fjs_verbose) {
+ const char *f = "findjsobjects: %30s => %d\n";
+ int elapsed = (int)((gethrtime() - start) / NANOSEC);
+
+ mdb_printf(f, "elapsed time (seconds)", elapsed);
+ mdb_printf(f, "heap objects", stats->fjss_heapobjs);
+ mdb_printf(f, "type reads", stats->fjss_typereads);
+ mdb_printf(f, "cached reads", stats->fjss_cached);
+ mdb_printf(f, "JavaScript objects", stats->fjss_jsobjs);
+ mdb_printf(f, "processed objects", stats->fjss_objects);
+ mdb_printf(f, "processed arrays", stats->fjss_arrays);
+ mdb_printf(f, "unique objects", stats->fjss_uniques);
+ }
+ }
+
+ if (listlike && !(flags & DCMD_ADDRSPEC)) {
+ if (propname != NULL || constructor != NULL) {
+ char opt = propname != NULL ? 'p' : 'c';
+
+ mdb_warn("cannot specify -l with -%c; instead, pipe "
+ "output of ::findjsobjects -%c to "
+ "::findjsobjects -l\n", opt, opt);
+ return (DCMD_ERR);
+ }
+
+ return (findjsobjects_match(&fjs, addr, flags,
+ findjsobjects_match_all, NULL));
+ }
+
+ if (propname != NULL) {
+ if (constructor != NULL) {
+ mdb_warn("cannot specify both a property name "
+ "and a constructor\n");
+ return (DCMD_ERR);
+ }
+
+ return (findjsobjects_match(&fjs, addr, flags,
+ findjsobjects_match_propname, propname));
+ }
+
+ if (constructor != NULL) {
+ return (findjsobjects_match(&fjs, addr, flags,
+ findjsobjects_match_constructor, constructor));
+ }
+
+ if (references && !(flags & DCMD_ADDRSPEC) &&
+ avl_is_empty(&fjs.fjs_referents)) {
+ mdb_warn("must specify or mark an object to find references\n");
+ return (DCMD_ERR);
+ }
+
+ if (fjs.fjs_marking && !(flags & DCMD_ADDRSPEC)) {
+ mdb_warn("must specify an object to mark\n");
+ return (DCMD_ERR);
+ }
+
+ if (references && fjs.fjs_marking) {
+ mdb_warn("can't both mark an object and find its references\n");
+ return (DCMD_ERR);
+ }
+
+ if (flags & DCMD_ADDRSPEC) {
+ findjsobjects_instance_t *inst, *head;
+
+ /*
+ * If we've been passed an address, it's to either list like
+ * objects (-l), mark an object (-m) or find references to the
+ * specified/marked objects (-r). (Note that the absence of
+ * any of these options implies -l.)
+ */
+ inst = findjsobjects_instance(&fjs, addr, &head);
+
+ if (inst == NULL) {
+ mdb_warn("%p is not a valid object\n", addr);
+ return (DCMD_ERR);
+ }
+
+ if (!references && !fjs.fjs_marking) {
+ for (inst = head; inst != NULL; inst = inst->fjsi_next)
+ mdb_printf("%p\n", inst->fjsi_addr);
+
+ return (DCMD_OK);
+ }
+
+ if (!listlike) {
+ findjsobjects_referent(&fjs, inst->fjsi_addr);
+ } else {
+ for (inst = head; inst != NULL; inst = inst->fjsi_next)
+ findjsobjects_referent(&fjs, inst->fjsi_addr);
+ }
+ }
+
+ if (references)
+ findjsobjects_references(&fjs);
+
+ if (references || fjs.fjs_marking)
+ return (DCMD_OK);
+
+ mdb_printf("%?s %8s %8s %s\n", "OBJECT",
+ "#OBJECTS", "#PROPS", "CONSTRUCTOR: PROPS");
+
+ for (obj = fjs.fjs_objects; obj != NULL; obj = obj->fjso_next) {
+ if (obj->fjso_malformed && !fjs.fjs_allobjs)
+ continue;
+
+ findjsobjects_print(obj);
+ }
+
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+dcmd_jsframe(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uintptr_t fptr, raddr;
+ boolean_t opt_v = B_FALSE, opt_i = B_FALSE;
+ char *opt_f = NULL, *opt_p = NULL;
+
+ if (mdb_getopts(argc, argv,
+ 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v,
+ 'i', MDB_OPT_SETBITS, B_TRUE, &opt_i,
+ 'f', MDB_OPT_STR, &opt_f,
+ 'p', MDB_OPT_STR, &opt_p, NULL) != argc)
+ return (DCMD_USAGE);
+
+ /*
+ * As with $C, we assume we are given a *pointer* to the frame pointer
+ * for a frame, rather than the actual frame pointer for the frame of
+ * interest. This is needed to show the instruction pointer, which is
+ * actually stored with the next frame. For debugging, this can be
+ * overridden with the "-i" option (for "immediate").
+ */
+ if (opt_i)
+ return (do_jsframe(addr, 0, opt_v, opt_f, opt_p));
+
+ if (mdb_vread(&raddr, sizeof (raddr),
+ addr + sizeof (uintptr_t)) == -1) {
+ mdb_warn("failed to read return address from %p",
+ addr + sizeof (uintptr_t));
+ return (DCMD_ERR);
+ }
+
+ if (mdb_vread(&fptr, sizeof (fptr), addr) == -1) {
+ mdb_warn("failed to read frame pointer from %p", addr);
+ return (DCMD_ERR);
+ }
+
+ if (fptr == NULL)
+ return (DCMD_OK);
+
+ return (do_jsframe(fptr, raddr, opt_v, opt_f, opt_p));
+}
+
+/* ARGSUSED */
+static int
+dcmd_jsprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ char *buf, *bufp;
+ size_t bufsz = 262144, len = bufsz;
+ jsobj_print_t jsop;
+ boolean_t opt_b = B_FALSE;
+ int rv, i;
+
+ bzero(&jsop, sizeof (jsop));
+ jsop.jsop_depth = 2;
+ jsop.jsop_printaddr = B_FALSE;
+
+ i = mdb_getopts(argc, argv,
+ 'a', MDB_OPT_SETBITS, B_TRUE, &jsop.jsop_printaddr,
+ 'b', MDB_OPT_SETBITS, B_TRUE, &opt_b,
+ 'd', MDB_OPT_UINT64, &jsop.jsop_depth, NULL);
+
+ if (opt_b)
+ jsop.jsop_baseaddr = addr;
+
+ do {
+ if (i != argc) {
+ const mdb_arg_t *member = &argv[i++];
+
+ if (member->a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ jsop.jsop_member = member->a_un.a_str;
+ }
+
+ for (;;) {
+ if ((buf = bufp =
+ mdb_zalloc(bufsz, UM_NOSLEEP)) == NULL)
+ return (DCMD_ERR);
+
+ jsop.jsop_bufp = &bufp;
+ jsop.jsop_lenp = &len;
+
+ rv = jsobj_print(addr, &jsop);
+
+ if (len > 0)
+ break;
+
+ mdb_free(buf, bufsz);
+ bufsz <<= 1;
+ len = bufsz;
+ }
+
+ if (jsop.jsop_member == NULL && rv != 0)
+ return (DCMD_ERR);
+
+ if (jsop.jsop_member && !jsop.jsop_found) {
+ if (jsop.jsop_baseaddr)
+ (void) mdb_printf("%p: ", jsop.jsop_baseaddr);
+
+ (void) mdb_printf("undefined%s",
+ i < argc ? " " : "");
+ } else {
+ (void) mdb_printf("%s%s", buf, i < argc &&
+ !isspace(buf[strlen(buf) - 1]) ? " " : "");
+ }
+
+ mdb_free(buf, bufsz);
+ jsop.jsop_found = B_FALSE;
+ jsop.jsop_baseaddr = NULL;
+ } while (i < argc);
+
+ mdb_printf("\n");
+
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+dcmd_v8field(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ v8_class_t *clp;
+ v8_field_t *flp;
+ const char *klass, *field;
+ uintptr_t offset = 0;
+
+ /*
+ * We may be invoked with either two arguments (class and field name) or
+ * three (an offset to save).
+ */
+ if (argc != 2 && argc != 3)
+ return (DCMD_USAGE);
+
+ if (argv[0].a_type != MDB_TYPE_STRING ||
+ argv[1].a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ klass = argv[0].a_un.a_str;
+ field = argv[1].a_un.a_str;
+
+ if (argc == 3) {
+ if (argv[2].a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ offset = mdb_strtoull(argv[2].a_un.a_str);
+ }
+
+ for (clp = v8_classes; clp != NULL; clp = clp->v8c_next)
+ if (strcmp(clp->v8c_name, klass) == 0)
+ break;
+
+ if (clp == NULL) {
+ (void) mdb_printf("error: no such class: \"%s\"", klass);
+ return (DCMD_ERR);
+ }
+
+ for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next)
+ if (strcmp(field, flp->v8f_name) == 0)
+ break;
+
+ if (flp == NULL) {
+ if (argc == 2) {
+ mdb_printf("error: no such field in class \"%s\": "
+ "\"%s\"", klass, field);
+ return (DCMD_ERR);
+ }
+
+ flp = conf_field_create(clp, field, offset);
+ if (flp == NULL) {
+ mdb_warn("failed to create field");
+ return (DCMD_ERR);
+ }
+ } else if (argc == 3) {
+ flp->v8f_offset = offset;
+ }
+
+ mdb_printf("%s::%s at offset 0x%x\n", klass, field, flp->v8f_offset);
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+dcmd_v8array(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint8_t type;
+ uintptr_t *array;
+ size_t ii, len;
+
+ if (read_typebyte(&type, addr) != 0)
+ return (DCMD_ERR);
+
+ if (type != V8_TYPE_FIXEDARRAY) {
+ mdb_warn("%p is not an instance of FixedArray\n", addr);
+ return (DCMD_ERR);
+ }
+
+ if (read_heap_array(addr, &array, &len, UM_SLEEP | UM_GC) != 0)
+ return (DCMD_ERR);
+
+ for (ii = 0; ii < len; ii++)
+ mdb_printf("%p\n", array[ii]);
+
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+dcmd_jsstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uintptr_t raddr;
+ boolean_t opt_v;
+ char *opt_f = NULL, *opt_p = NULL;
+
+ if (mdb_getopts(argc, argv,
+ 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v,
+ 'f', MDB_OPT_STR, &opt_f,
+ 'p', MDB_OPT_STR, &opt_p,
+ NULL) != argc)
+ return (DCMD_USAGE);
+
+ /*
+ * The "::jsframe" walker iterates the valid frame pointers, but the
+ * "::jsframe" dcmd looks at the frame after the one it was given, so we
+ * have to explicitly examine the top frame here.
+ */
+ if (!(flags & DCMD_ADDRSPEC)) {
+ if (load_current_context(&addr, &raddr) != 0 ||
+ do_jsframe(addr, raddr, opt_v, opt_f, opt_p) != 0)
+ return (DCMD_ERR);
+ }
+
+ if (mdb_pwalk_dcmd("jsframe", "jsframe", argc, argv, addr) == -1)
+ return (DCMD_ERR);
+
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+dcmd_v8str(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ boolean_t opt_v = B_FALSE;
+ char buf[512 * 1024];
+ char *bufp;
+ size_t len;
+
+ if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v,
+ NULL) != argc)
+ return (DCMD_USAGE);
+
+ bufp = buf;
+ len = sizeof (buf);
+ if (jsstr_print(addr, (opt_v ? JSSTR_VERBOSE : JSSTR_NONE) |
+ JSSTR_QUOTED, &bufp, &len) != 0)
+ return (DCMD_ERR);
+
+ mdb_printf("%s\n", buf);
+ return (DCMD_OK);
+}
+
+static void
+dcmd_v8load_help(void)
+{
+ v8_cfg_t *cfp, **cfgpp;
+
+ mdb_printf(
+ "To traverse in-memory V8 structures, the V8 dmod requires\n"
+ "configuration that describes the layout of various V8 structures\n"
+ "in memory. Normally, this information is pulled from metadata\n"
+ "in the target binary. However, it's possible to use the module\n"
+ "with a binary not built with metadata by loading one of the\n"
+ "canned configurations.\n\n");
+
+ mdb_printf("Available configurations:\n");
+
+ (void) mdb_inc_indent(4);
+
+ for (cfgpp = v8_cfgs; *cfgpp != NULL; cfgpp++) {
+ cfp = *cfgpp;
+ mdb_printf("%-10s %s\n", cfp->v8cfg_name, cfp->v8cfg_label);
+ }
+
+ (void) mdb_dec_indent(4);
+}
+
+/* ARGSUSED */
+static int
+dcmd_v8load(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ v8_cfg_t *cfgp = NULL, **cfgpp;
+
+ if (v8_classes != NULL) {
+ mdb_warn("v8 module already configured\n");
+ return (DCMD_ERR);
+ }
+
+ if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ for (cfgpp = v8_cfgs; *cfgpp != NULL; cfgpp++) {
+ cfgp = *cfgpp;
+ if (strcmp(argv->a_un.a_str, cfgp->v8cfg_name) == 0)
+ break;
+ }
+
+ if (cfgp == NULL || cfgp->v8cfg_name == NULL) {
+ mdb_warn("unknown configuration: \"%s\"\n", argv->a_un.a_str);
+ return (DCMD_ERR);
+ }
+
+ if (autoconfigure(cfgp) == -1) {
+ mdb_warn("autoconfigure failed\n");
+ return (DCMD_ERR);
+ }
+
+ mdb_printf("V8 dmod configured based on %s\n", cfgp->v8cfg_name);
+ return (DCMD_OK);
+}
+
+static int
+walk_jsframes_init(mdb_walk_state_t *wsp)
+{
+ if (wsp->walk_addr != NULL)
+ return (WALK_NEXT);
+
+ if (load_current_context(&wsp->walk_addr, NULL) != 0)
+ return (WALK_ERR);
+
+ return (WALK_NEXT);
+}
+
+static int
+walk_jsframes_step(mdb_walk_state_t *wsp)
+{
+ uintptr_t addr, next;
+ int rv;
+
+ addr = wsp->walk_addr;
+ rv = wsp->walk_callback(wsp->walk_addr, NULL, wsp->walk_cbdata);
+
+ if (rv != WALK_NEXT)
+ return (rv);
+
+ if (mdb_vread(&next, sizeof (next), addr) == -1)
+ return (WALK_ERR);
+
+ if (next == NULL)
+ return (WALK_DONE);
+
+ wsp->walk_addr = next;
+ return (WALK_NEXT);
+}
+
+typedef struct jsprop_walk_data {
+ int jspw_nprops;
+ int jspw_current;
+ uintptr_t *jspw_props;
+} jsprop_walk_data_t;
+
+/*ARGSUSED*/
+static int
+walk_jsprop_nprops(const char *desc, uintptr_t val, void *arg)
+{
+ jsprop_walk_data_t *jspw = arg;
+ jspw->jspw_nprops++;
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+walk_jsprop_props(const char *desc, uintptr_t val, void *arg)
+{
+ jsprop_walk_data_t *jspw = arg;
+ jspw->jspw_props[jspw->jspw_current++] = val;
+
+ return (0);
+}
+
+static int
+walk_jsprop_init(mdb_walk_state_t *wsp)
+{
+ jsprop_walk_data_t *jspw;
+ uintptr_t addr;
+ uint8_t type;
+
+ if ((addr = wsp->walk_addr) == NULL) {
+ mdb_warn("'jsprop' does not support global walks\n");
+ return (WALK_ERR);
+ }
+
+ if (!V8_IS_HEAPOBJECT(addr) || read_typebyte(&type, addr) != 0 ||
+ type != V8_TYPE_JSOBJECT) {
+ mdb_warn("%p is not a JSObject\n", addr);
+ return (WALK_ERR);
+ }
+
+ jspw = mdb_zalloc(sizeof (jsprop_walk_data_t), UM_SLEEP | UM_GC);
+
+ if (jsobj_properties(addr, walk_jsprop_nprops, jspw) == -1) {
+ mdb_warn("couldn't iterate over properties for %p\n", addr);
+ return (WALK_ERR);
+ }
+
+ jspw->jspw_props = mdb_zalloc(jspw->jspw_nprops *
+ sizeof (uintptr_t), UM_SLEEP | UM_GC);
+
+ if (jsobj_properties(addr, walk_jsprop_props, jspw) == -1) {
+ mdb_warn("couldn't iterate over properties for %p\n", addr);
+ return (WALK_ERR);
+ }
+
+ jspw->jspw_current = 0;
+ wsp->walk_data = jspw;
+
+ return (WALK_NEXT);
+}
+
+static int
+walk_jsprop_step(mdb_walk_state_t *wsp)
+{
+ jsprop_walk_data_t *jspw = wsp->walk_data;
+ int rv;
+
+ if (jspw->jspw_current >= jspw->jspw_nprops)
+ return (WALK_DONE);
+
+ if ((rv = wsp->walk_callback(jspw->jspw_props[jspw->jspw_current++],
+ NULL, wsp->walk_cbdata)) != WALK_NEXT)
+ return (rv);
+
+ return (WALK_NEXT);
+}
+
+/*
+ * MDB linkage
+ */
+
+static const mdb_dcmd_t v8_mdb_dcmds[] = {
+ /*
+ * Commands to inspect JavaScript-level state
+ */
+ { "jsframe", ":[-iv] [-f function] [-p property]",
+ "summarize a JavaScript stack frame", dcmd_jsframe },
+ { "jsprint", ":[-ab] [-d depth] [member]", "print a JavaScript object",
+ dcmd_jsprint },
+ { "jsstack", "[-v] [-f function] [-p property]",
+ "print a JavaScript stacktrace", dcmd_jsstack },
+ { "findjsobjects", "?[-vb] [-r | -c cons | -p prop]", "find JavaScript "
+ "objects", dcmd_findjsobjects, dcmd_findjsobjects_help },
+
+ /*
+ * Commands to inspect V8-level state
+ */
+ { "v8array", ":", "print elements of a V8 FixedArray",
+ dcmd_v8array },
+ { "v8classes", NULL, "list known V8 heap object C++ classes",
+ dcmd_v8classes },
+ { "v8code", ":[-d]", "print information about a V8 Code object",
+ dcmd_v8code },
+ { "v8field", "classname fieldname offset",
+ "manually add a field to a given class", dcmd_v8field },
+ { "v8function", ":[-d]", "print JSFunction object details",
+ dcmd_v8function },
+ { "v8load", "version", "load canned config for a specific V8 version",
+ dcmd_v8load, dcmd_v8load_help },
+ { "v8frametypes", NULL, "list known V8 frame types",
+ dcmd_v8frametypes },
+ { "v8print", ":[class]", "print a V8 heap object",
+ dcmd_v8print, dcmd_v8print_help },
+ { "v8str", ":[-v]", "print the contents of a V8 string",
+ dcmd_v8str },
+ { "v8type", ":", "print the type of a V8 heap object",
+ dcmd_v8type },
+ { "v8types", NULL, "list known V8 heap object types",
+ dcmd_v8types },
+
+ { NULL }
+};
+
+static const mdb_walker_t v8_mdb_walkers[] = {
+ { "jsframe", "walk V8 JavaScript stack frames",
+ walk_jsframes_init, walk_jsframes_step },
+ { "jsprop", "walk property values for an object",
+ walk_jsprop_init, walk_jsprop_step },
+ { NULL }
+};
+
+static mdb_modinfo_t v8_mdb = { MDB_API_VERSION, v8_mdb_dcmds, v8_mdb_walkers };
+
+static void
+configure(void)
+{
+ char *success;
+ v8_cfg_t *cfgp = NULL;
+ GElf_Sym sym;
+
+ if (mdb_readsym(&v8_major, sizeof (v8_major),
+ "_ZN2v88internal7Version6major_E") == -1 ||
+ mdb_readsym(&v8_minor, sizeof (v8_minor),
+ "_ZN2v88internal7Version6minor_E") == -1 ||
+ mdb_readsym(&v8_build, sizeof (v8_build),
+ "_ZN2v88internal7Version6build_E") == -1 ||
+ mdb_readsym(&v8_patch, sizeof (v8_patch),
+ "_ZN2v88internal7Version6patch_E") == -1) {
+ mdb_warn("failed to determine V8 version");
+ return;
+ }
+
+ mdb_printf("V8 version: %d.%d.%d.%d\n",
+ v8_major, v8_minor, v8_build, v8_patch);
+
+ /*
+ * First look for debug metadata embedded within the binary, which may
+ * be present in recent V8 versions built with postmortem metadata.
+ */
+ if (mdb_lookup_by_name("v8dbg_SmiTag", &sym) == 0) {
+ cfgp = &v8_cfg_target;
+ success = "Autoconfigured V8 support from target";
+ } else if (v8_major == 3 && v8_minor == 1 && v8_build == 8) {
+ cfgp = &v8_cfg_04;
+ success = "Configured V8 support based on node v0.4";
+ } else if (v8_major == 3 && v8_minor == 6 && v8_build == 6) {
+ cfgp = &v8_cfg_06;
+ success = "Configured V8 support based on node v0.6";
+ } else {
+ mdb_printf("mdb_v8: target has no debug metadata and "
+ "no existing config found\n");
+ return;
+ }
+
+ if (autoconfigure(cfgp) != 0) {
+ mdb_warn("failed to autoconfigure from target; "
+ "commands may have incorrect results!\n");
+ return;
+ }
+
+ mdb_printf("%s\n", success);
+}
+
+static void
+enable_demangling(void)
+{
+ const char *symname = "_ZN2v88internal7Version6major_E";
+ GElf_Sym sym;
+ char buf[64];
+
+ /*
+ * Try to determine whether C++ symbol demangling has been enabled. If
+ * not, enable it.
+ */
+ if (mdb_lookup_by_name("_ZN2v88internal7Version6major_E", &sym) != 0)
+ return;
+
+ (void) mdb_snprintf(buf, sizeof (buf), "%a", sym.st_value);
+ if (strstr(buf, symname) != NULL)
+ (void) mdb_eval("$G");
+}
+
+const mdb_modinfo_t *
+_mdb_init(void)
+{
+ configure();
+ enable_demangling();
+ return (&v8_mdb);
+}
diff --git a/usr/src/cmd/mdb/common/modules/v8/mdb_v8_cfg.c b/usr/src/cmd/mdb/common/modules/v8/mdb_v8_cfg.c
new file mode 100644
index 0000000000..d907242435
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/v8/mdb_v8_cfg.c
@@ -0,0 +1,728 @@
+/*
+ * 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) 2013, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * mdb_v8_cfg.c: canned configurations for previous V8 versions.
+ *
+ * The functions and data defined here enable this dmod to support debugging
+ * Node.js binaries that predated V8's built-in postmortem debugging support.
+ */
+
+#include "v8cfg.h"
+
+/*ARGSUSED*/
+static int
+v8cfg_target_iter(v8_cfg_t *cfgp, int (*func)(mdb_symbol_t *, void *),
+ void *arg)
+{
+ return (mdb_symbol_iter(MDB_OBJ_EVERY, MDB_DYNSYM,
+ MDB_BIND_GLOBAL | MDB_TYPE_OBJECT | MDB_TYPE_FUNC,
+ func, arg));
+}
+
+/*ARGSUSED*/
+static int
+v8cfg_target_readsym(v8_cfg_t *cfgp, const char *name, intptr_t *valp)
+{
+ int val, rval;
+
+ if ((rval = mdb_readsym(&val, sizeof (val), name)) != -1)
+ *valp = (intptr_t)val;
+
+ return (rval);
+}
+
+/*
+ * Analog of mdb_symbol_iter() for a canned configuration.
+ */
+static int
+v8cfg_canned_iter(v8_cfg_t *cfgp, int (*func)(mdb_symbol_t *, void *),
+ void *arg)
+{
+ v8_cfg_symbol_t *v8sym;
+ mdb_symbol_t mdbsym;
+ int rv;
+
+ for (v8sym = cfgp->v8cfg_symbols; v8sym->v8cs_name != NULL; v8sym++) {
+ mdbsym.sym_name = v8sym->v8cs_name;
+ mdbsym.sym_object = NULL;
+ mdbsym.sym_sym = NULL;
+ mdbsym.sym_table = 0;
+ mdbsym.sym_id = 0;
+
+ if ((rv = func(&mdbsym, arg)) != 0)
+ return (rv);
+ }
+
+ return (0);
+}
+
+/*
+ * Analog of mdb_readsym() for a canned configuration.
+ */
+static int
+v8cfg_canned_readsym(v8_cfg_t *cfgp, const char *name, intptr_t *valp)
+{
+ v8_cfg_symbol_t *v8sym;
+
+ for (v8sym = cfgp->v8cfg_symbols; v8sym->v8cs_name != NULL; v8sym++) {
+ if (strcmp(name, v8sym->v8cs_name) == 0)
+ break;
+ }
+
+ if (v8sym->v8cs_name == NULL)
+ return (-1);
+
+ *valp = v8sym->v8cs_value;
+ return (0);
+}
+
+/*
+ * Canned configuration for the V8 bundled with Node.js v0.4.8 and later.
+ */
+static v8_cfg_symbol_t v8_symbols_node_04[] = {
+ { "v8dbg_type_AccessCheckInfo__ACCESS_CHECK_INFO_TYPE", 0x91 },
+ { "v8dbg_type_AccessorInfo__ACCESSOR_INFO_TYPE", 0x90 },
+ { "v8dbg_type_BreakPointInfo__BREAK_POINT_INFO_TYPE", 0x9b },
+ { "v8dbg_type_ByteArray__BYTE_ARRAY_TYPE", 0x86 },
+ { "v8dbg_type_CallHandlerInfo__CALL_HANDLER_INFO_TYPE", 0x93 },
+ { "v8dbg_type_Code__CODE_TYPE", 0x81 },
+ { "v8dbg_type_CodeCache__CODE_CACHE_TYPE", 0x99 },
+ { "v8dbg_type_ConsString__CONS_ASCII_STRING_TYPE", 0x5 },
+ { "v8dbg_type_ConsString__CONS_ASCII_SYMBOL_TYPE", 0x45 },
+ { "v8dbg_type_ConsString__CONS_STRING_TYPE", 0x1 },
+ { "v8dbg_type_ConsString__CONS_SYMBOL_TYPE", 0x41 },
+ { "v8dbg_type_DebugInfo__DEBUG_INFO_TYPE", 0x9a },
+ { "v8dbg_type_ExternalAsciiString__EXTERNAL_ASCII_STRING_TYPE", 0x6 },
+ { "v8dbg_type_ExternalAsciiString__EXTERNAL_ASCII_SYMBOL_TYPE", 0x46 },
+ { "v8dbg_type_ExternalByteArray__EXTERNAL_BYTE_ARRAY_TYPE", 0x88 },
+ { "v8dbg_type_ExternalFloatArray__EXTERNAL_FLOAT_ARRAY_TYPE", 0x8e },
+ { "v8dbg_type_ExternalIntArray__EXTERNAL_INT_ARRAY_TYPE", 0x8c },
+ { "v8dbg_type_ExternalShortArray__EXTERNAL_SHORT_ARRAY_TYPE", 0x8a },
+ { "v8dbg_type_ExternalString__EXTERNAL_STRING_TYPE", 0x2 },
+ { "v8dbg_type_ExternalString__EXTERNAL_SYMBOL_TYPE", 0x42 },
+ { "v8dbg_type_ExternalUnsignedByteArray__"
+ "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE", 0x89 },
+ { "v8dbg_type_ExternalUnsignedIntArray__"
+ "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE", 0x8d },
+ { "v8dbg_type_ExternalUnsignedShortArray__"
+ "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE", 0x8b },
+ { "v8dbg_type_FixedArray__FIXED_ARRAY_TYPE", 0x9c },
+ { "v8dbg_type_FunctionTemplateInfo__"
+ "FUNCTION_TEMPLATE_INFO_TYPE", 0x94 },
+ { "v8dbg_type_HeapNumber__HEAP_NUMBER_TYPE", 0x84 },
+ { "v8dbg_type_InterceptorInfo__INTERCEPTOR_INFO_TYPE", 0x92 },
+ { "v8dbg_type_JSArray__JS_ARRAY_TYPE", 0xa5 },
+ { "v8dbg_type_JSBuiltinsObject__JS_BUILTINS_OBJECT_TYPE", 0xa3 },
+ { "v8dbg_type_JSFunction__JS_FUNCTION_TYPE", 0xa7 },
+ { "v8dbg_type_JSGlobalObject__JS_GLOBAL_OBJECT_TYPE", 0xa2 },
+ { "v8dbg_type_JSGlobalPropertyCell__"
+ "JS_GLOBAL_PROPERTY_CELL_TYPE", 0x83 },
+ { "v8dbg_type_JSGlobalProxy__JS_GLOBAL_PROXY_TYPE", 0xa4 },
+ { "v8dbg_type_JSMessageObject__JS_MESSAGE_OBJECT_TYPE", 0x9e },
+ { "v8dbg_type_JSObject__JS_OBJECT_TYPE", 0xa0 },
+ { "v8dbg_type_JSRegExp__JS_REGEXP_TYPE", 0xa6 },
+ { "v8dbg_type_JSValue__JS_VALUE_TYPE", 0x9f },
+ { "v8dbg_type_Map__MAP_TYPE", 0x80 },
+ { "v8dbg_type_ObjectTemplateInfo__OBJECT_TEMPLATE_INFO_TYPE", 0x95 },
+ { "v8dbg_type_Oddball__ODDBALL_TYPE", 0x82 },
+ { "v8dbg_type_Script__SCRIPT_TYPE", 0x98 },
+ { "v8dbg_type_SeqAsciiString__ASCII_STRING_TYPE", 0x4 },
+ { "v8dbg_type_SeqAsciiString__ASCII_SYMBOL_TYPE", 0x44 },
+ { "v8dbg_type_SharedFunctionInfo__SHARED_FUNCTION_INFO_TYPE", 0x9d },
+ { "v8dbg_type_SignatureInfo__SIGNATURE_INFO_TYPE", 0x96 },
+ { "v8dbg_type_String__STRING_TYPE", 0x0 },
+ { "v8dbg_type_String__SYMBOL_TYPE", 0x40 },
+ { "v8dbg_type_TypeSwitchInfo__TYPE_SWITCH_INFO_TYPE", 0x97 },
+
+ { "v8dbg_class_AccessCheckInfo__data__Object", 0xc },
+ { "v8dbg_class_AccessCheckInfo__indexed_callback__Object", 0x8 },
+ { "v8dbg_class_AccessCheckInfo__named_callback__Object", 0x4 },
+ { "v8dbg_class_AccessorInfo__data__Object", 0xc },
+ { "v8dbg_class_AccessorInfo__flag__Smi", 0x14 },
+ { "v8dbg_class_AccessorInfo__getter__Object", 0x4 },
+ { "v8dbg_class_AccessorInfo__name__Object", 0x10 },
+ { "v8dbg_class_AccessorInfo__setter__Object", 0x8 },
+ { "v8dbg_class_BreakPointInfo__break_point_objects__Object", 0x10 },
+ { "v8dbg_class_BreakPointInfo__code_position__Smi", 0x4 },
+ { "v8dbg_class_BreakPointInfo__source_position__Smi", 0x8 },
+ { "v8dbg_class_BreakPointInfo__statement_position__Smi", 0xc },
+ { "v8dbg_class_ByteArray__length__SMI", 0x4 },
+ { "v8dbg_class_CallHandlerInfo__callback__Object", 0x4 },
+ { "v8dbg_class_CallHandlerInfo__data__Object", 0x8 },
+ { "v8dbg_class_Code__deoptimization_data__FixedArray", 0xc },
+ { "v8dbg_class_Code__instruction_size__int", 0x4 },
+ { "v8dbg_class_Code__instruction_start__int", 0x20 },
+ { "v8dbg_class_Code__relocation_info__ByteArray", 0x8 },
+ { "v8dbg_class_CodeCache__default_cache__FixedArray", 0x4 },
+ { "v8dbg_class_CodeCache__normal_type_cache__Object", 0x8 },
+ { "v8dbg_class_ConsString__first__String", 0xc },
+ { "v8dbg_class_ConsString__second__String", 0x10 },
+ { "v8dbg_class_DebugInfo__break_points__FixedArray", 0x14 },
+ { "v8dbg_class_DebugInfo__code__Code", 0xc },
+ { "v8dbg_class_DebugInfo__original_code__Code", 0x8 },
+ { "v8dbg_class_DebugInfo__shared__SharedFunctionInfo", 0x4 },
+ { "v8dbg_class_ExternalString__resource__Object", 0xc },
+ { "v8dbg_class_FixedArray__data__uintptr_t", 0x8 },
+ { "v8dbg_class_FixedArray__length__SMI", 0x4 },
+ { "v8dbg_class_FunctionTemplateInfo__access_check_info__Object", 0x38 },
+ { "v8dbg_class_FunctionTemplateInfo__call_code__Object", 0x10 },
+ { "v8dbg_class_FunctionTemplateInfo__class_name__Object", 0x2c },
+ { "v8dbg_class_FunctionTemplateInfo__flag__Smi", 0x3c },
+ { "v8dbg_class_FunctionTemplateInfo__"
+ "indexed_property_handler__Object", 0x24 },
+ { "v8dbg_class_FunctionTemplateInfo__"
+ "instance_call_handler__Object", 0x34 },
+ { "v8dbg_class_FunctionTemplateInfo__instance_template__Object", 0x28 },
+ { "v8dbg_class_FunctionTemplateInfo__"
+ "named_property_handler__Object", 0x20 },
+ { "v8dbg_class_FunctionTemplateInfo__parent_template__Object", 0x1c },
+ { "v8dbg_class_FunctionTemplateInfo__"
+ "property_accessors__Object", 0x14 },
+ { "v8dbg_class_FunctionTemplateInfo__"
+ "prototype_template__Object", 0x18 },
+ { "v8dbg_class_FunctionTemplateInfo__serial_number__Object", 0xc },
+ { "v8dbg_class_FunctionTemplateInfo__signature__Object", 0x30 },
+ { "v8dbg_class_GlobalObject__builtins__JSBuiltinsObject", 0xc },
+ { "v8dbg_class_GlobalObject__global_context__Context", 0x10 },
+ { "v8dbg_class_GlobalObject__global_receiver__JSObject", 0x14 },
+ { "v8dbg_class_HeapNumber__value__SMI", 0x4 },
+ { "v8dbg_class_HeapObject__map__Map", 0x0 },
+ { "v8dbg_class_InterceptorInfo__data__Object", 0x18 },
+ { "v8dbg_class_InterceptorInfo__deleter__Object", 0x10 },
+ { "v8dbg_class_InterceptorInfo__enumerator__Object", 0x14 },
+ { "v8dbg_class_InterceptorInfo__getter__Object", 0x4 },
+ { "v8dbg_class_InterceptorInfo__query__Object", 0xc },
+ { "v8dbg_class_InterceptorInfo__setter__Object", 0x8 },
+ { "v8dbg_class_JSArray__length__Object", 0xc },
+ { "v8dbg_class_JSFunction__literals__FixedArray", 0x1c },
+ { "v8dbg_class_JSFunction__next_function_link__Object", 0x20 },
+ { "v8dbg_class_JSFunction__prototype_or_initial_map__Object", 0x10 },
+ { "v8dbg_class_JSFunction__shared__SharedFunctionInfo", 0x14 },
+ { "v8dbg_class_JSGlobalProxy__context__Object", 0xc },
+ { "v8dbg_class_JSMessageObject__arguments__JSArray", 0x10 },
+ { "v8dbg_class_JSMessageObject__end_position__SMI", 0x24 },
+ { "v8dbg_class_JSMessageObject__script__Object", 0x14 },
+ { "v8dbg_class_JSMessageObject__stack_frames__Object", 0x1c },
+ { "v8dbg_class_JSMessageObject__stack_trace__Object", 0x18 },
+ { "v8dbg_class_JSMessageObject__start_position__SMI", 0x20 },
+ { "v8dbg_class_JSMessageObject__type__String", 0xc },
+ { "v8dbg_class_JSObject__elements__Object", 0x8 },
+ { "v8dbg_class_JSObject__properties__FixedArray", 0x4 },
+ { "v8dbg_class_JSRegExp__data__Object", 0xc },
+ { "v8dbg_class_JSValue__value__Object", 0xc },
+ { "v8dbg_class_Map__code_cache__Object", 0x18 },
+ { "v8dbg_class_Map__constructor__Object", 0x10 },
+ { "v8dbg_class_Map__inobject_properties__int", 0x5 },
+ { "v8dbg_class_Map__instance_size__int", 0x4 },
+ { "v8dbg_class_Map__instance_attributes__int", 0x8 },
+ { "v8dbg_class_Map__instance_descriptors__DescriptorArray", 0x14 },
+ { "v8dbg_class_ObjectTemplateInfo__constructor__Object", 0xc },
+ { "v8dbg_class_ObjectTemplateInfo__"
+ "internal_field_count__Object", 0x10 },
+ { "v8dbg_class_Oddball__to_number__Object", 0x8 },
+ { "v8dbg_class_Oddball__to_string__String", 0x4 },
+ { "v8dbg_class_Script__column_offset__Smi", 0x10 },
+ { "v8dbg_class_Script__compilation_type__Smi", 0x24 },
+ { "v8dbg_class_Script__context_data__Object", 0x18 },
+ { "v8dbg_class_Script__data__Object", 0x14 },
+ { "v8dbg_class_Script__eval_from_instructions_offset__Smi", 0x34 },
+ { "v8dbg_class_Script__eval_from_shared__Object", 0x30 },
+ { "v8dbg_class_Script__id__Object", 0x2c },
+ { "v8dbg_class_Script__line_ends__Object", 0x28 },
+ { "v8dbg_class_Script__line_offset__Smi", 0xc },
+ { "v8dbg_class_Script__name__Object", 0x8 },
+ { "v8dbg_class_Script__source__Object", 0x4 },
+ { "v8dbg_class_Script__type__Smi", 0x20 },
+ { "v8dbg_class_Script__wrapper__Proxy", 0x1c },
+ { "v8dbg_class_SeqAsciiString__chars__char", 0xc },
+ { "v8dbg_class_SharedFunctionInfo__code__Code", 0x8 },
+ { "v8dbg_class_SharedFunctionInfo__compiler_hints__SMI", 0x50 },
+ { "v8dbg_class_SharedFunctionInfo__construct_stub__Code", 0x10 },
+ { "v8dbg_class_SharedFunctionInfo__debug_info__Object", 0x20 },
+ { "v8dbg_class_SharedFunctionInfo__end_position__SMI", 0x48 },
+ { "v8dbg_class_SharedFunctionInfo__"
+ "expected_nof_properties__SMI", 0x3c },
+ { "v8dbg_class_SharedFunctionInfo__formal_parameter_count__SMI", 0x38 },
+ { "v8dbg_class_SharedFunctionInfo__function_data__Object", 0x18 },
+ { "v8dbg_class_SharedFunctionInfo__"
+ "function_token_position__SMI", 0x4c },
+ { "v8dbg_class_SharedFunctionInfo__inferred_name__String", 0x24 },
+ { "v8dbg_class_SharedFunctionInfo__initial_map__Object", 0x28 },
+ { "v8dbg_class_SharedFunctionInfo__instance_class_name__Object", 0x14 },
+ { "v8dbg_class_SharedFunctionInfo__length__SMI", 0x34 },
+ { "v8dbg_class_SharedFunctionInfo__name__Object", 0x4 },
+ { "v8dbg_class_SharedFunctionInfo__num_literals__SMI", 0x40 },
+ { "v8dbg_class_SharedFunctionInfo__opt_count__SMI", 0x58 },
+ { "v8dbg_class_SharedFunctionInfo__script__Object", 0x1c },
+ { "v8dbg_class_SharedFunctionInfo__"
+ "start_position_and_type__SMI", 0x44 },
+ { "v8dbg_class_SharedFunctionInfo__"
+ "this_property_assignments__Object", 0x2c },
+ { "v8dbg_class_SharedFunctionInfo__"
+ "this_property_assignments_count__SMI", 0x54 },
+ { "v8dbg_class_SignatureInfo__args__Object", 0x8 },
+ { "v8dbg_class_SignatureInfo__receiver__Object", 0x4 },
+ { "v8dbg_class_String__length__SMI", 0x4 },
+ { "v8dbg_class_TemplateInfo__property_list__Object", 0x8 },
+ { "v8dbg_class_TemplateInfo__tag__Object", 0x4 },
+ { "v8dbg_class_TypeSwitchInfo__types__Object", 0x4 },
+
+ { "v8dbg_parent_AccessCheckInfo__Struct", 0x0 },
+ { "v8dbg_parent_AccessorInfo__Struct", 0x0 },
+ { "v8dbg_parent_BreakPointInfo__Struct", 0x0 },
+ { "v8dbg_parent_ByteArray__HeapObject", 0x0 },
+ { "v8dbg_parent_CallHandlerInfo__Struct", 0x0 },
+ { "v8dbg_parent_Code__HeapObject", 0x0 },
+ { "v8dbg_parent_CodeCache__Struct", 0x0 },
+ { "v8dbg_parent_ConsString__String", 0x0 },
+ { "v8dbg_parent_DebugInfo__Struct", 0x0 },
+ { "v8dbg_parent_DeoptimizationInputData__FixedArray", 0x0 },
+ { "v8dbg_parent_DeoptimizationOutputData__FixedArray", 0x0 },
+ { "v8dbg_parent_DescriptorArray__FixedArray", 0x0 },
+ { "v8dbg_parent_ExternalArray__HeapObject", 0x0 },
+ { "v8dbg_parent_ExternalAsciiString__ExternalString", 0x0 },
+ { "v8dbg_parent_ExternalByteArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalFloatArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalIntArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalShortArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalString__String", 0x0 },
+ { "v8dbg_parent_ExternalTwoByteString__ExternalString", 0x0 },
+ { "v8dbg_parent_ExternalUnsignedByteArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalUnsignedIntArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalUnsignedShortArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_Failure__MaybeObject", 0x0 },
+ { "v8dbg_parent_FixedArray__HeapObject", 0x0 },
+ { "v8dbg_parent_FunctionTemplateInfo__TemplateInfo", 0x0 },
+ { "v8dbg_parent_GlobalObject__JSObject", 0x0 },
+ { "v8dbg_parent_HeapNumber__HeapObject", 0x0 },
+ { "v8dbg_parent_HeapObject__Object", 0x0 },
+ { "v8dbg_parent_InterceptorInfo__Struct", 0x0 },
+ { "v8dbg_parent_JSArray__JSObject", 0x0 },
+ { "v8dbg_parent_JSBuiltinsObject__GlobalObject", 0x0 },
+ { "v8dbg_parent_JSFunction__JSObject", 0x0 },
+ { "v8dbg_parent_JSFunctionResultCache__FixedArray", 0x0 },
+ { "v8dbg_parent_JSGlobalObject__GlobalObject", 0x0 },
+ { "v8dbg_parent_JSGlobalPropertyCell__HeapObject", 0x0 },
+ { "v8dbg_parent_JSGlobalProxy__JSObject", 0x0 },
+ { "v8dbg_parent_JSMessageObject__JSObject", 0x0 },
+ { "v8dbg_parent_JSObject__HeapObject", 0x0 },
+ { "v8dbg_parent_JSRegExp__JSObject", 0x0 },
+ { "v8dbg_parent_JSRegExpResult__JSArray", 0x0 },
+ { "v8dbg_parent_JSValue__JSObject", 0x0 },
+ { "v8dbg_parent_Map__HeapObject", 0x0 },
+ { "v8dbg_parent_NormalizedMapCache__FixedArray", 0x0 },
+ { "v8dbg_parent_Object__MaybeObject", 0x0 },
+ { "v8dbg_parent_ObjectTemplateInfo__TemplateInfo", 0x0 },
+ { "v8dbg_parent_Oddball__HeapObject", 0x0 },
+ { "v8dbg_parent_Script__Struct", 0x0 },
+ { "v8dbg_parent_SeqAsciiString__SeqString", 0x0 },
+ { "v8dbg_parent_SeqString__String", 0x0 },
+ { "v8dbg_parent_SeqTwoByteString__SeqString", 0x0 },
+ { "v8dbg_parent_SharedFunctionInfo__HeapObject", 0x0 },
+ { "v8dbg_parent_SignatureInfo__Struct", 0x0 },
+ { "v8dbg_parent_Smi__Object", 0x0 },
+ { "v8dbg_parent_String__HeapObject", 0x0 },
+ { "v8dbg_parent_Struct__HeapObject", 0x0 },
+ { "v8dbg_parent_TemplateInfo__Struct", 0x0 },
+ { "v8dbg_parent_TypeSwitchInfo__Struct", 0x0 },
+
+ { "v8dbg_frametype_ArgumentsAdaptorFrame", 0x8 },
+ { "v8dbg_frametype_ConstructFrame", 0x7 },
+ { "v8dbg_frametype_EntryConstructFrame", 0x2 },
+ { "v8dbg_frametype_EntryFrame", 0x1 },
+ { "v8dbg_frametype_ExitFrame", 0x3 },
+ { "v8dbg_frametype_InternalFrame", 0x6 },
+ { "v8dbg_frametype_JavaScriptFrame", 0x4 },
+ { "v8dbg_frametype_OptimizedFrame", 0x5 },
+
+ { "v8dbg_off_fp_context", -0x4 },
+ { "v8dbg_off_fp_function", -0x8 },
+ { "v8dbg_off_fp_marker", -0x8 },
+ { "v8dbg_off_fp_args", 0x8 },
+
+ { "v8dbg_prop_idx_content", 0x0 },
+ { "v8dbg_prop_idx_first", 0x2 },
+ { "v8dbg_prop_type_field", 0x1 },
+ { "v8dbg_prop_type_first_phantom", 0x6 },
+ { "v8dbg_prop_type_mask", 0xf },
+
+ { "v8dbg_AsciiStringTag", 0x4 },
+ { "v8dbg_ConsStringTag", 0x1 },
+ { "v8dbg_ExternalStringTag", 0x2 },
+ { "v8dbg_FailureTag", 0x3 },
+ { "v8dbg_FailureTagMask", 0x3 },
+ { "v8dbg_FirstNonstringType", 0x80 },
+ { "v8dbg_HeapObjectTag", 0x1 },
+ { "v8dbg_HeapObjectTagMask", 0x3 },
+ { "v8dbg_IsNotStringMask", 0x80 },
+ { "v8dbg_NotStringTag", 0x80 },
+ { "v8dbg_SeqStringTag", 0x0 },
+ { "v8dbg_SmiTag", 0x0 },
+ { "v8dbg_SmiTagMask", 0x1 },
+ { "v8dbg_SmiValueShift", 0x1 },
+ { "v8dbg_StringEncodingMask", 0x4 },
+ { "v8dbg_StringRepresentationMask", 0x3 },
+ { "v8dbg_StringTag", 0x0 },
+ { "v8dbg_TwoByteStringTag", 0x0 },
+ { "v8dbg_PointerSizeLog2", 0x2 },
+
+ { NULL }
+};
+
+/*
+ * Canned configuration for the V8 bundled with Node.js v0.6.5.
+ */
+static v8_cfg_symbol_t v8_symbols_node_06[] = {
+ { "v8dbg_type_AccessCheckInfo__ACCESS_CHECK_INFO_TYPE", 0x93 },
+ { "v8dbg_type_AccessorInfo__ACCESSOR_INFO_TYPE", 0x92 },
+ { "v8dbg_type_BreakPointInfo__BREAK_POINT_INFO_TYPE", 0x9e },
+ { "v8dbg_type_ByteArray__BYTE_ARRAY_TYPE", 0x86 },
+ { "v8dbg_type_CallHandlerInfo__CALL_HANDLER_INFO_TYPE", 0x95 },
+ { "v8dbg_type_Code__CODE_TYPE", 0x81 },
+ { "v8dbg_type_CodeCache__CODE_CACHE_TYPE", 0x9b },
+ { "v8dbg_type_ConsString__CONS_ASCII_STRING_TYPE", 0x5 },
+ { "v8dbg_type_ConsString__CONS_ASCII_SYMBOL_TYPE", 0x45 },
+ { "v8dbg_type_ConsString__CONS_STRING_TYPE", 0x1 },
+ { "v8dbg_type_ConsString__CONS_SYMBOL_TYPE", 0x41 },
+ { "v8dbg_type_DebugInfo__DEBUG_INFO_TYPE", 0x9d },
+ { "v8dbg_type_ExternalAsciiString__EXTERNAL_ASCII_STRING_TYPE", 0x6 },
+ { "v8dbg_type_ExternalAsciiString__EXTERNAL_ASCII_SYMBOL_TYPE", 0x46 },
+ { "v8dbg_type_ExternalByteArray__EXTERNAL_BYTE_ARRAY_TYPE", 0x87 },
+ { "v8dbg_type_ExternalDoubleArray__EXTERNAL_DOUBLE_ARRAY_TYPE", 0x8e },
+ { "v8dbg_type_ExternalFloatArray__EXTERNAL_FLOAT_ARRAY_TYPE", 0x8d },
+ { "v8dbg_type_ExternalIntArray__EXTERNAL_INT_ARRAY_TYPE", 0x8b },
+ { "v8dbg_type_ExternalPixelArray__EXTERNAL_PIXEL_ARRAY_TYPE", 0x8f },
+ { "v8dbg_type_ExternalShortArray__EXTERNAL_SHORT_ARRAY_TYPE", 0x89 },
+ { "v8dbg_type_ExternalTwoByteString__EXTERNAL_STRING_TYPE", 0x2 },
+ { "v8dbg_type_ExternalTwoByteString__EXTERNAL_SYMBOL_TYPE", 0x42 },
+ { "v8dbg_type_ExternalUnsignedByteArray__"
+ "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE", 0x88 },
+ { "v8dbg_type_ExternalUnsignedIntArray__"
+ "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE", 0x8c },
+ { "v8dbg_type_ExternalUnsignedShortArray__"
+ "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE", 0x8a },
+ { "v8dbg_type_FixedArray__FIXED_ARRAY_TYPE", 0x9f },
+ { "v8dbg_type_FixedDoubleArray__FIXED_DOUBLE_ARRAY_TYPE", 0x90 },
+ { "v8dbg_type_Foreign__FOREIGN_TYPE", 0x85 },
+ { "v8dbg_type_FunctionTemplateInfo__FUNCTION_TEMPLATE_INFO_TYPE",
+ 0x96 },
+ { "v8dbg_type_HeapNumber__HEAP_NUMBER_TYPE", 0x84 },
+ { "v8dbg_type_InterceptorInfo__INTERCEPTOR_INFO_TYPE", 0x94 },
+ { "v8dbg_type_JSArray__JS_ARRAY_TYPE", 0xa8 },
+ { "v8dbg_type_JSBuiltinsObject__JS_BUILTINS_OBJECT_TYPE", 0xa6 },
+ { "v8dbg_type_JSFunction__JS_FUNCTION_TYPE", 0xac },
+ { "v8dbg_type_JSFunctionProxy__JS_FUNCTION_PROXY_TYPE", 0xad },
+ { "v8dbg_type_JSGlobalObject__JS_GLOBAL_OBJECT_TYPE", 0xa5 },
+ { "v8dbg_type_JSGlobalPropertyCell__JS_GLOBAL_PROPERTY_CELL_TYPE",
+ 0x83 },
+ { "v8dbg_type_JSMessageObject__JS_MESSAGE_OBJECT_TYPE", 0xa1 },
+ { "v8dbg_type_JSObject__JS_OBJECT_TYPE", 0xa3 },
+ { "v8dbg_type_JSProxy__JS_PROXY_TYPE", 0xa9 },
+ { "v8dbg_type_JSRegExp__JS_REGEXP_TYPE", 0xab },
+ { "v8dbg_type_JSValue__JS_VALUE_TYPE", 0xa2 },
+ { "v8dbg_type_JSWeakMap__JS_WEAK_MAP_TYPE", 0xaa },
+ { "v8dbg_type_Map__MAP_TYPE", 0x80 },
+ { "v8dbg_type_ObjectTemplateInfo__OBJECT_TEMPLATE_INFO_TYPE", 0x97 },
+ { "v8dbg_type_Oddball__ODDBALL_TYPE", 0x82 },
+ { "v8dbg_type_PolymorphicCodeCache__POLYMORPHIC_CODE_CACHE_TYPE",
+ 0x9c },
+ { "v8dbg_type_Script__SCRIPT_TYPE", 0x9a },
+ { "v8dbg_type_SeqAsciiString__ASCII_STRING_TYPE", 0x4 },
+ { "v8dbg_type_SeqAsciiString__ASCII_SYMBOL_TYPE", 0x44 },
+ { "v8dbg_type_SeqTwoByteString__STRING_TYPE", 0x0 },
+ { "v8dbg_type_SeqTwoByteString__SYMBOL_TYPE", 0x40 },
+ { "v8dbg_type_SharedFunctionInfo__SHARED_FUNCTION_INFO_TYPE", 0xa0 },
+ { "v8dbg_type_SignatureInfo__SIGNATURE_INFO_TYPE", 0x98 },
+ { "v8dbg_type_SlicedString__SLICED_ASCII_STRING_TYPE", 0x7 },
+ { "v8dbg_type_SlicedString__SLICED_STRING_TYPE", 0x3 },
+ { "v8dbg_type_TypeSwitchInfo__TYPE_SWITCH_INFO_TYPE", 0x99 },
+
+ { "v8dbg_class_AccessCheckInfo__data__Object", 0xc },
+ { "v8dbg_class_AccessCheckInfo__indexed_callback__Object", 0x8 },
+ { "v8dbg_class_AccessCheckInfo__named_callback__Object", 0x4 },
+ { "v8dbg_class_AccessorInfo__data__Object", 0xc },
+ { "v8dbg_class_AccessorInfo__flag__Smi", 0x14 },
+ { "v8dbg_class_AccessorInfo__getter__Object", 0x4 },
+ { "v8dbg_class_AccessorInfo__name__Object", 0x10 },
+ { "v8dbg_class_AccessorInfo__setter__Object", 0x8 },
+ { "v8dbg_class_BreakPointInfo__break_point_objects__Object", 0x10 },
+ { "v8dbg_class_BreakPointInfo__code_position__Smi", 0x4 },
+ { "v8dbg_class_BreakPointInfo__source_position__Smi", 0x8 },
+ { "v8dbg_class_BreakPointInfo__statement_position__Smi", 0xc },
+ { "v8dbg_class_CallHandlerInfo__callback__Object", 0x4 },
+ { "v8dbg_class_CallHandlerInfo__data__Object", 0x8 },
+ { "v8dbg_class_Code__deoptimization_data__FixedArray", 0xc },
+ { "v8dbg_class_Code__instruction_size__int", 0x4 },
+ { "v8dbg_class_Code__instruction_start__int", 0x20 },
+ { "v8dbg_class_Code__next_code_flushing_candidate__Object", 0x10 },
+ { "v8dbg_class_Code__relocation_info__ByteArray", 0x8 },
+ { "v8dbg_class_CodeCache__default_cache__FixedArray", 0x4 },
+ { "v8dbg_class_CodeCache__normal_type_cache__Object", 0x8 },
+ { "v8dbg_class_ConsString__first__String", 0xc },
+ { "v8dbg_class_ConsString__second__String", 0x10 },
+ { "v8dbg_class_DebugInfo__break_points__FixedArray", 0x14 },
+ { "v8dbg_class_DebugInfo__code__Code", 0xc },
+ { "v8dbg_class_DebugInfo__original_code__Code", 0x8 },
+ { "v8dbg_class_DebugInfo__shared__SharedFunctionInfo", 0x4 },
+ { "v8dbg_class_ExternalString__resource__Object", 0xc },
+ { "v8dbg_class_FixedArray__data__uintptr_t", 0x8 },
+ { "v8dbg_class_FixedArrayBase__length__SMI", 0x4 },
+ { "v8dbg_class_FunctionTemplateInfo__access_check_info__Object", 0x38 },
+ { "v8dbg_class_FunctionTemplateInfo__call_code__Object", 0x10 },
+ { "v8dbg_class_FunctionTemplateInfo__class_name__Object", 0x2c },
+ { "v8dbg_class_FunctionTemplateInfo__flag__Smi", 0x3c },
+ { "v8dbg_class_FunctionTemplateInfo__indexed_property_handler__Object",
+ 0x24 },
+ { "v8dbg_class_FunctionTemplateInfo__instance_call_handler__Object",
+ 0x34 },
+ { "v8dbg_class_FunctionTemplateInfo__instance_template__Object", 0x28 },
+ { "v8dbg_class_FunctionTemplateInfo__named_property_handler__Object",
+ 0x20 },
+ { "v8dbg_class_FunctionTemplateInfo__parent_template__Object", 0x1c },
+ { "v8dbg_class_FunctionTemplateInfo__property_accessors__Object",
+ 0x14 },
+ { "v8dbg_class_FunctionTemplateInfo__prototype_template__Object",
+ 0x18 },
+ { "v8dbg_class_FunctionTemplateInfo__serial_number__Object", 0xc },
+ { "v8dbg_class_FunctionTemplateInfo__signature__Object", 0x30 },
+ { "v8dbg_class_GlobalObject__builtins__JSBuiltinsObject", 0xc },
+ { "v8dbg_class_GlobalObject__global_context__Context", 0x10 },
+ { "v8dbg_class_GlobalObject__global_receiver__JSObject", 0x14 },
+ { "v8dbg_class_HeapNumber__value__double", 0x4 },
+ { "v8dbg_class_HeapObject__map__Map", 0x0 },
+ { "v8dbg_class_InterceptorInfo__data__Object", 0x18 },
+ { "v8dbg_class_InterceptorInfo__deleter__Object", 0x10 },
+ { "v8dbg_class_InterceptorInfo__enumerator__Object", 0x14 },
+ { "v8dbg_class_InterceptorInfo__getter__Object", 0x4 },
+ { "v8dbg_class_InterceptorInfo__query__Object", 0xc },
+ { "v8dbg_class_InterceptorInfo__setter__Object", 0x8 },
+ { "v8dbg_class_JSArray__length__Object", 0xc },
+ { "v8dbg_class_JSFunction__literals__FixedArray", 0x1c },
+ { "v8dbg_class_JSFunction__next_function_link__Object", 0x20 },
+ { "v8dbg_class_JSFunction__prototype_or_initial_map__Object", 0x10 },
+ { "v8dbg_class_JSFunction__shared__SharedFunctionInfo", 0x14 },
+ { "v8dbg_class_JSFunctionProxy__call_trap__Object", 0x8 },
+ { "v8dbg_class_JSFunctionProxy__construct_trap__Object", 0xc },
+ { "v8dbg_class_JSGlobalProxy__context__Object", 0xc },
+ { "v8dbg_class_JSMessageObject__arguments__JSArray", 0x10 },
+ { "v8dbg_class_JSMessageObject__end_position__SMI", 0x24 },
+ { "v8dbg_class_JSMessageObject__script__Object", 0x14 },
+ { "v8dbg_class_JSMessageObject__stack_frames__Object", 0x1c },
+ { "v8dbg_class_JSMessageObject__stack_trace__Object", 0x18 },
+ { "v8dbg_class_JSMessageObject__start_position__SMI", 0x20 },
+ { "v8dbg_class_JSMessageObject__type__String", 0xc },
+ { "v8dbg_class_JSObject__elements__Object", 0x8 },
+ { "v8dbg_class_JSObject__properties__FixedArray", 0x4 },
+ { "v8dbg_class_JSProxy__handler__Object", 0x4 },
+ { "v8dbg_class_JSRegExp__data__Object", 0xc },
+ { "v8dbg_class_JSValue__value__Object", 0xc },
+ { "v8dbg_class_JSWeakMap__next__Object", 0x10 },
+ { "v8dbg_class_JSWeakMap__table__ObjectHashTable", 0xc },
+ { "v8dbg_class_Map__code_cache__Object", 0x18 },
+ { "v8dbg_class_Map__constructor__Object", 0x10 },
+ { "v8dbg_class_Map__inobject_properties__int", 0x5 },
+ { "v8dbg_class_Map__instance_attributes__int", 0x8 },
+ { "v8dbg_class_Map__instance_descriptors__FixedArray", 0x14 },
+ { "v8dbg_class_Map__instance_size__int", 0x4 },
+ { "v8dbg_class_Map__prototype_transitions__FixedArray", 0x1c },
+ { "v8dbg_class_ObjectTemplateInfo__constructor__Object", 0xc },
+ { "v8dbg_class_ObjectTemplateInfo__internal_field_count__Object",
+ 0x10 },
+ { "v8dbg_class_Oddball__to_number__Object", 0x8 },
+ { "v8dbg_class_Oddball__to_string__String", 0x4 },
+ { "v8dbg_class_PolymorphicCodeCache__cache__Object", 0x4 },
+ { "v8dbg_class_Script__column_offset__Smi", 0x10 },
+ { "v8dbg_class_Script__compilation_type__Smi", 0x24 },
+ { "v8dbg_class_Script__context_data__Object", 0x18 },
+ { "v8dbg_class_Script__data__Object", 0x14 },
+ { "v8dbg_class_Script__eval_from_instructions_offset__Smi", 0x34 },
+ { "v8dbg_class_Script__eval_from_shared__Object", 0x30 },
+ { "v8dbg_class_Script__id__Object", 0x2c },
+ { "v8dbg_class_Script__line_ends__Object", 0x28 },
+ { "v8dbg_class_Script__line_offset__Smi", 0xc },
+ { "v8dbg_class_Script__name__Object", 0x8 },
+ { "v8dbg_class_Script__source__Object", 0x4 },
+ { "v8dbg_class_Script__type__Smi", 0x20 },
+ { "v8dbg_class_Script__wrapper__Foreign", 0x1c },
+ { "v8dbg_class_SeqAsciiString__chars__char", 0xc },
+ { "v8dbg_class_SharedFunctionInfo__code__Code", 0x8 },
+ { "v8dbg_class_SharedFunctionInfo__compiler_hints__SMI", 0x50 },
+ { "v8dbg_class_SharedFunctionInfo__construct_stub__Code", 0x10 },
+ { "v8dbg_class_SharedFunctionInfo__debug_info__Object", 0x20 },
+ { "v8dbg_class_SharedFunctionInfo__end_position__SMI", 0x48 },
+ { "v8dbg_class_SharedFunctionInfo__expected_nof_properties__SMI",
+ 0x3c },
+ { "v8dbg_class_SharedFunctionInfo__formal_parameter_count__SMI", 0x38 },
+ { "v8dbg_class_SharedFunctionInfo__function_data__Object", 0x18 },
+ { "v8dbg_class_SharedFunctionInfo__function_token_position__SMI",
+ 0x4c },
+ { "v8dbg_class_SharedFunctionInfo__inferred_name__String", 0x24 },
+ { "v8dbg_class_SharedFunctionInfo__initial_map__Object", 0x28 },
+ { "v8dbg_class_SharedFunctionInfo__instance_class_name__Object", 0x14 },
+ { "v8dbg_class_SharedFunctionInfo__length__SMI", 0x34 },
+ { "v8dbg_class_SharedFunctionInfo__name__Object", 0x4 },
+ { "v8dbg_class_SharedFunctionInfo__num_literals__SMI", 0x40 },
+ { "v8dbg_class_SharedFunctionInfo__opt_count__SMI", 0x58 },
+ { "v8dbg_class_SharedFunctionInfo__script__Object", 0x1c },
+ { "v8dbg_class_SharedFunctionInfo__"
+ "start_position_and_type__SMI", 0x44 },
+ { "v8dbg_class_SharedFunctionInfo__"
+ "this_property_assignments__Object", 0x2c },
+ { "v8dbg_class_SharedFunctionInfo__"
+ "this_property_assignments_count__SMI", 0x54 },
+ { "v8dbg_class_SignatureInfo__args__Object", 0x8 },
+ { "v8dbg_class_SignatureInfo__receiver__Object", 0x4 },
+ { "v8dbg_class_SlicedString__offset__SMI", 0x10 },
+ { "v8dbg_class_String__length__SMI", 0x4 },
+ { "v8dbg_class_TemplateInfo__property_list__Object", 0x8 },
+ { "v8dbg_class_TemplateInfo__tag__Object", 0x4 },
+ { "v8dbg_class_TypeSwitchInfo__types__Object", 0x4 },
+
+ { "v8dbg_parent_AccessCheckInfo__Struct", 0x0 },
+ { "v8dbg_parent_AccessorInfo__Struct", 0x0 },
+ { "v8dbg_parent_BreakPointInfo__Struct", 0x0 },
+ { "v8dbg_parent_ByteArray__FixedArrayBase", 0x0 },
+ { "v8dbg_parent_CallHandlerInfo__Struct", 0x0 },
+ { "v8dbg_parent_Code__HeapObject", 0x0 },
+ { "v8dbg_parent_CodeCache__Struct", 0x0 },
+ { "v8dbg_parent_ConsString__String", 0x0 },
+ { "v8dbg_parent_DebugInfo__Struct", 0x0 },
+ { "v8dbg_parent_DeoptimizationInputData__FixedArray", 0x0 },
+ { "v8dbg_parent_DeoptimizationOutputData__FixedArray", 0x0 },
+ { "v8dbg_parent_DescriptorArray__FixedArray", 0x0 },
+ { "v8dbg_parent_ExternalArray__FixedArrayBase", 0x0 },
+ { "v8dbg_parent_ExternalAsciiString__ExternalString", 0x0 },
+ { "v8dbg_parent_ExternalByteArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalDoubleArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalFloatArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalIntArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalPixelArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalShortArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalString__String", 0x0 },
+ { "v8dbg_parent_ExternalTwoByteString__ExternalString", 0x0 },
+ { "v8dbg_parent_ExternalUnsignedByteArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalUnsignedIntArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_ExternalUnsignedShortArray__ExternalArray", 0x0 },
+ { "v8dbg_parent_Failure__MaybeObject", 0x0 },
+ { "v8dbg_parent_FixedArray__FixedArrayBase", 0x0 },
+ { "v8dbg_parent_FixedArrayBase__HeapObject", 0x0 },
+ { "v8dbg_parent_FixedDoubleArray__FixedArrayBase", 0x0 },
+ { "v8dbg_parent_Foreign__HeapObject", 0x0 },
+ { "v8dbg_parent_FunctionTemplateInfo__TemplateInfo", 0x0 },
+ { "v8dbg_parent_GlobalObject__JSObject", 0x0 },
+ { "v8dbg_parent_HashTable__FixedArray", 0x0 },
+ { "v8dbg_parent_HeapNumber__HeapObject", 0x0 },
+ { "v8dbg_parent_HeapObject__Object", 0x0 },
+ { "v8dbg_parent_InterceptorInfo__Struct", 0x0 },
+ { "v8dbg_parent_JSArray__JSObject", 0x0 },
+ { "v8dbg_parent_JSBuiltinsObject__GlobalObject", 0x0 },
+ { "v8dbg_parent_JSFunction__JSObject", 0x0 },
+ { "v8dbg_parent_JSFunctionProxy__JSProxy", 0x0 },
+ { "v8dbg_parent_JSFunctionResultCache__FixedArray", 0x0 },
+ { "v8dbg_parent_JSGlobalObject__GlobalObject", 0x0 },
+ { "v8dbg_parent_JSGlobalPropertyCell__HeapObject", 0x0 },
+ { "v8dbg_parent_JSMessageObject__JSObject", 0x0 },
+ { "v8dbg_parent_JSObject__JSReceiver", 0x0 },
+ { "v8dbg_parent_JSProxy__JSReceiver", 0x0 },
+ { "v8dbg_parent_JSReceiver__HeapObject", 0x0 },
+ { "v8dbg_parent_JSRegExp__JSObject", 0x0 },
+ { "v8dbg_parent_JSRegExpResult__JSArray", 0x0 },
+ { "v8dbg_parent_JSValue__JSObject", 0x0 },
+ { "v8dbg_parent_JSWeakMap__JSObject", 0x0 },
+ { "v8dbg_parent_Map__HeapObject", 0x0 },
+ { "v8dbg_parent_NormalizedMapCache__FixedArray", 0x0 },
+ { "v8dbg_parent_ObjectTemplateInfo__TemplateInfo", 0x0 },
+ { "v8dbg_parent_Oddball__HeapObject", 0x0 },
+ { "v8dbg_parent_PolymorphicCodeCache__Struct", 0x0 },
+ { "v8dbg_parent_Script__Struct", 0x0 },
+ { "v8dbg_parent_SeqAsciiString__SeqString", 0x0 },
+ { "v8dbg_parent_SeqString__String", 0x0 },
+ { "v8dbg_parent_SeqTwoByteString__SeqString", 0x0 },
+ { "v8dbg_parent_SharedFunctionInfo__HeapObject", 0x0 },
+ { "v8dbg_parent_SignatureInfo__Struct", 0x0 },
+ { "v8dbg_parent_SlicedString__String", 0x0 },
+ { "v8dbg_parent_Smi__Object", 0x0 },
+ { "v8dbg_parent_String__HeapObject", 0x0 },
+ { "v8dbg_parent_Struct__HeapObject", 0x0 },
+ { "v8dbg_parent_TemplateInfo__Struct", 0x0 },
+ { "v8dbg_parent_TypeSwitchInfo__Struct", 0x0 },
+
+ { "v8dbg_frametype_ArgumentsAdaptorFrame", 0x8 },
+ { "v8dbg_frametype_ConstructFrame", 0x7 },
+ { "v8dbg_frametype_EntryConstructFrame", 0x2 },
+ { "v8dbg_frametype_EntryFrame", 0x1 },
+ { "v8dbg_frametype_ExitFrame", 0x3 },
+ { "v8dbg_frametype_InternalFrame", 0x6 },
+ { "v8dbg_frametype_JavaScriptFrame", 0x4 },
+ { "v8dbg_frametype_OptimizedFrame", 0x5 },
+
+ { "v8dbg_off_fp_args", 0x8 },
+ { "v8dbg_off_fp_context", -0x4 },
+ { "v8dbg_off_fp_function", -0x8 },
+ { "v8dbg_off_fp_marker", -0x8 },
+
+ { "v8dbg_prop_idx_content", 0x1 },
+ { "v8dbg_prop_idx_first", 0x3 },
+ { "v8dbg_prop_type_field", 0x1 },
+ { "v8dbg_prop_type_first_phantom", 0x6 },
+ { "v8dbg_prop_type_mask", 0xf },
+
+ { "v8dbg_AsciiStringTag", 0x4 },
+ { "v8dbg_PointerSizeLog2", 0x2 },
+ { "v8dbg_SeqStringTag", 0x0 },
+ { "v8dbg_SmiTag", 0x0 },
+ { "v8dbg_SmiTagMask", 0x1 },
+ { "v8dbg_SmiValueShift", 0x1 },
+ { "v8dbg_StringEncodingMask", 0x4 },
+ { "v8dbg_StringRepresentationMask", 0x3 },
+ { "v8dbg_StringTag", 0x0 },
+ { "v8dbg_TwoByteStringTag", 0x0 },
+ { "v8dbg_ConsStringTag", 0x1 },
+ { "v8dbg_ExternalStringTag", 0x2 },
+ { "v8dbg_FailureTag", 0x3 },
+ { "v8dbg_FailureTagMask", 0x3 },
+ { "v8dbg_FirstNonstringType", 0x80 },
+ { "v8dbg_HeapObjectTag", 0x1 },
+ { "v8dbg_HeapObjectTagMask", 0x3 },
+ { "v8dbg_IsNotStringMask", 0x80 },
+ { "v8dbg_NotStringTag", 0x80 },
+
+ { NULL },
+};
+
+v8_cfg_t v8_cfg_04 = { "node-0.4", "node v0.4", v8_symbols_node_04,
+ v8cfg_canned_iter, v8cfg_canned_readsym };
+
+v8_cfg_t v8_cfg_06 = { "node-0.6", "node v0.6", v8_symbols_node_06,
+ v8cfg_canned_iter, v8cfg_canned_readsym };
+
+v8_cfg_t *v8_cfgs[] = {
+ &v8_cfg_04,
+ &v8_cfg_06,
+ NULL
+};
+
+v8_cfg_t v8_cfg_target = { NULL, NULL, NULL, v8cfg_target_iter,
+ v8cfg_target_readsym };
diff --git a/usr/src/cmd/mdb/common/modules/v8/v8cfg.h b/usr/src/cmd/mdb/common/modules/v8/v8cfg.h
new file mode 100644
index 0000000000..9e722b0e2b
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/v8/v8cfg.h
@@ -0,0 +1,55 @@
+/*
+ * 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, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * v8cfg.h: canned configurations for previous V8 versions
+ */
+
+#ifndef V8CFG_H
+#define V8CFG_H
+
+#include <sys/types.h>
+#include <sys/mdb_modapi.h>
+
+typedef struct {
+ const char *v8cs_name; /* symbol name */
+ intptr_t v8cs_value; /* symbol value */
+} v8_cfg_symbol_t;
+
+typedef struct v8_cfg {
+ const char *v8cfg_name; /* canned config name */
+ const char *v8cfg_label; /* description */
+ v8_cfg_symbol_t *v8cfg_symbols; /* actual symbol values */
+
+ int (*v8cfg_iter)(struct v8_cfg *, int (*)(mdb_symbol_t *, void *),
+ void *);
+ int (*v8cfg_readsym)(struct v8_cfg *, const char *, intptr_t *);
+} v8_cfg_t;
+
+extern v8_cfg_t v8_cfg_04;
+extern v8_cfg_t v8_cfg_06;
+extern v8_cfg_t v8_cfg_target;
+extern v8_cfg_t *v8_cfgs[];
+
+#endif /* V8CFG_H */
diff --git a/usr/src/cmd/mdb/common/modules/v8/v8dbg.h b/usr/src/cmd/mdb/common/modules/v8/v8dbg.h
new file mode 100644
index 0000000000..c675dc56cb
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/v8/v8dbg.h
@@ -0,0 +1,81 @@
+/*
+ * 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) 2013, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * v8dbg.h: macros for use by V8 heap inspection tools. The consumer must
+ * define values for various tags and shifts. The MDB module gets these
+ * constants from information encoded in the binary itself.
+ */
+
+#ifndef _V8DBG_H
+#define _V8DBG_H
+
+/*
+ * Recall that while V8 heap objects are always 4-byte aligned, heap object
+ * pointers always have the last bit set. So when looking for a field nominally
+ * at offset X, one must be sure to clear the tag bit first.
+ */
+#define V8_OFF_HEAP(x) ((x) - V8_HeapObjectTag)
+
+/*
+ * Determine whether a given pointer refers to a SMI, Failure, or HeapObject.
+ */
+#define V8_IS_SMI(ptr) (((ptr) & V8_SmiTagMask) == V8_SmiTag)
+#define V8_IS_FAILURE(ptr) (((ptr) & V8_FailureTagMask) == V8_FailureTag)
+#define V8_IS_HEAPOBJECT(ptr) \
+ (((ptr) & V8_HeapObjectTagMask) == V8_HeapObjectTag)
+
+/*
+ * Extract the value of a SMI "pointer". Recall that small integers are stored
+ * using the upper 31 bits.
+ */
+#define V8_SMI_VALUE(smi) ((smi) >> (V8_SmiValueShift + V8_SmiShiftSize))
+
+/*
+ * Determine the encoding and representation of a V8 string.
+ */
+#define V8_TYPE_STRING(type) (((type) & V8_IsNotStringMask) == V8_StringTag)
+
+#define V8_STRENC_ASCII(type) \
+ (((type) & V8_StringEncodingMask) == V8_AsciiStringTag)
+
+#define V8_STRREP_SEQ(type) \
+ (((type) & V8_StringRepresentationMask) == V8_SeqStringTag)
+#define V8_STRREP_CONS(type) \
+ (((type) & V8_StringRepresentationMask) == V8_ConsStringTag)
+#define V8_STRREP_EXT(type) \
+ (((type) & V8_StringRepresentationMask) == V8_ExternalStringTag)
+
+/*
+ * Several of the following constants and transformations are hardcoded in V8 as
+ * well, so there's no way to extract them programmatically from the binary.
+ */
+#define V8_DESC_KEYIDX(x) ((x) + V8_PROP_IDX_FIRST)
+#define V8_DESC_VALIDX(x) ((x) << 1)
+#define V8_DESC_DETIDX(x) (((x) << 1) + 1)
+
+#define V8_DESC_ISFIELD(x) \
+ ((V8_SMI_VALUE(x) & V8_PROP_TYPE_MASK) == V8_PROP_TYPE_FIELD)
+
+#endif /* _V8DBG_H */
diff --git a/usr/src/cmd/mdb/i86pc/modules/unix/unix.c b/usr/src/cmd/mdb/i86pc/modules/unix/unix.c
index d774cde91f..b63876f1f8 100644
--- a/usr/src/cmd/mdb/i86pc/modules/unix/unix.c
+++ b/usr/src/cmd/mdb/i86pc/modules/unix/unix.c
@@ -22,6 +22,10 @@
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
*/
+/*
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ */
+
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_ctf.h>
#include <sys/cpuvar.h>
@@ -36,6 +40,8 @@
#include <sys/mutex_impl.h>
#include "i86mmu.h"
#include <sys/apix.h>
+#include <sys/x86_archext.h>
+#include <sys/bitmap.h>
#define TT_HDLR_WIDTH 17
@@ -745,6 +751,137 @@ ptable_help(void)
"-m Interpret the PFN as an MFN (machine frame number)\n");
}
+/*
+ * NSEC_SHIFT is replicated here (it is not defined in a header file),
+ * but for amusement, the reader is directed to the comment that explains
+ * the rationale for this particular value on x86. Spoiler: the value is
+ * selected to accommodate 60 MHz Pentiums! (And a confession: if the voice
+ * in that comment sounds too familiar, it's because your author also wrote
+ * that code -- some fifteen years prior to this writing in 2011...)
+ */
+#define NSEC_SHIFT 5
+
+/*ARGSUSED*/
+static int
+scalehrtime_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint32_t nsec_scale;
+ hrtime_t tsc = addr, hrt;
+ unsigned int *tscp = (unsigned int *)&tsc;
+ uintptr_t scalehrtimef;
+ uint64_t scale;
+ GElf_Sym sym;
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ if (argc != 1)
+ return (DCMD_USAGE);
+
+ switch (argv[0].a_type) {
+ case MDB_TYPE_STRING:
+ tsc = mdb_strtoull(argv[0].a_un.a_str);
+ break;
+ case MDB_TYPE_IMMEDIATE:
+ tsc = argv[0].a_un.a_val;
+ break;
+ default:
+ return (DCMD_USAGE);
+ }
+ }
+
+ if (mdb_readsym(&scalehrtimef,
+ sizeof (scalehrtimef), "scalehrtimef") == -1) {
+ mdb_warn("couldn't read 'scalehrtimef'");
+ return (DCMD_ERR);
+ }
+
+ if (mdb_lookup_by_name("tsc_scalehrtime", &sym) == -1) {
+ mdb_warn("couldn't find 'tsc_scalehrtime'");
+ return (DCMD_ERR);
+ }
+
+ if (sym.st_value != scalehrtimef) {
+ mdb_warn("::scalehrtime requires that scalehrtimef "
+ "be set to tsc_scalehrtime\n");
+ return (DCMD_ERR);
+ }
+
+ if (mdb_readsym(&nsec_scale, sizeof (nsec_scale), "nsec_scale") == -1) {
+ mdb_warn("couldn't read 'nsec_scale'");
+ return (DCMD_ERR);
+ }
+
+ scale = (uint64_t)nsec_scale;
+
+ hrt = ((uint64_t)tscp[1] * scale) << NSEC_SHIFT;
+ hrt += ((uint64_t)tscp[0] * scale) >> (32 - NSEC_SHIFT);
+
+ mdb_printf("0x%llx\n", hrt);
+
+ return (DCMD_OK);
+}
+
+/*
+ * The x86 feature set is implemented as a bitmap array. That bitmap array is
+ * stored across a number of uchars based on the BT_SIZEOFMAP(NUM_X86_FEATURES)
+ * macro. We have the names for each of these features in unix's text segment
+ * so we do not have to duplicate them and instead just look them up.
+ */
+/*ARGSUSED*/
+static int
+x86_featureset_cmd(uintptr_t addr, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ uchar_t *fset;
+ GElf_Sym sym;
+ uintptr_t nptr;
+ char name[128];
+ int ii;
+
+ size_t sz = sizeof (uchar_t) * BT_SIZEOFMAP(NUM_X86_FEATURES);
+
+ if (argc != 0)
+ return (DCMD_USAGE);
+
+ if (mdb_lookup_by_name("x86_feature_names", &sym) == -1) {
+ mdb_warn("couldn't find x86_feature_names");
+ return (DCMD_ERR);
+ }
+
+ fset = mdb_zalloc(sz, UM_NOSLEEP);
+ if (fset == NULL) {
+ mdb_warn("failed to allocate memory for x86_featureset");
+ return (DCMD_ERR);
+ }
+
+ if (mdb_readvar(fset, "x86_featureset") != sz) {
+ mdb_warn("failed to read x86_featureset");
+ mdb_free(fset, sz);
+ return (DCMD_ERR);
+ }
+
+ for (ii = 0; ii < NUM_X86_FEATURES; ii++) {
+ if (!BT_TEST((ulong_t *)fset, ii))
+ continue;
+
+ if (mdb_vread(&nptr, sizeof (char *), sym.st_value +
+ sizeof (void *) * ii) != sizeof (char *)) {
+ mdb_warn("failed to read feature array %d", ii);
+ mdb_free(fset, sz);
+ return (DCMD_ERR);
+ }
+
+ if (mdb_readstr(name, sizeof (name), nptr) == -1) {
+ mdb_warn("failed to read feature %d", ii);
+ mdb_free(fset, sz);
+ return (DCMD_ERR);
+ }
+ mdb_printf("%s\n", name);
+ }
+
+ mdb_free(fset, sz);
+ return (DCMD_OK);
+}
+
static const mdb_dcmd_t dcmds[] = {
{ "gate_desc", ":", "dump a gate descriptor", gate_desc },
{ "idt", ":[-v]", "dump an IDT", idt },
@@ -765,6 +902,10 @@ static const mdb_dcmd_t dcmds[] = {
{ "mfntopfn", ":", "convert hypervisor machine page to physical page",
mfntopfn_dcmd },
{ "memseg_list", ":", "show memseg list", memseg_list },
+ { "scalehrtime", ":",
+ "scale an unscaled high-res time", scalehrtime_cmd },
+ { "x86_featureset", NULL, "dump the x86_featureset vector",
+ x86_featureset_cmd },
{ NULL }
};
diff --git a/usr/src/cmd/mdb/intel/amd64/libumem/Makefile b/usr/src/cmd/mdb/intel/amd64/libumem/Makefile
index a8352ff865..ae22217a1b 100644
--- a/usr/src/cmd/mdb/intel/amd64/libumem/Makefile
+++ b/usr/src/cmd/mdb/intel/amd64/libumem/Makefile
@@ -22,6 +22,7 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
MODULE = libumem.so
MDBTGT = proc
@@ -42,6 +43,7 @@ include ../../../../Makefile.cmd
include ../../../../Makefile.cmd.64
CPPFLAGS += -I$(SRC)/lib/libumem/common
+CPPFLAGS += -I$(SRC)/lib/libc/inc
CPPFLAGS += -I$(MODSRCS_DIR)
include ../../Makefile.amd64
diff --git a/usr/src/cmd/mdb/intel/amd64/v8/Makefile b/usr/src/cmd/mdb/intel/amd64/v8/Makefile
new file mode 100644
index 0000000000..cf74ba1d1c
--- /dev/null
+++ b/usr/src/cmd/mdb/intel/amd64/v8/Makefile
@@ -0,0 +1,45 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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) 2011, Joyent, Inc. All rights reserved.
+#
+
+MODULE = v8.so
+MDBTGT = proc
+
+MODSRCS_DIR = ../../../common/modules/v8
+
+MODSRCS = mdb_v8.c mdb_v8_cfg.c
+
+include ../../../../Makefile.cmd
+include ../../../../Makefile.cmd.64
+include ../../Makefile.amd64
+include ../../../Makefile.module
+
+dmod/$(MODULE) := LDLIBS += -lproc -lavl
+
+%.o: $(MODSRCS_DIR)/%.c
+ $(COMPILE.c) $<
+ $(CTFCONVERT_O)
+
+%.ln: $(MODSRCS_DIR)/%.c
+ $(LINT.c) -c $<
diff --git a/usr/src/cmd/mdb/intel/ia32/libpython2.6/Makefile b/usr/src/cmd/mdb/intel/ia32/libpython2.6/Makefile
index 53703a48cc..ab57dbd02a 100644
--- a/usr/src/cmd/mdb/intel/ia32/libpython2.6/Makefile
+++ b/usr/src/cmd/mdb/intel/ia32/libpython2.6/Makefile
@@ -34,6 +34,8 @@ include ../../../../Makefile.cmd
include ../../Makefile.ia32
include ../../../Makefile.module
+$(__GNUC)CPPFLAGS += -isystem $(ADJUNCT_PROTO)/usr/include
+
dmod/$(MODULE) := LDLIBS += -lproc
%.o: $(MODSRCS_DIR)/%.c
diff --git a/usr/src/cmd/mdb/intel/ia32/libumem/Makefile b/usr/src/cmd/mdb/intel/ia32/libumem/Makefile
index 2399f51d31..bde1be90ac 100644
--- a/usr/src/cmd/mdb/intel/ia32/libumem/Makefile
+++ b/usr/src/cmd/mdb/intel/ia32/libumem/Makefile
@@ -22,6 +22,7 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
MODULE = libumem.so
MDBTGT = proc
@@ -40,6 +41,7 @@ MODSRCS = \
include ../../../../Makefile.cmd
+CPPFLAGS += -I$(SRC)/lib/libc/inc
CPPFLAGS += -I$(SRC)/lib/libumem/common
CPPFLAGS += -I$(MODSRCS_DIR)
diff --git a/usr/src/cmd/mdb/intel/ia32/v8/Makefile b/usr/src/cmd/mdb/intel/ia32/v8/Makefile
new file mode 100644
index 0000000000..c84532289e
--- /dev/null
+++ b/usr/src/cmd/mdb/intel/ia32/v8/Makefile
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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) 2011, Joyent, Inc. All rights reserved.
+#
+
+MODULE = v8.so
+MDBTGT = proc
+
+MODSRCS_DIR = ../../../common/modules/v8
+
+MODSRCS = mdb_v8.c mdb_v8_cfg.c
+
+include ../../../../Makefile.cmd
+include ../../Makefile.ia32
+include ../../../Makefile.module
+
+dmod/$(MODULE) := LDLIBS += -lproc -lavl
+
+%.o: $(MODSRCS_DIR)/%.c
+ $(COMPILE.c) $<
+ $(CTFCONVERT_O)
+
+%.ln: $(MODSRCS_DIR)/%.c
+ $(LINT.c) -c $<
diff --git a/usr/src/cmd/mdb/intel/mdb/kvm_isadep.c b/usr/src/cmd/mdb/intel/mdb/kvm_isadep.c
index 84db05c117..db04cb7a31 100644
--- a/usr/src/cmd/mdb/intel/mdb/kvm_isadep.c
+++ b/usr/src/cmd/mdb/intel/mdb/kvm_isadep.c
@@ -22,8 +22,9 @@
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ */
/*
* Libkvm Kernel Target Intel component
@@ -57,6 +58,14 @@ kt_getareg(mdb_tgt_t *t, mdb_tgt_tid_t tid,
for (rdp = kt->k_rds; rdp->rd_name != NULL; rdp++) {
if (strcmp(rname, rdp->rd_name) == 0) {
*rp = kt->k_regs->kregs[rdp->rd_num];
+ if (rdp->rd_flags & MDB_TGT_R_32)
+ *rp &= 0xffffffffULL;
+ else if (rdp->rd_flags & MDB_TGT_R_16)
+ *rp &= 0xffffULL;
+ else if (rdp->rd_flags & MDB_TGT_R_8H)
+ *rp = (*rp & 0xff00ULL) >> 8;
+ else if (rdp->rd_flags & MDB_TGT_R_8L)
+ *rp &= 0xffULL;
return (0);
}
}
@@ -75,6 +84,15 @@ kt_putareg(mdb_tgt_t *t, mdb_tgt_tid_t tid, const char *rname, mdb_tgt_reg_t r)
for (rdp = kt->k_rds; rdp->rd_name != NULL; rdp++) {
if (strcmp(rname, rdp->rd_name) == 0) {
+ if (rdp->rd_flags & MDB_TGT_R_32)
+ r &= 0xffffffffULL;
+ else if (rdp->rd_flags & MDB_TGT_R_16)
+ r &= 0xffffULL;
+ else if (rdp->rd_flags & MDB_TGT_R_8H)
+ r = (r & 0xffULL) << 8;
+ else if (rdp->rd_flags & MDB_TGT_R_8L)
+ r &= 0xffULL;
+
kt->k_regs->kregs[rdp->rd_num] = (kreg_t)r;
return (0);
}
diff --git a/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c b/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c
index 65adaf37be..60aa1ec991 100644
--- a/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c
+++ b/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c
@@ -23,6 +23,9 @@
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ */
#include <sys/types.h>
#include <sys/reg.h>
@@ -50,20 +53,69 @@ const mdb_tgt_regdesc_t mdb_amd64_kregs[] = {
{ "savfp", KREG_SAVFP, MDB_TGT_R_EXPORT },
{ "savpc", KREG_SAVPC, MDB_TGT_R_EXPORT },
{ "rdi", KREG_RDI, MDB_TGT_R_EXPORT },
+ { "edi", KREG_RDI, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "di", KREG_RDI, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "dil", KREG_RDI, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rsi", KREG_RSI, MDB_TGT_R_EXPORT },
+ { "esi", KREG_RSI, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "si", KREG_RSI, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "sil", KREG_RSI, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rdx", KREG_RDX, MDB_TGT_R_EXPORT },
+ { "edx", KREG_RDX, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "dx", KREG_RDX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "dh", KREG_RDX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "dl", KREG_RDX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rcx", KREG_RCX, MDB_TGT_R_EXPORT },
+ { "ecx", KREG_RCX, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "cx", KREG_RCX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "ch", KREG_RCX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "cl", KREG_RCX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r8", KREG_R8, MDB_TGT_R_EXPORT },
+ { "r8d", KREG_R8, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r8w", KREG_R8, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r8l", KREG_R8, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r9", KREG_R9, MDB_TGT_R_EXPORT },
+ { "r9d", KREG_R8, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r9w", KREG_R8, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r9l", KREG_R8, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rax", KREG_RAX, MDB_TGT_R_EXPORT },
+ { "eax", KREG_RAX, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "ax", KREG_RAX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "ah", KREG_RAX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "al", KREG_RAX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rbx", KREG_RBX, MDB_TGT_R_EXPORT },
+ { "ebx", KREG_RBX, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "bx", KREG_RBX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "bh", KREG_RBX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "bl", KREG_RBX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rbp", KREG_RBP, MDB_TGT_R_EXPORT },
+ { "ebp", KREG_RBP, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "bp", KREG_RBP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "bpl", KREG_RBP, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r10", KREG_R10, MDB_TGT_R_EXPORT },
+ { "r10d", KREG_R10, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r10w", KREG_R10, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r10l", KREG_R10, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r11", KREG_R11, MDB_TGT_R_EXPORT },
+ { "r11d", KREG_R11, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r11w", KREG_R11, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r11l", KREG_R11, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r12", KREG_R12, MDB_TGT_R_EXPORT },
+ { "r12d", KREG_R12, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r12w", KREG_R12, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r12l", KREG_R12, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r13", KREG_R13, MDB_TGT_R_EXPORT },
+ { "r13d", KREG_R13, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r13w", KREG_R13, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r13l", KREG_R13, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r14", KREG_R14, MDB_TGT_R_EXPORT },
+ { "r14d", KREG_R14, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r14w", KREG_R14, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r14l", KREG_R14, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r15", KREG_R15, MDB_TGT_R_EXPORT },
+ { "r15d", KREG_R15, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r15w", KREG_R15, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r15l", KREG_R15, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "ds", KREG_DS, MDB_TGT_R_EXPORT },
{ "es", KREG_ES, MDB_TGT_R_EXPORT },
{ "fs", KREG_FS, MDB_TGT_R_EXPORT },
@@ -73,7 +125,11 @@ const mdb_tgt_regdesc_t mdb_amd64_kregs[] = {
{ "rip", KREG_RIP, MDB_TGT_R_EXPORT },
{ "cs", KREG_CS, MDB_TGT_R_EXPORT },
{ "rflags", KREG_RFLAGS, MDB_TGT_R_EXPORT },
+ { "eflags", KREG_RFLAGS, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
{ "rsp", KREG_RSP, MDB_TGT_R_EXPORT },
+ { "esp", KREG_RSP, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "sp", KREG_RSP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "spl", KREG_RSP, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "ss", KREG_SS, MDB_TGT_R_EXPORT },
{ NULL, 0, 0 }
};
diff --git a/usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c b/usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c
index addb37e19f..43db94741b 100644
--- a/usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c
+++ b/usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c
@@ -22,8 +22,9 @@
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ */
#include <sys/types.h>
#include <sys/reg.h>
@@ -48,13 +49,29 @@ const mdb_tgt_regdesc_t mdb_ia32_kregs[] = {
{ "savfp", KREG_SAVFP, MDB_TGT_R_EXPORT },
{ "savpc", KREG_SAVPC, MDB_TGT_R_EXPORT },
{ "eax", KREG_EAX, MDB_TGT_R_EXPORT },
+ { "ax", KREG_EAX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "ah", KREG_EAX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "al", KREG_EAX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "ebx", KREG_EBX, MDB_TGT_R_EXPORT },
+ { "bx", KREG_EBX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "bh", KREG_EBX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "bl", KREG_EBX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "ecx", KREG_ECX, MDB_TGT_R_EXPORT },
+ { "cx", KREG_ECX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "ch", KREG_ECX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "cl", KREG_ECX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "edx", KREG_EDX, MDB_TGT_R_EXPORT },
+ { "dx", KREG_EDX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "dh", KREG_EDX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "dl", KREG_EDX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "esi", KREG_ESI, MDB_TGT_R_EXPORT },
+ { "si", KREG_ESI, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
{ "edi", KREG_EDI, MDB_TGT_R_EXPORT },
+ { "di", EDI, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
{ "ebp", KREG_EBP, MDB_TGT_R_EXPORT },
+ { "bp", KREG_EBP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
{ "esp", KREG_ESP, MDB_TGT_R_EXPORT },
+ { "sp", KREG_ESP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
{ "cs", KREG_CS, MDB_TGT_R_EXPORT },
{ "ds", KREG_DS, MDB_TGT_R_EXPORT },
{ "ss", KREG_SS, MDB_TGT_R_EXPORT },
@@ -64,6 +81,7 @@ const mdb_tgt_regdesc_t mdb_ia32_kregs[] = {
{ "eflags", KREG_EFLAGS, MDB_TGT_R_EXPORT },
{ "eip", KREG_EIP, MDB_TGT_R_EXPORT },
{ "uesp", KREG_UESP, MDB_TGT_R_EXPORT | MDB_TGT_R_PRIV },
+ { "usp", KREG_UESP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
{ "trapno", KREG_TRAPNO, MDB_TGT_R_EXPORT | MDB_TGT_R_PRIV },
{ "err", KREG_ERR, MDB_TGT_R_EXPORT | MDB_TGT_R_PRIV },
{ NULL, 0, 0 }
diff --git a/usr/src/cmd/mdb/intel/mdb/proc_amd64dep.c b/usr/src/cmd/mdb/intel/mdb/proc_amd64dep.c
index 29dd1682d1..75dd430c7d 100644
--- a/usr/src/cmd/mdb/intel/mdb/proc_amd64dep.c
+++ b/usr/src/cmd/mdb/intel/mdb/proc_amd64dep.c
@@ -23,8 +23,9 @@
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ */
/*
* User Process Target Intel 32-bit component
@@ -46,26 +47,79 @@
const mdb_tgt_regdesc_t pt_regdesc[] = {
{ "r15", REG_R15, MDB_TGT_R_EXPORT },
+ { "r15d", REG_R15, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r15w", REG_R15, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r15l", REG_R15, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r14", REG_R14, MDB_TGT_R_EXPORT },
+ { "r14d", REG_R14, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r14w", REG_R14, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r14l", REG_R14, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r13", REG_R13, MDB_TGT_R_EXPORT },
+ { "r13d", REG_R13, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r13w", REG_R13, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r13l", REG_R13, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r12", REG_R12, MDB_TGT_R_EXPORT },
+ { "r12d", REG_R12, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r12w", REG_R12, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r12l", REG_R12, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r11", REG_R11, MDB_TGT_R_EXPORT },
+ { "r11d", REG_R11, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r11w", REG_R11, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r11l", REG_R11, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r10", REG_R10, MDB_TGT_R_EXPORT },
+ { "r10d", REG_R10, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r10w", REG_R10, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r10l", REG_R10, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r9", REG_R9, MDB_TGT_R_EXPORT },
+ { "r9d", REG_R8, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r9w", REG_R8, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r9l", REG_R8, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "r8", REG_R8, MDB_TGT_R_EXPORT },
+ { "r8d", REG_R8, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "r8w", REG_R8, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "r8l", REG_R8, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rdi", REG_RDI, MDB_TGT_R_EXPORT },
+ { "edi", REG_RDI, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "di", REG_RDI, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "dil", REG_RDI, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rsi", REG_RSI, MDB_TGT_R_EXPORT },
+ { "esi", REG_RSI, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "si", REG_RSI, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "sil", REG_RSI, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rbp", REG_RBP, MDB_TGT_R_EXPORT },
+ { "ebp", REG_RBP, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "bp", REG_RBP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "bpl", REG_RBP, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rbx", REG_RBX, MDB_TGT_R_EXPORT },
+ { "ebx", REG_RBX, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "bx", REG_RBX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "bh", REG_RBX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "bl", REG_RBX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rdx", REG_RDX, MDB_TGT_R_EXPORT },
+ { "edx", REG_RDX, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "dx", REG_RDX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "dh", REG_RDX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "dl", REG_RDX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rcx", REG_RCX, MDB_TGT_R_EXPORT },
+ { "ecx", REG_RCX, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "cx", REG_RCX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "ch", REG_RCX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "cl", REG_RCX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "rax", REG_RAX, MDB_TGT_R_EXPORT },
+ { "eax", REG_RAX, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "ax", REG_RAX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "ah", REG_RAX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "al", REG_RAX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "trapno", REG_TRAPNO, MDB_TGT_R_EXPORT },
{ "err", REG_ERR, MDB_TGT_R_EXPORT },
{ "rip", REG_RIP, MDB_TGT_R_EXPORT },
{ "cs", REG_CS, MDB_TGT_R_EXPORT },
{ "rflags", REG_RFL, MDB_TGT_R_EXPORT },
+ { "eflags", REG_RFL, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
{ "rsp", REG_RSP, MDB_TGT_R_EXPORT },
+ { "esp", REG_RSP, MDB_TGT_R_EXPORT | MDB_TGT_R_32 },
+ { "sp", REG_RSP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "spl", REG_RSP, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "ss", REG_SS, MDB_TGT_R_EXPORT },
{ "fs", REG_FS, MDB_TGT_R_EXPORT },
{ "gs", REG_GS, MDB_TGT_R_EXPORT },
diff --git a/usr/src/cmd/mdb/intel/mdb/proc_ia32dep.c b/usr/src/cmd/mdb/intel/mdb/proc_ia32dep.c
index bca8445d86..b532dc9149 100644
--- a/usr/src/cmd/mdb/intel/mdb/proc_ia32dep.c
+++ b/usr/src/cmd/mdb/intel/mdb/proc_ia32dep.c
@@ -23,8 +23,9 @@
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ */
/*
* User Process Target Intel 32-bit component
@@ -50,19 +51,36 @@ const mdb_tgt_regdesc_t pt_regdesc[] = {
{ "es", ES, MDB_TGT_R_EXPORT },
{ "ds", DS, MDB_TGT_R_EXPORT },
{ "edi", EDI, MDB_TGT_R_EXPORT },
+ { "di", EDI, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
{ "esi", ESI, MDB_TGT_R_EXPORT },
+ { "si", ESI, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
{ "ebp", EBP, MDB_TGT_R_EXPORT },
+ { "bp", EBP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
{ "kesp", ESP, MDB_TGT_R_EXPORT },
+ { "ksp", ESP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
{ "ebx", EBX, MDB_TGT_R_EXPORT },
+ { "bx", EBX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "bh", EBX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "bl", EBX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "edx", EDX, MDB_TGT_R_EXPORT },
+ { "dx", EDX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "dh", EDX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "dl", EDX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "ecx", ECX, MDB_TGT_R_EXPORT },
+ { "cx", ECX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "ch", ECX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "cl", ECX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "eax", EAX, MDB_TGT_R_EXPORT },
+ { "ax", EAX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
+ { "ah", EAX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H },
+ { "al", EAX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L },
{ "trapno", TRAPNO, MDB_TGT_R_EXPORT },
{ "err", ERR, MDB_TGT_R_EXPORT },
{ "eip", EIP, MDB_TGT_R_EXPORT },
{ "cs", CS, MDB_TGT_R_EXPORT },
{ "eflags", EFL, MDB_TGT_R_EXPORT },
{ "esp", UESP, MDB_TGT_R_EXPORT },
+ { "sp", UESP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 },
{ "ss", SS, MDB_TGT_R_EXPORT },
{ NULL, 0, 0 }
};
diff --git a/usr/src/cmd/mdb/sparc/v7/libumem/Makefile b/usr/src/cmd/mdb/sparc/v7/libumem/Makefile
index 4553b15eba..0488e6739a 100644
--- a/usr/src/cmd/mdb/sparc/v7/libumem/Makefile
+++ b/usr/src/cmd/mdb/sparc/v7/libumem/Makefile
@@ -22,6 +22,7 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
MODULE = libumem.so
MDBTGT = proc
@@ -41,6 +42,7 @@ MODSRCS = \
include ../../../../Makefile.cmd
CPPFLAGS += -I$(SRC)/lib/libumem/common
+CPPFLAGS += -I$(SRC)/lib/libc/inc
CPPFLAGS += -I$(MODSRCS_DIR)
include ../../Makefile.sparcv7
diff --git a/usr/src/cmd/mdb/sparc/v9/libumem/Makefile b/usr/src/cmd/mdb/sparc/v9/libumem/Makefile
index 2cbeb25f5e..87ce977423 100644
--- a/usr/src/cmd/mdb/sparc/v9/libumem/Makefile
+++ b/usr/src/cmd/mdb/sparc/v9/libumem/Makefile
@@ -22,6 +22,7 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
MODULE = libumem.so
MDBTGT = proc
@@ -54,6 +55,7 @@ KMOD_SOURCES_DIFFERENT=$(POUND_SIGN)
include ../../../../Makefile.cmd
CPPFLAGS += -I$(SRC)/lib/libumem/common
+CPPFLAGS += -I$(SRC)/lib/libc/inc
CPPFLAGS += -I$(MODSRCS_DIR)
include ../../../../Makefile.cmd.64
diff --git a/usr/src/cmd/mdb/test/README b/usr/src/cmd/mdb/test/README
new file mode 100644
index 0000000000..28127e5916
--- /dev/null
+++ b/usr/src/cmd/mdb/test/README
@@ -0,0 +1,18 @@
+MDB Test suite
+
+This provides a primordial version of a test suite for mdb. To run the tests,
+run mtest. Tests exist in various subdirectories. The name of the test is
+important.
+
+A test must start with either:
+
+ o tst - Indicating that it should exit zero
+ o err - Indicating that it should exit non-zero
+
+A test must end with either:
+
+ o mdb - Indicating that the file should be passed as standard input to mdb
+ o ksh - Indicating that it should be run with ksh
+
+A test may have an optional .out file which if present indicates that the test
+should pass if and only if its standard ouput matches its standar error.
diff --git a/usr/src/cmd/mdb/test/exit-e/err.cmdbadopt.ksh b/usr/src/cmd/mdb/test/exit-e/err.cmdbadopt.ksh
new file mode 100644
index 0000000000..de50faeb01
--- /dev/null
+++ b/usr/src/cmd/mdb/test/exit-e/err.cmdbadopt.ksh
@@ -0,0 +1,2 @@
+$MDB -e '::typegraph'
+exit $?
diff --git a/usr/src/cmd/mdb/test/exit-e/err.enocmd.ksh b/usr/src/cmd/mdb/test/exit-e/err.enocmd.ksh
new file mode 100644
index 0000000000..038f121b26
--- /dev/null
+++ b/usr/src/cmd/mdb/test/exit-e/err.enocmd.ksh
@@ -0,0 +1,2 @@
+$MDB -e '::commandthatdoesnotexist'
+exit $?
diff --git a/usr/src/cmd/mdb/test/exit-e/tst.output.ksh b/usr/src/cmd/mdb/test/exit-e/tst.output.ksh
new file mode 100644
index 0000000000..13b5db072d
--- /dev/null
+++ b/usr/src/cmd/mdb/test/exit-e/tst.output.ksh
@@ -0,0 +1 @@
+$MDB -e '0t2 << 0t10=K'
diff --git a/usr/src/cmd/mdb/test/exit-e/tst.output.ksh.out b/usr/src/cmd/mdb/test/exit-e/tst.output.ksh.out
new file mode 100644
index 0000000000..7a38ab6e65
--- /dev/null
+++ b/usr/src/cmd/mdb/test/exit-e/tst.output.ksh.out
@@ -0,0 +1 @@
+ 800
diff --git a/usr/src/cmd/mdb/test/exit-e/tst.simple.ksh b/usr/src/cmd/mdb/test/exit-e/tst.simple.ksh
new file mode 100644
index 0000000000..4c8a3c58ad
--- /dev/null
+++ b/usr/src/cmd/mdb/test/exit-e/tst.simple.ksh
@@ -0,0 +1 @@
+$MDB -e '::dcmds'
diff --git a/usr/src/cmd/mdb/test/mtest.sh b/usr/src/cmd/mdb/test/mtest.sh
new file mode 100755
index 0000000000..f21d0faa21
--- /dev/null
+++ b/usr/src/cmd/mdb/test/mtest.sh
@@ -0,0 +1,235 @@
+#!/bin/bash
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2012 (c), Joyent, Inc.
+#
+
+#
+# mdb test driver
+#
+unalias -a
+shopt -s xpg_echo
+#set -o xtrace
+
+mt_arg0=$(basename $0)
+mt_ksh="/usr/bin/ksh"
+mt_mdb="/usr/bin/mdb"
+mt_outdir=
+mt_keep=
+mt_all=
+mt_tests=
+mt_tnum=0
+mt_tfail=0
+mt_tsuc=0
+
+function usage
+{
+ local msg="$*"
+ [[ -z "$msg" ]] || echo "$msg" 2>&1
+ cat <<USAGE >&2
+Usage: $mt_arg0 [ -o dir ] [ -k ] [ -m executable ] [ -a | test ... ]
+
+ -o dir Sets 'dir' as the output directory
+ -a Runs all tests, ignores tests passed in
+ -k Keep output from all tests, not just failures
+ -m mdb binary to test
+USAGE
+ exit 2
+}
+
+function fatal
+{
+ local msg="$*"
+ [[ -z "$msg" ]] && msg="failed"
+ echo "$mt_arg0: $msg" >&2
+ exit 1
+}
+
+function setup_outdir
+{
+ mt_outdir="$mt_outdir/$mt_arg0.$$"
+ mkdir -p $mt_outdir || fatal "failed to make output dir $mt_outdir"
+}
+
+function run_single
+{
+ local name=$1
+ local expect base ext exe command odir res reason input
+
+ [[ -z "$name" ]] && fail "missing test to run"
+ base=${name##*/}
+ ext=${base##*.}
+ expect=${base%%.*}
+ odir="$mt_outdir/current"
+ [[ -z "$ext" ]] && fatal "found test without ext: $name"
+ [[ -z "$expect" ]] && fatal "found test without prefix: $name"
+
+ case "$ext" in
+ "ksh")
+ command="$mt_ksh $name"
+ ;;
+ "mdb")
+ command="$mt_mdb"
+ input="$name"
+ ;;
+ "out")
+ #
+ # This is the file format for checking output against.
+ #
+ return 0
+ ;;
+ *)
+ echo "skipping test $name (unknown extensino)"
+ return 0
+ ;;
+ esac
+
+ echo "Executing test $name ... \c"
+ mkdir -p "$odir" >/dev/null || fatal "can't make output directory"
+ if [[ -z "$input" ]]; then
+ MDB=$mt_mdb $command > "$odir/stdout" 2>"$odir/stderr"
+ res=$?
+ else
+ MDB=$mt_mdb $command < $input > "$odir/stdout" 2>"$odir/stderr"
+ res=$?
+ fi
+
+ if [[ -f "$name.out" ]] && ! diff "$name.out" "$odir/stdout" >/dev/null; then
+ cp $name.out $odir/$base.out
+ reason="stdout mismatch"
+ elif [[ "$expect" == "tst" && $res -ne 0 ]]; then
+ reason="test exited $res, not zero"
+ elif [[ "$expect" == "err" && $res -eq 0 ]]; then
+ reason="test exited $res, not non-zero"
+ fi
+
+ if [[ -n "$reason" ]]; then
+ echo "$reason"
+ ((mt_tfail++))
+ mv "$odir" "$mt_outdir/failure.$mt_tfail" || fatal \
+ "failed to move test output directory"
+ cp "$name" "$mt_outdir/failure.$mt_tfail/test" || fatal \
+ "failed to copy test into output directory"
+ else
+ echo "passed"
+ ((mt_tsuc++))
+ mv "$odir" "$mt_outdir/success.$mt_tsuc" || fatal \
+ "failed to move test directory"
+ fi
+
+ ((mt_tnum++))
+}
+
+function run_all
+{
+ local tests t
+
+ tests=$(find . -type f -name '[tst,err]*.*.[ksh,mdb]*')
+ for t in $tests; do
+ run_single $t
+ done
+}
+
+function welcome
+{
+ cat <<WELCOME
+Starting tests...
+mtest target: $mt_mdb
+output directory: $mt_outdir
+WELCOME
+}
+
+function cleanup
+{
+ [[ -n "$mt_keep" ]] && return
+ rm -rf "$mt_outdir"/success.* || fatal \
+ "failed to remove successful test cases"
+ if [[ $mt_tfail -eq 0 ]]; then
+ rmdir "$mt_outdir" || fatal \
+ "failed to remove test output directory"
+ fi
+}
+
+function goodbye
+{
+ cat <<EOF
+
+-------------
+Results
+-------------
+
+Tests passed: $mt_tsuc
+Tests failed: $mt_tfail
+Tests ran: $mt_tnum
+
+EOF
+ if [[ $mt_tfail -eq 0 ]]; then
+ echo "Congrats, mdb isn't completely broken, the tests pass".
+ else
+ echo "Some tests failed, you have some work to do."
+ fi
+}
+
+while getopts ":ahko:m:" c $@; do
+ case "$c" in
+ a)
+ mt_all="y"
+ ;;
+ k)
+ mt_keep="y"
+ ;;
+ m)
+ mt_mdb="$OPTARG"
+ ;;
+ o)
+ mt_outdir="$OPTARG"
+ ;;
+ h)
+ usage
+ ;;
+ :)
+ usage "option requires an argument -- $OPTARG"
+ ;;
+ *)
+ usage "invalid option -- $OPTARG"
+ ;;
+ esac
+done
+
+shift $((OPTIND-1))
+
+[[ -z "$mt_all" && $# == 0 ]] && usage "no tests to run"
+
+[[ -x "$mt_mdb" ]] || fatal "unable to execute mdb binary: $mt_mdb"
+
+[[ -z "$mt_outdir" ]] && mt_outdir=/var/tmp
+
+setup_outdir
+welcome
+
+if [[ ! -z "$mt_all" ]]; then
+ run_all
+else
+ for t in $@; do
+ [[ -f $t ]] || fatal "cannot find test $t"
+ run_single $t
+ done
+fi
+
+goodbye
+cleanup
+
+#
+# Exit 1 if we have tests that return non-zero
+#
+[[ $mt_tfai -eq 0 ]]
diff --git a/usr/src/cmd/mdb/test/typedef/err.badid-leadnum.ksh b/usr/src/cmd/mdb/test/typedef/err.badid-leadnum.ksh
new file mode 100644
index 0000000000..f297cf99f1
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badid-leadnum.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef lp32; ::typedef uint8_t 42foo'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badid-leadschar.ksh b/usr/src/cmd/mdb/test/typedef/err.badid-leadschar.ksh
new file mode 100644
index 0000000000..5c7582b89c
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badid-leadschar.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef lp32; ::typedef uint8_t %foo'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badmodel.ksh b/usr/src/cmd/mdb/test/typedef/err.badmodel.ksh
new file mode 100644
index 0000000000..28d6cf05a5
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badmodel.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c LLP64'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-extrabraces.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-extrabraces.ksh
new file mode 100644
index 0000000000..3543aab1df
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-extrabraces.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct { uintptr_t stone[7]; {} void {**white; }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-neglenarr.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-neglenarr.ksh
new file mode 100644
index 0000000000..ec41576179
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-neglenarr.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct { uintptr_t stone[-3]; void **white; }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-noarrayclose.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-noarrayclose.ksh
new file mode 100644
index 0000000000..b91094ae44
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-noarrayclose.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct { uintptr_t stone[7; void **white; }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-noarraylen.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-noarraylen.ksh
new file mode 100644
index 0000000000..4fe23fcee2
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-noarraylen.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct { uintptr_t stone[]; void **white; }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-noarrayopen.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-noarrayopen.ksh
new file mode 100644
index 0000000000..9fe7e77200
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-noarrayopen.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct { uintptr_t stone7]; void **white; }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-nobraces.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-nobraces.ksh
new file mode 100644
index 0000000000..d50d8e3fb9
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-nobraces.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct uintptr_t stone[7]; void **white;" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-noclosebrace.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-noclosebrace.ksh
new file mode 100644
index 0000000000..4b20fac8c5
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-noclosebrace.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct { uintptr_t stone[7]; void **white; " gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-nomembers.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-nomembers.ksh
new file mode 100644
index 0000000000..9fd82a01cc
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-nomembers.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct { }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-nomemname.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-nomemname.ksh
new file mode 100644
index 0000000000..e1e4eacf13
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-nomemname.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct { void; }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-nomemsemi.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-nomemsemi.ksh
new file mode 100644
index 0000000000..9ab93a792f
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-nomemsemi.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct { uintptr_t stone[7]; void **white }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-noopenbrace.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-noopenbrace.ksh
new file mode 100644
index 0000000000..450b35ade2
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-noopenbrace.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct uintptr_t stone[7]; void **white; }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-noquotes.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-noquotes.ksh
new file mode 100644
index 0000000000..789caf63b0
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-noquotes.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef struct { uintptr_t stone[7]; void **white; } gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-repmemname.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-repmemname.ksh
new file mode 100644
index 0000000000..c00aca88fb
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-repmemname.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct { uintptr_t stone[7]; void **stone; }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-vlaonly.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-vlaonly.ksh
new file mode 100644
index 0000000000..a678d136b6
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-vlaonly.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct { uintptr_t stone[]; }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badstruct-zerolenarr.ksh b/usr/src/cmd/mdb/test/typedef/err.badstruct-zerolenarr.ksh
new file mode 100644
index 0000000000..7999c97a5a
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badstruct-zerolenarr.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "struct { uintptr_t stone[0]; void **white; }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.badunion-hasvla.ksh b/usr/src/cmd/mdb/test/typedef/err.badunion-hasvla.ksh
new file mode 100644
index 0000000000..57a82a7752
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.badunion-hasvla.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "union { int foo; uintptr_t stone[]; }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.extraargs.ksh b/usr/src/cmd/mdb/test/typedef/err.extraargs.ksh
new file mode 100644
index 0000000000..510797265b
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.extraargs.ksh
@@ -0,0 +1 @@
+$MDB -e "::typedef -c lp32; ::typedef uint8_t rm_t extra_t"
diff --git a/usr/src/cmd/mdb/test/typedef/err.noargs.ksh b/usr/src/cmd/mdb/test/typedef/err.noargs.ksh
new file mode 100644
index 0000000000..77eeacc5f2
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.noargs.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef'
diff --git a/usr/src/cmd/mdb/test/typedef/err.nokeyword.ksh b/usr/src/cmd/mdb/test/typedef/err.nokeyword.ksh
new file mode 100644
index 0000000000..9a5e1a1fcc
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.nokeyword.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c lp32; ::typedef "{ uintptr_t stone[7]; void **white; }" gift_t'
diff --git a/usr/src/cmd/mdb/test/typedef/err.nomodel.ksh b/usr/src/cmd/mdb/test/typedef/err.nomodel.ksh
new file mode 100644
index 0000000000..4614ac23d6
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.nomodel.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef -c'
diff --git a/usr/src/cmd/mdb/test/typedef/err.noname.ksh b/usr/src/cmd/mdb/test/typedef/err.noname.ksh
new file mode 100644
index 0000000000..04ebca1204
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.noname.ksh
@@ -0,0 +1 @@
+$MDB -e '::typedef int'
diff --git a/usr/src/cmd/mdb/test/typedef/err.typeexists.ksh b/usr/src/cmd/mdb/test/typedef/err.typeexists.ksh
new file mode 100644
index 0000000000..a1158cd830
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/err.typeexists.ksh
@@ -0,0 +1 @@
+$MDB /lib/libc.so -e '::typedef uint8_t uint16_t'
diff --git a/usr/src/cmd/mdb/test/typedef/tst.anonstruct.mdb b/usr/src/cmd/mdb/test/typedef/tst.anonstruct.mdb
new file mode 100644
index 0000000000..27de9428c6
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.anonstruct.mdb
@@ -0,0 +1,4 @@
+::typedef -c lp32
+::typedef "struct { uintptr_t stone[7]; void **white; }" gift_t
+::sizeof gift_t
+::print -at gift_t
diff --git a/usr/src/cmd/mdb/test/typedef/tst.anonstruct.mdb.out b/usr/src/cmd/mdb/test/typedef/tst.anonstruct.mdb.out
new file mode 100644
index 0000000000..605e0facfc
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.anonstruct.mdb.out
@@ -0,0 +1,5 @@
+sizeof (gift_t) = 0x28
+0 gift_t {
+ 0 uintptr_t [7] stone
+ 20 void **white
+}
diff --git a/usr/src/cmd/mdb/test/typedef/tst.anonunion.mdb b/usr/src/cmd/mdb/test/typedef/tst.anonunion.mdb
new file mode 100644
index 0000000000..1aa17b5366
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.anonunion.mdb
@@ -0,0 +1,4 @@
+::typedef -c lp64
+::typedef "union { int frodo; char sam; long gandalf; }" ringbearer_t;
+::sizeof ringbearer_t
+::print -at ringbearer_t
diff --git a/usr/src/cmd/mdb/test/typedef/tst.anonunion.mdb.out b/usr/src/cmd/mdb/test/typedef/tst.anonunion.mdb.out
new file mode 100644
index 0000000000..0a762dc1f8
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.anonunion.mdb.out
@@ -0,0 +1,6 @@
+sizeof (ringbearer_t) = 8
+0 ringbearer_t {
+ 0 int frodo
+ 0 char sam
+ 0 long gandalf
+}
diff --git a/usr/src/cmd/mdb/test/typedef/tst.cleanupstruct.ksh b/usr/src/cmd/mdb/test/typedef/tst.cleanupstruct.ksh
new file mode 100644
index 0000000000..1abf7540ce
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.cleanupstruct.ksh
@@ -0,0 +1,22 @@
+#
+# One of the problems that we can encounter involves trying to typedef a struct
+# that has an error in it. The problem here is that we actually create the type
+# itself for the struct before we add members. So what we need is something that
+# will fail validation. So here we go!
+#
+
+TMPFILE="/tmp/$(mktemp mtest.XXXXXX)"
+if [[ -z "$TMPFILE" ]]; then
+ echo "Failed to get a temp file" 2>&1
+ exit 1
+fi
+
+$MDB <<EOF
+::typedef "struct foo { int r; }" foo_t
+::typedef -l ! cat > $TMPFILE
+EOF
+
+DATA=$(cat $TMPFILE)
+rm -f $TMPFILE
+
+[[ -z $DATA ]]
diff --git a/usr/src/cmd/mdb/test/typedef/tst.deftypes32.mdb b/usr/src/cmd/mdb/test/typedef/tst.deftypes32.mdb
new file mode 100644
index 0000000000..12c28075a8
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.deftypes32.mdb
@@ -0,0 +1,27 @@
+::typedef -c LP32
+::sizeof int8_t
+::sizeof int16_t
+::sizeof int32_t
+::sizeof int64_t
+::sizeof uint8_t
+::sizeof uint16_t
+::sizeof uint32_t
+::sizeof uint64_t
+::sizeof intptr_t
+::sizeof uintptr_t
+::sizeof uchar_t
+::sizeof ushort_t
+::sizeof uint_t
+::sizeof ulong_t
+::sizeof u_longlong_t
+::sizeof ptrdiff_t
+::sizeof signed
+::sizeof unsigned
+::sizeof void
+::sizeof char
+::sizeof short
+::sizeof int
+::sizeof long
+::sizeof _Bool
+::sizeof float
+::sizeof double
diff --git a/usr/src/cmd/mdb/test/typedef/tst.deftypes32.mdb.out b/usr/src/cmd/mdb/test/typedef/tst.deftypes32.mdb.out
new file mode 100644
index 0000000000..792f42fbf2
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.deftypes32.mdb.out
@@ -0,0 +1,26 @@
+sizeof (int8_t) = 1
+sizeof (int16_t) = 2
+sizeof (int32_t) = 4
+sizeof (int64_t) = 8
+sizeof (uint8_t) = 1
+sizeof (uint16_t) = 2
+sizeof (uint32_t) = 4
+sizeof (uint64_t) = 8
+sizeof (intptr_t) = 4
+sizeof (uintptr_t) = 4
+sizeof (uchar_t) = 1
+sizeof (ushort_t) = 2
+sizeof (uint_t) = 4
+sizeof (ulong_t) = 4
+sizeof (u_longlong_t) = 8
+sizeof (ptrdiff_t) = 4
+sizeof (signed) = 4
+sizeof (unsigned) = 4
+sizeof (void) = 0
+sizeof (char) = 1
+sizeof (short) = 2
+sizeof (int) = 4
+sizeof (long) = 4
+sizeof (_Bool) = 1
+sizeof (float) = 4
+sizeof (double) = 8
diff --git a/usr/src/cmd/mdb/test/typedef/tst.deftypes64.mdb b/usr/src/cmd/mdb/test/typedef/tst.deftypes64.mdb
new file mode 100644
index 0000000000..5ae94cdea0
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.deftypes64.mdb
@@ -0,0 +1,27 @@
+::typedef -c LP64
+::sizeof int8_t
+::sizeof int16_t
+::sizeof int32_t
+::sizeof int64_t
+::sizeof uint8_t
+::sizeof uint16_t
+::sizeof uint32_t
+::sizeof uint64_t
+::sizeof intptr_t
+::sizeof uintptr_t
+::sizeof uchar_t
+::sizeof ushort_t
+::sizeof uint_t
+::sizeof ulong_t
+::sizeof u_longlong_t
+::sizeof ptrdiff_t
+::sizeof signed
+::sizeof unsigned
+::sizeof void
+::sizeof char
+::sizeof short
+::sizeof int
+::sizeof long
+::sizeof _Bool
+::sizeof float
+::sizeof double
diff --git a/usr/src/cmd/mdb/test/typedef/tst.deftypes64.mdb.out b/usr/src/cmd/mdb/test/typedef/tst.deftypes64.mdb.out
new file mode 100644
index 0000000000..83b85b943a
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.deftypes64.mdb.out
@@ -0,0 +1,26 @@
+sizeof (int8_t) = 1
+sizeof (int16_t) = 2
+sizeof (int32_t) = 4
+sizeof (int64_t) = 8
+sizeof (uint8_t) = 1
+sizeof (uint16_t) = 2
+sizeof (uint32_t) = 4
+sizeof (uint64_t) = 8
+sizeof (intptr_t) = 8
+sizeof (uintptr_t) = 8
+sizeof (uchar_t) = 1
+sizeof (ushort_t) = 2
+sizeof (uint_t) = 4
+sizeof (ulong_t) = 8
+sizeof (u_longlong_t) = 8
+sizeof (ptrdiff_t) = 8
+sizeof (signed) = 4
+sizeof (unsigned) = 4
+sizeof (void) = 0
+sizeof (char) = 1
+sizeof (short) = 2
+sizeof (int) = 4
+sizeof (long) = 8
+sizeof (_Bool) = 1
+sizeof (float) = 4
+sizeof (double) = 8
diff --git a/usr/src/cmd/mdb/test/typedef/tst.dellist.mdb b/usr/src/cmd/mdb/test/typedef/tst.dellist.mdb
new file mode 100644
index 0000000000..e9009d7b68
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.dellist.mdb
@@ -0,0 +1,3 @@
+::typdef -c lp32
+::typedef -d
+::typedef -l
diff --git a/usr/src/cmd/mdb/test/typedef/tst.dellist.mdb.out b/usr/src/cmd/mdb/test/typedef/tst.dellist.mdb.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.dellist.mdb.out
diff --git a/usr/src/cmd/mdb/test/typedef/tst.emptylist.mdb b/usr/src/cmd/mdb/test/typedef/tst.emptylist.mdb
new file mode 100644
index 0000000000..6c7505ac1c
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.emptylist.mdb
@@ -0,0 +1 @@
+::typedef -l
diff --git a/usr/src/cmd/mdb/test/typedef/tst.emptylist.mdb.out b/usr/src/cmd/mdb/test/typedef/tst.emptylist.mdb.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.emptylist.mdb.out
diff --git a/usr/src/cmd/mdb/test/typedef/tst.libctype.ksh b/usr/src/cmd/mdb/test/typedef/tst.libctype.ksh
new file mode 100644
index 0000000000..cf517cf937
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.libctype.ksh
@@ -0,0 +1,6 @@
+$MDB /lib/libc.so <<EOF
+::typedef uint8_t rm_t
+::typedef -l
+::print -at rm_t
+::sizeof rm_t
+EOF
diff --git a/usr/src/cmd/mdb/test/typedef/tst.libctype.ksh.out b/usr/src/cmd/mdb/test/typedef/tst.libctype.ksh.out
new file mode 100644
index 0000000000..d37ffafbfb
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.libctype.ksh.out
@@ -0,0 +1,4 @@
+uint8_t
+rm_t
+0 rm_t
+sizeof (rm_t) = 1
diff --git a/usr/src/cmd/mdb/test/typedef/tst.models.ksh b/usr/src/cmd/mdb/test/typedef/tst.models.ksh
new file mode 100644
index 0000000000..8537ec443c
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.models.ksh
@@ -0,0 +1,11 @@
+lp64m="lp64 Lp64 LP64 lP64"
+lp32m="lp32 Lp32 LP32 lP32"
+ilp32m="ilp32 ilP32 iLp32 iLP32 Ilp32 IlP32 ILp32 ILP32"
+for m in $lp64m $lp32m $ilp32m; do
+ $MDB -e "::typedef -c $m"
+ if [[ ! $? -eq 0 ]]; then
+ echo "failed to create model $m" 2>&1
+ exit 1
+ fi
+done
+exit 0
diff --git a/usr/src/cmd/mdb/test/typedef/tst.struct.mdb b/usr/src/cmd/mdb/test/typedef/tst.struct.mdb
new file mode 100644
index 0000000000..2e0206e7ec
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.struct.mdb
@@ -0,0 +1,6 @@
+::typedef -c lp32
+::typedef "struct gift { uintptr_t stone[7]; void **white; }" gift_t
+::sizeof gift_t
+::print -at gift_t
+::sizeof struct gift
+::print -at struct gift
diff --git a/usr/src/cmd/mdb/test/typedef/tst.struct.mdb.out b/usr/src/cmd/mdb/test/typedef/tst.struct.mdb.out
new file mode 100644
index 0000000000..9a7d801742
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.struct.mdb.out
@@ -0,0 +1,10 @@
+sizeof (gift_t) = 0x28
+0 gift_t {
+ 0 uintptr_t [7] stone
+ 20 void **white
+}
+sizeof (struct gift) = 0x28
+0 struct gift {
+ 0 uintptr_t [7] stone
+ 20 void **white
+}
diff --git a/usr/src/cmd/mdb/test/typedef/tst.structselfref.mdb b/usr/src/cmd/mdb/test/typedef/tst.structselfref.mdb
new file mode 100644
index 0000000000..85b3068e00
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.structselfref.mdb
@@ -0,0 +1,4 @@
+::typedef -c lp32
+::typedef "struct list { struct list *prev; struct list *next; }" list_t
+::sizeof list_t
+::print -at list_t
diff --git a/usr/src/cmd/mdb/test/typedef/tst.structselfref.mdb.out b/usr/src/cmd/mdb/test/typedef/tst.structselfref.mdb.out
new file mode 100644
index 0000000000..a6f4823ad1
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.structselfref.mdb.out
@@ -0,0 +1,5 @@
+sizeof (list_t) = 0x10
+0 list_t {
+ 0 struct list *prev
+ 8 struct list *next
+}
diff --git a/usr/src/cmd/mdb/test/typedef/tst.structvla.mdb b/usr/src/cmd/mdb/test/typedef/tst.structvla.mdb
new file mode 100644
index 0000000000..27cd56c186
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.structvla.mdb
@@ -0,0 +1,4 @@
+::typedef -c lp32
+::typedef "struct gift { uintptr_t stone[7]; void **white; char owner[]; }" gift_t
+::sizeof gift_t
+::print -at gift_t
diff --git a/usr/src/cmd/mdb/test/typedef/tst.structvla.mdb.out b/usr/src/cmd/mdb/test/typedef/tst.structvla.mdb.out
new file mode 100644
index 0000000000..0a0d3d4029
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.structvla.mdb.out
@@ -0,0 +1,6 @@
+sizeof (gift_t) = 0x28
+0 gift_t {
+ 0 uintptr_t [7] stone
+ 20 void **white
+ 28 char [0] owner
+}
diff --git a/usr/src/cmd/mdb/test/typedef/tst.union.mdb b/usr/src/cmd/mdb/test/typedef/tst.union.mdb
new file mode 100644
index 0000000000..966446caee
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.union.mdb
@@ -0,0 +1,6 @@
+::typedef -c lp64
+::typedef "union ringbearer { int frodo; char sam; long gandalf; }" ringbearer_t;
+::sizeof ringbearer_t
+::print -at ringbearer_t
+::sizeof union ringbearer
+::print -at union ringbearer
diff --git a/usr/src/cmd/mdb/test/typedef/tst.union.mdb.out b/usr/src/cmd/mdb/test/typedef/tst.union.mdb.out
new file mode 100644
index 0000000000..2afc2eabb9
--- /dev/null
+++ b/usr/src/cmd/mdb/test/typedef/tst.union.mdb.out
@@ -0,0 +1,12 @@
+sizeof (ringbearer_t) = 8
+0 ringbearer_t {
+ 0 int frodo
+ 0 char sam
+ 0 long gandalf
+}
+sizeof (union ringbearer) = 8
+0 union ringbearer {
+ 0 int frodo
+ 0 char sam
+ 0 long gandalf
+}