Skip to content

Instantly share code, notes, and snippets.

@davepacheco
Created December 16, 2011 19:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davepacheco/273ef0ed61fd821ca59b to your computer and use it in GitHub Desktop.
Save davepacheco/273ef0ed61fd821ca59b to your computer and use it in GitHub Desktop.
From 52cfbde31bdeeaa5c3dac62ec3d32c88cd7d5227 Mon Sep 17 00:00:00 2001
From: Dave Pacheco <dap@joyent.com>
Date: Thu, 15 Dec 2011 16:51:33 -0800
Subject: [PATCH] simple DTrace ustack helper
---
src/v8constants.h | 83 +++++++
src/v8ustack.d | 626 +++++++++++++++++++++++++++++++++++++++++++++++++++++
wscript | 32 +++-
3 files changed, 737 insertions(+), 4 deletions(-)
create mode 100644 src/v8constants.h
create mode 100644 src/v8ustack.d
diff --git a/src/v8constants.h b/src/v8constants.h
new file mode 100644
index 0000000..70c26a2
--- /dev/null
+++ b/src/v8constants.h
@@ -0,0 +1,83 @@
+/*
+ * The following offsets are derived from the V8 3.6.6.14 source. A future
+ * version of this helper will automatically generate this file based on the
+ * debug metadata included in libv8. See v8ustack.d for details on how these
+ * values are used.
+ */
+
+#ifndef V8_CONSTANTS_H
+#define V8_CONSTANTS_H
+
+#if defined(__i386)
+
+/*
+ * Frame pointer offsets
+ */
+#define V8_OFF_FP_FUNC (-0x8)
+#define V8_OFF_FP_CONTEXT (-0x4)
+#define V8_OFF_FP_MARKER (-0x8)
+
+/*
+ * Heap class->field offsets
+ */
+#define V8_OFF_HEAP(off) ((off) - 1)
+
+#define V8_OFF_FUNC_SHARED V8_OFF_HEAP(0x14)
+#define V8_OFF_SHARED_NAME V8_OFF_HEAP(0x4)
+#define V8_OFF_SHARED_INFERRED V8_OFF_HEAP(0x24)
+#define V8_OFF_SHARED_SCRIPT V8_OFF_HEAP(0x1c)
+#define V8_OFF_SHARED_FUNTOK V8_OFF_HEAP(0x4c)
+#define V8_OFF_SCRIPT_NAME V8_OFF_HEAP(0x8)
+#define V8_OFF_SCRIPT_LENDS V8_OFF_HEAP(0x28)
+#define V8_OFF_STR_LENGTH V8_OFF_HEAP(0x4)
+#define V8_OFF_STR_CHARS V8_OFF_HEAP(0xc)
+#define V8_OFF_CONSSTR_CAR V8_OFF_HEAP(0xc)
+#define V8_OFF_CONSSTR_CDR V8_OFF_HEAP(0x10)
+#define V8_OFF_EXTSTR_RSRC V8_OFF_HEAP(0xc)
+#define V8_OFF_FA_SIZE V8_OFF_HEAP(0x4)
+#define V8_OFF_FA_DATA V8_OFF_HEAP(0x8)
+#define V8_OFF_HEAPOBJ_MAP V8_OFF_HEAP(0x0)
+#define V8_OFF_MAP_ATTRS V8_OFF_HEAP(0x8)
+
+#define NODE_OFF_EXTSTR_DATA 0x4
+
+/*
+ * Stack frame types
+ */
+#define V8_FT_ENTRY 0x1
+#define V8_FT_ENTRYCONSTRUCT 0x2
+#define V8_FT_EXIT 0x3
+#define V8_FT_JAVASCRIPT 0x4
+#define V8_FT_OPTIMIZED 0x5
+#define V8_FT_INTERNAL 0x6
+#define V8_FT_CONSTRUCT 0x7
+#define V8_FT_ADAPTOR 0x8
+
+/*
+ * Instance types
+ */
+#define V8_IT_FIXEDARRAY 0x9f
+
+/*
+ * Identification masks and tags
+ */
+#define V8_SmiTagMask 0x1
+#define V8_SmiTag 0x0
+#define V8_SmiValueShift V8_SmiTagMask
+
+#define V8_IsNotStringMask 0x80
+#define V8_StringTag 0x0
+
+#define V8_StringEncodingMask 0x4
+#define V8_AsciiStringTag 0x4
+
+#define V8_StringRepresentationMask 0x3
+#define V8_SeqStringTag 0x0
+#define V8_ConsStringTag 0x1
+#define V8_ExternalStringTag 0x2
+
+#else
+#error "only i386 is supported for DTrace ustack helper"
+#endif
+
+#endif /* V8_CONSTANTS_H */
diff --git a/src/v8ustack.d b/src/v8ustack.d
new file mode 100644
index 0000000..25bd85e
--- /dev/null
+++ b/src/v8ustack.d
@@ -0,0 +1,626 @@
+/*
+ * V8 DTrace ustack helper for annotating native stack traces with JavaScript
+ * function names. We start with a frame pointer (arg1) and emit a string
+ * describing the current function. We do this by chasing pointers to extract
+ * the function's name (if any) and the filename and line number where the
+ * function is defined.
+ *
+ * To use the helper, run node, then use the jstack() DTrace action to capture
+ * a JavaScript stacktrace. You may need to tune the dtrace_helper_actions_max
+ * kernel variable to 128.
+ */
+
+#include <v8constants.h>
+
+/*
+ * V8 represents small integers (SMI) using the upper 31 bits of a 32-bit
+ * value. To extract the actual integer value, we must shift it over.
+ */
+#define IS_SMI(value) ((value & V8_SmiTagMask) == V8_SmiTag)
+#define SMI_VALUE(value) ((int32_t)(value) >> V8_SmiValueShift)
+
+/*
+ * Determine the encoding and representation of a V8 string.
+ */
+#define V8_TYPE_STRING(type) (((type) & V8_IsNotStringMask) == V8_StringTag)
+
+#define V8_STRENC_ASCII(type) \
+ (((type) & V8_StringEncodingMask) == V8_AsciiStringTag)
+
+#define V8_STRREP_SEQ(type) \
+ (((type) & V8_StringRepresentationMask) == V8_SeqStringTag)
+#define V8_STRREP_CONS(type) \
+ (((type) & V8_StringRepresentationMask) == V8_ConsStringTag)
+#define V8_STRREP_EXT(type) \
+ (((type) & V8_StringRepresentationMask) == V8_ExternalStringTag)
+
+/*
+ * String type predicates
+ */
+#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 ASCII_EXTSTR(value) \
+ (V8_TYPE_STRING(value) && V8_STRENC_ASCII(value) && V8_STRREP_EXT(value))
+
+/*
+ * General helper macros
+ */
+#define COPYIN_UINT8(addr) (*(uint8_t *)copyin((addr), sizeof (uint8_t)))
+#define COPYIN_UINT32(addr) (*(uint32_t *)copyin((addr), sizeof (uint32_t)))
+
+#define APPEND_CHR(c) (this->buf[this->off++] = (c))
+
+#define APPEND_DGT(i, d) \
+ (((i) / (d)) ? APPEND_CHR('0' + ((i)/(d) % 10)) : 0)
+
+#define APPEND_NUM(i) \
+ APPEND_DGT((i), 10000); \
+ APPEND_DGT((i), 1000); \
+ APPEND_DGT((i), 100); \
+ APPEND_DGT((i), 10); \
+ APPEND_DGT((i), 1);
+
+/*
+ * The following macros are used to output ASCII SeqStrings, ConsStrings, and
+ * Node.js ExternalStrings. To represent each string, we use three fields:
+ *
+ * "str": a pointer to the string itself
+ *
+ * "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:
+ *
+ * SeqString stored directly as a char array inside the object
+ *
+ * ConsString pointer to two strings that should be concatenated
+ *
+ * ExternalString pointer to a char* outside the V8 heap
+ */
+
+/*
+ * Load "len" and "attrs" for the given "str".
+ */
+#define LOAD_STRFIELDS(str, len, attrs) \
+ len = SMI_VALUE(COPYIN_UINT32(str + V8_OFF_STR_LENGTH)); \
+ this->map = COPYIN_UINT32(str + V8_OFF_HEAPOBJ_MAP); \
+ attrs = COPYIN_UINT8(this->map + V8_OFF_MAP_ATTRS);
+
+/*
+ * Print out the given SeqString, or do nothing if the string is not an ASCII
+ * SeqString.
+ */
+#define APPEND_SEQSTR(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; \
+}
+
+/*
+ * 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_UINT32(str + V8_OFF_EXTSTR_RSRC); \
+ this->dataptr = COPYIN_UINT32(this->resource + NODE_OFF_EXTSTR_DATA); \
+ copyinto(this->dataptr, len, this->buf + this->off); \
+ this->off += len; \
+}
+
+/*
+ * Recall that each ConsString points to two other strings which are
+ * semantically concatenated. Of course, these strings may themselves by
+ * ConsStrings, but in D we can only expand this recursion to a finite level.
+ * Thankfully, function and script names are generally not more than a few
+ * levels deep, so we unroll the expansion up to three levels. Even this is
+ * pretty hairy: we use strings "s0", ..., "s13", (each with "str", "len", and
+ * "attr" fields -- see above) to store the expanded strings. We expand the
+ * original string into s0 and s7, then s0 into s1 and s4, etc:
+ *
+ *
+ * +---- str ----+
+ * / \ <-- 1st expansion
+ * / \
+ * s0 s7
+ * / \ / \
+ * / \ / \ <-- 2nd expansion
+ * / \ / \
+ * s1 s4 s8 s11
+ * / \ / \ / \ / \ <-- 3rd expansion
+ * s2 s3 s5 s6 s9 s10 s12 s13
+ *
+ * Of course, for a given string, any of these expansions may not be needed.
+ * For example, we may expand str and find that s0 is already a SeqString,
+ * while s7 requires further expansion. So when we expand a ConsString, we
+ * zero the length of the string itself, and then at the end we print out
+ * all non-zero-length strings in order (including both internal nodes and
+ * leafs in the tree above) to get the final output.
+ */
+#define EXPAND_START() \
+dtrace:helper:ustack: \
+/!this->done/ \
+{ \
+ this->s0str = this->s1str = this->s2str = 0; \
+ this->s3str = this->s4str = this->s5str = 0; \
+ this->s6str = this->s7str = this->s8str = 0; \
+ this->s9str = this->s10str = this->s11str = 0; \
+ this->s12str = this->s13str = 0; \
+ \
+ this->s0len = this->s1len = this->s2len = 0; \
+ this->s3len = this->s4len = this->s5len = 0; \
+ this->s6len = this->s7len = this->s8len = 0; \
+ this->s9len = this->s10len = this->s11len = 0; \
+ this->s12len = this->s13len = 0; \
+ \
+ this->s0attrs = this->s1attrs = this->s2attrs = 0; \
+ this->s3attrs = this->s4attrs = this->s5attrs = 0; \
+ this->s6attrs = this->s7attrs = this->s8attrs = 0; \
+ this->s9attrs = this->s10attrs = this->s11attrs = 0; \
+ this->s12attrs = this->s13attrs = 0; \
+}
+
+/*
+ * Expand the ConsString "str" (represensted by "str", "len", and "attrs") into
+ * strings "s1" (represented by "s1s", "s1l", and "s1a") and "s2" (represented
+ * by "s2s", "s2l", "s2a"). If "str" is not a ConsString, do nothing.
+ */
+#define EXPAND_STR(str, len, attrs, s1s, s1l, s1a, s2s, s2l, s2a) \
+dtrace:helper:ustack: \
+/!this->done && len > 0 && ASCII_CONSSTR(attrs)/ \
+{ \
+ len = 0; \
+ \
+ s1s = COPYIN_UINT32(str + V8_OFF_CONSSTR_CAR); \
+ LOAD_STRFIELDS(s1s, s1l, s1a) \
+ \
+ s2s = COPYIN_UINT32(str + V8_OFF_CONSSTR_CDR); \
+ LOAD_STRFIELDS(s2s, s2l, s2a) \
+}
+
+/*
+ * Print out a ConsString by expanding it up to three levels and printing out
+ * the resulting SeqStrings.
+ */
+#define APPEND_CONSSTR(str, len, attrs) \
+ EXPAND_START() \
+ EXPAND_STR(str, len, attrs, \
+ this->s0str, this->s0len, this->s0attrs, \
+ this->s7str, this->s7len, this->s7attrs) \
+ EXPAND_STR(this->s0str, this->s0len, this->s0attrs, \
+ this->s1str, this->s1len, this->s1attrs, \
+ this->s4str, this->s4len, this->s4attrs) \
+ EXPAND_STR(this->s1str, this->s1len, this->s1attrs, \
+ this->s2str, this->s2len, this->s2attrs, \
+ this->s3str, this->s3len, this->s3attrs) \
+ EXPAND_STR(this->s4str, this->s4len, this->s4attrs, \
+ this->s5str, this->s5len, this->s5attrs, \
+ this->s6str, this->s6len, this->s6attrs) \
+ EXPAND_STR(this->s7str, this->s7len, this->s7attrs, \
+ this->s8str, this->s8len, this->s8attrs, \
+ this->s11str, this->s11len, this->s11attrs) \
+ EXPAND_STR(this->s8str, this->s8len, this->s8attrs, \
+ this->s9str, this->s9len, this->s9attrs, \
+ this->s10str, this->s10len, this->s10attrs) \
+ EXPAND_STR(this->s11str, this->s11len, this->s11attrs, \
+ this->s12str, this->s12len, this->s12attrs, \
+ this->s13str, this->s13len, this->s13attrs) \
+ \
+ APPEND_SEQSTR(str, len, attrs) \
+ APPEND_SEQSTR(this->s0str, this->s0len, this->s0attrs) \
+ APPEND_SEQSTR(this->s1str, this->s1len, this->s1attrs) \
+ APPEND_SEQSTR(this->s2str, this->s2len, this->s2attrs) \
+ APPEND_SEQSTR(this->s3str, this->s3len, this->s3attrs) \
+ APPEND_SEQSTR(this->s4str, this->s4len, this->s4attrs) \
+ APPEND_SEQSTR(this->s5str, this->s5len, this->s5attrs) \
+ APPEND_SEQSTR(this->s6str, this->s6len, this->s6attrs) \
+ APPEND_SEQSTR(this->s7str, this->s7len, this->s7attrs) \
+ APPEND_SEQSTR(this->s8str, this->s8len, this->s8attrs) \
+ APPEND_SEQSTR(this->s9str, this->s9len, this->s9attrs) \
+ APPEND_SEQSTR(this->s10str, this->s10len, this->s10attrs) \
+ APPEND_SEQSTR(this->s11str, this->s11len, this->s11attrs) \
+ APPEND_SEQSTR(this->s12str, this->s12len, this->s12attrs) \
+ APPEND_SEQSTR(this->s13str, this->s13len, this->s13attrs) \
+
+
+/*
+ * Print out the given SeqString, ConsString, or ExternalString.
+ * APPEND_CONSSTR implicitly handles SeqStrings as the degenerate case of an
+ * expanded ConsString.
+ */
+#define APPEND_V8STR(str, len, attrs) \
+ APPEND_CONSSTR(str, len, attrs) \
+ APPEND_NODESTR(str, len, attrs)
+
+/*
+ * In this first clause we initialize all variables. We must explicitly clear
+ * them because they may contain values left over from previous iterations.
+ */
+dtrace:helper:ustack:
+{
+ /* input */
+ this->fp = arg1;
+
+ /* output/flow control */
+ this->buf = (char *)alloca(128);
+ this->off = 0;
+ this->done = 0;
+
+ /* program state */
+ this->ctx = 0;
+ this->marker = 0;
+ this->func = 0;
+ this->shared = 0;
+ this->map = 0;
+ this->funcnamestr = 0;
+ this->funcnamelen = 0;
+ this->funcnameattrs = 0;
+ this->script = 0;
+ this->scriptnamestr = 0;
+ this->scriptnamelen = 0;
+ this->scriptnameattrs = 0;
+ this->position = 0;
+ this->line_ends = 0;
+ this->le_attrs = 0;
+
+ /* binary search fields */
+ this->bsearch_min = 0;
+ this->bsearch_max = 0;
+ this->ii = 0;
+}
+
+/*
+ * Like V8, we first check if we've got an ArgumentsAdaptorFrame. We've got
+ * nothing to add for such frames, so we bail out quickly.
+ */
+dtrace:helper:ustack:
+{
+ this->ctx = COPYIN_UINT32(this->fp + V8_OFF_FP_CONTEXT);
+}
+
+dtrace:helper:ustack:
+/IS_SMI(this->ctx) && SMI_VALUE(this->ctx) == V8_FT_ADAPTOR/
+{
+ this->done = 1;
+ APPEND_CHR('<');
+ APPEND_CHR('<');
+ APPEND_CHR(' ');
+ APPEND_CHR('a');
+ APPEND_CHR('d');
+ APPEND_CHR('a');
+ APPEND_CHR('p');
+ APPEND_CHR('t');
+ APPEND_CHR('o');
+ APPEND_CHR('r');
+ APPEND_CHR(' ');
+ APPEND_CHR('>');
+ APPEND_CHR('>');
+ APPEND_CHR('\0');
+ stringof(this->buf);
+}
+
+/*
+ * Check for other common frame types for which we also have nothing to add.
+ */
+dtrace:helper:ustack:
+/!this->done/
+{
+ this->marker = COPYIN_UINT32(this->fp + V8_OFF_FP_MARKER);
+}
+
+dtrace:helper:ustack:
+/!this->done && IS_SMI(this->marker) &&
+ SMI_VALUE(this->marker) == V8_FT_ENTRY/
+{
+ this->done = 1;
+ APPEND_CHR('<');
+ APPEND_CHR('<');
+ APPEND_CHR(' ');
+ APPEND_CHR('e');
+ APPEND_CHR('n');
+ APPEND_CHR('t');
+ APPEND_CHR('r');
+ APPEND_CHR('y');
+ APPEND_CHR(' ');
+ APPEND_CHR('>');
+ APPEND_CHR('>');
+ APPEND_CHR('\0');
+ stringof(this->buf);
+}
+
+dtrace:helper:ustack:
+/!this->done && IS_SMI(this->marker) &&
+ SMI_VALUE(this->marker) == V8_FT_ENTRYCONSTRUCT/
+{
+ this->done = 1;
+ APPEND_CHR('<');
+ APPEND_CHR('<');
+ APPEND_CHR(' ');
+ APPEND_CHR('e');
+ APPEND_CHR('n');
+ APPEND_CHR('t');
+ APPEND_CHR('r');
+ APPEND_CHR('y');
+ APPEND_CHR('_');
+ APPEND_CHR('c');
+ APPEND_CHR('o');
+ APPEND_CHR('n');
+ APPEND_CHR('s');
+ APPEND_CHR('t');
+ APPEND_CHR('r');
+ APPEND_CHR('u');
+ APPEND_CHR('c');
+ APPEND_CHR('t');
+ APPEND_CHR(' ');
+ APPEND_CHR('>');
+ APPEND_CHR('>');
+ APPEND_CHR('\0');
+ stringof(this->buf);
+}
+
+dtrace:helper:ustack:
+/!this->done && IS_SMI(this->marker) &&
+ SMI_VALUE(this->marker) == V8_FT_EXIT/
+{
+ this->done = 1;
+ APPEND_CHR('<');
+ APPEND_CHR('<');
+ APPEND_CHR(' ');
+ APPEND_CHR('e');
+ APPEND_CHR('x');
+ APPEND_CHR('i');
+ APPEND_CHR('t');
+ APPEND_CHR(' ');
+ APPEND_CHR('>');
+ APPEND_CHR('>');
+ APPEND_CHR('\0');
+ stringof(this->buf);
+}
+
+dtrace:helper:ustack:
+/!this->done && IS_SMI(this->marker) &&
+ SMI_VALUE(this->marker) == V8_FT_INTERNAL/
+{
+ this->done = 1;
+ APPEND_CHR('<');
+ APPEND_CHR('<');
+ APPEND_CHR(' ');
+ APPEND_CHR('i');
+ APPEND_CHR('n');
+ APPEND_CHR('t');
+ APPEND_CHR('e');
+ APPEND_CHR('r');
+ APPEND_CHR('n');
+ APPEND_CHR('a');
+ APPEND_CHR('l');
+ APPEND_CHR(' ');
+ APPEND_CHR('>');
+ APPEND_CHR('>');
+ APPEND_CHR('\0');
+ stringof(this->buf);
+}
+
+dtrace:helper:ustack:
+/!this->done && IS_SMI(this->marker) &&
+ SMI_VALUE(this->marker) == V8_FT_CONSTRUCT/
+{
+ this->done = 1;
+ APPEND_CHR('<');
+ APPEND_CHR('<');
+ APPEND_CHR(' ');
+ APPEND_CHR('c');
+ APPEND_CHR('o');
+ APPEND_CHR('n');
+ APPEND_CHR('s');
+ APPEND_CHR('t');
+ APPEND_CHR('r');
+ APPEND_CHR('u');
+ APPEND_CHR('c');
+ APPEND_CHR('t');
+ APPEND_CHR('o');
+ APPEND_CHR('r');
+ APPEND_CHR(' ');
+ APPEND_CHR('>');
+ APPEND_CHR('>');
+ APPEND_CHR('\0');
+ stringof(this->buf);
+}
+
+/*
+ * At this point, we're either looking at a JavaScriptFrame or an
+ * OptimizedFrame. For now, we assume JavaScript and start by grabbing the
+ * function name.
+ */
+dtrace:helper:ustack:
+/!this->done/
+{
+ this->func = COPYIN_UINT32(this->fp + V8_OFF_FP_FUNC);
+ this->shared = COPYIN_UINT32(this->func + V8_OFF_FUNC_SHARED);
+ this->funcnamestr = COPYIN_UINT32(this->shared + V8_OFF_SHARED_NAME);
+ LOAD_STRFIELDS(this->funcnamestr, this->funcnamelen,
+ this->funcnameattrs);
+}
+
+dtrace:helper:ustack:
+/!this->done && this->funcnamelen == 0/
+{
+ /*
+ * This is an anonymous function, but if it was invoked as a method of
+ * some object then V8 will have computed an inferred name that we can
+ * include in the stack trace.
+ */
+ APPEND_CHR('(');
+ APPEND_CHR('a');
+ APPEND_CHR('n');
+ APPEND_CHR('o');
+ APPEND_CHR('n');
+ APPEND_CHR(')');
+ APPEND_CHR(' ');
+ APPEND_CHR('a');
+ APPEND_CHR('s');
+ APPEND_CHR(' ');
+
+ this->funcnamestr = COPYIN_UINT32(this->shared + V8_OFF_SHARED_INFERRED);
+ LOAD_STRFIELDS(this->funcnamestr, this->funcnamelen,
+ this->funcnameattrs);
+}
+
+dtrace:helper:ustack:
+/!this->done && this->funcnamelen == 0/
+{
+ APPEND_CHR('(');
+ APPEND_CHR('a');
+ APPEND_CHR('n');
+ APPEND_CHR('o');
+ APPEND_CHR('n');
+ APPEND_CHR(')');
+}
+
+APPEND_V8STR(this->funcnamestr, this->funcnamelen, this->funcnameattrs)
+
+/*
+ * Now look for the name of the script where the function was defined.
+ */
+dtrace:helper:ustack:
+/!this->done/
+{
+ this->script = COPYIN_UINT32(this->shared + V8_OFF_SHARED_SCRIPT);
+ this->scriptnamestr = COPYIN_UINT32(this->script +
+ V8_OFF_SCRIPT_NAME);
+ LOAD_STRFIELDS(this->scriptnamestr, this->scriptnamelen,
+ this->scriptnameattrs);
+
+ APPEND_CHR(' ');
+ APPEND_CHR('a');
+ APPEND_CHR('t');
+ APPEND_CHR(' ');
+}
+
+APPEND_V8STR(this->scriptnamestr, this->scriptnamelen, this->scriptnameattrs)
+
+/*
+ * Now look for file position and line number information.
+ */
+dtrace:helper:ustack:
+/!this->done/
+{
+ this->position = COPYIN_UINT32(this->shared + V8_OFF_SHARED_FUNTOK);
+ this->line_ends = COPYIN_UINT32(this->script + V8_OFF_SCRIPT_LENDS);
+ this->map = COPYIN_UINT32(this->line_ends + V8_OFF_HEAPOBJ_MAP);
+ this->le_attrs = COPYIN_UINT8(this->map + V8_OFF_MAP_ATTRS);
+}
+
+dtrace:helper:ustack:
+/!this->done && this->le_attrs != V8_IT_FIXEDARRAY/
+{
+ /*
+ * If the line number array was not a valid FixedArray, it's probably
+ * 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_CHR(' ');
+ APPEND_CHR('p');
+ APPEND_CHR('o');
+ APPEND_CHR('s');
+ APPEND_CHR('i');
+ APPEND_CHR('t');
+ APPEND_CHR('i');
+ APPEND_CHR('o');
+ APPEND_CHR('n');
+ APPEND_CHR(' ');
+ APPEND_NUM(this->position);
+ APPEND_CHR('\0');
+ this->done = 1;
+ stringof(this->buf);
+}
+
+/*
+ * At this point, we've got both a position in the script and an array
+ * describing where each line of the file ends. We can use this to compute the
+ * line number by binary searching the array. (This is also what V8 does when
+ * computing stack traces.)
+ */
+dtrace:helper:ustack:
+/!this->done/
+{
+ /* initialize binary search */
+ this->bsearch_line = this->position < COPYIN_UINT32(
+ this->line_ends + V8_OFF_FA_DATA) ? 1 : 0;
+ this->bsearch_min = 0;
+ this->bsearch_max = this->bsearch_line != 0 ? 0 :
+ SMI_VALUE(COPYIN_UINT32(this->line_ends + V8_OFF_FA_SIZE)) - 1;
+}
+
+/*
+ * Of course, we can't iterate the binary search indefinitely, so we hardcode 15
+ * iterations. That's enough to precisely identify the line number in files up
+ * to 32768 lines of code.
+ */
+#define BSEARCH_LOOP \
+dtrace:helper:ustack: \
+/!this->done && this->bsearch_max >= 1/ \
+{ \
+ this->ii = (this->bsearch_min + this->bsearch_max) >> 1; \
+} \
+ \
+dtrace:helper:ustack: \
+/!this->done && this->bsearch_max >= 1 && \
+ this->position > COPYIN_UINT32(this->line_ends + V8_OFF_FA_DATA + \
+ this->ii * sizeof (uint32_t))/ \
+{ \
+ this->bsearch_min = this->ii + 1; \
+} \
+ \
+dtrace:helper:ustack: \
+/!this->done && this->bsearch_max >= 1 && \
+ this->position <= COPYIN_UINT32(this->line_ends + V8_OFF_FA_DATA + \
+ (this->ii - 1) * sizeof (uint32_t))/ \
+{ \
+ this->bsearch_max = this->ii - 1; \
+}
+
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+BSEARCH_LOOP
+
+dtrace:helper:ustack:
+/!this->done && !this->bsearch_line/
+{
+ this->bsearch_line = this->ii + 1;
+}
+
+dtrace:helper:ustack:
+/!this->done/
+{
+ APPEND_CHR(' ');
+ APPEND_CHR('l');
+ APPEND_CHR('i');
+ APPEND_CHR('n');
+ APPEND_CHR('e');
+ APPEND_CHR(' ');
+ APPEND_NUM(this->bsearch_line);
+ APPEND_CHR('\0');
+ this->done = 1;
+ stringof(this->buf);
+}
diff --git a/wscript b/wscript
index 3247444..2b04358 100644
--- a/wscript
+++ b/wscript
@@ -792,8 +792,8 @@ def build(bld):
native_cc.rule = javascript_in_c_debug
if bld.env["USE_DTRACE"]:
- dtrace = bld.new_task_gen(
- name = "dtrace",
+ dtrace_usdt = bld.new_task_gen(
+ name = "dtrace_usdt",
source = "src/node_provider.d",
target = "src/node_provider.h",
rule = "%s -x nolibs -h -o ${TGT} -s ${SRC}" % (bld.env.DTRACE),
@@ -801,7 +801,7 @@ def build(bld):
)
if bld.env["USE_DEBUG"]:
- dtrace_g = dtrace.clone("Debug")
+ dtrace_usdt_g = dtrace_usdt.clone("Debug")
bld.install_files('${LIBDIR}/dtrace', 'src/node.d')
@@ -828,8 +828,29 @@ def build(bld):
' '.join(objs))
Utils.exec_command(cmd)
+ #
+ # ustack helpers do not currently work on MacOS. We currently only
+ # support 32-bit x86.
+ #
+ def dtrace_do_ustack(task):
+ abspath = bld.srcnode.abspath(bld.env_of_name(task.env.variant()))
+ source = task.inputs[0].srcpath(task.env)
+ target = task.outputs[0].srcpath(task.env)
+ cmd = '%s -32 -I../src -C -G -s %s -o %s' % (task.env.DTRACE, source, target)
+ Utils.exec_command(cmd)
+
+ dtrace_ustack = bld.new_task_gen(
+ name = "dtrace_ustack-postprocess",
+ source = "src/v8ustack.d",
+ target = "v8ustack.o",
+ always = True,
+ before = "cxx_link",
+ after = "cxx",
+ rule = dtrace_do_ustack
+ )
+
dtracepost = bld.new_task_gen(
- name = "dtrace-postprocess",
+ name = "dtrace_usdt-postprocess",
source = "src/node_provider.d",
target = "node_provider.o",
always = True,
@@ -841,6 +862,9 @@ def build(bld):
t = join(bld.srcnode.abspath(bld.env_of_name("Release")), dtracepost.target)
bld.env_of_name('Release').append_value('LINKFLAGS', t)
+ t = join(bld.srcnode.abspath(bld.env_of_name("Release")), dtrace_ustack.target)
+ bld.env_of_name('Release').append_value('LINKFLAGS', t)
+
#
# Note that for the same (mysterious) issue outlined above with respect
# to assigning the rule to native_cc/native_cc_debug, we must apply the
--
1.7.3.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment