diff options
Diffstat (limited to 'usr/src/cmd')
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c | 5 | ||||
-rw-r--r-- | usr/src/cmd/cpc/common/cpustat.c | 6 | ||||
-rw-r--r-- | usr/src/cmd/file/elf_read.c | 20 | ||||
-rw-r--r-- | usr/src/cmd/fs.d/nfs/mount/Makefile | 14 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c | 30 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/v8/mdb_v8.c | 1535 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/v8/v8dbg.h | 12 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/zfs/zfs.c | 37 | ||||
-rw-r--r-- | usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c | 39 | ||||
-rw-r--r-- | usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c | 40 | ||||
-rw-r--r-- | usr/src/cmd/perl/Makefile.perl | 8 | ||||
-rw-r--r-- | usr/src/cmd/perl/Makefile.targ | 6 | ||||
-rw-r--r-- | usr/src/cmd/ptools/pflags/pflags.c | 5 | ||||
-rw-r--r-- | usr/src/cmd/rpcsvc/net_files/rpc | 26 | ||||
-rw-r--r-- | usr/src/cmd/sgs/packages/common/SUNWonld-README | 1 | ||||
-rw-r--r-- | usr/src/cmd/sgs/rtld/common/util.c | 14 | ||||
-rw-r--r-- | usr/src/cmd/tar/Makefile | 2 | ||||
-rw-r--r-- | usr/src/cmd/tar/tar.c | 38 | ||||
-rw-r--r-- | usr/src/cmd/zlogin/zlogin.c | 35 | ||||
-rw-r--r-- | usr/src/cmd/zoneadmd/vplat.c | 49 |
20 files changed, 1574 insertions, 348 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c index 54fbdc844b..8fbf3fc15f 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c @@ -29,6 +29,7 @@ #include <strings.h> #include <errno.h> #include <fcntl.h> +#include <limits.h> #include <setjmp.h> #include <sys/types.h> #include <sys/signal.h> @@ -618,6 +619,10 @@ cap_open_read(const char *name) if (fstat(capfile_in, &st) < 0) pr_err("couldn't stat %s: %m", name); + if (st.st_size > INT_MAX) + pr_err("input file size (%llu bytes) exceeds maximum " + "supported size (%d bytes)", + (unsigned long long)st.st_size, INT_MAX); cap_len = st.st_size; cap_buffp = mmap(0, cap_len, PROT_READ, MAP_PRIVATE, capfile_in, 0); diff --git a/usr/src/cmd/cpc/common/cpustat.c b/usr/src/cmd/cpc/common/cpustat.c index 8d405bb625..965fbadfea 100644 --- a/usr/src/cmd/cpc/common/cpustat.c +++ b/usr/src/cmd/cpc/common/cpustat.c @@ -251,9 +251,9 @@ main(int argc, char *argv[]) if (errcnt != 0 || opts->dohelp || (opts->nsets = cpc_setgrp_numsets(opts->master)) == 0) { (void) fprintf(opts->dohelp ? stdout : stderr, gettext( - "Usage:\n\t%s [-c events] [-p period] [-nstD] " - "[-T d|u] [interval [count]]\n\n" - "\t-c events specify processor events to be monitored\n" + "Usage:\n\t%s -c spec [-c spec]... [-p period] [-T u|d]\n" + "\t\t[-sntD] [interval [count]]\n\n" + "\t-c spec\t specify processor events to be monitored\n" "\t-n\t suppress titles\n" "\t-p period cycle through event list periodically\n" "\t-s\t run user soaker thread for system-only events\n" diff --git a/usr/src/cmd/file/elf_read.c b/usr/src/cmd/file/elf_read.c index 7ccf8e2bb3..03c73f57a9 100644 --- a/usr/src/cmd/file/elf_read.c +++ b/usr/src/cmd/file/elf_read.c @@ -419,7 +419,8 @@ process_shdr(Elf_Info *EI) int i, j, idx; FILE_ELF_OFF_T cap_off; FILE_ELF_SIZE_T csize; - char *section_name; + char *strtab; + size_t strtab_sz; Elf_Cap Chdr; Elf_Shdr *shdr = &EI_Shdr; @@ -435,16 +436,18 @@ process_shdr(Elf_Info *EI) if (get_shdr(EI, EI_Ehdr_shstrndx) == ELF_READ_FAIL) return (ELF_READ_FAIL); - if ((section_name = malloc(shdr->sh_size)) == NULL) + if ((strtab = malloc(shdr->sh_size)) == NULL) return (ELF_READ_FAIL); - if (pread64(EI->elffd, section_name, shdr->sh_size, shdr->sh_offset) + if (pread64(EI->elffd, strtab, shdr->sh_size, shdr->sh_offset) != shdr->sh_size) return (ELF_READ_FAIL); + strtab_sz = shdr->sh_size; + /* read all the sections and process them */ for (idx = 1, i = 0; i < EI_Ehdr_shnum; idx++, i++) { - char *str; + char *shnam; if (get_shdr(EI, i) == ELF_READ_FAIL) return (ELF_READ_FAIL); @@ -538,16 +541,19 @@ process_shdr(Elf_Info *EI) continue; } - str = §ion_name[shdr->sh_name]; + if (shdr->sh_name >= strtab_sz) + shnam = NULL; + else + shnam = &strtab[shdr->sh_name]; if (!(EI->stripped & E_DBGINF) && ((shdr->sh_type == SHT_SUNW_DEBUG) || (shdr->sh_type == SHT_SUNW_DEBUGSTR) || - (is_in_list(str)))) { + (shnam != NULL && is_in_list(shnam)))) { EI->stripped |= E_DBGINF; } } - free(section_name); + free(strtab); return (ELF_READ_OKAY); } diff --git a/usr/src/cmd/fs.d/nfs/mount/Makefile b/usr/src/cmd/fs.d/nfs/mount/Makefile index dad33922a3..c2485208a8 100644 --- a/usr/src/cmd/fs.d/nfs/mount/Makefile +++ b/usr/src/cmd/fs.d/nfs/mount/Makefile @@ -19,18 +19,12 @@ # CDDL HEADER END # # Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2014, Joyent, Inc. All rights reserved. # # cmd/fs.d/nfs/mount/Makefile FSTYPE= nfs LIBPROG= mount -ROOTFS_PROG= $(LIBPROG) - -# duplicate ROOTLIBFSTYPE value needed for installation rule -# we must define this before including Makefile.fstype -ROOTLIBFSTYPE = $(ROOT)/usr/lib/fs/$(FSTYPE) -$(ROOTLIBFSTYPE)/%: $(ROOTLIBFSTYPE) % - $(RM) $@; $(SYMLINK) ../../../../etc/fs/$(FSTYPE)/$(LIBPROG) $@ include ../../Makefile.fstype @@ -65,7 +59,7 @@ CLOBBERFILES += $(LIBPROG) .KEEP_STATE: -all: $(ROOTFS_PROG) +all: $(LIBPROG) $(LIBPROG): webnfs.h $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) @@ -114,7 +108,9 @@ $(POFILE): $(SRCS) sed "/^domain/d" messages.po > $@ $(RM) $(POFILE).i messages.po -install: $(ROOTETCPROG) +install: all $(FSTYPEPROG) + $(RM) $(ROOTETCPROG) + $(SYMLINK) ../../../usr/lib/fs/$(FSTYPE)/$(LIBPROG) $(ROOTETCPROG) lint: webnfs.h webnfs_xdr.c webnfs_client.c lint_SRCS diff --git a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c index 83ffae0634..eaa381cc47 100644 --- a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c +++ b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c @@ -688,9 +688,12 @@ static const smb_exp_t smb_session_exp[] = { SMB_OPT_REQUEST, offsetof(smb_session_t, s_req_list.sl_list), "smbreq", "smb_request"}, - { SMB_OPT_USER | SMB_OPT_TREE | SMB_OPT_OFILE | SMB_OPT_ODIR, + { SMB_OPT_USER, offsetof(smb_session_t, s_user_list.ll_list), "smbuser", "smb_user"}, + { SMB_OPT_TREE | SMB_OPT_OFILE | SMB_OPT_ODIR, + offsetof(smb_session_t, s_tree_list.ll_list), + "smbtree", "smb_tree"}, { 0, 0, NULL, NULL} }; @@ -963,17 +966,6 @@ static const char *smb_user_state[SMB_USER_STATE_SENTINEL] = "LOGGED_OFF" }; -/* - * List of objects that can be expanded under a user structure. - */ -static const smb_exp_t smb_user_exp[] = -{ - { SMB_OPT_TREE | SMB_OPT_OFILE | SMB_OPT_ODIR, - offsetof(smb_user_t, u_tree_list.ll_list), - "smbtree", "smb_tree"}, - { 0, 0, NULL, NULL} -}; - static void smb_dcmd_user_help(void) { @@ -983,17 +975,13 @@ smb_dcmd_user_help(void) mdb_printf("%<b>OPTIONS%</b>\n"); (void) mdb_inc_indent(2); mdb_printf( - "-v\tDisplay verbose smb_user information\n" - "-d\tDisplay the list of smb_odirs attached\n" - "-f\tDisplay the list of smb_ofiles attached\n" - "-t\tDisplay the list of smb_trees attached\n"); + "-v\tDisplay verbose smb_user information\n"); } static int smb_dcmd_user(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { uint_t opts; - ulong_t indent = 0; if (smb_dcmd_getopt(&opts, argc, argv)) return (DCMD_USAGE); @@ -1009,8 +997,6 @@ smb_dcmd_user(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) smb_user_t *user; char *account; - indent = SMB_DCMD_INDENT; - user = mdb_alloc(sizeof (*user), UM_SLEEP | UM_GC); if (mdb_vread(user, sizeof (*user), addr) == -1) { mdb_warn("failed to read smb_user at %p", addr); @@ -1058,8 +1044,6 @@ smb_dcmd_user(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) account); } } - if (smb_obj_expand(addr, opts, smb_user_exp, indent)) - return (DCMD_ERR); return (DCMD_OK); } @@ -1217,6 +1201,8 @@ smb_dcmd_odir(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) addr); mdb_printf("State: %d (%s)\n", od->d_state, state); mdb_printf("SID: %u\n", od->d_odid); + mdb_printf("User: %p\n", od->d_user); + mdb_printf("Tree: %p\n", od->d_tree); mdb_printf("Reference Count: %d\n", od->d_refcnt); mdb_printf("Pattern: %s\n", od->d_pattern); mdb_printf("SMB Node: %p\n\n", od->d_dnode); @@ -1292,6 +1278,8 @@ smb_dcmd_ofile(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) ((of->f_flags & SMB_OFLAGS_LLF_POS_VALID) ? "Valid" : "Invalid")); mdb_printf("Flags: 0x%08x\n", of->f_flags); + mdb_printf("User: %p\n", of->f_user); + mdb_printf("Tree: %p\n", of->f_tree); mdb_printf("Credential: %p\n\n", of->f_cr); } else { if (DCMD_HDRSPEC(flags)) diff --git a/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c b/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c index aa597e7a01..6609097742 100644 --- a/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c +++ b/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright (c) 2014, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. All rights reserved. */ /* @@ -50,6 +50,7 @@ #include <sys/mdb_modapi.h> #include <assert.h> #include <ctype.h> +#include <inttypes.h> #include <stdarg.h> #include <stdio.h> #include <string.h> @@ -148,10 +149,11 @@ 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_FIELDINDEX_MASK; +static intptr_t V8_FIELDINDEX_SHIFT; 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; @@ -161,6 +163,7 @@ 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_JSFUNCTION = -1; static intptr_t V8_TYPE_FIXEDARRAY = -1; static intptr_t V8_ELEMENTS_KIND_SHIFT; @@ -214,10 +217,12 @@ static ssize_t V8_OFF_SLICEDSTRING_PARENT; static ssize_t V8_OFF_SLICEDSTRING_OFFSET; static ssize_t V8_OFF_STRING_LENGTH; -#define NODE_OFF_EXTSTR_DATA 0x4 /* see node_string.h */ +/* see node_string.h */ +#define NODE_OFF_EXTSTR_DATA sizeof (uintptr_t) #define V8_CONSTANT_OPTIONAL 1 #define V8_CONSTANT_HASFALLBACK 2 +#define V8_CONSTANT_REMOVED 4 #define V8_CONSTANT_MAJORSHIFT 3 #define V8_CONSTANT_MAJORMASK ((1 << 4) - 1) @@ -233,6 +238,10 @@ static ssize_t V8_OFF_STRING_LENGTH; (V8_CONSTANT_OPTIONAL | V8_CONSTANT_HASFALLBACK | \ ((maj) << V8_CONSTANT_MAJORSHIFT) | ((min) << V8_CONSTANT_MINORSHIFT)) +#define V8_CONSTANT_REMOVED_SINCE(maj, min) \ + (V8_CONSTANT_REMOVED | \ + ((maj) << V8_CONSTANT_MAJORSHIFT) | ((min) << V8_CONSTANT_MINORSHIFT)) + /* * Table of constants used directly by this file. */ @@ -262,8 +271,10 @@ static v8_constant_t v8_constants[] = { { &V8_SlicedStringTag, "v8dbg_SlicedStringTag", V8_CONSTANT_FALLBACK(0, 0), 0x3 }, { &V8_ExternalStringTag, "v8dbg_ExternalStringTag" }, - { &V8_FailureTag, "v8dbg_FailureTag" }, - { &V8_FailureTagMask, "v8dbg_FailureTagMask" }, + { &V8_FailureTag, "v8dbg_FailureTag", + V8_CONSTANT_REMOVED_SINCE(3, 28) }, + { &V8_FailureTagMask, "v8dbg_FailureTagMask", + V8_CONSTANT_REMOVED_SINCE(3, 28) }, { &V8_HeapObjectTag, "v8dbg_HeapObjectTag" }, { &V8_HeapObjectTagMask, "v8dbg_HeapObjectTagMask" }, { &V8_SmiTag, "v8dbg_SmiTag" }, @@ -277,7 +288,7 @@ static v8_constant_t v8_constants[] = { #endif { &V8_PointerSizeLog2, "v8dbg_PointerSizeLog2" }, - { &V8_DICT_SHIFT, "v8dbg_dict_shift", + { &V8_DICT_SHIFT, "v8dbg_bit_field3_dictionary_map_shift", V8_CONSTANT_FALLBACK(3, 13), 24 }, { &V8_DICT_PREFIX_SIZE, "v8dbg_dict_prefix_size", V8_CONSTANT_FALLBACK(3, 11), 2 }, @@ -285,11 +296,14 @@ static v8_constant_t v8_constants[] = { V8_CONSTANT_FALLBACK(3, 11), 3 }, { &V8_DICT_START_INDEX, "v8dbg_dict_start_index", V8_CONSTANT_FALLBACK(3, 11), 3 }, + { &V8_FIELDINDEX_MASK, "v8dbg_fieldindex_mask", + V8_CONSTANT_FALLBACK(3, 26), 0x3ff00000 }, + { &V8_FIELDINDEX_SHIFT, "v8dbg_fieldindex_shift", + V8_CONSTANT_FALLBACK(3, 26), 20 }, { &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 }, @@ -430,6 +444,66 @@ 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 *); +static int jsfunc_name(uintptr_t, char **, size_t *); + +/* + * When iterating properties, it's useful to keep track of what kinds of + * properties were found. This is useful for developers to identify objects of + * different kinds in order to debug them. + */ +typedef enum { + JPI_NONE = 0, + + /* + * Indicates how properties are stored in this object. There can be + * both numeric properties and some of the other kinds. + */ + JPI_NUMERIC = 0x01, /* numeric-named properties in "elements" */ + JPI_DICT = 0x02, /* dictionary properties */ + JPI_INOBJECT = 0x04, /* properties stored inside object */ + JPI_PROPS = 0x08, /* "properties" array */ + + /* error-like cases */ + JPI_SKIPPED = 0x10, /* some properties were skipped */ + JPI_BADLAYOUT = 0x20, /* we didn't recognize the layout at all */ + + /* fallback cases */ + JPI_HASTRANSITIONS = 0x100, /* found a transitions array */ + JPI_HASCONTENT = 0x200, /* found a separate content array */ +} jspropinfo_t; + +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; + boolean_t jsop_descended; + jspropinfo_t jsop_propinfo; +} 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 *); + +/* + * Returns 1 if the V8 version v8_major.v8.minor is strictly older than + * the V8 version represented by "flags". + * Returns 0 otherwise. + */ +static int +v8_version_older(uintptr_t v8_major, uintptr_t v8_minor, uint32_t flags) { + return (v8_major < V8_CONSTANT_MAJOR(flags) || + (v8_major == V8_CONSTANT_MAJOR(flags) && + v8_minor < V8_CONSTANT_MINOR(flags))); +} /* * Invoked when this dmod is initially loaded to load the set of classes, enums, @@ -477,7 +551,9 @@ autoconfigure(v8_cfg_t *cfgp) continue; } - if (!(cnp->v8c_flags & V8_CONSTANT_OPTIONAL)) { + if (!(cnp->v8c_flags & V8_CONSTANT_OPTIONAL) && + (!(cnp->v8c_flags & V8_CONSTANT_REMOVED) || + v8_version_older(v8_major, v8_minor, cnp->v8c_flags))) { mdb_warn("failed to read \"%s\"", cnp->v8c_symbol); failed++; continue; @@ -508,6 +584,9 @@ autoconfigure(v8_cfg_t *cfgp) if (strcmp(ep->v8e_name, "JSArray") == 0) V8_TYPE_JSARRAY = ep->v8e_value; + if (strcmp(ep->v8e_name, "JSFunction") == 0) + V8_TYPE_JSFUNCTION = ep->v8e_value; + if (strcmp(ep->v8e_name, "FixedArray") == 0) V8_TYPE_FIXEDARRAY = ep->v8e_value; } @@ -522,6 +601,11 @@ autoconfigure(v8_cfg_t *cfgp) failed++; } + if (V8_TYPE_JSFUNCTION == -1) { + mdb_warn("couldn't find JSFunction type\n"); + failed++; + } + if (V8_TYPE_FIXEDARRAY == -1) { mdb_warn("couldn't find FixedArray type\n"); failed++; @@ -963,11 +1047,8 @@ v8_warn(const char *format, ...) } } -/* - * 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) +static v8_field_t * +conf_field_lookup(const char *klass, const char *field) { v8_class_t *clp; v8_field_t *flp; @@ -978,13 +1059,26 @@ heap_offset(const char *klass, const char *field, ssize_t *offp) } if (clp == NULL) - return (-1); + return (NULL); for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next) { if (strcmp(field, flp->v8f_name) == 0) break; } + return (flp); +} + +/* + * 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_field_t *flp; + + flp = conf_field_lookup(klass, field); + if (flp == NULL) return (-1); @@ -1094,6 +1188,46 @@ read_heap_byte(uint8_t *valp, uintptr_t addr, ssize_t off) } /* + * This is truly horrific. Inside the V8 Script class are a number of + * small-integer fields like the function_token_position (an offset into the + * script's text where the "function" token appears). For 32-bit processes, V8 + * stores these as a sequence of SMI fields, which we know how to interpret well + * enough. For 64-bit processes, "to avoid wasting space", they use a different + * trick: each 8-byte word contains two integer fields. The low word is + * represented like an SMI: shifted left by one. They don't bother shifting the + * high word, since its low bit will never be looked at (since it's not + * word-aligned). + * + * This function is used for cases where we would use read_heap_smi(), except + * that this is one of those fields that might be encoded or might not be, + * depending on whether the address is word-aligned. + */ +static int +read_heap_maybesmi(uintptr_t *valp, uintptr_t addr, ssize_t off) +{ +#ifdef _LP64 + uint32_t readval; + + if (mdb_vread(&readval, sizeof (readval), addr + off) == -1) { + *valp = -1; + v8_warn("failed to read offset %d from %p", off, addr); + return (-1); + } + + /* + * If this was the low half-word, it needs to be shifted right. + */ + if ((addr + off) % sizeof (uintptr_t) == 0) + readval >>= 1; + + *valp = (uintptr_t)readval; + return (0); +#else + return (read_heap_smi(valp, addr, off)); +#endif +} + +/* * 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. @@ -1181,8 +1315,7 @@ read_heap_dict(uintptr_t addr, if (V8_IS_SMI(dict[i])) { intptr_t val = V8_SMI_VALUE(dict[i]); - - (void) snprintf(buf, sizeof (buf), "%ld", val); + (void) snprintf(buf, sizeof (buf), "%" PRIdPTR, val); } else { if (jsobj_is_hole(dict[i])) { /* @@ -1217,13 +1350,69 @@ out: } /* + * Given an object, returns in "buf" the name of the constructor function. With + * "verbose", prints the pointer to the JSFunction object. Given anything else, + * returns an error (and warns the user why). + */ +static int +obj_jsconstructor(uintptr_t addr, char **bufp, size_t *lenp, boolean_t verbose) +{ + uint8_t type; + uintptr_t map, consfunc, funcinfop; + const char *constype; + + if (!V8_IS_HEAPOBJECT(addr) || + read_typebyte(&type, addr) != 0 || + (type != V8_TYPE_JSOBJECT && type != V8_TYPE_JSARRAY)) { + mdb_warn("%p is not a JSObject\n", addr); + return (-1); + } + + if (mdb_vread(&map, sizeof (map), addr + V8_OFF_HEAPOBJECT_MAP) == -1 || + mdb_vread(&consfunc, sizeof (consfunc), + map + V8_OFF_MAP_CONSTRUCTOR) == -1) { + mdb_warn("unable to read object map\n"); + return (-1); + } + + if (read_typebyte(&type, consfunc) != 0) + return (-1); + + constype = enum_lookup_str(v8_types, type, ""); + if (strcmp(constype, "Oddball") == 0) { + jsobj_print_t jsop; + bzero(&jsop, sizeof (jsop)); + jsop.jsop_bufp = bufp; + jsop.jsop_lenp = lenp; + return (jsobj_print_oddball(consfunc, &jsop)); + } + + if (strcmp(constype, "JSFunction") != 0) { + mdb_warn("constructor: expected JSFunction, found %s\n", + constype); + return (-1); + } + + if (read_heap_ptr(&funcinfop, consfunc, V8_OFF_JSFUNCTION_SHARED) != 0) + return (-1); + + if (jsfunc_name(funcinfop, bufp, lenp) != 0) + return (-1); + + if (verbose) + bsnprintf(bufp, lenp, " (JSFunction: %p)", consfunc); + + return (0); +} + +/* * 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; + uintptr_t strptr, map, consfunc, funcinfop; const char *typename; if (V8_IS_FAILURE(addr)) { @@ -1259,10 +1448,75 @@ obj_jstype(uintptr_t addr, char **bufp, size_t *lenp, uint8_t *typep) } } + if (strcmp(typename, "JSObject") == 0 && + mdb_vread(&map, sizeof (map), addr + V8_OFF_HEAPOBJECT_MAP) != -1 && + mdb_vread(&consfunc, sizeof (consfunc), + map + V8_OFF_MAP_CONSTRUCTOR) != -1 && + read_typebyte(&typebyte, consfunc) == 0 && + strcmp(enum_lookup_str(v8_types, typebyte, ""), + "JSFunction") == 0 && + mdb_vread(&funcinfop, sizeof (funcinfop), + consfunc + V8_OFF_JSFUNCTION_SHARED) != -1) { + (void) bsnprintf(bufp, lenp, ": "); + (void) jsfunc_name(funcinfop, bufp, lenp); + } + return (0); } /* + * V8 allows implementers (like Node) to store pointer-sized values into + * internal fields within V8 heap objects. Implementors access these values by + * 0-based index (e.g., SetInternalField(0, value)). These values are stored as + * an array directly after the last actual C++ field in the C++ object. + * + * Node uses internal fields to refer to handles. For example, a socket's C++ + * HandleWrap object is typically stored as internal field 0 in the JavaScript + * Socket object. Similarly, the native-heap-allocated chunk of memory + * associated with a Node Buffer is referenced by field 0 in the External array + * pointed-to by the Node Buffer JSObject. + */ +static int +obj_v8internal(uintptr_t addr, uint_t idx, uintptr_t *valp) +{ + char *bufp; + size_t len; + ssize_t off; + uint8_t type; + + v8_class_t *clp; + char buf[256]; + + bufp = buf; + len = sizeof (buf); + if (obj_jstype(addr, &bufp, &len, &type) != 0) + return (DCMD_ERR); + + if (type == 0) { + mdb_warn("%p: unsupported type\n", addr); + return (DCMD_ERR); + } + + for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) { + if (strcmp(buf, clp->v8c_name) == 0) + break; + } + + if (clp == NULL) { + mdb_warn("%p: didn't find expected class\n", addr); + return (DCMD_ERR); + } + + off = clp->v8c_end + (idx * sizeof (uintptr_t)) - 1; + if (read_heap_ptr(valp, addr, off) != 0) { + mdb_warn("%p: failed to read from %p\n", addr, addr + off); + return (DCMD_ERR); + } + + return (DCMD_OK); +} + +/* * Print out the fields of the given object that come from the given class. */ static int @@ -1456,11 +1710,6 @@ jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp, char *buf; uint16_t chrval; - if ((blen = MIN(*lenp, 256 * 1024)) == 0) - return (0); - - buf = alloca(blen); - if (read_heap_smi(&nstrchrs, addr, V8_OFF_STRING_LENGTH) != 0) { (void) bsnprintf(bufp, lenp, "<string (failed to read length)>"); @@ -1470,17 +1719,22 @@ jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp, if (slicelen != -1) nstrchrs = slicelen; + blen = ((flags & JSSTR_ISASCII) != 0) ? *lenp : 2 * (*lenp); + if ((blen = MIN(blen, 256 * 1024)) == 0) + return (0); + if ((flags & JSSTR_ISASCII) != 0) { nstrbytes = nstrchrs; nreadoffset = sliceoffset; + nreadbytes = nstrbytes + sizeof ("\"\"") <= *lenp ? + nstrbytes : *lenp - sizeof ("\"\"[...]"); } else { nstrbytes = 2 * nstrchrs; nreadoffset = 2 * sliceoffset; + nreadbytes = nstrchrs + sizeof ("\"\"") <= *lenp ? + nstrbytes : 2 * (*lenp - sizeof ("\"\"[...]")); } - nreadbytes = nstrbytes + sizeof ("\"\"") <= blen ? nstrbytes : - blen - sizeof ("\"\"[...]"); - if (nreadbytes < 0) { /* * We don't even have the room to store the ellipsis; zero @@ -1491,10 +1745,13 @@ jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp, return (0); } - if (verbose) + if (verbose) { mdb_printf("length: %d chars (%d bytes), " "will read %d bytes from offset %d\n", nstrchrs, nstrbytes, nreadbytes, nreadoffset); + mdb_printf("given buffer size: %d, internal buffer: %d\n", + *lenp, blen); + } if (nstrbytes == 0) { (void) bsnprintf(bufp, lenp, "%s%s", @@ -1502,6 +1759,7 @@ jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp, return (0); } + buf = alloca(blen); buf[0] = '\0'; if ((flags & JSSTR_ISASCII) != 0) { @@ -1742,9 +2000,126 @@ jsobj_is_hole(uintptr_t addr) return (jsobj_is_oddball(addr, "hole")); } +/* + * Iterate the properties of a JavaScript object "addr". + * + * Every heap object refers to a Map that describes how that heap object is laid + * out. The Map includes information like the constructor function used to + * create the object, how many bytes each object uses, and how many properties + * are stored inside the object. (A single Map object can be shared by many + * objects of the same general type, which is why this information is encoded by + * reference rather than contained in each object.) + * + * V8 knows about lots of different kinds of properties: + * + * o properties with numeric names (e.g., array elements) + * o dictionary properties + * o "fast" properties stored inside each object, much like a C struct + * o properties stored in the separate "properties" array + * o getters, setters, and other magic (not supported by this module) + * + * While property lookup in JavaScript involves traversing an object's prototype + * chain, this module only iterates the properties local to the object itself. + * + * + * Numeric properties + * + * Properties having numeric indexes are stored in the "elements" array attached + * to each object. Objects with numeric properties can also have other + * properties. + * + * + * Dictionary properties + * + * An object with dictionary properties is identified by one of the bits in + * "bitfield3" in the object's Map. For details on slow properties, see + * read_heap_dict(). + * + * + * Other properties + * + * The Map object refers to an array of "instance descriptors". This array has + * a few metadata entries at the front, followed by groups of three entries for + * each property. In Node v0.10 and later, it looks roughly like this: + * + * +--------------+ +----------------------+ + * | JSObject | +--> | Map | + * +--------------| | +----------------------+ + * | map | ---+ | ... | + * | ... | | instance_descriptors | --+ + * in-object | [prop 0 val] | | ... | | + * properties | [prop 1 val] | +----------------------+ | + * (not for all | ... | | + * objects) | [prop N val] | | + * +--------------+ | + * +------------------------------------------------+ + * | + * +----> +------------------------------+ + * | FixedArray | + * +------------------------------+ + * | ... | + * | prop 0 "key" descriptor | + * | prop 0 "details" descriptor | + * | prop 0 "value" descriptor | + * | prop 1 "key" descriptor | + * | prop 1 "details" descriptor | + * | prop 1 "value" descriptor | + * | ... | + * | prop N "key" descriptor | + * | prop N "details" descriptor | + * | prop N "value" descriptor | + * +------------------------------+ + * + * In versions of Node prior to 0.10, there's an extra level of indirection. + * The Map refers to a "transitions" array, which has an entry that points to + * the instance descriptors. In both cases, the descriptors look roughly the + * same. + * + * Each property is described by three pointer-sized entries: + * + * o key: a string denoting the name of the property + * o details: a bitfield describing attributes of this property + * o value: an integer describing where this property's value is stored + * + * "key" is straightforward: it's just the name of the property as the + * JavaScript programmer knows it. + * + * In versions prior to Node 0.12, "value" is an integer. If "value" is less + * than the number of properties stored inside the object (which is also + * recorded in the Map), then it denotes which of the in-object property value + * slots (shown above inside the JSObject object) stores the value for this + * property. If "value" is greater than the number of properties stored inside + * the object, then it denotes which index into the separate "properties" array + * (a separate field in the JSObject, not shown above) contains the value for + * this property. + * + * In Node 0.12, for properties that are stored inside the object, the offset is + * obtained not using "value", but using a bitfield from the "details" part of + * the descriptor. + * + * Terminology notes: it's important to keep straight the different senses of + * "object" and "property" here. We use "JavaScript objects" to refer to the + * things that JavaScript programmers would call objects, including instances of + * Object and Array and subclasses of those. These are a subset of V8 heap + * objects, since V8 uses its heap to manage lots of other objects that + * JavaScript programmers don't think about. This function iterates JavaScript + * properties of these JavaScript objects, not internal properties of heap + * objects in general. + * + * Relatedly, while JavaScript programmers frequently interchange the notions of + * property names, property values, and property configurations (e.g., getters + * and setters, read-only or not, hidden or not), these are all distinct in the + * implementation of the VM, and "property" typically refers to the whole + * configuration, which may include a way to get the property name and value. + * + * The canonical source of the information used here is the implementation of + * property lookup in the V8 source code, currently in Object::GetProperty. + */ + static int jsobj_properties(uintptr_t addr, - int (*func)(const char *, uintptr_t, void *), void *arg) + int (*func)(const char *, uintptr_t, void *), void *arg, + jspropinfo_t *propinfop) { uintptr_t ptr, map, elements; uintptr_t *props = NULL, *descs = NULL, *content = NULL, *trans, *elts; @@ -1754,10 +2129,12 @@ jsobj_properties(uintptr_t addr, int rval = -1; size_t ps = sizeof (uintptr_t); ssize_t off; + jspropinfo_t propinfo = JPI_NONE; /* - * Objects have either "fast" properties represented with a FixedArray - * or slow properties represented with a Dictionary. + * First, check if the JSObject's "properties" field is a FixedArray. + * If not, then this is something we don't know how to deal with, and + * we'll just pass the caller a NULL value. */ if (mdb_vread(&ptr, ps, addr + V8_OFF_JSOBJECT_PROPERTIES) == -1) return (-1); @@ -1766,30 +2143,26 @@ jsobj_properties(uintptr_t addr, 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")); - + if (propinfop != NULL) + *propinfop = JPI_BADLAYOUT; 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. + * As described above, we need the Map to figure out how to iterate the + * properties for this object. */ if (mdb_vread(&map, ps, addr + V8_OFF_HEAPOBJECT_MAP) == -1) goto err; /* * Check to see if our elements member is an array and non-zero; if - * so, it contains numerically-named properties. + * so, it contains numerically-named properties. Whether or not there + * are any numerically-named properties, there may be other kinds of + * properties. */ if (V8_ELEMENTS_KIND_SHIFT != -1 && read_heap_ptr(&elements, addr, V8_OFF_JSOBJECT_ELEMENTS) == 0 && @@ -1805,6 +2178,7 @@ jsobj_properties(uintptr_t addr, kind = bit_field2 >> V8_ELEMENTS_KIND_SHIFT; kind &= (1 << V8_ELEMENTS_KIND_BITCOUNT) - 1; + propinfo |= JPI_NUMERIC; if (kind == V8_ELEMENTS_FAST_ELEMENTS || kind == V8_ELEMENTS_FAST_HOLEY_ELEMENTS) { @@ -1815,7 +2189,7 @@ jsobj_properties(uintptr_t addr, jsobj_is_hole(elts[ii])) continue; - snprintf(name, sizeof (name), "%d", ii); + snprintf(name, sizeof (name), "%" PRIdPTR, ii); if (func(name, elts[ii], arg) != 0) { mdb_free(elts, sz); @@ -1823,6 +2197,7 @@ jsobj_properties(uintptr_t addr, } } } else if (kind == V8_ELEMENTS_DICTIONARY_ELEMENTS) { + propinfo |= JPI_DICT; if (read_heap_dict(elements, func, arg) != 0) { mdb_free(elts, sz); goto err; @@ -1833,14 +2208,49 @@ jsobj_properties(uintptr_t addr, } if (V8_DICT_SHIFT != -1) { + v8_field_t *flp; uintptr_t bit_field3; - if (mdb_vread(&bit_field3, sizeof (bit_field3), - map + V8_OFF_MAP_BIT_FIELD3) == -1) - goto err; + /* + * If dictionary properties are supported (the V8_DICT_SHIFT + * offset is not -1), then bitfield 3 tells us if the properties + * for this object are stored in "properties" field of the + * object using a Dictionary representation. + * + * Versions of V8 prior to Node 0.12 treated bit_field3 as an + * SMI, so it was pointer-sized, and it has to be converted from + * an SMI before using it. In 0.12, it's treated as a raw + * uint32_t, meaning it's always int-sized and it should not be + * converted. We can tell which case we're in because the debug + * constant (v8dbg_class_map__bit_field3__TYPE) tells us whether + * the TYPE is "SMI" or "int". + */ - if (V8_SMI_VALUE(bit_field3) & (1 << V8_DICT_SHIFT)) + flp = conf_field_lookup("Map", "bit_field3"); + if (flp == NULL || flp->v8f_isbyte) { + /* + * v8f_isbyte indicates the type is "int", so we're in + * the int-sized not-a-SMI world. + */ + unsigned int bf3_value; + if (mdb_vread(&bf3_value, sizeof (bf3_value), + map + V8_OFF_MAP_BIT_FIELD3) == -1) + goto err; + bit_field3 = (uintptr_t)bf3_value; + } else { + /* The metadata indicates this is an SMI. */ + if (mdb_vread(&bit_field3, sizeof (bit_field3), + map + V8_OFF_MAP_BIT_FIELD3) == -1) + goto err; + bit_field3 = V8_SMI_VALUE(bit_field3); + } + + if (bit_field3 & (1 << V8_DICT_SHIFT)) { + propinfo |= JPI_DICT; + if (propinfop != NULL) + *propinfop = propinfo; return (read_heap_dict(ptr, func, arg)); + } } else if (V8_OFF_MAP_INSTANCE_DESCRIPTORS != -1) { uintptr_t bit_field3; @@ -1860,6 +2270,9 @@ jsobj_properties(uintptr_t addr, * dictionary -- an assumption that is assuredly in * error in some cases. */ + propinfo |= JPI_DICT; + if (propinfop != NULL) + *propinfop = propinfo; return (read_heap_dict(ptr, func, arg)); } } @@ -1867,7 +2280,12 @@ jsobj_properties(uintptr_t addr, if (read_heap_array(ptr, &props, &nprops, UM_SLEEP) != 0) goto err; - if ((off = V8_OFF_MAP_INSTANCE_DESCRIPTORS) == -1) { + /* + * Check if we're looking at an older version of V8, where the instance + * descriptors are stored not directly in the Map, but in the + * "transitions" array that's stored in the Map. + */ + if (V8_OFF_MAP_INSTANCE_DESCRIPTORS == -1) { if (V8_OFF_MAP_TRANSITIONS == -1 || V8_TRANSITIONS_IDX_DESC == -1 || V8_PROP_IDX_CONTENT != -1) { @@ -1877,47 +2295,71 @@ jsobj_properties(uintptr_t addr, goto err; } + propinfo |= JPI_HASTRANSITIONS; off = V8_OFF_MAP_TRANSITIONS; - } - - if (mdb_vread(&ptr, ps, map + off) == -1) - goto err; + if (mdb_vread(&ptr, ps, map + off) == -1) + goto err; - if (V8_OFF_MAP_INSTANCE_DESCRIPTORS == -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)); + } else { + off = V8_OFF_MAP_INSTANCE_DESCRIPTORS; + if (mdb_vread(&ptr, ps, map + off) == -1) + goto err; } + /* + * Either way, at this point "ptr" should refer to the descriptors + * array. + */ if (read_heap_array(ptr, &descs, &ndescs, UM_SLEEP) != 0) goto err; + /* + * For cases where property values are stored directly inside the object + * ("fast properties"), we need to know the whole size of the object and + * the number of properties in the object in order to calculate the + * correct offset for each property. + */ 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) + if (mdb_vread(&ninprops, ps, + map + V8_OFF_MAP_INOBJECT_PROPERTIES) == -1) 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. + * On node v0.8 and later, the content is not stored in a + * separate FixedArray, but rather with the descriptors. The + * number of actual properties is the length of the array minus + * the first (non-property) elements divided by the number of + * elements per property. */ content = descs; ncontent = ndescs; rndescs = ndescs > V8_PROP_IDX_FIRST ? (ndescs - V8_PROP_IDX_FIRST) / V8_PROP_DESC_SIZE : 0; } else { + /* + * On older versions, the content is stored in a separate array, + * and there's one entry per property (rather than three). + */ + if (V8_PROP_IDX_CONTENT < ndescs && + read_heap_array(descs[V8_PROP_IDX_CONTENT], &content, + &ncontent, UM_SLEEP) != 0) + goto err; + rndescs = ndescs - V8_PROP_IDX_FIRST; + propinfo |= JPI_HASCONTENT; } + /* + * At this point, we've read all the pieces we need to process the list + * of instance descriptors. + */ for (ii = 0; ii < rndescs; ii++) { uintptr_t keyidx, validx, detidx, baseidx; char buf[1024]; @@ -1941,48 +2383,91 @@ jsobj_properties(uintptr_t addr, detidx = baseidx + V8_PROP_DESC_DETAILS; } + /* + * Ignore cases where our understanding doesn't appear to match + * what's here. + */ if (detidx >= ncontent) { + propinfo |= JPI_SKIPPED; v8_warn("property descriptor %d: detidx (%d) " "out of bounds for content array (length %d)\n", ii, detidx, ncontent); continue; } + /* + * We only process fields. There are other entries here + * (notably: transitions) that we don't care about (and these + * are not errors). + */ if (!V8_DESC_ISFIELD(content[detidx])) continue; if (keyidx >= ndescs) { + propinfo |= JPI_SKIPPED; 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) + if (jsstr_print(descs[keyidx], JSSTR_NUDE, &c, &len) != 0) { + propinfo |= JPI_SKIPPED; continue; + } val = (intptr_t)content[validx]; - if (!V8_IS_SMI(val)) { + propinfo |= JPI_SKIPPED; v8_warn("object %p: property descriptor %d: value " - "index value is not an SMI: %p\n", addr, ii, val); + "index is not an SMI: %p\n", addr, ii, val); continue; } + /* + * The "value" part of each property descriptor tells us whether + * the property value is stored directly in the object or in the + * related "props" array. See JSObject::RawFastPropertyAt() in + * the V8 source. + */ 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) { + uintptr_t propaddr; + + /* + * The property is stored directly inside the object. + * In Node 0.10, "val - ninprops" is the (negative) + * index of the property counted from the end of the + * object. In that context, -1 refers to the last + * word in the object; -2 refers to the second-last + * word, and so on. + * + * In Node 0.12, we get the 0-based index from the + * first property inside the object by reading certain + * bits from the property descriptor details word. + * These constants are literal here because they're + * literal in the V8 source itself. + */ + if (v8_major > 3 || (v8_major == 3 && v8_minor >= 26)) { + val = V8_PROP_FIELDINDEX(content[detidx]); + propaddr = addr + V8_OFF_HEAP( + size - (ninprops - val) * ps); + } else { + propaddr = addr + V8_OFF_HEAP(size + val * ps); + } + + if (mdb_vread(&ptr, sizeof (ptr), propaddr) == -1) { + propinfo |= JPI_SKIPPED; v8_warn("object %p: failed to read in-object " - "property at %p\n", addr, addr + - V8_OFF_HEAP(size + val * - sizeof (uintptr_t))); + "property at %p", addr, propaddr); continue; } + + propinfo |= JPI_INOBJECT; } else { - /* property should be in "props" array */ + /* + * The property is in the separate "props" array. + */ if (val >= nprops) { /* * This can happen when properties are deleted. @@ -1992,12 +2477,14 @@ jsobj_properties(uintptr_t addr, if (val < rndescs) continue; + propinfo |= JPI_SKIPPED; v8_warn("object %p: property descriptor %d: " "value index value (%d) out of bounds " "(%d)\n", addr, ii, val, nprops); goto err; } + propinfo |= JPI_PROPS; ptr = props[val]; } @@ -2006,6 +2493,9 @@ jsobj_properties(uintptr_t addr, } rval = 0; + if (propinfop != NULL) + *propinfop = propinfo; + err: if (props != NULL) mdb_free(props, nprops * sizeof (uintptr_t)); @@ -2039,9 +2529,14 @@ jsfunc_lineno(uintptr_t lendsp, uintptr_t tokpos, * 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. + * we must convert it here, unless we're checking against the + * "-1" sentinel. */ - mdb_snprintf(buf, buflen, "position %d", V8_SMI_VALUE(tokpos)); + if (tokpos == V8_VALUE_SMI(-1)) + mdb_snprintf(buf, buflen, "unknown position"); + else + mdb_snprintf(buf, buflen, "position %d", + V8_SMI_VALUE(tokpos)); if (lineno != NULL) *lineno = 0; @@ -2167,7 +2662,7 @@ jsfunc_lines(uintptr_t scriptp, if (startline == -1 || endline == -1) { mdb_warn("for script %p, could not determine startline/endline" - " (start %ld, end %ld, nlines %d)", + " (start %ld, end %ld, nlines %d)\n", scriptp, start, end, nlines); mdb_free(buf, bufsz); return; @@ -2256,25 +2751,6 @@ jsfunc_name(uintptr_t funcinfop, char **bufp, size_t *lenp) /* * 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; - boolean_t jsop_descended; -} 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) @@ -2377,7 +2853,7 @@ jsobj_print_prop(const char *desc, uintptr_t val, void *arg) char **bufp = jsop->jsop_bufp; size_t *lenp = jsop->jsop_lenp; - (void) bsnprintf(bufp, lenp, "%s\n%*s%s: ", jsop->jsop_nprops == 0 ? + (void) bsnprintf(bufp, lenp, "%s\n%*s\"%s\": ", jsop->jsop_nprops == 0 ? "{" : "", jsop->jsop_indent + 4, "", desc); descend = *jsop; @@ -2438,7 +2914,8 @@ jsobj_print_jsobject(uintptr_t addr, jsobj_print_t *jsop) size_t *lenp = jsop->jsop_lenp; if (jsop->jsop_member != NULL) - return (jsobj_properties(addr, jsobj_print_prop_member, jsop)); + return (jsobj_properties(addr, jsobj_print_prop_member, + jsop, &jsop->jsop_propinfo)); if (jsop->jsop_depth == 0) { (void) bsnprintf(bufp, lenp, "[...]"); @@ -2447,7 +2924,8 @@ jsobj_print_jsobject(uintptr_t addr, jsobj_print_t *jsop) jsop->jsop_nprops = 0; - if (jsobj_properties(addr, jsobj_print_prop, jsop) != 0) + if (jsobj_properties(addr, jsobj_print_prop, jsop, + &jsop->jsop_propinfo) != 0) return (-1); if (jsop->jsop_nprops > 0) { @@ -2760,7 +3238,7 @@ dcmd_v8function(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) } if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0 || - read_heap_ptr(&tokpos, funcinfop, + read_heap_maybesmi(&tokpos, funcinfop, V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0 || read_heap_ptr(&scriptp, funcinfop, V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 || @@ -2768,6 +3246,13 @@ dcmd_v8function(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0) goto err; + /* + * The token position is normally a SMI, so read_heap_maybesmi() will + * interpret the value for us. However, this code uses its SMI-encoded + * value, so convert it back here. + */ + tokpos = V8_VALUE_SMI(tokpos); + bufp = buf; len = sizeof (buf); if (jsfunc_name(funcinfop, &bufp, &len) != 0) @@ -2800,6 +3285,28 @@ err: return (DCMD_ERR); } +/* + * Access an internal field of a V8 object. + */ +/* ARGSUSED */ +static int +dcmd_v8internal(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + uintptr_t idx; + uintptr_t fieldaddr; + + if (mdb_getopts(argc, argv, NULL) != argc - 1 || + argv[argc - 1].a_type != MDB_TYPE_STRING) + return (DCMD_USAGE); + + idx = mdb_strtoull(argv[argc - 1].a_un.a_str); + if (obj_v8internal(addr, idx, &fieldaddr) != 0) + return (DCMD_ERR); + + mdb_printf("%p\n", fieldaddr); + return (DCMD_OK); +} + /* ARGSUSED */ static int dcmd_v8frametypes(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) @@ -2921,11 +3428,39 @@ load_current_context(uintptr_t *fpp, uintptr_t *raddrp) return (0); } +typedef struct jsframe { + boolean_t jsf_showall; /* show hidden frames and pointers */ + boolean_t jsf_verbose; /* show arguments and JS code */ + char *jsf_func; /* filter frames for named function */ + char *jsf_prop; /* filter arguments */ + uintptr_t jsf_nlines; /* lines of context (for verbose) */ + uint_t jsf_nskipped; /* skipped frames */ +} jsframe_t; + +static void +jsframe_skip(jsframe_t *jsf) +{ + jsf->jsf_nskipped++; +} + +static void +jsframe_print_skipped(jsframe_t *jsf) +{ + if (jsf->jsf_nskipped == 1) + mdb_printf(" (1 internal frame elided)\n"); + else if (jsf->jsf_nskipped > 1) + mdb_printf(" (%d internal frames elided)\n", + jsf->jsf_nskipped); + jsf->jsf_nskipped = 0; +} + static int -do_jsframe_special(uintptr_t fptr, uintptr_t raddr, char *prop) +do_jsframe_special(uintptr_t fptr, uintptr_t raddr, jsframe_t *jsf) { + uint_t count; uintptr_t ftype; const char *ftypename; + char *prop = jsf->jsf_prop; /* * First see if this looks like a native frame rather than a JavaScript @@ -2933,11 +3468,21 @@ do_jsframe_special(uintptr_t fptr, uintptr_t raddr, char *prop) * 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) { + count = mdb_snprintf(NULL, 0, "%A", raddr); + if (count > 1) { if (prop != NULL) return (0); - mdb_printf("%p %a\n", fptr, raddr); + jsframe_print_skipped(jsf); + if (jsf->jsf_showall) { + mdb_printf("%p %a\n", fptr, raddr); + } else if (count <= 65) { + mdb_printf("native: %a\n", raddr); + } else { + char buf[65]; + mdb_snprintf(buf, sizeof (buf), "%a", raddr); + mdb_printf("native: %s...\n", buf); + } return (0); } @@ -2952,7 +3497,12 @@ do_jsframe_special(uintptr_t fptr, uintptr_t raddr, char *prop) if (prop != NULL) return (0); - mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename); + if (jsf->jsf_showall) { + jsframe_print_skipped(jsf); + mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename); + } else { + jsframe_skip(jsf); + } return (0); } @@ -2967,10 +3517,12 @@ do_jsframe_special(uintptr_t fptr, uintptr_t raddr, char *prop) ftypename = enum_lookup_str(v8_frametypes, V8_SMI_VALUE(ftype), NULL); - if (ftypename != NULL) + if (jsf->jsf_showall && ftypename != NULL) { + jsframe_print_skipped(jsf); mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename); - else - mdb_printf("%p %a\n", fptr, raddr); + } else { + jsframe_skip(jsf); + } return (0); } @@ -2979,9 +3531,14 @@ do_jsframe_special(uintptr_t fptr, uintptr_t raddr, char *prop) } static int -do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose, - char *func, char *prop, uintptr_t nlines) +do_jsframe(uintptr_t fptr, uintptr_t raddr, jsframe_t *jsf) { + boolean_t showall = jsf->jsf_showall; + boolean_t verbose = jsf->jsf_verbose; + const char *func = jsf->jsf_func; + const char *prop = jsf->jsf_prop; + uintptr_t nlines = jsf->jsf_nlines; + uintptr_t funcp, funcinfop, tokpos, endpos, scriptp, lendsp, ptrp; uintptr_t ii, nargs; const char *typename; @@ -2994,7 +3551,7 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose, /* * Check for non-JavaScript frames first. */ - if (func == NULL && do_jsframe_special(fptr, raddr, prop) == 0) + if (func == NULL && do_jsframe_special(fptr, raddr, jsf) == 0) return (DCMD_OK); /* @@ -3017,7 +3574,12 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose, if (func != NULL || prop != NULL) return (DCMD_OK); - mdb_printf("%p %a\n", fptr, raddr); + if (showall) { + jsframe_print_skipped(jsf); + mdb_printf("%p %a\n", fptr, raddr); + } else { + jsframe_skip(jsf); + } return (DCMD_OK); } @@ -3025,7 +3587,13 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose, if (func != NULL || prop != NULL) return (DCMD_OK); - mdb_printf("%p %a internal (Code: %p)\n", fptr, raddr, funcp); + if (showall) { + jsframe_print_skipped(jsf); + mdb_printf("%p %a internal (Code: %p)\n", + fptr, raddr, funcp); + } else { + jsframe_skip(jsf); + } return (DCMD_OK); } @@ -3033,8 +3601,13 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose, if (func != NULL || prop != NULL) return (DCMD_OK); - mdb_printf("%p %a unknown (%s: %p)", fptr, raddr, typename, - funcp); + if (showall) { + jsframe_print_skipped(jsf); + mdb_printf("%p %a unknown (%s: %p)", + fptr, raddr, typename, funcp); + } else { + jsframe_skip(jsf); + } return (DCMD_OK); } @@ -3049,19 +3622,33 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose, 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 (prop == NULL) { + jsframe_print_skipped(jsf); + if (showall) + mdb_printf("%p %a ", fptr, raddr); + else + mdb_printf("js: "); + mdb_printf("%s", buf); + if (showall) + mdb_printf(" (JSFunction: %p)\n", funcp); + else + mdb_printf("\n"); + } if (!verbose && prop == NULL) return (DCMD_OK); + if (verbose) + jsframe_print_skipped(jsf); + /* * 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, + if (read_heap_maybesmi(&tokpos, funcinfop, V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0) return (DCMD_ERR); + tokpos = V8_VALUE_SMI(tokpos); if (read_heap_ptr(&scriptp, funcinfop, V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0) @@ -3080,25 +3667,54 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose, } if (prop == NULL) { - (void) mdb_inc_indent(4); + (void) mdb_inc_indent(10); mdb_printf("file: %s\n", buf); } if (read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0) return (DCMD_ERR); - if (read_heap_smi(&nargs, funcinfop, + (void) jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf), &lineno); + + 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_maybesmi(&nargs, funcinfop, V8_OFF_SHAREDFUNCTIONINFO_LENGTH) == 0) { - for (ii = 0; ii < nargs; ii++) { - uintptr_t argptr; - char arg[10]; + uintptr_t argptr; + char arg[10]; + + if (mdb_vread(&argptr, sizeof (argptr), + fptr + V8_OFF_FP_ARGS + nargs * sizeof (uintptr_t)) != -1 && + argptr != NULL) { + (void) snprintf(arg, sizeof (arg), "this"); + if (prop != NULL && strcmp(arg, prop) == 0) { + mdb_printf("%p\n", argptr); + return (DCMD_OK); + } + + if (prop == NULL) { + bufp = buf; + len = sizeof (buf); + (void) obj_jstype(argptr, &bufp, &len, NULL); + mdb_printf("this: %p (%s)\n", argptr, buf); + } + } + + for (ii = 0; ii < nargs; ii++) { 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); + (void) snprintf(arg, sizeof (arg), "arg%" PRIuPTR, + ii + 1); if (prop != NULL) { if (strcmp(arg, prop) != 0) @@ -3116,28 +3732,20 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose, } } - (void) jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf), &lineno); if (prop != NULL) { - if (strcmp(prop, "posn") == 0) { - mdb_printf("%s\n", buf); - return (DCMD_OK); - } - mdb_warn("unknown frame property '%s'\n", prop); return (DCMD_ERR); } - mdb_printf("posn: %s", buf); - - if (nlines != 0 && read_heap_smi(&endpos, funcinfop, + if (nlines != 0 && read_heap_maybesmi(&endpos, funcinfop, V8_OFF_SHAREDFUNCTIONINFO_END_POSITION) == 0) { jsfunc_lines(scriptp, V8_SMI_VALUE(tokpos), endpos, nlines, "%5d "); + mdb_printf("\n"); } - mdb_printf("\n"); - (void) mdb_dec_indent(4); + (void) mdb_dec_indent(10); return (DCMD_OK); } @@ -3155,6 +3763,7 @@ typedef struct findjsobjects_instance { typedef struct findjsobjects_obj { findjsobjects_prop_t *fjso_props; findjsobjects_prop_t *fjso_last; + jspropinfo_t fjso_propinfo; size_t fjso_nprops; findjsobjects_instance_t fjso_instances; int fjso_ninstances; @@ -3164,6 +3773,17 @@ typedef struct findjsobjects_obj { char fjso_constructor[80]; } findjsobjects_obj_t; +typedef struct findjsobjects_func { + findjsobjects_instance_t fjsf_instances; + int fjsf_ninstances; + avl_node_t fjsf_node; + struct findjsobjects_func *fjsf_next; + uintptr_t fjsf_shared; + char fjsf_funcname[40]; + char fjsf_scriptname[80]; + char fjsf_location[20]; +} findjsobjects_func_t; + typedef struct findjsobjects_stats { int fjss_heapobjs; int fjss_cached; @@ -3172,6 +3792,9 @@ typedef struct findjsobjects_stats { int fjss_objects; int fjss_arrays; int fjss_uniques; + int fjss_funcs; + int fjss_funcs_skipped; + int fjss_funcs_unique; } findjsobjects_stats_t; typedef struct findjsobjects_reference { @@ -3200,10 +3823,12 @@ typedef struct findjsobjects_state { boolean_t fjs_referred; avl_tree_t fjs_tree; avl_tree_t fjs_referents; + avl_tree_t fjs_funcinfo; findjsobjects_referent_t *fjs_head; findjsobjects_referent_t *fjs_tail; findjsobjects_obj_t *fjs_current; findjsobjects_obj_t *fjs_objects; + findjsobjects_func_t *fjs_funcs; findjsobjects_stats_t fjs_stats; } findjsobjects_state_t; @@ -3268,6 +3893,14 @@ findjsobjects_cmp(findjsobjects_obj_t *lhs, findjsobjects_obj_t *rhs) } int +findjsobjects_cmp_funcinfo(findjsobjects_func_t *lhs, + findjsobjects_func_t *rhs) +{ + int diff = lhs->fjsf_shared - rhs->fjsf_shared; + return (diff < 0 ? -1 : diff > 0 ? 1 : 0); +} + +int findjsobjects_cmp_referents(findjsobjects_referent_t *lhs, findjsobjects_referent_t *rhs) { @@ -3369,6 +4002,71 @@ out: v8_silent--; } +static void +findjsobjects_jsfunc(findjsobjects_state_t *fjs, uintptr_t addr) +{ + findjsobjects_func_t *func, *ofunc; + findjsobjects_instance_t *inst; + uintptr_t funcinfo, script, name; + avl_index_t where; + int err; + char *bufp; + size_t len; + + /* + * This may be somewhat expensive to do for all JSFunctions, but in most + * core files, there aren't that many. We could defer some of this work + * until the user tries to print the function ::jsfunctions, but this + * step is useful to do early to filter out garbage data. + */ + + v8_silent++; + if (read_heap_ptr(&funcinfo, addr, V8_OFF_JSFUNCTION_SHARED) != 0 || + read_heap_ptr(&script, funcinfo, + V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 || + read_heap_ptr(&name, script, V8_OFF_SCRIPT_NAME) != 0) { + fjs->fjs_stats.fjss_funcs_skipped++; + v8_silent--; + return; + } + + func = mdb_zalloc(sizeof (findjsobjects_func_t), UM_SLEEP); + func->fjsf_ninstances = 1; + func->fjsf_instances.fjsi_addr = addr; + func->fjsf_shared = funcinfo; + + bufp = func->fjsf_funcname; + len = sizeof (func->fjsf_funcname); + err = jsfunc_name(funcinfo, &bufp, &len); + + bufp = func->fjsf_scriptname; + len = sizeof (func->fjsf_scriptname); + err |= jsstr_print(name, JSSTR_NUDE, &bufp, &len); + + v8_silent--; + if (err != 0) { + fjs->fjs_stats.fjss_funcs_skipped++; + mdb_free(func, sizeof (findjsobjects_func_t)); + return; + } + + fjs->fjs_stats.fjss_funcs++; + ofunc = avl_find(&fjs->fjs_funcinfo, func, &where); + if (ofunc == NULL) { + avl_add(&fjs->fjs_funcinfo, func); + func->fjsf_next = fjs->fjs_funcs; + fjs->fjs_funcs = func; + fjs->fjs_stats.fjss_funcs_unique++; + } else { + inst = mdb_alloc(sizeof (findjsobjects_instance_t), UM_SLEEP); + inst->fjsi_addr = addr; + inst->fjsi_next = ofunc->fjsf_instances.fjsi_next; + ofunc->fjsf_instances.fjsi_next = inst; + ofunc->fjsf_ninstances++; + mdb_free(func, sizeof (findjsobjects_func_t)); + } +} + int findjsobjects_range(findjsobjects_state_t *fjs, uintptr_t addr, uintptr_t size) { @@ -3376,6 +4074,7 @@ findjsobjects_range(findjsobjects_state_t *fjs, uintptr_t addr, uintptr_t size) findjsobjects_stats_t *stats = &fjs->fjs_stats; uint8_t type; int jsobject = V8_TYPE_JSOBJECT, jsarray = V8_TYPE_JSARRAY; + int jsfunction = V8_TYPE_JSFUNCTION; caddr_t range = mdb_alloc(size, UM_SLEEP); uintptr_t base = addr, mapaddr; @@ -3414,6 +4113,11 @@ findjsobjects_range(findjsobjects_state_t *fjs, uintptr_t addr, uintptr_t size) continue; } + if (type == jsfunction) { + findjsobjects_jsfunc(fjs, addr); + continue; + } + if (type != jsobject && type != jsarray) continue; @@ -3423,7 +4127,8 @@ findjsobjects_range(findjsobjects_state_t *fjs, uintptr_t addr, uintptr_t size) if (type == jsobject) { if (jsobj_properties(addr, - findjsobjects_prop, fjs) != 0) { + findjsobjects_prop, fjs, + &fjs->fjs_current->fjso_propinfo) != 0) { findjsobjects_free(fjs->fjs_current); fjs->fjs_current = NULL; continue; @@ -3625,7 +4330,7 @@ findjsobjects_references(findjsobjects_state_t *fjs) fjs->fjs_addr = inst->fjsi_addr; (void) jsobj_properties(inst->fjsi_addr, - findjsobjects_references_prop, fjs); + findjsobjects_references_prop, fjs, NULL); } } @@ -3716,6 +4421,26 @@ findjsobjects_match_constructor(findjsobjects_obj_t *obj, mdb_printf("%p\n", obj->fjso_instances.fjsi_addr); } +static void +findjsobjects_match_kind(findjsobjects_obj_t *obj, const char *propkind) +{ + jspropinfo_t p = obj->fjso_propinfo; + + if (((p & JPI_NUMERIC) != 0 && strstr(propkind, "numeric") != NULL) || + ((p & JPI_DICT) != 0 && strstr(propkind, "dict") != NULL) || + ((p & JPI_INOBJECT) != 0 && strstr(propkind, "inobject") != NULL) || + ((p & JPI_PROPS) != 0 && strstr(propkind, "props") != NULL) || + ((p & JPI_HASTRANSITIONS) != 0 && + strstr(propkind, "transitions") != NULL) || + ((p & JPI_HASCONTENT) != 0 && + strstr(propkind, "content") != NULL) || + ((p & JPI_SKIPPED) != 0 && strstr(propkind, "skipped") != NULL) || + ((p & JPI_BADLAYOUT) != 0 && + strstr(propkind, "badlayout") != NULL)) { + 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 *), @@ -3823,84 +4548,71 @@ dcmd_findjsobjects_help(void) " -v Provide verbose statistics\n"); } +static findjsobjects_state_t findjsobjects_state; + static int -dcmd_findjsobjects(uintptr_t addr, - uint_t flags, int argc, const mdb_arg_t *argv) +findjsobjects_run(findjsobjects_state_t *fjs) { - 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); + findjsobjects_obj_t *obj; + findjsobjects_stats_t *stats = &fjs->fjs_stats; - if (!fjs.fjs_initialized) { - avl_create(&fjs.fjs_tree, + 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, + 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; + avl_create(&fjs->fjs_funcinfo, + (int(*)(const void *, const void*)) + findjsobjects_cmp_funcinfo, + sizeof (findjsobjects_func_t), + offsetof(findjsobjects_func_t, fjsf_node)); + + fjs->fjs_initialized = B_TRUE; } - if (avl_is_empty(&fjs.fjs_tree)) { + 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); + return (-1); } v8_silent++; if (Pmapping_iter(Pr, - (proc_map_f *)findjsobjects_mapping, &fjs) != 0) { + (proc_map_f *)findjsobjects_mapping, fjs) != 0) { v8_silent--; - return (DCMD_ERR); + return (-1); } - if ((nobjs = avl_numnodes(&fjs.fjs_tree)) != 0) { + 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; + for (obj = fjs->fjs_objects, i = 0; obj != NULL; obj = obj->fjso_next, i++) { sorted[i] = obj; } - qsort(sorted, avl_numnodes(&fjs.fjs_tree), + qsort(sorted, avl_numnodes(&fjs->fjs_tree), sizeof (void *), findjsobjects_cmp_ninstances); - for (i = 1, fjs.fjs_objects = sorted[0]; i < nobjs; i++) + for (i = 1, fjs->fjs_objects = sorted[0]; + i < nobjs; i++) sorted[i - 1]->fjso_next = sorted[i]; sorted[nobjs - 1]->fjso_next = NULL; @@ -3908,7 +4620,7 @@ dcmd_findjsobjects(uintptr_t addr, v8_silent--; - if (fjs.fjs_verbose) { + if (fjs->fjs_verbose) { const char *f = "findjsobjects: %30s => %d\n"; int elapsed = (int)((gethrtime() - start) / NANOSEC); @@ -3920,12 +4632,54 @@ dcmd_findjsobjects(uintptr_t addr, mdb_printf(f, "processed objects", stats->fjss_objects); mdb_printf(f, "processed arrays", stats->fjss_arrays); mdb_printf(f, "unique objects", stats->fjss_uniques); + mdb_printf(f, "functions found", stats->fjss_funcs); + mdb_printf(f, "unique functions", + stats->fjss_funcs_unique); + mdb_printf(f, "functions skipped", + stats->fjss_funcs_skipped); } } + return (0); +} + +static int +dcmd_findjsobjects(uintptr_t addr, + uint_t flags, int argc, const mdb_arg_t *argv) +{ + findjsobjects_state_t *fjs = &findjsobjects_state; + findjsobjects_obj_t *obj; + boolean_t references = B_FALSE, listlike = B_FALSE; + const char *propname = NULL; + const char *constructor = NULL; + const char *propkind = 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, + 'k', MDB_OPT_STR, &propkind, + '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 (findjsobjects_run(fjs) != 0) + return (DCMD_ERR); + if (listlike && !(flags & DCMD_ADDRSPEC)) { - if (propname != NULL || constructor != NULL) { - char opt = propname != NULL ? 'p' : 'c'; + if (propname != NULL || constructor != NULL || + propkind != NULL) { + char opt = propname != NULL ? 'p' : + propkind != NULL ? 'k' :'c'; mdb_warn("cannot specify -l with -%c; instead, pipe " "output of ::findjsobjects -%c to " @@ -3933,38 +4687,50 @@ dcmd_findjsobjects(uintptr_t addr, return (DCMD_ERR); } - return (findjsobjects_match(&fjs, addr, flags, + return (findjsobjects_match(fjs, addr, flags, findjsobjects_match_all, NULL)); } if (propname != NULL) { - if (constructor != NULL) { + if (constructor != NULL || propkind != NULL) { mdb_warn("cannot specify both a property name " - "and a constructor\n"); + "and a %s\n", constructor != NULL ? + "constructor" : "property kind"); return (DCMD_ERR); } - return (findjsobjects_match(&fjs, addr, flags, + return (findjsobjects_match(fjs, addr, flags, findjsobjects_match_propname, propname)); } if (constructor != NULL) { - return (findjsobjects_match(&fjs, addr, flags, + if (propkind != NULL) { + mdb_warn("cannot specify both a constructor name " + "and a property kind\n"); + return (DCMD_ERR); + } + + return (findjsobjects_match(fjs, addr, flags, findjsobjects_match_constructor, constructor)); } + if (propkind != NULL) { + return (findjsobjects_match(fjs, addr, flags, + findjsobjects_match_kind, propkind)); + } + if (references && !(flags & DCMD_ADDRSPEC) && - avl_is_empty(&fjs.fjs_referents)) { + 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)) { + if (fjs->fjs_marking && !(flags & DCMD_ADDRSPEC)) { mdb_warn("must specify an object to mark\n"); return (DCMD_ERR); } - if (references && fjs.fjs_marking) { + if (references && fjs->fjs_marking) { mdb_warn("can't both mark an object and find its references\n"); return (DCMD_ERR); } @@ -3978,14 +4744,14 @@ dcmd_findjsobjects(uintptr_t addr, * specified/marked objects (-r). (Note that the absence of * any of these options implies -l.) */ - inst = findjsobjects_instance(&fjs, addr, &head); + 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) { + if (!references && !fjs->fjs_marking) { for (inst = head; inst != NULL; inst = inst->fjsi_next) mdb_printf("%p\n", inst->fjsi_addr); @@ -3993,24 +4759,24 @@ dcmd_findjsobjects(uintptr_t addr, } if (!listlike) { - findjsobjects_referent(&fjs, inst->fjsi_addr); + findjsobjects_referent(fjs, inst->fjsi_addr); } else { for (inst = head; inst != NULL; inst = inst->fjsi_next) - findjsobjects_referent(&fjs, inst->fjsi_addr); + findjsobjects_referent(fjs, inst->fjsi_addr); } } if (references) - findjsobjects_references(&fjs); + findjsobjects_references(fjs); - if (references || fjs.fjs_marking) + 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) + for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) { + if (obj->fjso_malformed && !fjs->fjs_allobjs) continue; findjsobjects_print(obj); @@ -4019,21 +4785,90 @@ dcmd_findjsobjects(uintptr_t addr, return (DCMD_OK); } +/* + * Given a Node Buffer object, print out details about it. With "-a", just + * print the address. + */ +/* ARGSUSED */ +static int +dcmd_nodebuffer(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + boolean_t opt_f = B_FALSE; + char buf[80]; + char *bufp = buf; + size_t len = sizeof (buf); + uintptr_t elts, rawbuf; + + /* + * The undocumented "-f" option allows users to override constructor + * checks. + */ + if (mdb_getopts(argc, argv, + 'f', MDB_OPT_SETBITS, B_TRUE, &opt_f, NULL) != argc) + return (DCMD_USAGE); + + if (!opt_f) { + if (obj_jsconstructor(addr, &bufp, &len, B_FALSE) != 0) + return (DCMD_ERR); + + if (strcmp(buf, "Buffer") != 0) { + mdb_warn("%p does not appear to be a buffer\n", addr); + return (DCMD_ERR); + } + } + + if (read_heap_ptr(&elts, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0) + return (DCMD_ERR); + + if (obj_v8internal(elts, 0, &rawbuf) != 0) + return (DCMD_ERR); + + mdb_printf("%p\n", rawbuf); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_jsconstructor(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + boolean_t opt_v = B_FALSE; + char buf[80]; + char *bufp; + size_t len = sizeof (buf); + + if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v, + NULL) != argc) + return (DCMD_USAGE); + + bufp = buf; + if (obj_jsconstructor(addr, &bufp, &len, opt_v)) + return (DCMD_ERR); + + mdb_printf("%s\n", buf); + 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; - uintptr_t opt_n = 5; + boolean_t opt_i = B_FALSE; + jsframe_t jsf; + int rv; + + bzero(&jsf, sizeof (jsf)); + jsf.jsf_nlines = 5; if (mdb_getopts(argc, argv, - 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v, + 'a', MDB_OPT_SETBITS, B_TRUE, &jsf.jsf_showall, + 'v', MDB_OPT_SETBITS, B_TRUE, &jsf.jsf_verbose, 'i', MDB_OPT_SETBITS, B_TRUE, &opt_i, - 'f', MDB_OPT_STR, &opt_f, - 'n', MDB_OPT_UINTPTR, &opt_n, - 'p', MDB_OPT_STR, &opt_p, NULL) != argc) + 'f', MDB_OPT_STR, &jsf.jsf_func, + 'n', MDB_OPT_UINTPTR, &jsf.jsf_nlines, + 'p', MDB_OPT_STR, &jsf.jsf_prop, NULL) != argc) return (DCMD_USAGE); /* @@ -4043,8 +4878,12 @@ dcmd_jsframe(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) * 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, opt_n)); + if (opt_i) { + rv = do_jsframe(addr, 0, &jsf); + if (rv == 0) + jsframe_print_skipped(&jsf); + return (rv); + } if (mdb_vread(&raddr, sizeof (raddr), addr + sizeof (uintptr_t)) == -1) { @@ -4061,7 +4900,43 @@ dcmd_jsframe(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) if (fptr == NULL) return (DCMD_OK); - return (do_jsframe(fptr, raddr, opt_v, opt_f, opt_p, opt_n)); + rv = do_jsframe(fptr, raddr, &jsf); + if (rv == 0) + jsframe_print_skipped(&jsf); + return (rv); +} + +static void +jsobj_print_propinfo(jspropinfo_t propinfo) +{ + if (propinfo == JPI_NONE) + return; + + mdb_printf("property kind: "); + if ((propinfo & JPI_NUMERIC) != 0) + mdb_printf("numeric-named "); + if ((propinfo & JPI_DICT) != 0) + mdb_printf("dictionary "); + if ((propinfo & JPI_INOBJECT) != 0) + mdb_printf("in-object "); + if ((propinfo & JPI_PROPS) != 0) + mdb_printf("\"properties\" array "); + mdb_printf("\n"); + + if ((propinfo & (JPI_HASTRANSITIONS | JPI_HASCONTENT)) != 0) { + mdb_printf("fallbacks: "); + if ((propinfo & JPI_HASTRANSITIONS) != 0) + mdb_printf("transitions "); + if ((propinfo & JPI_HASCONTENT) != 0) + mdb_printf("content "); + mdb_printf("\n"); + } + + if ((propinfo & JPI_SKIPPED) != 0) + mdb_printf( + "some properties skipped due to unexpected layout\n"); + if ((propinfo & JPI_BADLAYOUT) != 0) + mdb_printf("object has unexpected layout\n"); } /* ARGSUSED */ @@ -4072,6 +4947,7 @@ dcmd_jsprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) size_t bufsz = 262144, len = bufsz; jsobj_print_t jsop; boolean_t opt_b = B_FALSE; + boolean_t opt_v = B_FALSE; int rv, i; bzero(&jsop, sizeof (jsop)); @@ -4081,7 +4957,8 @@ dcmd_jsprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 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); + 'd', MDB_OPT_UINT64, &jsop.jsop_depth, + 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v, NULL); if (opt_b) jsop.jsop_baseaddr = addr; @@ -4139,9 +5016,209 @@ dcmd_jsprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) mdb_printf("\n"); + if (opt_v) + jsobj_print_propinfo(jsop.jsop_propinfo); + + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_jssource(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + const char *typename; + uintptr_t nlines = 5; + uintptr_t funcinfop, scriptp, funcnamep; + uintptr_t tokpos, endpos; + uint8_t type; + char buf[256]; + char *bufp = buf; + size_t len = sizeof (buf); + + if (mdb_getopts(argc, argv, 'n', MDB_OPT_UINTPTR, &nlines, + NULL) != argc) + return (DCMD_USAGE); + + if (!V8_IS_HEAPOBJECT(addr) || read_typebyte(&type, addr) != 0) { + mdb_warn("%p is not a heap object\n", addr); + return (DCMD_ERR); + } + + typename = enum_lookup_str(v8_types, type, ""); + if (strcmp(typename, "JSFunction") != 0) { + mdb_warn("%p is not a JSFunction\n", addr); + return (DCMD_ERR); + } + + if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0 || + read_heap_ptr(&scriptp, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 || + read_heap_ptr(&funcnamep, scriptp, V8_OFF_SCRIPT_NAME) != 0) { + mdb_warn("%p: failed to find script for function\n", addr); + return (DCMD_ERR); + } + + if (read_heap_maybesmi(&tokpos, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0 || + read_heap_maybesmi(&endpos, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_END_POSITION) != 0) { + mdb_warn("%p: failed to find function's boundaries\n", addr); + } + + if (jsstr_print(funcnamep, JSSTR_NUDE, &bufp, &len) == 0) + mdb_printf("file: %s\n", buf); + + if (tokpos != endpos) + jsfunc_lines(scriptp, tokpos, endpos, nlines, "%5d "); + mdb_printf("\n"); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_jsfunctions(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + findjsobjects_state_t *fjs = &findjsobjects_state; + findjsobjects_func_t *func; + uintptr_t funcinfo; + boolean_t showrange = B_FALSE; + const char *name = NULL, *filename = NULL; + uintptr_t instr = 0; + + if (mdb_getopts(argc, argv, + 'x', MDB_OPT_UINTPTR, &instr, + 'X', MDB_OPT_SETBITS, B_TRUE, &showrange, + 'n', MDB_OPT_STR, &name, + 's', MDB_OPT_STR, &filename, + NULL) != argc) + return (DCMD_USAGE); + + if (findjsobjects_run(fjs) != 0) + return (DCMD_ERR); + + if (!showrange) + mdb_printf("%?s %8s %-40s %s\n", "FUNC", "#FUNCS", "NAME", + "FROM"); + else + mdb_printf("%?s %8s %?s %?s %-40s %s\n", "FUNC", "#FUNCS", + "START", "END", "NAME", "FROM"); + + for (func = fjs->fjs_funcs; func != NULL; func = func->fjsf_next) { + uintptr_t code, ilen; + + funcinfo = func->fjsf_shared; + + if (func->fjsf_location[0] == '\0') { + uintptr_t tokpos, script, lends; + ptrdiff_t tokposoff = + V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION; + + /* + * We don't want to actually decode the token position + * as an SMI here, so we re-encode it when we pass it to + * jsfunc_lineno() below. + */ + if (read_heap_maybesmi(&tokpos, funcinfo, + tokposoff) != 0 || + read_heap_ptr(&script, funcinfo, + V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 || + read_heap_ptr(&lends, script, + V8_OFF_SCRIPT_LINE_ENDS) != 0 || + jsfunc_lineno(lends, V8_VALUE_SMI(tokpos), + func->fjsf_location, + sizeof (func->fjsf_location), NULL) != 0) { + func->fjsf_location[0] = '\0'; + } + } + + if (name != NULL && strstr(func->fjsf_funcname, name) == NULL) + continue; + + if (filename != NULL && + strstr(func->fjsf_scriptname, filename) == NULL) + continue; + + code = 0; + ilen = 0; + if ((showrange || instr != 0) && + (read_heap_ptr(&code, funcinfo, + V8_OFF_SHAREDFUNCTIONINFO_CODE) != 0 || + read_heap_ptr(&ilen, code, + V8_OFF_CODE_INSTRUCTION_SIZE) != 0)) { + code = 0; + ilen = 0; + } + + if ((instr != 0 && ilen != 0) && + (instr < code + V8_OFF_CODE_INSTRUCTION_START || + instr >= code + V8_OFF_CODE_INSTRUCTION_START + ilen)) + continue; + + if (!showrange) { + mdb_printf("%?p %8d %-40s %s %s\n", + func->fjsf_instances.fjsi_addr, + func->fjsf_ninstances, func->fjsf_funcname, + func->fjsf_scriptname, func->fjsf_location); + } else { + uintptr_t code, ilen; + + if (read_heap_ptr(&code, funcinfo, + V8_OFF_SHAREDFUNCTIONINFO_CODE) != 0 || + read_heap_ptr(&ilen, code, + V8_OFF_CODE_INSTRUCTION_SIZE) != 0) { + mdb_printf("%?p %8d %?s %?s %-40s %s %s\n", + func->fjsf_instances.fjsi_addr, + func->fjsf_ninstances, "?", "?", + func->fjsf_funcname, func->fjsf_scriptname, + func->fjsf_location); + } else { + mdb_printf("%?p %8d %?p %?p %-40s %s %s\n", + func->fjsf_instances.fjsi_addr, + func->fjsf_ninstances, + code + V8_OFF_CODE_INSTRUCTION_START, + code + V8_OFF_CODE_INSTRUCTION_START + ilen, + func->fjsf_funcname, func->fjsf_scriptname, + func->fjsf_location); + } + } + } + return (DCMD_OK); } +static void +dcmd_jsfunctions_help(void) +{ + mdb_printf("%s\n\n", +"Lists JavaScript functions, optionally filtered by a substring of the\n" +"function name or script filename or by the instruction address. This uses\n" +"the cache created by ::findjsobjects. If ::findjsobjects has not already\n" +"been run, this command runs it automatically without printing the output.\n" +"This can take anywhere from a second to several minutes, depending on the\n" +"size of the core dump.\n" +"\n" +"It's important to keep in mind that each time you create a function in\n" +"JavaScript (even from a function definition that has already been used),\n" +"the VM must create a new object to represent it. For example, if your\n" +"program has a function A that returns a closure B, the VM will create new\n" +"instances of the closure function (B) each time the surrounding function (A)\n" +"is called. To show this, the output of this command consists of one line \n" +"per function definition that appears in the JavaScript source, and the\n" +"\"#FUNCS\" column shows how many different functions were created by VM from\n" +"this definition."); + + mdb_dec_indent(2); + mdb_printf("%<b>OPTIONS%</b>\n"); + mdb_inc_indent(2); + + mdb_printf("%s\n", +" -f file List functions that were defined in a file whose name contains\n" +" this substring.\n" +" -n func List functions whose name contains this substring\n" +" -x instr List functions whose compiled instructions include this address\n" +" -X Show where the function's instructions are stored in memory\n"); +} + /* ARGSUSED */ static int dcmd_v8field(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) @@ -4234,15 +5311,18 @@ dcmd_v8array(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) static int dcmd_jsstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { - uintptr_t raddr, opt_n = 5; - boolean_t opt_v = B_FALSE; - char *opt_f = NULL, *opt_p = NULL; + uintptr_t raddr; + jsframe_t jsf; + + bzero(&jsf, sizeof (jsf)); + jsf.jsf_nlines = 5; if (mdb_getopts(argc, argv, - 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v, - 'f', MDB_OPT_STR, &opt_f, - 'n', MDB_OPT_UINTPTR, &opt_n, - 'p', MDB_OPT_STR, &opt_p, + 'a', MDB_OPT_SETBITS, B_TRUE, &jsf.jsf_showall, + 'v', MDB_OPT_SETBITS, B_TRUE, &jsf.jsf_verbose, + 'f', MDB_OPT_STR, &jsf.jsf_func, + 'n', MDB_OPT_UINTPTR, &jsf.jsf_nlines, + 'p', MDB_OPT_STR, &jsf.jsf_prop, NULL) != argc) return (DCMD_USAGE); @@ -4253,13 +5333,14 @@ dcmd_jsstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) */ if (!(flags & DCMD_ADDRSPEC)) { if (load_current_context(&addr, &raddr) != 0 || - do_jsframe(addr, raddr, opt_v, opt_f, opt_p, opt_n) != 0) + do_jsframe(addr, raddr, &jsf) != 0) return (DCMD_ERR); } if (mdb_pwalk_dcmd("jsframe", "jsframe", argc, argv, addr) == -1) return (DCMD_ERR); + jsframe_print_skipped(&jsf); return (DCMD_OK); } @@ -4435,7 +5516,7 @@ walk_jsprop_init(mdb_walk_state_t *wsp) jspw = mdb_zalloc(sizeof (jsprop_walk_data_t), UM_SLEEP | UM_GC); - if (jsobj_properties(addr, walk_jsprop_nprops, jspw) == -1) { + if (jsobj_properties(addr, walk_jsprop_nprops, jspw, NULL) == -1) { mdb_warn("couldn't iterate over properties for %p\n", addr); return (WALK_ERR); } @@ -4443,7 +5524,7 @@ walk_jsprop_init(mdb_walk_state_t *wsp) jspw->jspw_props = mdb_zalloc(jspw->jspw_nprops * sizeof (uintptr_t), UM_SLEEP | UM_GC); - if (jsobj_properties(addr, walk_jsprop_props, jspw) == -1) { + if (jsobj_properties(addr, walk_jsprop_props, jspw, NULL) == -1) { mdb_warn("couldn't iterate over properties for %p\n", addr); return (WALK_ERR); } @@ -4476,16 +5557,31 @@ walk_jsprop_step(mdb_walk_state_t *wsp) static const mdb_dcmd_t v8_mdb_dcmds[] = { /* + * Commands to inspect Node-level state + */ + { "nodebuffer", ":[-a]", + "print details about the given Node Buffer", dcmd_nodebuffer }, + + /* * Commands to inspect JavaScript-level state */ - { "jsframe", ":[-iv] [-f function] [-p property] [-n numlines]", + { "jsconstructor", ":[-v]", + "print the constructor for a JavaScript object", + dcmd_jsconstructor }, + { "jsframe", ":[-aiv] [-f function] [-p property] [-n numlines]", "summarize a JavaScript stack frame", dcmd_jsframe }, { "jsprint", ":[-ab] [-d depth] [member]", "print a JavaScript object", dcmd_jsprint }, - { "jsstack", "[-v] [-f function] [-p property] [-n numlines]", + { "jssource", ":[-n numlines]", + "print the source code for a JavaScript function", + dcmd_jssource }, + { "jsstack", "[-av] [-f function] [-p property] [-n numlines]", "print a JavaScript stacktrace", dcmd_jsstack }, { "findjsobjects", "?[-vb] [-r | -c cons | -p prop]", "find JavaScript " "objects", dcmd_findjsobjects, dcmd_findjsobjects_help }, + { "jsfunctions", "[-X] [-s file_filter] [-n name_filter] " + "[-x instr_filter]", "list JavaScript functions", + dcmd_jsfunctions, dcmd_jsfunctions_help }, /* * Commands to inspect V8-level state @@ -4500,6 +5596,8 @@ static const mdb_dcmd_t v8_mdb_dcmds[] = { "manually add a field to a given class", dcmd_v8field }, { "v8function", ":[-d]", "print JSFunction object details", dcmd_v8function }, + { "v8internal", ":[fieldidx]", "print v8 object internal fields", + dcmd_v8internal }, { "v8load", "version", "load canned config for a specific V8 version", dcmd_v8load, dcmd_v8load_help }, { "v8frametypes", NULL, "list known V8 frame types", @@ -4534,19 +5632,24 @@ configure(void) char *success; v8_cfg_t *cfgp = NULL; GElf_Sym sym; + int major, minor, build, patch; - if (mdb_readsym(&v8_major, sizeof (v8_major), + if (mdb_readsym(&major, sizeof (major), "_ZN2v88internal7Version6major_E") == -1 || - mdb_readsym(&v8_minor, sizeof (v8_minor), + mdb_readsym(&minor, sizeof (minor), "_ZN2v88internal7Version6minor_E") == -1 || - mdb_readsym(&v8_build, sizeof (v8_build), + mdb_readsym(&build, sizeof (build), "_ZN2v88internal7Version6build_E") == -1 || - mdb_readsym(&v8_patch, sizeof (v8_patch), + mdb_readsym(&patch, sizeof (patch), "_ZN2v88internal7Version6patch_E") == -1) { mdb_warn("failed to determine V8 version"); return; } + v8_major = major; + v8_minor = minor; + v8_build = build; + v8_patch = patch; mdb_printf("V8 version: %d.%d.%d.%d\n", v8_major, v8_minor, v8_build, v8_patch); diff --git a/usr/src/cmd/mdb/common/modules/v8/v8dbg.h b/usr/src/cmd/mdb/common/modules/v8/v8dbg.h index 36a30a79ef..b17f241fac 100644 --- a/usr/src/cmd/mdb/common/modules/v8/v8dbg.h +++ b/usr/src/cmd/mdb/common/modules/v8/v8dbg.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. All rights reserved. */ /* @@ -42,7 +42,10 @@ * 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_FAILURE(ptr) (V8_FailureTagMask != -1 && \ + V8_FailureTagMask != -1 && \ + ((ptr) & V8_FailureTagMask) == V8_FailureTag) + #define V8_IS_HEAPOBJECT(ptr) \ (((ptr) & V8_HeapObjectTagMask) == V8_HeapObjectTag) @@ -51,6 +54,8 @@ * using the upper 31 bits. */ #define V8_SMI_VALUE(smi) ((smi) >> (V8_SmiValueShift + V8_SmiShiftSize)) +#define V8_VALUE_SMI(value) \ + ((value) << (V8_SmiValueShift + V8_SmiShiftSize)) /* * Determine the encoding and representation of a V8 string. @@ -80,4 +85,7 @@ #define V8_DESC_ISFIELD(x) \ ((V8_SMI_VALUE(x) & V8_PROP_TYPE_MASK) == V8_PROP_TYPE_FIELD) +#define V8_PROP_FIELDINDEX(value) \ + ((V8_SMI_VALUE(value) & V8_FIELDINDEX_MASK) >> V8_FIELDINDEX_SHIFT) + #endif /* _V8DBG_H */ diff --git a/usr/src/cmd/mdb/common/modules/zfs/zfs.c b/usr/src/cmd/mdb/common/modules/zfs/zfs.c index 225cf3dee1..21a8c956c3 100644 --- a/usr/src/cmd/mdb/common/modules/zfs/zfs.c +++ b/usr/src/cmd/mdb/common/modules/zfs/zfs.c @@ -449,6 +449,7 @@ blkptr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) typedef struct mdb_dmu_buf_impl { struct { uint64_t db_object; + uintptr_t db_data; } db; uintptr_t db_objset; uint64_t db_level; @@ -1691,8 +1692,12 @@ typedef struct mdb_spa { uintptr_t spa_root_vdev; } mdb_spa_t; +typedef struct mdb_dsl_pool { + uintptr_t dp_root_dir; +} mdb_dsl_pool_t; + typedef struct mdb_dsl_dir { - uintptr_t dd_phys; + uintptr_t dd_dbuf; int64_t dd_space_towrite[TXG_SIZE]; } mdb_dsl_dir_t; @@ -1772,11 +1777,10 @@ static int spa_space(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { mdb_spa_t spa; - uintptr_t dp_root_dir; + mdb_dsl_pool_t dp; mdb_dsl_dir_t dd; + mdb_dmu_buf_impl_t db; mdb_dsl_dir_phys_t dsp; - uint64_t children; - uintptr_t childaddr; space_data_t sd; int shift = 20; char *suffix = "M"; @@ -1793,21 +1797,16 @@ spa_space(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) suffix = ""; } - if (GETMEMB(addr, "spa", spa_dsl_pool, spa.spa_dsl_pool) || - GETMEMB(addr, "spa", spa_root_vdev, spa.spa_root_vdev) || - GETMEMB(spa.spa_root_vdev, "vdev", vdev_children, children) || - GETMEMB(spa.spa_root_vdev, "vdev", vdev_child, childaddr) || - GETMEMB(spa.spa_dsl_pool, "dsl_pool", - dp_root_dir, dp_root_dir) || - GETMEMB(dp_root_dir, "dsl_dir", dd_phys, dd.dd_phys) || - GETMEMB(dp_root_dir, "dsl_dir", - dd_space_towrite, dd.dd_space_towrite) || - GETMEMB(dd.dd_phys, "dsl_dir_phys", - dd_used_bytes, dsp.dd_used_bytes) || - GETMEMB(dd.dd_phys, "dsl_dir_phys", - dd_compressed_bytes, dsp.dd_compressed_bytes) || - GETMEMB(dd.dd_phys, "dsl_dir_phys", - dd_uncompressed_bytes, dsp.dd_uncompressed_bytes)) { + if (mdb_ctf_vread(&spa, ZFS_STRUCT "spa", "mdb_spa_t", + addr, 0) == -1 || + mdb_ctf_vread(&dp, ZFS_STRUCT "dsl_pool", "mdb_dsl_pool_t", + spa.spa_dsl_pool, 0) == -1 || + mdb_ctf_vread(&dd, ZFS_STRUCT "dsl_dir", "mdb_dsl_dir_t", + dp.dp_root_dir, 0) == -1 || + mdb_ctf_vread(&db, ZFS_STRUCT "dmu_buf_impl", "mdb_dmu_buf_impl_t", + dd.dd_dbuf, 0) == -1 || + mdb_ctf_vread(&dsp, ZFS_STRUCT "dsl_dir_phys", + "mdb_dsl_dir_phys_t", db.db.db_data, 0) == -1) { return (DCMD_ERR); } diff --git a/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c b/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c index 5f41df26f3..2465146a38 100644 --- a/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c +++ b/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c @@ -208,7 +208,7 @@ mdb_amd64_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp, int err; int i; - struct { + struct fr { uintptr_t fr_savfp; uintptr_t fr_savpc; } fr; @@ -225,6 +225,8 @@ mdb_amd64_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp, mdb_syminfo_t sip; mdb_ctf_funcinfo_t mfp; int xpv_panic = 0; + int advance_tortoise = 1; + uintptr_t tortoise_fp = 0; #ifndef _KMDB int xp; @@ -237,19 +239,38 @@ mdb_amd64_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp, while (fp != 0) { int args_style = 0; - /* - * Ensure progress (increasing fp), and prevent - * endless loop with the same FP. - */ - if (fp <= lastfp) { - err = EMDB_STKFRAME; - goto badfp; - } if (mdb_tgt_vread(t, &fr, sizeof (fr), fp) != sizeof (fr)) { err = EMDB_NOMAP; goto badfp; } + if (tortoise_fp == 0) { + tortoise_fp = fp; + } else { + /* + * Advance tortoise_fp every other frame, so we detect + * cycles with Floyd's tortoise/hare. + */ + if (advance_tortoise != 0) { + struct fr tfr; + + if (mdb_tgt_vread(t, &tfr, sizeof (tfr), + tortoise_fp) != sizeof (tfr)) { + err = EMDB_NOMAP; + goto badfp; + } + + tortoise_fp = tfr.fr_savfp; + } + + if (fp == tortoise_fp) { + err = EMDB_STKFRAME; + goto badfp; + } + } + + advance_tortoise = !advance_tortoise; + if ((mdb_tgt_lookup_by_addr(t, pc, MDB_TGT_SYM_FUZZY, NULL, 0, &s, &sip) == 0) && (mdb_ctf_func_info(&s, &sip, &mfp) == 0)) { diff --git a/usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c b/usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c index 80ce1c7ad2..d6db4811b2 100644 --- a/usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c +++ b/usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c @@ -197,7 +197,7 @@ mdb_ia32_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp, int got_pc = (gsp->kregs[KREG_EIP] != 0); int err; - struct { + struct fr { uintptr_t fr_savfp; uintptr_t fr_savpc; long fr_argv[32]; @@ -210,6 +210,8 @@ mdb_ia32_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp, ssize_t size; uint_t argc; int detect_exception_frames = 0; + int advance_tortoise = 1; + uintptr_t tortoise_fp = 0; #ifndef _KMDB int xp; @@ -220,15 +222,6 @@ mdb_ia32_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp, bcopy(gsp, &gregs, sizeof (gregs)); while (fp != 0) { - - /* - * Ensure progress (increasing fp), and prevent - * endless loop with the same FP. - */ - if (fp <= lastfp) { - err = EMDB_STKFRAME; - goto badfp; - } if (fp & (STACK_ALIGN - 1)) { err = EMDB_STKALIGN; goto badfp; @@ -242,6 +235,33 @@ mdb_ia32_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp, goto badfp; } + if (tortoise_fp == 0) { + tortoise_fp = fp; + } else { + /* + * Advance tortoise_fp every other frame, so we detect + * cycles with Floyd's tortoise/hare. + */ + if (advance_tortoise != 0) { + struct fr tfr; + + if (mdb_tgt_vread(t, &tfr, sizeof (tfr), + tortoise_fp) != sizeof (tfr)) { + err = EMDB_NOMAP; + goto badfp; + } + + tortoise_fp = tfr.fr_savfp; + } + + if (fp == tortoise_fp) { + err = EMDB_STKFRAME; + goto badfp; + } + } + + advance_tortoise = !advance_tortoise; + if (got_pc && func(arg, pc, argc, fr.fr_argv, &gregs) != 0) break; diff --git a/usr/src/cmd/perl/Makefile.perl b/usr/src/cmd/perl/Makefile.perl index 3ee32fef0f..25fdc53f50 100644 --- a/usr/src/cmd/perl/Makefile.perl +++ b/usr/src/cmd/perl/Makefile.perl @@ -10,17 +10,15 @@ # # # Copyright (c) 2014 Racktop Systems. +# Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. # include $(SRC)/lib/Makefile.lib -# PERL_VERSION used to be set here, -# but as it is also needed in usr/src/pkg/Makefile, +# PERL_VERSION and PERL_ARCH used to be set here, +# but as they were also needed in usr/src/pkg/Makefile, # the definition was moved to usr/src/Makefile.master -PERL_ARCH = i86pc-solaris-64int -$(SPARC_BLD)PERL_ARCH = sun4-solaris-64int - PERLDIR = $(ADJUNCT_PROTO)/usr/perl5/$(PERL_VERSION) PERLLIBDIR = $(PERLDIR)/lib/$(PERL_ARCH) PERLINCDIR = $(PERLLIBDIR)/CORE diff --git a/usr/src/cmd/perl/Makefile.targ b/usr/src/cmd/perl/Makefile.targ index a3211dff7f..b3f5ec9bd3 100644 --- a/usr/src/cmd/perl/Makefile.targ +++ b/usr/src/cmd/perl/Makefile.targ @@ -10,7 +10,7 @@ # # # Copyright (c) 2014 Racktop Systems. -# Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved. +# Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. # # Link against libc as perl solaris specs @@ -23,8 +23,8 @@ $(ROOTPERLEXT) := FILEMODE = 0555 $(ROOTPERLMOD) := FILEMODE = 0444 # CFLAGS for perl, specifically. -PCFLAGS= -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DPERL_USE_SAFE_PUTENV \ - -D_TS_ERRNO +PCFLAGS= -DPERL_EUPXS_ALWAYS_EXPORT -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \ + -DPERL_USE_SAFE_PUTENV -D_TS_ERRNO $(MACH): $(INS.dir) diff --git a/usr/src/cmd/ptools/pflags/pflags.c b/usr/src/cmd/ptools/pflags/pflags.c index 8054a80d3c..f19a945d95 100644 --- a/usr/src/cmd/ptools/pflags/pflags.c +++ b/usr/src/cmd/ptools/pflags/pflags.c @@ -25,7 +25,7 @@ */ /* - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright 2015, Joyent, Inc. */ #include <stdio.h> @@ -469,6 +469,9 @@ prwhy(int why) case PR_SUSPENDED: str = "PR_SUSPENDED"; break; + case PR_BRAND: + str = "PR_BRAND"; + break; default: str = buf; (void) sprintf(str, "%d", why); diff --git a/usr/src/cmd/rpcsvc/net_files/rpc b/usr/src/cmd/rpcsvc/net_files/rpc index 9d1b728ab8..a50557dab8 100644 --- a/usr/src/cmd/rpcsvc/net_files/rpc +++ b/usr/src/cmd/rpcsvc/net_files/rpc @@ -1,7 +1,4 @@ # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# # CDDL HEADER START # # The contents of this file are subject to the terms of the @@ -21,9 +18,14 @@ # # CDDL HEADER END # +# Copyright 2015 Nexenta Systems, Inc. All rights reserved. +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# # rpc # -rpcbind 100000 portmap sunrpc rpcbind +rpcbind 100000 portmap sunrpc rstatd 100001 rstat rup perfmeter rusersd 100002 rusers nfs 100003 nfsprog @@ -47,9 +49,9 @@ nlockmgr 100021 x25.inr 100022 statmon 100023 status 100024 +bootparam 100026 ypupdated 100028 ypupdate keyserv 100029 keyserver -bootparam 100026 sunlink_mapper 100033 tfsd 100037 nsed 100038 @@ -76,14 +78,16 @@ iproutes 100120 na.iproutes layers 100121 na.layers snmp 100122 na.snmp snmp-cmc snmp-synoptics snmp-unisys snmp-utk traffic 100123 na.traffic +nsm_addr 100133 ktkt_warnd 100134 +smserverd 100155 +fmd_adm 100169 +idmap 100172 nfs_acl 100227 +metad 100229 +metamhd 100230 sadmind 100232 +ufsd 100233 gssd 100234 -ufsd 100233 ufsd +metamedd 100242 pcnfsd 150001 -metad 100229 metad -metamhd 100230 metamhd -metamedd 100242 metamedd -smserverd 100155 smserverd -idmap 100172 idmap diff --git a/usr/src/cmd/sgs/packages/common/SUNWonld-README b/usr/src/cmd/sgs/packages/common/SUNWonld-README index 6688c34255..f303ba6053 100644 --- a/usr/src/cmd/sgs/packages/common/SUNWonld-README +++ b/usr/src/cmd/sgs/packages/common/SUNWonld-README @@ -1654,3 +1654,4 @@ Bugid Risk Synopsis 4270 ld(1) argument error reporting is still pretty bad 4383 libelf can't write extended sections when ELF_F_LAYOUT 4959 completely discarded merged string sections will corrupt output objects +4996 rtld _init race leads to incorrect symbol values diff --git a/usr/src/cmd/sgs/rtld/common/util.c b/usr/src/cmd/sgs/rtld/common/util.c index bd80f05a37..09cabeb31b 100644 --- a/usr/src/cmd/sgs/rtld/common/util.c +++ b/usr/src/cmd/sgs/rtld/common/util.c @@ -628,17 +628,17 @@ is_dep_init(Rt_map *dlmp, Rt_map *clmp) if ((dlmp == clmp) || (rtld_flags & RT_FL_INITFIRST)) return; - rt_mutex_lock(&dlmp->rt_lock); + (void) rt_mutex_lock(&dlmp->rt_lock); while (dlmp->rt_init_thread != rt_thr_self() && (FLAGS(dlmp) & (FLG_RT_RELOCED | FLG_RT_INITCALL | FLG_RT_INITDONE)) == (FLG_RT_RELOCED | FLG_RT_INITCALL)) { leave(LIST(dlmp), 0); (void) _lwp_cond_wait(&dlmp->rt_cv, (mutex_t *)&dlmp->rt_lock); - rt_mutex_unlock(&dlmp->rt_lock); + (void) rt_mutex_unlock(&dlmp->rt_lock); (void) enter(0); - rt_mutex_lock(&dlmp->rt_lock); + (void) rt_mutex_lock(&dlmp->rt_lock); } - rt_mutex_unlock(&dlmp->rt_lock); + (void) rt_mutex_unlock(&dlmp->rt_lock); if ((FLAGS(dlmp) & (FLG_RT_RELOCED | FLG_RT_INITDONE)) == (FLG_RT_RELOCED | FLG_RT_INITDONE)) @@ -760,11 +760,11 @@ call_init(Rt_map **tobj, int flag) * signifies that a .fini must be called should it exist. * Clear the sort field for use in later .fini processing. */ - rt_mutex_lock(&lmp->rt_lock); + (void) rt_mutex_lock(&lmp->rt_lock); FLAGS(lmp) |= FLG_RT_INITDONE; lmp->rt_init_thread = (thread_t)0; - _lwp_cond_broadcast(&lmp->rt_cv); - rt_mutex_unlock(&lmp->rt_lock); + (void) _lwp_cond_broadcast(&lmp->rt_cv); + (void) rt_mutex_unlock(&lmp->rt_lock); SORTVAL(lmp) = -1; /* diff --git a/usr/src/cmd/tar/Makefile b/usr/src/cmd/tar/Makefile index 5da69ec0f9..93a02e58e0 100644 --- a/usr/src/cmd/tar/Makefile +++ b/usr/src/cmd/tar/Makefile @@ -37,8 +37,6 @@ LINTFLAGS += -u LDLIBS += -lsec -lcmdutils -lnvpair -ltsol CFLAGS += $(CCVERBOSE) -CERRWARN += -_gcc=-Wno-unused-variable -CERRWARN += -_gcc=-Wno-parentheses CERRWARN += -_gcc=-Wno-uninitialized CPPFLAGS += -DEUC diff --git a/usr/src/cmd/tar/tar.c b/usr/src/cmd/tar/tar.c index 9e2aef3bee..7b48e927e1 100644 --- a/usr/src/cmd/tar/tar.c +++ b/usr/src/cmd/tar/tar.c @@ -565,7 +565,7 @@ static char *myname; static char *xtract_chdir = NULL; static int checkflag = 0; static int Xflag, Fflag, iflag, hflag, Bflag, Iflag; -static int rflag, xflag, vflag, tflag, mt, svmt, cflag, mflag, pflag; +static int rflag, xflag, vflag, tflag, mt, cflag, mflag, pflag; static int uflag; static int errflag; static int oflag; @@ -643,6 +643,8 @@ static int charset_type = 0; static u_longlong_t xhdr_flgs; /* Bits set determine which items */ /* need to be in extended header. */ +static pid_t comp_pid = 0; + #define _X_DEVMAJOR 0x1 #define _X_DEVMINOR 0x2 #define _X_GID 0x4 @@ -725,8 +727,6 @@ main(int argc, char *argv[]) char *cp; char *tmpdirp; pid_t thispid; - pid_t pid; - int wstat; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ @@ -1114,10 +1114,8 @@ main(int argc, char *argv[]) if (Aflag && vflag) (void) printf( gettext("Suppressing absolute pathnames\n")); - if (cflag && compress_opt != NULL) { - pid = compress_file(); - wait_pid(pid); - } + if (cflag && compress_opt != NULL) + comp_pid = compress_file(); dorep(argv); if (rflag && !cflag && (compress_opt != NULL)) compress_back(); @@ -1168,10 +1166,8 @@ main(int argc, char *argv[]) if (strcmp(usefile, "-") != 0) { check_compression(); - if (compress_opt != NULL) { - pid = uncompress_file(); - wait_pid(pid); - } + if (compress_opt != NULL) + comp_pid = uncompress_file(); } if (xflag) { if (xtract_chdir != NULL) { @@ -4876,6 +4872,13 @@ done(int n) exit(2); } } + /* + * If we have a compression child, we should have a child process that + * we're waiting for to finish compressing or uncompressing the tar + * stream. + */ + if (n == 0 && comp_pid != 0) + wait_pid(comp_pid); exit(n); } @@ -6109,7 +6112,6 @@ check_prefix(char **namep, char **dirp, char **compp) if ((tflag || xflag) && !Pflag) { if (is_absolute(fullname) || has_dot_dot(fullname)) { char *stripped_prefix; - size_t prefix_len = 0; (void) strcpy(savename, fullname); strcpy(fullname, @@ -7891,7 +7893,7 @@ xattrs_put(char *longname, char *shortname, char *parent, char *attrparent) return; } - while (dp = readdir(dirp)) { + while ((dp = readdir(dirp)) != NULL) { if (strcmp(dp->d_name, "..") == 0) { continue; } else if (strcmp(dp->d_name, ".") == 0) { @@ -9191,9 +9193,6 @@ static void compress_back() { pid_t pid; - int status; - int wret; - struct stat statb; if (vflag) { (void) fprintf(vfile, @@ -9299,9 +9298,6 @@ void decompress_file(void) { pid_t pid; - int status; - char cmdstr[PATH_MAX]; - char fname[PATH_MAX]; char *added_suffix; @@ -9344,7 +9340,7 @@ compress_file(void) if (pipe(fd) < 0) { vperror(1, gettext("Could not create pipe")); } - if (pid = fork() > 0) { + if ((pid = fork()) > 0) { mt = fd[1]; (void) close(fd[0]); return (pid); @@ -9373,7 +9369,7 @@ uncompress_file(void) if (pipe(fd) < 0) { vperror(1, gettext("Could not create pipe")); } - if (pid = fork() > 0) { + if ((pid = fork()) > 0) { mt = fd[0]; (void) close(fd[1]); return (pid); diff --git a/usr/src/cmd/zlogin/zlogin.c b/usr/src/cmd/zlogin/zlogin.c index 5f3972db17..909bdfda18 100644 --- a/usr/src/cmd/zlogin/zlogin.c +++ b/usr/src/cmd/zlogin/zlogin.c @@ -1821,6 +1821,35 @@ get_username() return (nptr->pw_name); } +static boolean_t +zlog_mode_logging(char *zonename) +{ + boolean_t lm = B_FALSE; + zone_dochandle_t handle; + struct zone_attrtab attr; + + if ((handle = zonecfg_init_handle()) == NULL) + return (lm); + + if (zonecfg_get_handle(zonename, handle) != Z_OK) + goto done; + + if (zonecfg_setattrent(handle) != Z_OK) + goto done; + while (zonecfg_getattrent(handle, &attr) == Z_OK) { + if (strcmp("zlog-mode", attr.zone_attr_name) == 0) { + if (strncmp("log", attr.zone_attr_value, 3) == 0) + lm = B_TRUE; + break; + } + } + (void) zonecfg_endattrent(handle); + +done: + zonecfg_fini_handle(handle); + return (lm); +} + int main(int argc, char **argv) { @@ -2056,6 +2085,10 @@ main(int argc, char **argv) */ if (console) { int gz_stderr_fd = -1; + boolean_t set_raw = B_TRUE; + + if (imode && zlog_mode_logging(zonename)) + set_raw = B_FALSE; /* * Ensure that zoneadmd for this zone is running. @@ -2082,7 +2115,7 @@ main(int argc, char **argv) "console]\n"), zonename); } - if (set_tty_rawmode(STDIN_FILENO) == -1) { + if (set_raw && set_tty_rawmode(STDIN_FILENO) == -1) { reset_tty(); zperror(gettext("failed to set stdin pty to raw mode")); return (1); diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c index 5a86b1cf50..522de5b779 100644 --- a/usr/src/cmd/zoneadmd/vplat.c +++ b/usr/src/cmd/zoneadmd/vplat.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2014, Joyent Inc. All rights reserved. + * Copyright 2015, Joyent Inc. All rights reserved. */ /* @@ -163,6 +163,19 @@ static priv_set_t *zprivs = NULL; static const char *DFLT_FS_ALLOWED = "hsfs,smbfs,nfs,nfs3,nfs4,nfsdyn"; +typedef struct zone_proj_rctl_map { + char *zpr_zone_rctl; + char *zpr_project_rctl; +} zone_proj_rctl_map_t; + +static zone_proj_rctl_map_t zone_proj_rctl_map[] = { + {"zone.max-msg-ids", "project.max-msg-ids"}, + {"zone.max-sem-ids", "project.max-sem-ids"}, + {"zone.max-shm-ids", "project.max-shm-ids"}, + {"zone.max-shm-memory", "project.max-shm-memory"}, + {NULL, NULL} +}; + /* from libsocket, not in any header file */ extern int getnetmaskbyaddr(struct in_addr, struct in_addr *); @@ -3245,6 +3258,19 @@ get_privset(zlog_t *zlogp, priv_set_t *privs, zone_mnt_t mount_cmd) return (error); } +static char * +zone_proj_rctl(const char *name) +{ + int i; + + for (i = 0; zone_proj_rctl_map[i].zpr_zone_rctl != NULL; i++) { + if (strcmp(name, zone_proj_rctl_map[i].zpr_zone_rctl) == 0) { + return (zone_proj_rctl_map[i].zpr_project_rctl); + } + } + return (NULL); +} + static int get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) { @@ -3298,6 +3324,7 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) struct zone_rctlvaltab *rctlval; uint_t i, count; const char *name = rctltab.zone_rctl_name; + char *proj_nm; /* zoneadm should have already warned about unknown rctls. */ if (!zonecfg_is_rctl(name)) { @@ -3364,6 +3391,26 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) } zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); rctltab.zone_rctl_valptr = NULL; + + /* + * With no action on our part we will start zsched with the + * project rctl values for our (zoneadmd) current project. For + * brands running a variant of Illumos, that's not a problem + * since they will setup their own projects, but for a + * non-native brand like lx, where there are no projects, we + * want to start things up with the same project rctls as the + * corresponding zone rctls, since nothing within the zone will + * ever change the project rctls. + */ + if ((proj_nm = zone_proj_rctl(name)) != NULL) { + if (nvlist_add_nvlist_array(nvl, proj_nm, nvlv, count) + != 0) { + zerror(zlogp, B_FALSE, + "nvlist_add_nvlist_arrays failed"); + goto out; + } + } + if (nvlist_add_nvlist_array(nvl, (char *)name, nvlv, count) != 0) { zerror(zlogp, B_FALSE, "%s failed", |