dtrace, mdb_v8: support more string, frame types
authorDave Pacheco <dap@joyent.com>
Mon, 7 Oct 2013 20:17:49 +0000 (13:17 -0700)
committerTimothy J Fontaine <tjfontaine@gmail.com>
Tue, 8 Oct 2013 20:48:53 +0000 (13:48 -0700)
This change makes several improvements to the ustack helper and MDB
support:

- ustack helper and MDB: add support for two-byte strings
  (necessary to print many filenames in stacktraces in 0.10 and later).
- ustack helper: fix position numbers, which were off by a factor of two
- ustack helper: fix frames with undefined Scripts (e.g., "RegExp")
- ustack helper: add stub frames
- MDB: add support for sliced strings
- MDB: sync up with changes from the illumos version of the module

Fixes #6309
Closes #6318

deps/mdb_v8/mdb_v8.c
deps/mdb_v8/v8dbg.h
src/v8abbr.h
src/v8ustack.d

index 5ebfb65..998028d 100644 (file)
  * makes heavy use of metadata defined in the V8 binary for inspecting in-memory
  * structures.  Canned configurations can be manually loaded for V8 binaries
  * that predate this metadata.  See mdb_v8_cfg.c for details.
+ *
+ * NOTE: This dmod implementation (including this file and related headers and C
+ * files) exist in both the Node and illumos source trees.  THESE SHOULD BE KEPT
+ * IN SYNC.  The version in the Node tree is built directly into modern Node
+ * binaries as part of the build process, and the version in the illumos source
+ * tree is delivered with the OS for debugging Node binaries that predate
+ * support for including the dmod directly in the binary.  Note too that these
+ * files have different licenses to match their corresponding repositories.
  */
 
 /*
@@ -119,6 +127,7 @@ static intptr_t     V8_AsciiStringTag;
 static intptr_t        V8_StringRepresentationMask;
 static intptr_t        V8_SeqStringTag;
 static intptr_t        V8_ConsStringTag;
+static intptr_t        V8_SlicedStringTag;
 static intptr_t        V8_ExternalStringTag;
 static intptr_t        V8_FailureTag;
 static intptr_t        V8_FailureTagMask;
@@ -180,12 +189,15 @@ static ssize_t V8_OFF_SCRIPT_LINE_ENDS;
 static ssize_t V8_OFF_SCRIPT_NAME;
 static ssize_t V8_OFF_SEQASCIISTR_CHARS;
 static ssize_t V8_OFF_SEQONEBYTESTR_CHARS;
+static ssize_t V8_OFF_SEQTWOBYTESTR_CHARS;
 static ssize_t V8_OFF_SHAREDFUNCTIONINFO_CODE;
 static ssize_t V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION;
 static ssize_t V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME;
 static ssize_t V8_OFF_SHAREDFUNCTIONINFO_LENGTH;
 static ssize_t V8_OFF_SHAREDFUNCTIONINFO_SCRIPT;
 static ssize_t V8_OFF_SHAREDFUNCTIONINFO_NAME;
+static ssize_t V8_OFF_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 */
@@ -233,6 +245,8 @@ static v8_constant_t v8_constants[] = {
        { &V8_StringRepresentationMask, "v8dbg_StringRepresentationMask" },
        { &V8_SeqStringTag,             "v8dbg_SeqStringTag"            },
        { &V8_ConsStringTag,            "v8dbg_ConsStringTag"           },
+       { &V8_SlicedStringTag,          "v8dbg_SlicedStringTag",
+           V8_CONSTANT_FALLBACK(0, 0), 0x3 },
        { &V8_ExternalStringTag,        "v8dbg_ExternalStringTag"       },
        { &V8_FailureTag,               "v8dbg_FailureTag"              },
        { &V8_FailureTagMask,           "v8dbg_FailureTagMask"          },
@@ -284,6 +298,7 @@ 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[] = {
@@ -339,6 +354,8 @@ static v8_offset_t v8_offsets[] = {
            "SeqAsciiString", "chars", B_TRUE },
        { &V8_OFF_SEQONEBYTESTR_CHARS,
            "SeqOneByteString", "chars", B_TRUE },
+       { &V8_OFF_SEQTWOBYTESTR_CHARS,
+           "SeqTwoByteString", "chars", B_TRUE },
        { &V8_OFF_SHAREDFUNCTIONINFO_CODE,
            "SharedFunctionInfo", "code" },
        { &V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION,
@@ -351,6 +368,10 @@ static v8_offset_t v8_offsets[] = {
            "SharedFunctionInfo", "name" },
        { &V8_OFF_SHAREDFUNCTIONINFO_SCRIPT,
            "SharedFunctionInfo", "script" },
+       { &V8_OFF_SLICEDSTRING_OFFSET,
+           "SlicedString", "offset" },
+       { &V8_OFF_SLICEDSTRING_PARENT,
+           "SlicedString", "parent", B_TRUE },
        { &V8_OFF_STRING_LENGTH,
            "String", "length" },
 };
@@ -516,6 +537,13 @@ again:
        if (V8_OFF_SEQONEBYTESTR_CHARS != -1)
                V8_OFF_SEQASCIISTR_CHARS = V8_OFF_SEQONEBYTESTR_CHARS;
 
+       if (V8_OFF_SEQTWOBYTESTR_CHARS == -1)
+               V8_OFF_SEQTWOBYTESTR_CHARS = V8_OFF_SEQASCIISTR_CHARS;
+
+       if (V8_OFF_SLICEDSTRING_PARENT == -1)
+               V8_OFF_SLICEDSTRING_PARENT = V8_OFF_SLICEDSTRING_OFFSET -
+                   sizeof (uintptr_t);
+
        return (failed ? -1 : 0);
 }
 
@@ -795,6 +823,7 @@ conf_class_compute_offsets(v8_class_t *clp)
 #define        JSSTR_NUDE              JSSTR_NONE
 #define        JSSTR_VERBOSE           0x1
 #define        JSSTR_QUOTED            0x2
+#define        JSSTR_ISASCII           0x4
 
 static int jsstr_print(uintptr_t, uint_t, char **, size_t *);
 static boolean_t jsobj_is_undefined(uintptr_t addr);
@@ -996,6 +1025,7 @@ read_heap_array(uintptr_t addr, uintptr_t **retp, size_t *lenp, int flags)
                if (!(flags & UM_GC))
                        mdb_free(*retp, len * sizeof (uintptr_t));
 
+               *retp = NULL;
                return (-1);
        }
 
@@ -1290,11 +1320,13 @@ obj_print_class(uintptr_t addr, v8_class_t *clp)
 }
 
 /*
- * Print the ASCII string for the given ASCII JS string, expanding ConsStrings
- * and ExternalStrings as needed.
+ * Print the ASCII string for the given JS string, expanding ConsStrings and
+ * ExternalStrings as needed.
  */
-static int jsstr_print_seq(uintptr_t, uint_t, char **, size_t *);
+static int jsstr_print_seq(uintptr_t, uint_t, char **, size_t *, size_t,
+    ssize_t);
 static int jsstr_print_cons(uintptr_t, uint_t, char **, size_t *);
+static int jsstr_print_sliced(uintptr_t, uint_t, char **, size_t *);
 static int jsstr_print_external(uintptr_t, uint_t, char **, size_t *);
 
 static int
@@ -1315,11 +1347,6 @@ jsstr_print(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
                return (0);
        }
 
-       if (!V8_STRENC_ASCII(typebyte)) {
-               (void) bsnprintf(bufp, lenp, "<two-byte string>");
-               return (0);
-       }
-
        if (verbose) {
                lbufp = buf;
                llen = sizeof (buf);
@@ -1328,12 +1355,19 @@ jsstr_print(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
                (void) mdb_inc_indent(4);
        }
 
+       if (V8_STRENC_ASCII(typebyte))
+               flags |= JSSTR_ISASCII;
+       else
+               flags &= ~JSSTR_ISASCII;
+
        if (V8_STRREP_SEQ(typebyte))
-               err = jsstr_print_seq(addr, flags, bufp, lenp);
+               err = jsstr_print_seq(addr, flags, bufp, lenp, 0, -1);
        else if (V8_STRREP_CONS(typebyte))
                err = jsstr_print_cons(addr, flags, bufp, lenp);
        else if (V8_STRREP_EXT(typebyte))
                err = jsstr_print_external(addr, flags, bufp, lenp);
+       else if (V8_STRREP_SLICED(typebyte))
+               err = jsstr_print_sliced(addr, flags, bufp, lenp);
        else {
                (void) bsnprintf(bufp, lenp, "<unknown string type>");
                err = -1;
@@ -1346,42 +1380,85 @@ jsstr_print(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
 }
 
 static int
-jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
+jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp,
+    size_t sliceoffset, ssize_t slicelen)
 {
        /*
         * To allow the caller to allocate a very large buffer for strings,
         * we'll allocate a buffer sized based on our input, making it at
         * least enough space for our ellipsis and at most 256K.
         */
-       uintptr_t len, rlen, blen = *lenp + sizeof ("[...]") + 1;
-       char *buf = alloca(MIN(blen, 256 * 1024));
+       uintptr_t i, nstrchrs, nreadbytes, nreadoffset, blen, nstrbytes;
        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);
+       buf = alloca(blen);
 
-       if (read_heap_smi(&len, addr, V8_OFF_STRING_LENGTH) != 0)
+       if (read_heap_smi(&nstrchrs, addr, V8_OFF_STRING_LENGTH) != 0)
                return (-1);
 
-       rlen = len <= blen - 1 ? len : blen - sizeof ("[...]");
+       if (slicelen != -1)
+               nstrchrs = slicelen;
+       if (nstrchrs < 0)
+               nstrchrs = 0;
+
+       if ((flags & JSSTR_ISASCII) != 0) {
+               nstrbytes = nstrchrs;
+               nreadoffset = sliceoffset;
+       } else {
+               nstrbytes = 2 * nstrchrs;
+               nreadoffset = 2 * sliceoffset;
+       }
+
+       nreadbytes = nstrbytes + sizeof ("\"\"") <= blen ? nstrbytes :
+           blen - sizeof ("\"\"[...]");
 
        if (verbose)
-               mdb_printf("length: %d, will read: %d\n", len, rlen);
+               mdb_printf("length: %d chars (%d bytes), "
+                   "will read %d bytes from offset %d\n",
+                   nstrchrs, nstrbytes, nreadbytes, nreadoffset);
+
+       if (nstrbytes == 0) {
+               (void) bsnprintf(bufp, lenp, "%s%s",
+                   quoted ? "\"" : "", quoted ? "\"" : "");
+               return (0);
+       }
 
        buf[0] = '\0';
 
-       if (rlen > 0 && mdb_readstr(buf, rlen + 1,
-           addr + V8_OFF_SEQASCIISTR_CHARS) == -1) {
-               v8_warn("failed to read SeqString data");
-               return (-1);
-       }
+       if ((flags & JSSTR_ISASCII) != 0) {
+               if (mdb_readstr(buf, nreadbytes + 1,
+                   addr + V8_OFF_SEQASCIISTR_CHARS + nreadoffset) == -1) {
+                       v8_warn("failed to read SeqString data");
+                       return (-1);
+               }
 
-       if (rlen != len)
-               (void) strlcat(buf, "[...]", blen);
+               if (nreadbytes != nstrbytes)
+                       (void) strlcat(buf, "[...]", blen);
 
-       if (verbose)
-               mdb_printf("value: \"%s\"\n", buf);
+               (void) bsnprintf(bufp, lenp, "%s%s%s",
+                   quoted ? "\"" : "", buf, quoted ? "\"" : "");
+       } else {
+               if (mdb_readstr(buf, nreadbytes,
+                   addr + V8_OFF_SEQTWOBYTESTR_CHARS + nreadoffset) == -1) {
+                       v8_warn("failed to read SeqTwoByteString data");
+                       return (-1);
+               }
 
-       (void) bsnprintf(bufp, lenp, "%s%s%s",
-           quoted ? "\"" : "", buf, quoted ? "\"" : "");
+               (void) bsnprintf(bufp, lenp, "%s", quoted ? "\"" : "");
+               for (i = 0; i < nreadbytes; i += 2) {
+                       chrval = *((uint16_t *)(buf + i));
+                       (void) bsnprintf(bufp, lenp, "%c",
+                           (isascii(chrval) || chrval == 0) ?
+                           (char)chrval : '?');
+               }
+               if (nreadbytes != nstrbytes)
+                       (void) bsnprintf(bufp, lenp, "[...]");
+               (void) bsnprintf(bufp, lenp, "%s", quoted ? "\"" : "");
+       }
 
        return (0);
 }
