diff options
Diffstat (limited to 'usr/src/cmd/mdb')
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", ®fp) != 0 || + mdb_getareg(1, "rip", ®ip) != 0) { +#else +#ifdef __i386 + if (mdb_getareg(1, "ebp", ®fp) != 0 || + mdb_getareg(1, "eip", ®ip) != 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 +} |
