From: Bryan Cantrill Date: Tue, 11 Mar 2014 01:07:52 +0000 (-0700) Subject: mdb_v8: update to latest version X-Git-Tag: v0.11.12~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e496707d3971d45cd0b9f4147ce6abe00e7d6cf1;p=platform%2Fupstream%2Fnodejs.git mdb_v8: update to latest version * ::jsstack -v prints function defintion * ::jsprint works with objects with only numeric properties * update tests to use builtin mdb_v8 * add more symbols to postmortem script - pending upstream inclusion --- diff --git a/deps/mdb_v8/mdb_v8.c b/deps/mdb_v8/mdb_v8.c index a2f5795..5653431 100644 --- a/deps/mdb_v8/mdb_v8.c +++ b/deps/mdb_v8/mdb_v8.c @@ -100,6 +100,7 @@ static int v8_next_type; static v8_enum_t v8_frametypes[16]; static int v8_next_frametype; +static int v8_warnings; static int v8_silent; /* @@ -159,6 +160,12 @@ static intptr_t V8_TYPE_JSOBJECT = -1; static intptr_t V8_TYPE_JSARRAY = -1; static intptr_t V8_TYPE_FIXEDARRAY = -1; +static intptr_t V8_ELEMENTS_KIND_SHIFT; +static intptr_t V8_ELEMENTS_KIND_BITCOUNT; +static intptr_t V8_ELEMENTS_FAST_ELEMENTS; +static intptr_t V8_ELEMENTS_FAST_HOLEY_ELEMENTS; +static intptr_t V8_ELEMENTS_DICTIONARY_ELEMENTS; + /* * Although we have this information in v8_classes, the following offsets are * defined explicitly because they're used directly in code below. @@ -182,15 +189,19 @@ static ssize_t V8_OFF_MAP_INOBJECT_PROPERTIES; static ssize_t V8_OFF_MAP_INSTANCE_ATTRIBUTES; static ssize_t V8_OFF_MAP_INSTANCE_DESCRIPTORS; static ssize_t V8_OFF_MAP_INSTANCE_SIZE; +static ssize_t V8_OFF_MAP_BIT_FIELD; +static ssize_t V8_OFF_MAP_BIT_FIELD2; static ssize_t V8_OFF_MAP_BIT_FIELD3; static ssize_t V8_OFF_MAP_TRANSITIONS; static ssize_t V8_OFF_ODDBALL_TO_STRING; static ssize_t V8_OFF_SCRIPT_LINE_ENDS; static ssize_t V8_OFF_SCRIPT_NAME; +static ssize_t V8_OFF_SCRIPT_SOURCE; static ssize_t V8_OFF_SEQASCIISTR_CHARS; static ssize_t V8_OFF_SEQONEBYTESTR_CHARS; static ssize_t V8_OFF_SEQTWOBYTESTR_CHARS; static ssize_t V8_OFF_SHAREDFUNCTIONINFO_CODE; +static ssize_t V8_OFF_SHAREDFUNCTIONINFO_END_POSITION; static ssize_t V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION; static ssize_t V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME; static ssize_t V8_OFF_SHAREDFUNCTIONINFO_LENGTH; @@ -289,6 +300,20 @@ static v8_constant_t v8_constants[] = { V8_CONSTANT_FALLBACK(0, 0), 3 }, { &V8_TRANSITIONS_IDX_DESC, "v8dbg_transitions_idx_descriptors", V8_CONSTANT_OPTIONAL }, + + { &V8_ELEMENTS_KIND_SHIFT, "v8dbg_elements_kind_shift", + V8_CONSTANT_FALLBACK(0, 0), 3 }, + { &V8_ELEMENTS_KIND_BITCOUNT, "v8dbg_elements_kind_bitcount", + V8_CONSTANT_FALLBACK(0, 0), 5 }, + { &V8_ELEMENTS_FAST_ELEMENTS, + "v8dbg_elements_fast_elements", + V8_CONSTANT_FALLBACK(0, 0), 2 }, + { &V8_ELEMENTS_FAST_HOLEY_ELEMENTS, + "v8dbg_elements_fast_holey_elements", + V8_CONSTANT_FALLBACK(0, 0), 3 }, + { &V8_ELEMENTS_DICTIONARY_ELEMENTS, + "v8dbg_elements_dictionary_elements", + V8_CONSTANT_FALLBACK(0, 0), 6 }, }; static int v8_nconstants = sizeof (v8_constants) / sizeof (v8_constants[0]); @@ -298,7 +323,6 @@ typedef struct v8_offset { const char *v8o_class; const char *v8o_member; boolean_t v8o_optional; - intptr_t v8o_fallback; } v8_offset_t; static v8_offset_t v8_offsets[] = { @@ -342,6 +366,8 @@ static v8_offset_t v8_offsets[] = { "Map", "transitions", B_TRUE }, { &V8_OFF_MAP_INSTANCE_SIZE, "Map", "instance_size" }, + { &V8_OFF_MAP_BIT_FIELD2, + "Map", "bit_field2", B_TRUE }, { &V8_OFF_MAP_BIT_FIELD3, "Map", "bit_field3", B_TRUE }, { &V8_OFF_ODDBALL_TO_STRING, @@ -350,6 +376,8 @@ static v8_offset_t v8_offsets[] = { "Script", "line_ends" }, { &V8_OFF_SCRIPT_NAME, "Script", "name" }, + { &V8_OFF_SCRIPT_SOURCE, + "Script", "source" }, { &V8_OFF_SEQASCIISTR_CHARS, "SeqAsciiString", "chars", B_TRUE }, { &V8_OFF_SEQONEBYTESTR_CHARS, @@ -358,6 +386,8 @@ static v8_offset_t v8_offsets[] = { "SeqTwoByteString", "chars", B_TRUE }, { &V8_OFF_SHAREDFUNCTIONINFO_CODE, "SharedFunctionInfo", "code" }, + { &V8_OFF_SHAREDFUNCTIONINFO_END_POSITION, + "SharedFunctionInfo", "end_position" }, { &V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION, "SharedFunctionInfo", "function_token_position" }, { &V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME, @@ -544,6 +574,16 @@ again: V8_OFF_SLICEDSTRING_PARENT = V8_OFF_SLICEDSTRING_OFFSET - sizeof (uintptr_t); + /* + * If we don't have bit_field/bit_field2 for Map, we know that they're + * the second and third byte of instance_attributes. + */ + if (V8_OFF_MAP_BIT_FIELD == -1) + V8_OFF_MAP_BIT_FIELD = V8_OFF_MAP_INSTANCE_ATTRIBUTES + 2; + + if (V8_OFF_MAP_BIT_FIELD2 == -1) + V8_OFF_MAP_BIT_FIELD2 = V8_OFF_MAP_INSTANCE_ATTRIBUTES + 3; + return (failed ? -1 : 0); } @@ -821,12 +861,19 @@ conf_class_compute_offsets(v8_class_t *clp) */ #define JSSTR_NONE 0 #define JSSTR_NUDE JSSTR_NONE -#define JSSTR_VERBOSE 0x1 -#define JSSTR_QUOTED 0x2 -#define JSSTR_ISASCII 0x4 + +#define JSSTR_FLAGSHIFT 16 +#define JSSTR_VERBOSE (0x1 << JSSTR_FLAGSHIFT) +#define JSSTR_QUOTED (0x2 << JSSTR_FLAGSHIFT) +#define JSSTR_ISASCII (0x4 << JSSTR_FLAGSHIFT) + +#define JSSTR_MAXDEPTH 512 +#define JSSTR_DEPTH(f) ((f) & ((1 << JSSTR_FLAGSHIFT) - 1)) +#define JSSTR_BUMPDEPTH(f) ((f) + 1) static int jsstr_print(uintptr_t, uint_t, char **, size_t *); static boolean_t jsobj_is_undefined(uintptr_t addr); +static boolean_t jsobj_is_hole(uintptr_t addr); static const char * enum_lookup_str(v8_enum_t *enums, int val, const char *dflt) @@ -893,7 +940,7 @@ v8_warn(const char *format, ...) va_list alist; int len; - if (v8_silent) + if (!v8_warnings || v8_silent) return; va_start(alist, format); @@ -1110,7 +1157,6 @@ read_heap_dict(uintptr_t addr, char *bufp; int rval = -1; uintptr_t *dict, ndict, i; - const char *typename; if (read_heap_array(addr, &dict, &ndict, UM_SLEEP) != 0) return (-1); @@ -1130,28 +1176,31 @@ read_heap_dict(uintptr_t addr, if (jsobj_is_undefined(dict[i])) continue; - if (read_typebyte(&type, dict[i]) != 0) - goto out; + if (V8_IS_SMI(dict[i])) { + intptr_t val = V8_SMI_VALUE(dict[i]); - typename = enum_lookup_str(v8_types, type, NULL); + (void) snprintf(buf, sizeof (buf), "%ld", val); + } else { + if (jsobj_is_hole(dict[i])) { + /* + * In some cases, the key can (apparently) be a + * hole, in which case we skip over it. + */ + continue; + } - if (typename != NULL && strcmp(typename, "Oddball") == 0) { - /* - * In some cases, the key can (apparently) be a hole; - * assume that any Oddball in the key field falls into - * this case and skip over it. - */ - continue; - } + if (read_typebyte(&type, dict[i]) != 0) + goto out; - if (!V8_TYPE_STRING(type)) - goto out; + if (!V8_TYPE_STRING(type)) + goto out; - bufp = buf; - len = sizeof (buf); + bufp = buf; + len = sizeof (buf); - if (jsstr_print(dict[i], JSSTR_NUDE, &bufp, &len) != 0) - goto out; + if (jsstr_print(dict[i], JSSTR_NUDE, &bufp, &len) != 0) + goto out; + } if (func(buf, dict[i + 1], arg) == -1) goto out; @@ -1339,12 +1388,14 @@ jsstr_print(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp) char buf[64]; boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE; - if (read_typebyte(&typebyte, addr) != 0) - return (0); + if (read_typebyte(&typebyte, addr) != 0) { + (void) bsnprintf(bufp, lenp, ""); + return (-1); + } if (!V8_TYPE_STRING(typebyte)) { (void) bsnprintf(bufp, lenp, ""); - return (0); + return (-1); } if (verbose) { @@ -1355,11 +1406,18 @@ jsstr_print(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp) (void) mdb_inc_indent(4); } + if (JSSTR_DEPTH(flags) > JSSTR_MAXDEPTH) { + (void) bsnprintf(bufp, lenp, ""); + return (-1); + } + if (V8_STRENC_ASCII(typebyte)) flags |= JSSTR_ISASCII; else flags &= ~JSSTR_ISASCII; + flags = JSSTR_BUMPDEPTH(flags); + if (V8_STRREP_SEQ(typebyte)) err = jsstr_print_seq(addr, flags, bufp, lenp, 0, -1); else if (V8_STRREP_CONS(typebyte)) @@ -1388,17 +1446,23 @@ jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp, * we'll allocate a buffer sized based on our input, making it at * least enough space for our ellipsis and at most 256K. */ - uintptr_t i, nstrchrs, nreadbytes, nreadoffset, blen, nstrbytes; + uintptr_t i, nreadoffset, blen, nstrbytes, nstrchrs; + ssize_t nreadbytes; boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE; boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE; char *buf; uint16_t chrval; - blen = MIN(*lenp, 256 * 1024); + if ((blen = MIN(*lenp, 256 * 1024)) == 0) + return (0); + buf = alloca(blen); - if (read_heap_smi(&nstrchrs, addr, V8_OFF_STRING_LENGTH) != 0) + if (read_heap_smi(&nstrchrs, addr, V8_OFF_STRING_LENGTH) != 0) { + (void) bsnprintf(bufp, lenp, + ""); return (-1); + } if (slicelen != -1) nstrchrs = slicelen; @@ -1414,6 +1478,16 @@ jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp, nreadbytes = nstrbytes + sizeof ("\"\"") <= blen ? nstrbytes : blen - sizeof ("\"\"[...]"); + if (nreadbytes < 0) { + /* + * We don't even have the room to store the ellipsis; zero + * the buffer out and set the length to zero. + */ + *bufp = '\0'; + *lenp = 0; + return (0); + } + if (verbose) mdb_printf("length: %d chars (%d bytes), " "will read %d bytes from offset %d\n", @@ -1448,6 +1522,7 @@ jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp, (void) bsnprintf(bufp, lenp, "%s", quoted ? "\"" : ""); for (i = 0; i < nreadbytes; i += 2) { + /*LINTED*/ chrval = *((uint16_t *)(buf + i)); (void) bsnprintf(bufp, lenp, "%c", (isascii(chrval) || chrval == 0) ? @@ -1468,9 +1543,17 @@ jsstr_print_cons(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp) boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE; uintptr_t ptr1, ptr2; - if (read_heap_ptr(&ptr1, addr, V8_OFF_CONSSTRING_FIRST) != 0 || - read_heap_ptr(&ptr2, addr, V8_OFF_CONSSTRING_SECOND) != 0) + if (read_heap_ptr(&ptr1, addr, V8_OFF_CONSSTRING_FIRST) != 0) { + (void) bsnprintf(bufp, lenp, + ""); return (-1); + } + + if (read_heap_ptr(&ptr2, addr, V8_OFF_CONSSTRING_SECOND) != 0) { + (void) bsnprintf(bufp, lenp, + ""); + return (-1); + } if (verbose) { mdb_printf("ptr1: %p\n", ptr1); @@ -1480,10 +1563,12 @@ jsstr_print_cons(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp) if (quoted) (void) bsnprintf(bufp, lenp, "\""); - if (jsstr_print(ptr1, verbose, bufp, lenp) != 0) + flags = JSSTR_BUMPDEPTH(flags) & ~JSSTR_QUOTED; + + if (jsstr_print(ptr1, flags, bufp, lenp) != 0) return (-1); - if (jsstr_print(ptr2, verbose, bufp, lenp) != 0) + if (jsstr_print(ptr2, flags, bufp, lenp) != 0) return (-1); if (quoted) @@ -1499,37 +1584,50 @@ jsstr_print_sliced(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp) uint8_t typebyte; boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE; boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE; - uint_t newflags; - if (read_heap_ptr(&parent, addr, V8_OFF_SLICEDSTRING_PARENT) != 0 || - read_heap_smi(&offset, addr, V8_OFF_SLICEDSTRING_OFFSET) != 0 || - read_heap_smi(&length, addr, V8_OFF_STRING_LENGTH) != 0) + if (read_heap_ptr(&parent, addr, V8_OFF_SLICEDSTRING_PARENT) != 0) { + (void) bsnprintf(bufp, lenp, + ""); + return (-1); + } + + if (read_heap_smi(&offset, addr, V8_OFF_SLICEDSTRING_OFFSET) != 0) { + (void) bsnprintf(bufp, lenp, + ""); return (-1); + } + + if (read_heap_smi(&length, addr, V8_OFF_STRING_LENGTH) != 0) { + (void) bsnprintf(bufp, lenp, + ""); + return (-1); + } if (verbose) mdb_printf("parent: %p, offset = %d, length = %d\n", parent, offset, length); if (read_typebyte(&typebyte, parent) != 0) { - v8_warn("SlicedString %s: failed to read parent's type", addr); - (void) bsnprintf(bufp, lenp, ""); + (void) bsnprintf(bufp, lenp, + ""); return (0); } if (!V8_STRREP_SEQ(typebyte)) { - v8_warn("SlicedString %s: parent is not a sequential string", - addr); - (void) bsnprintf(bufp, lenp, ""); + (void) bsnprintf(bufp, lenp, + ""); return (0); } if (quoted) (void) bsnprintf(bufp, lenp, "\""); - newflags = verbose ? JSSTR_VERBOSE : 0; + flags = JSSTR_BUMPDEPTH(flags) & ~JSSTR_QUOTED; + if (V8_STRENC_ASCII(typebyte)) - newflags |= JSSTR_ISASCII; - if (jsstr_print_seq(parent, newflags, bufp, lenp, offset, length) != 0) + flags |= JSSTR_ISASCII; + + if (jsstr_print_seq(parent, flags, bufp, lenp, offset, length) != 0) return (-1); if (quoted) @@ -1543,8 +1641,9 @@ jsstr_print_external(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp) { uintptr_t ptr1, ptr2; size_t blen = *lenp + 1; - char *buf = alloca(blen); + char *buf; boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE; + int rval = -1; if ((flags & JSSTR_ISASCII) == 0) { (void) bsnprintf(bufp, lenp, ""); @@ -1554,38 +1653,51 @@ jsstr_print_external(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp) if (flags & JSSTR_VERBOSE) mdb_printf("assuming Node.js string\n"); - if (read_heap_ptr(&ptr1, addr, V8_OFF_EXTERNALSTRING_RESOURCE) != 0) + if (read_heap_ptr(&ptr1, addr, V8_OFF_EXTERNALSTRING_RESOURCE) != 0) { + (void) bsnprintf(bufp, lenp, + ""); return (-1); + } if (mdb_vread(&ptr2, sizeof (ptr2), ptr1 + NODE_OFF_EXTSTR_DATA) == -1) { - v8_warn("failed to read node external pointer: %p", + (void) bsnprintf(bufp, lenp, "", ptr1 + NODE_OFF_EXTSTR_DATA); return (-1); } + buf = mdb_alloc(blen, UM_SLEEP); + if (mdb_readstr(buf, blen, ptr2) == -1) { - v8_warn("failed to read ExternalString data"); - return (-1); + (void) bsnprintf(bufp, lenp, ""); + goto out; } if (buf[0] != '\0' && !isascii(buf[0])) { - v8_warn("failed to read ExternalString ascii data\n"); - return (-1); + (void) bsnprintf(bufp, lenp, ""); + goto out; } (void) bsnprintf(bufp, lenp, "%s%s%s", quoted ? "\"" : "", buf, quoted ? "\"" : ""); - return (0); + rval = 0; +out: + mdb_free(buf, blen); + + return (rval); } /* - * Returns true if the given address refers to the "undefined" object. Returns - * false on failure (since we shouldn't fail on the actual "undefined" value). + * Returns true if the given address refers to the named oddball object (e.g. + * "undefined"). Returns false on failure (since we shouldn't fail on the + * actual "undefined" value). */ static boolean_t -jsobj_is_undefined(uintptr_t addr) +jsobj_is_oddball(uintptr_t addr, char *oddball) { uint8_t type; uintptr_t strptr; @@ -1602,7 +1714,6 @@ jsobj_is_undefined(uintptr_t addr) } v8_silent--; - typename = enum_lookup_str(v8_types, type, ""); if (strcmp(typename, "Oddball") != 0) return (B_FALSE); @@ -1613,16 +1724,28 @@ jsobj_is_undefined(uintptr_t addr) if (jsstr_print(strptr, JSSTR_NUDE, &bufp, &len) != 0) return (B_FALSE); - return (strcmp(buf, "undefined") == 0); + return (strcmp(buf, oddball) == 0); +} + +static boolean_t +jsobj_is_undefined(uintptr_t addr) +{ + return (jsobj_is_oddball(addr, "undefined")); +} + +static boolean_t +jsobj_is_hole(uintptr_t addr) +{ + return (jsobj_is_oddball(addr, "hole")); } static int jsobj_properties(uintptr_t addr, int (*func)(const char *, uintptr_t, void *), void *arg) { - uintptr_t ptr, map; - uintptr_t *props = NULL, *descs = NULL, *content = NULL, *trans; - size_t size, nprops, ndescs, ncontent, ntrans; + uintptr_t ptr, map, elements; + uintptr_t *props = NULL, *descs = NULL, *content = NULL, *trans, *elts; + size_t size, nprops, ndescs, ncontent, ntrans, len; ssize_t ii, rndescs; uint8_t type, ninprops; int rval = -1; @@ -1661,6 +1784,51 @@ jsobj_properties(uintptr_t addr, 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. + */ + if (V8_ELEMENTS_KIND_SHIFT != -1 && + read_heap_ptr(&elements, addr, V8_OFF_JSOBJECT_ELEMENTS) == 0 && + read_heap_array(elements, &elts, &len, UM_SLEEP) == 0 && len != 0) { + uint8_t bit_field2, kind; + size_t sz = len * sizeof (uintptr_t); + + if (mdb_vread(&bit_field2, sizeof (bit_field2), + map + V8_OFF_MAP_BIT_FIELD2) == -1) { + mdb_free(elts, sz); + goto err; + } + + kind = bit_field2 >> V8_ELEMENTS_KIND_SHIFT; + kind &= (1 << V8_ELEMENTS_KIND_BITCOUNT) - 1; + + if (kind == V8_ELEMENTS_FAST_ELEMENTS || + kind == V8_ELEMENTS_FAST_HOLEY_ELEMENTS) { + for (ii = 0; ii < len; ii++) { + char name[10]; + + if (kind == V8_ELEMENTS_FAST_HOLEY_ELEMENTS && + jsobj_is_hole(elts[ii])) + continue; + + snprintf(name, sizeof (name), "%d", ii); + + if (func(name, elts[ii], arg) != 0) { + mdb_free(elts, sz); + goto err; + } + } + } else if (kind == V8_ELEMENTS_DICTIONARY_ELEMENTS) { + if (read_heap_dict(elements, func, arg) != 0) { + mdb_free(elts, sz); + goto err; + } + } + + mdb_free(elts, sz); + } + if (V8_DICT_SHIFT != -1) { uintptr_t bit_field3; @@ -1804,18 +1972,27 @@ jsobj_properties(uintptr_t addr, /* property is stored directly in the object */ if (mdb_vread(&ptr, sizeof (ptr), addr + V8_OFF_HEAP( size + val * sizeof (uintptr_t))) == -1) { - v8_warn("failed to read in-object " - "property at %p\n", addr + V8_OFF_HEAP( - size + val * sizeof (uintptr_t))); + v8_warn("object %p: failed to read in-object " + "property at %p\n", addr, addr + + V8_OFF_HEAP(size + val * + sizeof (uintptr_t))); continue; } } else { /* property should be in "props" array */ if (val >= nprops) { - v8_warn("property descriptor %d: value index " - "value (%d) out of bounds (%d)\n", ii, val, - nprops); - continue; + /* + * This can happen when properties are deleted. + * If this value isn't obviously corrupt, we'll + * just silently ignore it. + */ + if (val < rndescs) + continue; + + v8_warn("object %p: property descriptor %d: " + "value index value (%d) out of bounds " + "(%d)\n", addr, ii, val, nprops); + goto err; } ptr = props[val]; @@ -1845,11 +2022,15 @@ err: * undefined, prints the token position instead. */ static int -jsfunc_lineno(uintptr_t lendsp, uintptr_t tokpos, char *buf, size_t buflen) +jsfunc_lineno(uintptr_t lendsp, uintptr_t tokpos, + char *buf, size_t buflen, int *lineno) { uintptr_t size, bufsz, lower, upper, ii = 0; uintptr_t *data; + if (lineno != NULL) + *lineno = -1; + if (jsobj_is_undefined(lendsp)) { /* * The token position is an SMI, but it comes in as its raw @@ -1858,6 +2039,10 @@ jsfunc_lineno(uintptr_t lendsp, uintptr_t tokpos, char *buf, size_t buflen) * we must convert it here. */ mdb_snprintf(buf, buflen, "position %d", V8_SMI_VALUE(tokpos)); + + if (lineno != NULL) + *lineno = 0; + return (0); } @@ -1883,12 +2068,20 @@ jsfunc_lineno(uintptr_t lendsp, uintptr_t tokpos, char *buf, size_t buflen) if (tokpos > data[upper]) { (void) strlcpy(buf, "position out of range", buflen); mdb_free(data, bufsz); + + if (lineno != NULL) + *lineno = 0; + return (0); } if (tokpos <= data[0]) { (void) strlcpy(buf, "line 1", buflen); mdb_free(data, bufsz); + + if (lineno != NULL) + *lineno = 1; + return (0); } @@ -1902,12 +2095,118 @@ jsfunc_lineno(uintptr_t lendsp, uintptr_t tokpos, char *buf, size_t buflen) break; } + if (lineno != NULL) + *lineno = ii + 1; + (void) mdb_snprintf(buf, buflen, "line %d", ii + 1); mdb_free(data, bufsz); return (0); } /* + * Given a Script object, prints nlines on either side of lineno, with each + * line prefixed by prefix (if non-NULL). + */ +static void +jsfunc_lines(uintptr_t scriptp, + uintptr_t start, uintptr_t end, int nlines, char *prefix) +{ + uintptr_t src; + char *buf, *bufp; + size_t bufsz = 1024, len; + int i, line, slop = 10; + boolean_t newline = B_TRUE; + int startline = -1, endline = -1; + + if (read_heap_ptr(&src, scriptp, V8_OFF_SCRIPT_SOURCE) != 0) + return; + + for (;;) { + if ((buf = mdb_zalloc(bufsz, UM_NOSLEEP)) == NULL) { + mdb_warn("failed to allocate source code " + "buffer of size %d", bufsz); + return; + } + + bufp = buf; + len = bufsz; + + if (jsstr_print(src, JSSTR_NUDE, &bufp, &len) != 0) { + mdb_free(buf, bufsz); + return; + } + + if (len > slop) + break; + + mdb_free(buf, bufsz); + bufsz <<= 1; + } + + if (end >= bufsz) + return; + + /* + * First, take a pass to determine where our lines actually start. + */ + for (i = 0, line = 1; buf[i] != '\0'; i++) { + if (buf[i] == '\n') + line++; + + if (i == start) + startline = line; + + if (i == end) { + endline = line; + break; + } + } + + if (startline == -1 || endline == -1) { + mdb_warn("for script %p, could not determine startline/endline" + " (start %ld, end %ld, nlines %d)", + scriptp, start, end, nlines); + mdb_free(buf, bufsz); + return; + } + + for (i = 0, line = 1; buf[i] != '\0'; i++) { + if (buf[i] == '\n') { + line++; + newline = B_TRUE; + } + + if (line < startline - nlines) + continue; + + if (line > endline + nlines) + break; + + mdb_printf("%c", buf[i]); + + if (newline) { + if (line >= startline && line <= endline) + mdb_printf("%"); + + if (prefix != NULL) + mdb_printf(prefix, line); + + if (line >= startline && line <= endline) + mdb_printf("%"); + + newline = B_FALSE; + } + } + + mdb_printf("\n"); + + if (line == endline) + mdb_printf("%"); + + mdb_free(buf, bufsz); +} + +/* * Given a SharedFunctionInfo object, prints into bufp a name of the function * suitable for printing. This function attempts to infer a name for anonymous * functions. @@ -1919,8 +2218,13 @@ jsfunc_name(uintptr_t funcinfop, char **bufp, size_t *lenp) char *bufs = *bufp; if (read_heap_ptr(&ptrp, funcinfop, - V8_OFF_SHAREDFUNCTIONINFO_NAME) != 0 || - jsstr_print(ptrp, JSSTR_NUDE, bufp, lenp) != 0) + V8_OFF_SHAREDFUNCTIONINFO_NAME) != 0) { + (void) bsnprintf(bufp, lenp, + ""); + return (-1); + } + + if (jsstr_print(ptrp, JSSTR_NUDE, bufp, lenp) != 0) return (-1); if (*bufp != bufs) @@ -1959,6 +2263,7 @@ typedef struct jsobj_print { 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 *); @@ -2001,12 +2306,14 @@ jsobj_print(uintptr_t addr, jsobj_print_t *jsop) } if (!V8_IS_HEAPOBJECT(addr)) { - v8_warn("not a heap object: %p\n", addr); + (void) bsnprintf(bufp, lenp, ""); return (-1); } - if (read_typebyte(&type, addr) != 0) + if (read_typebyte(&type, addr) != 0) { + (void) bsnprintf(bufp, lenp, ""); return (-1); + } if (V8_TYPE_STRING(type)) { if (jsstr_print(addr, JSSTR_QUOTED, bufp, lenp) == -1) @@ -2018,11 +2325,14 @@ jsobj_print(uintptr_t addr, jsobj_print_t *jsop) klass = enum_lookup_str(v8_types, type, ""); for (ent = &table[0]; ent->name != NULL; ent++) { - if (strcmp(klass, ent->name) == 0) + if (strcmp(klass, ent->name) == 0) { + jsop->jsop_descended = B_TRUE; return (ent->func(addr, jsop)); + } } - v8_warn("%p: unknown JavaScript object type \"%s\"\n", addr, klass); + (void) bsnprintf(bufp, lenp, + "", klass); return (-1); } @@ -2158,10 +2468,20 @@ jsobj_print_jsarray_member(uintptr_t addr, jsobj_print_t *jsop) uintptr_t ptr; const char *member = jsop->jsop_member, *end, *p; size_t elt = 0, place = 1, len, rv; + char **bufp = jsop->jsop_bufp; + size_t *lenp = jsop->jsop_lenp; - if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0 || - read_heap_array(ptr, &elts, &len, UM_SLEEP | UM_GC) != 0) + if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0) { + (void) bsnprintf(bufp, lenp, + ""); return (-1); + } + + if (read_heap_array(ptr, &elts, &len, UM_SLEEP | UM_GC) != 0) { + (void) bsnprintf(bufp, lenp, + ""); + return (-1); + } if (*member != '[') { mdb_warn("expected bracketed array index; " @@ -2245,9 +2565,16 @@ jsobj_print_jsarray(uintptr_t addr, jsobj_print_t *jsop) return (0); } - if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0 || - read_heap_array(ptr, &elts, &len, UM_SLEEP | UM_GC) != 0) + if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0) { + (void) bsnprintf(bufp, lenp, + ""); return (-1); + } + + if (read_heap_array(ptr, &elts, &len, UM_SLEEP | UM_GC) != 0) { + (void) bsnprintf(bufp, lenp, ""); + return (-1); + } if (len == 0) { (void) bsnprintf(bufp, lenp, "[]"); @@ -2267,7 +2594,7 @@ jsobj_print_jsarray(uintptr_t addr, jsobj_print_t *jsop) (void) bsnprintf(bufp, lenp, "[\n"); - for (ii = 0; ii < len; ii++) { + for (ii = 0; ii < len && *lenp > 0; ii++) { (void) bsnprintf(bufp, lenp, "%*s", indent + 4, ""); (void) jsobj_print(elts[ii], &descend); (void) bsnprintf(bufp, lenp, ",\n"); @@ -2308,17 +2635,23 @@ jsobj_print_jsdate(uintptr_t addr, jsobj_print_t *jsop) return (0); } - if (read_heap_ptr(&value, addr, V8_OFF_JSDATE_VALUE) != 0) + if (read_heap_ptr(&value, addr, V8_OFF_JSDATE_VALUE) != 0) { + (void) bsnprintf(bufp, lenp, ""); return (-1); + } - if (read_typebyte(&type, value) != 0) + if (read_typebyte(&type, value) != 0) { + (void) bsnprintf(bufp, lenp, ""); return (-1); + } if (strcmp(enum_lookup_str(v8_types, type, ""), "HeapNumber") != 0) return (-1); - if (read_heap_double(&numval, value, V8_OFF_HEAPNUMBER_VALUE) == -1) + if (read_heap_double(&numval, value, V8_OFF_HEAPNUMBER_VALUE) == -1) { + (void) bsnprintf(bufp, lenp, ""); return (-1); + } mdb_snprintf(buf, sizeof (buf), "%Y", (time_t)((long long)numval / MILLISEC)); @@ -2413,12 +2746,14 @@ dcmd_v8function(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) NULL) != argc) return (DCMD_USAGE); + v8_warnings++; + if (read_typebyte(&type, addr) != 0) - return (DCMD_ERR); + goto err; if (strcmp(enum_lookup_str(v8_types, type, ""), "JSFunction") != 0) { v8_warn("%p is not an instance of JSFunction\n", addr); - return (DCMD_ERR); + goto err; } if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0 || @@ -2428,12 +2763,12 @@ dcmd_v8function(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 || read_heap_ptr(&namep, scriptp, V8_OFF_SCRIPT_NAME) != 0 || read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0) - return (DCMD_ERR); + goto err; bufp = buf; len = sizeof (buf); if (jsfunc_name(funcinfop, &bufp, &len) != 0) - return (DCMD_ERR); + goto err; mdb_printf("%p: JSFunction: %s\n", addr, buf); @@ -2444,16 +2779,22 @@ dcmd_v8function(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) if (jsstr_print(namep, JSSTR_NUDE, &bufp, &len) == 0) mdb_printf("%s ", buf); - if (jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf)) == 0) + if (jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf), NULL) == 0) mdb_printf("%s", buf); mdb_printf("\n"); if (read_heap_ptr(&codep, funcinfop, V8_OFF_SHAREDFUNCTIONINFO_CODE) != 0) - return (DCMD_ERR); + goto err; + + v8_warnings--; return (do_v8code(codep, opt_d)); + +err: + v8_warnings--; + return (DCMD_ERR); } /* ARGSUSED */ @@ -2636,15 +2977,16 @@ 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) + char *func, char *prop, uintptr_t nlines) { - uintptr_t funcp, funcinfop, tokpos, scriptp, lendsp, ptrp; + uintptr_t funcp, funcinfop, tokpos, endpos, scriptp, lendsp, ptrp; uintptr_t ii, nargs; const char *typename; char *bufp; size_t len; uint8_t type; char buf[256]; + int lineno; /* * Check for non-JavaScript frames first. @@ -2667,12 +3009,8 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose, * Check if this thing is really a JSFunction at all. For some frames, * it's a Code object, presumably indicating some internal frame. */ - v8_silent++; - if (read_typebyte(&type, funcp) != 0 || (typename = enum_lookup_str(v8_types, type, NULL)) == NULL) { - v8_silent--; - if (func != NULL || prop != NULL) return (DCMD_OK); @@ -2680,8 +3018,6 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose, return (DCMD_OK); } - v8_silent--; - if (strcmp("Code", typename) == 0) { if (func != NULL || prop != NULL) return (DCMD_OK); @@ -2748,16 +3084,6 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose, if (read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0) return (DCMD_ERR); - (void) jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf)); - - if (prop != NULL && strcmp(prop, "posn") == 0) { - mdb_printf("%s\n", buf); - return (DCMD_OK); - } - - if (prop == NULL) - mdb_printf("posn: %s\n", buf); - if (read_heap_smi(&nargs, funcinfop, V8_OFF_SHAREDFUNCTIONINFO_LENGTH) == 0) { for (ii = 0; ii < nargs; ii++) { @@ -2787,11 +3113,27 @@ 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, + V8_OFF_SHAREDFUNCTIONINFO_END_POSITION) == 0) { + jsfunc_lines(scriptp, + V8_SMI_VALUE(tokpos), endpos, nlines, "%5d "); + } + + mdb_printf("\n"); (void) mdb_dec_indent(4); return (DCMD_OK); @@ -3681,11 +4023,13 @@ 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; if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v, 'i', MDB_OPT_SETBITS, B_TRUE, &opt_i, 'f', MDB_OPT_STR, &opt_f, + 'n', MDB_OPT_UINTPTR, &opt_n, 'p', MDB_OPT_STR, &opt_p, NULL) != argc) return (DCMD_USAGE); @@ -3697,7 +4041,7 @@ dcmd_jsframe(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) * overridden with the "-i" option (for "immediate"). */ if (opt_i) - return (do_jsframe(addr, 0, opt_v, opt_f, opt_p)); + return (do_jsframe(addr, 0, opt_v, opt_f, opt_p, opt_n)); if (mdb_vread(&raddr, sizeof (raddr), addr + sizeof (uintptr_t)) == -1) { @@ -3714,7 +4058,7 @@ 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)); + return (do_jsframe(fptr, raddr, opt_v, opt_f, opt_p, opt_n)); } /* ARGSUSED */ @@ -3767,8 +4111,12 @@ dcmd_jsprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) len = bufsz; } - if (jsop.jsop_member == NULL && rv != 0) + if (jsop.jsop_member == NULL && rv != 0) { + if (!jsop.jsop_descended) + mdb_warn("%s\n", buf); + return (DCMD_ERR); + } if (jsop.jsop_member && !jsop.jsop_found) { if (jsop.jsop_baseaddr) @@ -3883,13 +4231,14 @@ 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; - boolean_t opt_v; + uintptr_t raddr, opt_n = 5; + boolean_t opt_v = B_FALSE; char *opt_f = NULL, *opt_p = NULL; if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v, 'f', MDB_OPT_STR, &opt_f, + 'n', MDB_OPT_UINTPTR, &opt_n, 'p', MDB_OPT_STR, &opt_p, NULL) != argc) return (DCMD_USAGE); @@ -3901,7 +4250,7 @@ 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) != 0) + do_jsframe(addr, raddr, opt_v, opt_f, opt_p, opt_n) != 0) return (DCMD_ERR); } @@ -3993,6 +4342,16 @@ dcmd_v8load(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) return (DCMD_OK); } +/* ARGSUSED */ +static int +dcmd_v8warnings(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + v8_warnings ^= 1; + mdb_printf("v8 warnings are now %s\n", v8_warnings ? "on" : "off"); + + return (DCMD_OK); +} + static int walk_jsframes_init(mdb_walk_state_t *wsp) { @@ -4116,11 +4475,11 @@ static const mdb_dcmd_t v8_mdb_dcmds[] = { /* * Commands to inspect JavaScript-level state */ - { "jsframe", ":[-iv] [-f function] [-p property]", + { "jsframe", ":[-iv] [-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]", + { "jsstack", "[-v] [-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 }, @@ -4150,6 +4509,8 @@ static const mdb_dcmd_t v8_mdb_dcmds[] = { dcmd_v8type }, { "v8types", NULL, "list known V8 heap object types", dcmd_v8types }, + { "v8warnings", NULL, "toggle V8 warnings", + dcmd_v8warnings }, { NULL } }; diff --git a/deps/v8/tools/gen-postmortem-metadata.py b/deps/v8/tools/gen-postmortem-metadata.py index 2837727..1a4f221 100644 --- a/deps/v8/tools/gen-postmortem-metadata.py +++ b/deps/v8/tools/gen-postmortem-metadata.py @@ -106,6 +106,14 @@ consts_misc = [ 'value': 'JavaScriptFrameConstants::kFunctionOffset' }, { 'name': 'off_fp_args', 'value': 'JavaScriptFrameConstants::kLastParameterOffset' }, + + { 'name': 'elements_kind_shift', + 'value': 'Map::kElementsKindShift' }, + { 'name': 'elements_kind_bitcount', + 'value': 'Map::kElementsKindBitCount' }, + { 'name': 'elements_fast_elements', 'value': 'FAST_ELEMENTS' }, + { 'name': 'elements_fast_holey_elements', 'value': 'FAST_HOLEY_ELEMENTS' }, + { 'name': 'elements_dictionary_elements', 'value': 'DICTIONARY_ELEMENTS' }, ]; # diff --git a/test/pummel/test-postmortem-findjsobjects.js b/test/pummel/test-postmortem-findjsobjects.js index 292b060..65214a0 100644 --- a/test/pummel/test-postmortem-findjsobjects.js +++ b/test/pummel/test-postmortem-findjsobjects.js @@ -22,6 +22,7 @@ var common = require('../common'); var assert = require('assert'); var os = require('os'); +var path = require('path'); var util = require('util'); if (os.type() != 'SunOS') { @@ -89,7 +90,14 @@ gcore.on('exit', function (code) { console.log('mdb stderr: ' + data); }); - mdb.stdin.write('::load v8.so\n'); + var mod = util.format('::load %s\n', + path.join(__dirname, + '..', + '..', + 'out', + 'Release', + 'mdb_v8.so')); + mdb.stdin.write(mod); mdb.stdin.write('::findjsobjects -c LanguageH | '); mdb.stdin.write('::findjsobjects | ::jsprint\n'); mdb.stdin.write('::findjsobjects -p OBEY | '); diff --git a/test/pummel/test-postmortem-jsstack.js b/test/pummel/test-postmortem-jsstack.js index 735e39e..04b3352 100644 --- a/test/pummel/test-postmortem-jsstack.js +++ b/test/pummel/test-postmortem-jsstack.js @@ -90,6 +90,14 @@ dtrace.on('exit', function (code) { */ var mdb = spawn('mdb', args, { stdio: 'pipe' }); + var mod = util.format('::load %s\n', + path.join(__dirname, + '..', + '..', + 'out', + 'Release', + 'mdb_v8.so')); + mdb.on('exit', function (code) { var retained = '; core retained as ' + corefile; @@ -157,7 +165,7 @@ dtrace.on('exit', function (code) { console.log('mdb (second) stderr: ' + data); }); - mdb.stdin.write('::load v8.so\n'); + mdb.stdin.write(mod); mdb.stdin.write(straddr + '::v8str\n'); mdb.stdin.end(); }); @@ -170,7 +178,7 @@ dtrace.on('exit', function (code) { console.log('mdb stderr: ' + data); }); - mdb.stdin.write('::load v8.so\n'); + mdb.stdin.write(mod); mdb.stdin.write('::jsstack -v\n'); mdb.stdin.end(); });