@@ -1418,14 +1495,64 @@ jsstr_print_cons(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
 }
 
 static int
-jsstr_print_external(uintptr_t addr, uint_t flags, char **bufp,
-    size_t *lenp)
+jsstr_print_sliced(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
+{
+       uintptr_t parent, offset, length;
+       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)
+               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, "<sliced string>");
+               return (0);
+       }
+
+       if (!V8_STRREP_SEQ(typebyte)) {
+               v8_warn("SlicedString %s: parent is not a sequential string",
+                   addr);
+               (void) bsnprintf(bufp, lenp, "<sliced string>");
+               return (0);
+       }
+
+       if (quoted)
+               (void) bsnprintf(bufp, lenp, "\"");
+
+       newflags = verbose ? JSSTR_VERBOSE : 0;
+       if (V8_STRENC_ASCII(typebyte))
+               newflags |= JSSTR_ISASCII;
+       if (jsstr_print_seq(parent, newflags, bufp, lenp, offset, length) != 0)
+               return (-1);
+
+       if (quoted)
+               (void) bsnprintf(bufp, lenp, "\"");
+
+       return (0);
+}
+
+static int
+jsstr_print_external(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
 {
        uintptr_t ptr1, ptr2;
        size_t blen = *lenp + 1;
        char *buf = alloca(blen);
        boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE;
 
+       if ((flags & JSSTR_ISASCII) == 0) {
+               (void) bsnprintf(bufp, lenp, "<external two-byte string>");
+               return (0);
+       }
+
        if (flags & JSSTR_VERBOSE)
                mdb_printf("assuming Node.js string\n");
 
index 6e56e97..a66329e 100644 (file)
@@ -61,6 +61,8 @@
        (((type) & V8_StringRepresentationMask) == V8_SeqStringTag)
 #define        V8_STRREP_CONS(type)    \
        (((type) & V8_StringRepresentationMask) == V8_ConsStringTag)
+#define        V8_STRREP_SLICED(type)  \
+       (((type) & V8_StringRepresentationMask) == V8_SlicedStringTag)
 #define        V8_STRREP_EXT(type)     \
        (((type) & V8_StringRepresentationMask) == V8_ExternalStringTag)
 
index 7a5f9e7..6c7a765 100644 (file)
@@ -42,6 +42,7 @@
 #define V8_FT_INTERNAL              V8DBG_FRAMETYPE_INTERNALFRAME
 #define V8_FT_CONSTRUCT             V8DBG_FRAMETYPE_CONSTRUCTFRAME
 #define V8_FT_ADAPTOR               V8DBG_FRAMETYPE_ARGUMENTSADAPTORFRAME
+#define V8_FT_STUB                  V8DBG_FRAMETYPE_STUBFRAME
 
 /* Identification masks and tags */
 #define V8_SmiTagMask               (V8DBG_SMITAGMASK)
 /* Instance types */
 #define V8_IT_FIXEDARRAY            V8DBG_TYPE_FIXEDARRAY__FIXED_ARRAY_TYPE
 #define V8_IT_CODE                  V8DBG_TYPE_CODE__CODE_TYPE
+#define V8_IT_SCRIPT                V8DBG_TYPE_SCRIPT__SCRIPT_TYPE
 
 /* Node-specific offsets */
 #define NODE_OFF_EXTSTR_DATA        sizeof(void*)
 
+/*
+ * Not all versions of V8 have the offset for the "chars" array in the
+ * SeqTwoByteString class, but it's the same as the one for SeqOneByteString.
+ */
+#ifndef V8DBG_CLASS_SEQTWOBYTESTRING__CHARS__CHAR
+#define V8DBG_CLASS_SEQTWOBYTESTRING__CHARS__CHAR V8DBG_CLASS_SEQONEBYTESTRING__CHARS__CHAR
+#endif
+
 /* Heap class->field offsets */
 #define V8_OFF_HEAP(off)            ((off) - 1)
 
     V8_OFF_HEAP(V8DBG_CLASS_HEAPOBJECT__MAP__MAP)
 #define V8_OFF_MAP_ATTRS  \
     V8_OFF_HEAP(V8DBG_CLASS_MAP__INSTANCE_ATTRIBUTES__INT)
+#define V8_OFF_TWOBYTESTR_CHARS  \
+    V8_OFF_HEAP(V8DBG_CLASS_SEQTWOBYTESTRING__CHARS__CHAR)
 
 #endif  /* SRC_V8ABBR_H_ */
index d643f8f..08d06e2 100644 (file)
@@ -31,6 +31,9 @@
 #define        V8_MAP_PTR(ptr)         \
     ((ptr & ~V8_HeapObjectTagMask) | V8_HeapObjectTag)
 
+#define V8_TYPE_SCRIPT(type) \
+    ((type) == V8_IT_SCRIPT)
+
 /*
  * Determine the encoding and representation of a V8 string.
  */
 #define        ASCII_SEQSTR(value)     \
     (V8_TYPE_STRING(value) && V8_STRENC_ASCII(value) && V8_STRREP_SEQ(value))
 
-#define        ASCII_CONSSTR(value)    \
-    (V8_TYPE_STRING(value) && V8_STRENC_ASCII(value) && V8_STRREP_CONS(value))
+#define        TWOBYTE_SEQSTR(value)   \
+    (V8_TYPE_STRING(value) && !V8_STRENC_ASCII(value) && V8_STRREP_SEQ(value))
+
+#define        IS_CONSSTR(value)       \
+    (V8_TYPE_STRING(value) && V8_STRREP_CONS(value))
 
 #define        ASCII_EXTSTR(value)     \
     (V8_TYPE_STRING(value) && V8_STRENC_ASCII(value) && V8_STRREP_EXT(value))
  *    "len":   the string length
  *
  *    "attrs": the type identifier for the string, which indicates the
- *             encoding and representation.  We're only interested in ASCII
- *             encoded strings whose representation is one of:
+ *             encoding and representation.  We're only interested in strings
+ *             whose representation is one of:
  *
- *     SeqString       stored directly as a char array inside the object
+ *     SeqOneByteString stored directly as a char array inside the object
  *
- *     ConsString      pointer to two strings that should be concatenated
+ *     SeqTwoByteString stored as a UTF-16 char array inside the object
  *
- *     ExternalString  pointer to a char* outside the V8 heap
+ *     ConsString       pointer to two strings that should be concatenated
+ *
+ *     ExternalString   pointer to a char* outside the V8 heap
  */
 
 /*
     this->map = V8_MAP_PTR(COPYIN_PTR(str + V8_OFF_HEAPOBJ_MAP));      \
     attrs = COPYIN_UINT8(this->map + V8_OFF_MAP_ATTRS);
 
+#define        APPEND_SEQSTR(str, len, attrs) \
+    APPEND_SEQONEBYTESTR(str, len, attrs) \
+    APPEND_SEQTWOBYTESTR(str, len, attrs)
+
 /*
- * Print out the given SeqString, or do nothing if the string is not an ASCII
- * SeqString.
+ * Print out the given SeqOneByteString, or do nothing if the string is not an ASCII
+ * SeqOneByteString.
  */
-#define        APPEND_SEQSTR(str, len, attrs)                                  \
-    dtrace:helper:ustack:      \
-    /!this->done && len > 0 && ASCII_SEQSTR(attrs)/    \
-    {  \
+#define        APPEND_SEQONEBYTESTR(str, len, attrs)                           \
+    dtrace:helper:ustack:                                              \
+    /!this->done && len > 0 && ASCII_SEQSTR(attrs)/                    \
+    {                                                                  \
        copyinto(str + V8_OFF_STR_CHARS, len, this->buf + this->off);   \
        this->off += len;                                               \
     }
 
 /*
+ * LOOP_ITER: macro to paste "block" while "ivar" is less than "dynmax" and
+ * "statmax".  The subsequent LOOP_{4,8} macros facilitate pasting the same
+ * thing 4 and 8 times, respectively.  Like much of the rest of the code in this
+ * file, this is regrettably necessary given the constraints under which we're
+ * expected to run.
+ */
+#define        LOOP_ITER(ivar, dynmax, statmax, block) \
+       ((ivar) < (dynmax)) && ((ivar) < (statmax)) && (block); (ivar)++;
+
+#define        LOOP_4(block) \
+       block \
+       block \
+       block \
+       block \
+
+#define        LOOP_8(block) \
+       LOOP_4(block) \
+       LOOP_4(block)
+
+/*
+ * Print out the given SeqTwoByteString, or do nothing if the string is not an ASCII
+ * SeqTwoByteString.  NOTE: if you bump MAX_TWOBYTESTR_CHARS, you'll also need
+ * to modify the LOOP_* macro calls below to match.
+ */
+#define        MAX_TWOBYTESTR_CHARS    128
+#define        MAX_TWOBYTESTR_BYTES    (2 * MAX_TWOBYTESTR_CHARS)
+#define        TO_ASCII(c)             ((c) < 128 ? (c) : '?')
+
+#define        APPEND_SEQTWOBYTESTR(str, len, attrs)                           \
+    dtrace:helper:ustack:                                              \
+    /!this->done && len > 0 && TWOBYTE_SEQSTR(attrs)/                  \
+    {                                                                  \
+       this->i = 0;                                                    \
+       this->stbuf = (uint16_t *)alloca(MAX_TWOBYTESTR_BYTES + 2);     \
+       copyinto(str + V8_OFF_TWOBYTESTR_CHARS,                         \
+           MAX_TWOBYTESTR_BYTES, this->stbuf);                         \
+       this->stbuf[MAX_TWOBYTESTR_BYTES - 1] = '\0';                   \
+       this->stbuf[MAX_TWOBYTESTR_BYTES] = '\0';                       \
+                                                                       \
+       LOOP_8(LOOP_8(LOOP_4(LOOP_ITER(this->i, len,                    \
+           MAX_TWOBYTESTR_CHARS,                                       \
+           APPEND_CHR(TO_ASCII(this->stbuf[this->i]))))))              \
+                                                                       \
+       this->i = 0;                                                    \
+       this->stbuf = 0;                                                \
+    }
+
+/*
  * Print out the given Node.js ExternalString, or do nothing if the string is
  * not an ASCII ExternalString.
  */
 #define        APPEND_NODESTR(str, len, attrs)                                 \
-    dtrace:helper:ustack:      \
-    /!this->done && len > 0 && ASCII_EXTSTR(attrs)/    \
-    {  \
-       this->resource = COPYIN_PTR(str + V8_OFF_EXTSTR_RSRC);  \
+    dtrace:helper:ustack:                                              \
+    /!this->done && len > 0 && ASCII_EXTSTR(attrs)/                    \
+    {                                                                  \
+       this->resource = COPYIN_PTR(str + V8_OFF_EXTSTR_RSRC);          \
        this->dataptr = COPYIN_PTR(this->resource + NODE_OFF_EXTSTR_DATA);      \
        copyinto(this->dataptr, len, this->buf + this->off);                    \
        this->off += len;                                                       \
  */
 #define        EXPAND_STR(str, len, attrs, s1s, s1l, s1a, s2s, s2l, s2a)       \
     dtrace:helper:ustack:      \
-    /!this->done && len > 0 && ASCII_CONSSTR(attrs)/   \
+    /!this->done && len > 0 && IS_CONSSTR(attrs)/      \
     {  \
        len = 0;                                                        \
                                                                        \
@@ -316,6 +376,7 @@ dtrace:helper:ustack:
        this->funcnamelen = 0;
        this->funcnameattrs = 0;
        this->script = (off_t) 0;
+       this->scriptattrs = 0;
        this->scriptnamestr = (off_t) 0;
        this->scriptnamelen = 0;
        this->scriptnameattrs = 0;
@@ -409,6 +470,16 @@ dtrace:helper:ustack:
        stringof(this->buf);
 }
 
+dtrace:helper:ustack:
+/!this->done && IS_SMI(this->marker) &&
+ SMI_VALUE(this->marker) == V8_FT_STUB/
+{
+       this->done = 1;
+       APPEND_CHR8('<','<',' ','s','t','u','b',' ');
+       APPEND_CHR4('>','>','\0','\0');
+       stringof(this->buf);
+}
+
 /*
  * Now check for internal frames that we can only identify by seeing that
  * there's a Code object where there would be a JSFunction object for a
@@ -477,24 +548,38 @@ dtrace:helper:ustack:
 APPEND_V8STR(this->funcnamestr, this->funcnamelen, this->funcnameattrs)
 
 /*
- * Now look for the name of the script where the function was defined.
+ * Now look for the name of the script where the function was defined.  The
+ * "script" itself may be undefined for special functions like "RegExp".
  */
 dtrace:helper:ustack:
 /!this->done/
 {
        this->script = COPYIN_PTR(this->shared + V8_OFF_SHARED_SCRIPT);
+       this->map = V8_MAP_PTR(COPYIN_PTR(this->script + V8_OFF_HEAPOBJ_MAP));
+       this->scriptattrs = COPYIN_UINT8(this->map + V8_OFF_MAP_ATTRS);
+}
+
+dtrace:helper:ustack:
+/!this->done && !V8_TYPE_SCRIPT(this->scriptattrs)/
+{
+       APPEND_CHR('\0');
+       this->done = 1;
+       stringof(this->buf);
+}
+
+
+dtrace:helper:ustack:
+/!this->done/
+{
        this->scriptnamestr = COPYIN_PTR(this->script + V8_OFF_SCRIPT_NAME);
        LOAD_STRFIELDS(this->scriptnamestr, this->scriptnamelen,
            this->scriptnameattrs);
-
-       APPEND_CHR4(' ','a','t',' ');
 }
 
 dtrace:helper:ustack:
-/!this->done && this->scriptnamelen == 0/
+/!this->done && this->scriptnamelen != 0/
 {
-       APPEND_CHR8('<','u','n','k','n','o','w','n');
-       APPEND_CHR('>');
+       APPEND_CHR4(' ','a','t',' ');
 }
 
 APPEND_V8STR(this->scriptnamestr, this->scriptnamelen, this->scriptnameattrs)
@@ -512,6 +597,14 @@ dtrace:helper:ustack:
 }
 
 dtrace:helper:ustack:
+/!this->done && this->le_attrs != V8_IT_FIXEDARRAY && this->position == 0/
+{
+       APPEND_CHR('\0');
+       this->done = 1;
+       stringof(this->buf);
+}
+
+dtrace:helper:ustack:
 /!this->done && this->le_attrs != V8_IT_FIXEDARRAY/
 {
        /*
@@ -519,10 +612,9 @@ dtrace:helper:ustack:
         * undefined because V8 has not had to compute it yet.  In this case we
         * just show the raw position and call it a day.
         */
-       APPEND_CHR8(' ','p','o','s','i','t','i','o');
-       APPEND_CHR('n');
+       APPEND_CHR4(' ','p','o','s');
        APPEND_CHR(' ');
-       APPEND_NUM(this->position);
+       APPEND_NUM(SMI_VALUE(this->position));
        APPEND_CHR('\0');
        this->done = 1;
        stringof(this->buf);