From b9049d2f903cd412faa17662bc07255280fcdfcc Mon Sep 17 00:00:00 2001 From: Dave Pacheco 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 0000000000..70c26a208f --- /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 0000000000..d559523556 --- /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 + +/* + * 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 324744459b..2b04358559 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