• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * V8 DTrace ustack helper for annotating native stack traces with JavaScript
3  * function names.  We start with a frame pointer (arg1) and emit a string
4  * describing the current function.  We do this by chasing pointers to extract
5  * the function's name (if any) and the filename and line number where the
6  * function is defined.
7  *
8  * To use the helper, run node, then use the jstack() DTrace action to capture
9  * a JavaScript stacktrace.  You may need to tune the dtrace_helper_actions_max
10  * kernel variable to 128.
11  */
12 
13 #include <v8constants.h>
14 #include <v8abbr.h>
15 
16 /*
17  * V8 represents small integers (SMI) using the upper 31 bits of a 32/64-bit
18  * value.  To extract the actual integer value, we must shift it over.
19  */
20 #define	IS_SMI(value)	\
21     ((value & V8_SmiTagMask) == V8_SmiTag)
22 #define	SMI_VALUE(value)	\
23     ((uint32_t) ((value) >> V8_SmiValueShift))
24 #define NO_SHARED_FUNCTION_NAME_SENTINEL NULL
25 
26 /*
27  * Heap objects usually start off with a Map pointer, itself another heap
28  * object.  However, during garbage collection, the low order bits of the
29  * pointer (which are normally 01) are used to record GC state.  Of course, we
30  * have no idea if we're in GC or not, so we must always normalize the pointer.
31  */
32 #define	V8_MAP_PTR(ptr)		\
33     ((ptr & ~V8_HeapObjectTagMask) | V8_HeapObjectTag)
34 
35 #define V8_TYPE_SCRIPT(type) \
36     ((type) == V8_IT_SCRIPT)
37 
38 /*
39  * Determine the encoding and representation of a V8 string.
40  */
41 #define	V8_TYPE_STRING(type)	\
42     (((type) & V8_IsNotStringMask) == V8_StringTag)
43 
44 #define	V8_STRENC_ASCII(type)	\
45     (((type) & V8_StringEncodingMask) == V8_AsciiStringTag)
46 
47 #define	V8_STRREP_SEQ(type)	\
48     (((type) & V8_StringRepresentationMask) == V8_SeqStringTag)
49 #define	V8_STRREP_CONS(type)	\
50     (((type) & V8_StringRepresentationMask) == V8_ConsStringTag)
51 #define	V8_STRREP_EXT(type)	\
52     (((type) & V8_StringRepresentationMask) == V8_ExternalStringTag)
53 
54 /*
55  * String type predicates
56  */
57 #define	ASCII_SEQSTR(value)	\
58     (V8_TYPE_STRING(value) && V8_STRENC_ASCII(value) && V8_STRREP_SEQ(value))
59 
60 #define	TWOBYTE_SEQSTR(value)	\
61     (V8_TYPE_STRING(value) && !V8_STRENC_ASCII(value) && V8_STRREP_SEQ(value))
62 
63 #define	IS_CONSSTR(value)	\
64     (V8_TYPE_STRING(value) && V8_STRREP_CONS(value))
65 
66 #define	ASCII_EXTSTR(value)	\
67     (V8_TYPE_STRING(value) && V8_STRENC_ASCII(value) && V8_STRREP_EXT(value))
68 
69 /*
70  * General helper macros
71  */
72 #define	COPYIN_UINT8(addr) (*(uint8_t*) copyin((addr), sizeof(uint8_t)))
73 #define	COPYIN_UINT32(addr) (*(uint32_t*) copyin((addr), sizeof(uint32_t)))
74 #define	COPYIN_UINT64(addr) (*(uint64_t*) copyin((addr), sizeof(uint64_t)))
75 
76 #if defined(__i386)
77 # define	COPYIN_PTR(addr) COPYIN_UINT32(addr)
78 # define	off_t uint32_t
79 # define	APPEND_PTR(p) APPEND_PTR_32(p)
80 #else
81 # define	COPYIN_PTR(addr) COPYIN_UINT64(addr)
82 # define	off_t uint64_t
83 # define	APPEND_PTR(p) APPEND_PTR_64(p)
84 #endif
85 
86 #define	APPEND_CHR(c)			(this->buf[this->off++] = (c))
87 #define	APPEND_CHR4(s0, s1, s2, s3)	\
88     APPEND_CHR(s0);	\
89     APPEND_CHR(s1);	\
90     APPEND_CHR(s2);	\
91     APPEND_CHR(s3);
92 #define	APPEND_CHR8(s0, s1, s2, s3, s4, s5, s6, s7)	\
93     APPEND_CHR4(s0, s1, s2, s3)	\
94     APPEND_CHR4(s4, s5, s6, s7)
95 
96 #define	APPEND_DGT(i, d)	\
97     (((i) / (d)) ? APPEND_CHR('0' + ((i)/(d) % 10)) : 0)
98 
99 #define	APPEND_NUM(i)		\
100     APPEND_DGT((i), 100000);	\
101     APPEND_DGT((i), 10000);	\
102     APPEND_DGT((i), 1000);	\
103     APPEND_DGT((i), 100);	\
104     APPEND_DGT((i), 10);	\
105     APPEND_DGT((i), 1);
106 
107 #define	APPEND_HEX(d)	\
108     APPEND_CHR((d) < 10 ? '0' + (d) : 'a' - 10 + (d))
109 
110 #define	APPEND_PTR_32(p)	\
111     APPEND_HEX((p >> 28) & 0xf);	\
112     APPEND_HEX((p >> 24) & 0xf);	\
113     APPEND_HEX((p >> 20) & 0xf);	\
114     APPEND_HEX((p >> 16) & 0xf);	\
115     APPEND_HEX((p >> 12) & 0xf);	\
116     APPEND_HEX((p >> 8) & 0xf);	\
117     APPEND_HEX((p >> 4) & 0xf);	\
118     APPEND_HEX((p) & 0xf);
119 
120 #define	APPEND_PTR_64(p)	\
121     APPEND_PTR_32(p >> 32)	\
122     APPEND_PTR_32(p)
123 
124 /*
125  * The following macros are used to output ASCII SeqStrings, ConsStrings, and
126  * Node.js ExternalStrings.  To represent each string, we use three fields:
127  *
128  *    "str":	a pointer to the string itself
129  *
130  *    "len":	the string length
131  *
132  *    "attrs":	the type identifier for the string, which indicates the
133  *    		encoding and representation.  We're only interested in strings
134  *    		whose representation is one of:
135  *
136  *	SeqOneByteString stored directly as a char array inside the object
137  *
138  *	SeqTwoByteString stored as a UTF-16 char array inside the object
139  *
140  *	ConsString	 pointer to two strings that should be concatenated
141  *
142  * 	ExternalString	 pointer to a char* outside the V8 heap
143  */
144 
145 /*
146  * Load "len" and "attrs" for the given "str".
147  */
148 #define	LOAD_STRFIELDS(str, len, attrs)					\
149     len = SMI_VALUE(COPYIN_PTR(str + V8_OFF_STR_LENGTH));	\
150     this->map = V8_MAP_PTR(COPYIN_PTR(str + V8_OFF_HEAPOBJ_MAP));	\
151     attrs = COPYIN_UINT8(this->map + V8_OFF_MAP_ATTRS);
152 
153 #define	APPEND_SEQSTR(str, len, attrs) \
154     APPEND_SEQONEBYTESTR(str, len, attrs) \
155     APPEND_SEQTWOBYTESTR(str, len, attrs)
156 
157 /*
158  * Print out the given SeqOneByteString, or do nothing if the string is not an ASCII
159  * SeqOneByteString.
160  */
161 #define	APPEND_SEQONEBYTESTR(str, len, attrs) 				\
162     dtrace:helper:ustack:						\
163     /!this->done && len > 0 && ASCII_SEQSTR(attrs)/			\
164     {									\
165 	copyinto(str + V8_OFF_STR_CHARS, len, this->buf + this->off);	\
166 	this->off += len;						\
167     }
168 
169 /*
170  * LOOP_ITER: macro to paste "block" while "ivar" is less than "dynmax" and
171  * "statmax".  The subsequent LOOP_{4,8} macros facilitate pasting the same
172  * thing 4 and 8 times, respectively.  Like much of the rest of the code in this
173  * file, this is regrettably necessary given the constraints under which we're
174  * expected to run.
175  */
176 #define	LOOP_ITER(ivar, dynmax, statmax, block) \
177 	((ivar) < (dynmax)) && ((ivar) < (statmax)) && (block); (ivar)++;
178 
179 #define	LOOP_4(block) \
180 	block \
181 	block \
182 	block \
183 	block \
184 
185 #define	LOOP_8(block) \
186 	LOOP_4(block) \
187 	LOOP_4(block)
188 
189 /*
190  * Print out the given SeqTwoByteString, or do nothing if the string is not an ASCII
191  * SeqTwoByteString.  NOTE: if you bump MAX_TWOBYTESTR_CHARS, you'll also need
192  * to modify the LOOP_* macro calls below to match.
193  */
194 #define	MAX_TWOBYTESTR_CHARS	128
195 #define	MAX_TWOBYTESTR_BYTES	(2 * MAX_TWOBYTESTR_CHARS)
196 #define	TO_ASCII(c)		((c) < 128 ? (c) : '?')
197 
198 #define	APPEND_SEQTWOBYTESTR(str, len, attrs) 				\
199     dtrace:helper:ustack: 						\
200     /!this->done && len > 0 && TWOBYTE_SEQSTR(attrs)/ 			\
201     {									\
202 	this->i = 0;							\
203 	this->stbuf = (uint16_t *)alloca(MAX_TWOBYTESTR_BYTES + 2); 	\
204 	copyinto(str + V8_OFF_TWOBYTESTR_CHARS,				\
205 	    MAX_TWOBYTESTR_BYTES, this->stbuf);				\
206 	this->stbuf[MAX_TWOBYTESTR_BYTES - 1] = '\0';			\
207 	this->stbuf[MAX_TWOBYTESTR_BYTES] = '\0';			\
208 									\
209 	LOOP_8(LOOP_8(LOOP_4(LOOP_ITER(this->i, len,			\
210 	    MAX_TWOBYTESTR_CHARS,					\
211 	    APPEND_CHR(TO_ASCII(this->stbuf[this->i]))))))		\
212 									\
213 	this->i = 0;							\
214 	this->stbuf = 0;						\
215     }
216 
217 /*
218  * Print out the given Node.js ExternalString, or do nothing if the string is
219  * not an ASCII ExternalString.
220  */
221 #define	APPEND_NODESTR(str, len, attrs)					\
222     dtrace:helper:ustack:						\
223     /!this->done && len > 0 && ASCII_EXTSTR(attrs)/			\
224     {									\
225 	this->resource = COPYIN_PTR(str + V8_OFF_EXTSTR_RSRC);		\
226 	this->dataptr = COPYIN_PTR(this->resource + NODE_OFF_EXTSTR_DATA);	\
227 	copyinto(this->dataptr, len, this->buf + this->off);			\
228 	this->off += len;							\
229     }
230 
231 /*
232  * Recall that each ConsString points to two other strings which are
233  * semantically concatenated.  Of course, these strings may themselves by
234  * ConsStrings, but in D we can only expand this recursion to a finite level.
235  * Thankfully, function and script names are generally not more than a few
236  * levels deep, so we unroll the expansion up to three levels.  Even this is
237  * pretty hairy: we use strings "s0", ..., "s13", (each with "str", "len", and
238  * "attr" fields -- see above) to store the expanded strings.  We expand the
239  * original string into s0 and s7, then s0 into s1 and s4, etc:
240  *
241  *
242  *                   +----  str  ----+
243  *                  /                 \                  <--  1st expansion
244  *                 /                   \
245  *              s0                       s7
246  *            /    \                  /     \
247  *           /      \                /       \           <--  2nd expansion
248  *          /        \              /         \
249  *        s1          s4          s8           s11
250  *       /  \        /  \        /  \          /  \      <--  3rd expansion
251  *     s2    s3    s5    s6    s9    s10    s12    s13
252  *
253  * Of course, for a given string, any of these expansions may not be needed.
254  * For example, we may expand str and find that s0 is already a SeqString,
255  * while s7 requires further expansion.  So when we expand a ConsString, we
256  * zero the length of the string itself, and then at the end we print out
257  * all non-zero-length strings in order (including both internal nodes and
258  * leafs in the tree above) to get the final output.
259  */
260 #define	EXPAND_START()							\
261     dtrace:helper:ustack:	\
262     /!this->done/	\
263     {	\
264 	this->s0str = this->s1str = this->s2str = (off_t) 0;	\
265 	this->s3str = this->s4str = this->s5str = (off_t) 0;	\
266 	this->s6str = this->s7str = this->s8str = (off_t) 0;	\
267 	this->s9str = this->s10str = this->s11str = (off_t) 0;	\
268 	this->s12str = this->s13str = (off_t) 0;	\
269 									\
270 	this->s0len = this->s1len = this->s2len = (off_t) 0;	\
271 	this->s3len = this->s4len = this->s5len = (off_t) 0;	\
272 	this->s6len = this->s7len = this->s8len = (off_t) 0;	\
273 	this->s9len = this->s10len = this->s11len = (off_t) 0;	\
274 	this->s12len = this->s13len = (off_t) 0;	\
275 									\
276 	this->s0attrs = this->s1attrs = this->s2attrs = 0;		\
277 	this->s3attrs = this->s4attrs = this->s5attrs = 0;		\
278 	this->s6attrs = this->s7attrs = this->s8attrs = 0;		\
279 	this->s9attrs = this->s10attrs = this->s11attrs = 0;		\
280 	this->s12attrs = this->s13attrs = 0;				\
281     }
282 
283 /*
284  * Expand the ConsString "str" (represented by "str", "len", and "attrs") into
285  * strings "s1" (represented by "s1s", "s1l", and "s1a") and "s2" (represented
286  * by "s2s", "s2l", "s2a").  If "str" is not a ConsString, do nothing.
287  */
288 #define	EXPAND_STR(str, len, attrs, s1s, s1l, s1a, s2s, s2l, s2a)	\
289     dtrace:helper:ustack:	\
290     /!this->done && len > 0 && IS_CONSSTR(attrs)/	\
291     {	\
292 	len = 0;							\
293 									\
294 	s1s = COPYIN_PTR(str + V8_OFF_CONSSTR_CAR);	\
295 	LOAD_STRFIELDS(s1s, s1l, s1a)					\
296 									\
297 	s2s = COPYIN_PTR(str + V8_OFF_CONSSTR_CDR);	\
298 	LOAD_STRFIELDS(s2s, s2l, s2a)					\
299     }
300 
301 /*
302  * Print out a ConsString by expanding it up to three levels and printing out
303  * the resulting SeqStrings.
304  */
305 #define	APPEND_CONSSTR(str, len, attrs)					\
306     EXPAND_START()							\
307     EXPAND_STR(str, len, attrs,						\
308 	this->s0str, this->s0len, this->s0attrs,			\
309 	this->s7str, this->s7len, this->s7attrs)			\
310     EXPAND_STR(this->s0str, this->s0len, this->s0attrs,			\
311 	this->s1str, this->s1len, this->s1attrs,			\
312 	this->s4str, this->s4len, this->s4attrs)			\
313     EXPAND_STR(this->s1str, this->s1len, this->s1attrs,			\
314 	this->s2str, this->s2len, this->s2attrs,			\
315 	this->s3str, this->s3len, this->s3attrs)			\
316     EXPAND_STR(this->s4str, this->s4len, this->s4attrs,			\
317 	this->s5str, this->s5len, this->s5attrs,			\
318 	this->s6str, this->s6len, this->s6attrs)			\
319     EXPAND_STR(this->s7str, this->s7len, this->s7attrs,			\
320 	this->s8str, this->s8len, this->s8attrs,			\
321 	this->s11str, this->s11len, this->s11attrs)			\
322     EXPAND_STR(this->s8str, this->s8len, this->s8attrs,			\
323 	this->s9str, this->s9len, this->s9attrs,			\
324 	this->s10str, this->s10len, this->s10attrs)			\
325     EXPAND_STR(this->s11str, this->s11len, this->s11attrs,		\
326 	this->s12str, this->s12len, this->s12attrs,			\
327 	this->s13str, this->s13len, this->s13attrs)			\
328 									\
329     APPEND_SEQSTR(str, len, attrs)					\
330     APPEND_SEQSTR(this->s0str, this->s0len, this->s0attrs)		\
331     APPEND_SEQSTR(this->s1str, this->s1len, this->s1attrs)		\
332     APPEND_SEQSTR(this->s2str, this->s2len, this->s2attrs)		\
333     APPEND_SEQSTR(this->s3str, this->s3len, this->s3attrs)		\
334     APPEND_SEQSTR(this->s4str, this->s4len, this->s4attrs)		\
335     APPEND_SEQSTR(this->s5str, this->s5len, this->s5attrs)		\
336     APPEND_SEQSTR(this->s6str, this->s6len, this->s6attrs)		\
337     APPEND_SEQSTR(this->s7str, this->s7len, this->s7attrs)		\
338     APPEND_SEQSTR(this->s8str, this->s8len, this->s8attrs)		\
339     APPEND_SEQSTR(this->s9str, this->s9len, this->s9attrs)		\
340     APPEND_SEQSTR(this->s10str, this->s10len, this->s10attrs)		\
341     APPEND_SEQSTR(this->s11str, this->s11len, this->s11attrs)		\
342     APPEND_SEQSTR(this->s12str, this->s12len, this->s12attrs)		\
343     APPEND_SEQSTR(this->s13str, this->s13len, this->s13attrs)		\
344 
345 
346 /*
347  * Print out the given SeqString, ConsString, or ExternalString.
348  * APPEND_CONSSTR implicitly handles SeqStrings as the degenerate case of an
349  * expanded ConsString.
350  */
351 #define	APPEND_V8STR(str, len, attrs)					\
352     APPEND_CONSSTR(str, len, attrs)					\
353     APPEND_NODESTR(str, len, attrs)
354 
355 /*
356  * In this first clause we initialize all variables.  We must explicitly clear
357  * them because they may contain values left over from previous iterations.
358  */
359 dtrace:helper:ustack:
360 {
361 	/* input */
362 	this->fp = arg1;
363 
364 	/* output/flow control */
365 	this->buf = (char*) alloca(128);
366 	this->off = 0;
367 	this->done = 0;
368 
369 	/* program state */
370 	this->ctx = (off_t) 0;
371 	this->marker = (off_t) 0;
372 	this->func = (off_t) 0;
373 	this->shared = (off_t) 0;
374 	this->map = (off_t) 0;
375 	this->attrs = 0;
376 	this->funcrawnamestr = (off_t) 0;
377 	this->hassharedname = 0;
378 	this->funcnamelen = 0;
379 	this->funcnameattrs = 0;
380 	this->script = (off_t) 0;
381 	this->scriptattrs = 0;
382 	this->scriptnamestr = (off_t) 0;
383 	this->scriptnamelen = 0;
384 	this->scriptnameattrs = 0;
385 	this->position = 0;
386 	this->line_ends = (off_t) 0;
387 	this->le_attrs = 0;
388 
389 	/* binary search fields */
390 	this->bsearch_min = 0;
391 	this->bsearch_max = 0;
392 	this->ii = 0;
393 }
394 
395 /*
396  * Like V8, we first check if we've got an ArgumentsAdaptorFrame.  We've got
397  * nothing to add for such frames, so we bail out quickly.
398  */
399 dtrace:helper:ustack:
400 {
401 	this->ctx = COPYIN_PTR(this->fp + V8_OFF_FP_CONTEXT);
402 }
403 
404 dtrace:helper:ustack:
405 /IS_SMI(this->ctx) && SMI_VALUE(this->ctx) == V8_FT_ADAPTOR/
406 {
407 	this->done = 1;
408 	APPEND_CHR8('<','<',' ','a','d','a','p','t');
409 	APPEND_CHR8('o','r',' ','>','>','\0','\0','\0');
410 	stringof(this->buf);
411 }
412 
413 /*
414  * Check for other common frame types for which we also have nothing to add.
415  */
416 dtrace:helper:ustack:
417 /!this->done/
418 {
419 	this->marker = COPYIN_PTR(this->fp + V8_OFF_FP_CONTEXT);
420 }
421 
422 dtrace:helper:ustack:
423 /!this->done && IS_SMI(this->marker) &&
424  SMI_VALUE(this->marker) == V8_FT_ENTRY/
425 {
426 	this->done = 1;
427 	APPEND_CHR8('<','<',' ','e','n','t','r','y');
428 	APPEND_CHR4(' ','>','>','\0');
429 	stringof(this->buf);
430 }
431 
432 dtrace:helper:ustack:
433 /!this->done && IS_SMI(this->marker) &&
434  SMI_VALUE(this->marker) == V8_FT_ENTRYCONSTRUCT/
435 {
436 	this->done = 1;
437 	APPEND_CHR8('<','<',' ','e','n','t','r','y');
438 	APPEND_CHR8('_','c','o','n','s','t','r','u');
439 	APPEND_CHR4('t',' ','>','>');
440 	APPEND_CHR('\0');
441 	stringof(this->buf);
442 }
443 
444 dtrace:helper:ustack:
445 /!this->done && IS_SMI(this->marker) &&
446  SMI_VALUE(this->marker) == V8_FT_EXIT/
447 {
448 	this->done = 1;
449 	APPEND_CHR8('<','<',' ','e','x','i','t',' ');
450 	APPEND_CHR4('>','>','\0','\0');
451 	stringof(this->buf);
452 }
453 
454 dtrace:helper:ustack:
455 /!this->done && IS_SMI(this->marker) &&
456  SMI_VALUE(this->marker) == V8_FT_INTERNAL/
457 {
458 	this->done = 1;
459 	APPEND_CHR8('<','<',' ','i','n','t','e','r');
460 	APPEND_CHR8('n','a','l',' ','>','>','\0','\0');
461 	stringof(this->buf);
462 }
463 
464 dtrace:helper:ustack:
465 /!this->done && IS_SMI(this->marker) &&
466  SMI_VALUE(this->marker) == V8_FT_CONSTRUCT/
467 {
468 	this->done = 1;
469 	APPEND_CHR8('<','<',' ','c','o','n','s','t');
470 	APPEND_CHR8('r','u','c','t','o','r',' ','>');
471 	APPEND_CHR4('>','\0','\0','\0');
472 	stringof(this->buf);
473 }
474 
475 dtrace:helper:ustack:
476 /!this->done && IS_SMI(this->marker) &&
477  SMI_VALUE(this->marker) == V8_FT_STUB/
478 {
479 	this->done = 1;
480 	APPEND_CHR8('<','<',' ','s','t','u','b',' ');
481 	APPEND_CHR4('>','>','\0','\0');
482 	stringof(this->buf);
483 }
484 
485 /*
486  * Now check for internal frames that we can only identify by seeing that
487  * there's a Code object where there would be a JSFunction object for a
488  * JavaScriptFrame.
489  */
490 dtrace:helper:ustack:
491 /!this->done/
492 {
493 	this->func = COPYIN_PTR(this->fp + V8_OFF_FP_FUNC);
494 	this->map = V8_MAP_PTR(COPYIN_PTR(this->func + V8_OFF_HEAPOBJ_MAP));
495 	this->attrs = COPYIN_UINT8(this->map + V8_OFF_MAP_ATTRS);
496 }
497 
498 dtrace:helper:ustack:
499 /!this->done && this->attrs == V8_IT_CODE/
500 {
501 	this->done = 1;
502 	APPEND_CHR8('<','<',' ','i','n','t','e','r');
503 	APPEND_CHR8('n','a','l',' ','c','o','d','e');
504 	APPEND_CHR4(' ','>','>','\0');
505 	stringof(this->buf);
506 }
507 
508 /*
509  * At this point, we're either looking at a JavaScriptFrame or an
510  * OptimizedFrame.  For now, we assume JavaScript and start by grabbing the
511  * function name.
512  */
513 dtrace:helper:ustack:
514 /!this->done/
515 {
516 	this->map = 0;
517 	this->attrs = 0;
518 
519 	this->shared = COPYIN_PTR(this->func + V8_OFF_FUNC_SHARED);
520 	this->funcrawnamestr = COPYIN_PTR(this->shared + V8_OFF_RAW_NAME);
521 	this->hassharedname = this->funcrawnamestr !=
522 		NO_SHARED_FUNCTION_NAME_SENTINEL;
523 }
524 
525 dtrace:helper:ustack:
526 /!this->done && this->hassharedname/
527 {
528 	LOAD_STRFIELDS(this->funcrawnamestr, this->funcnamelen,
529 		this->funcnameattrs);
530 }
531 
532 dtrace:helper:ustack:
533 /!this->done && this->funcnamelen == 0/
534 {
535 	/*
536 	 * This is an anonymous function, but if it was invoked as a method of
537 	 * some object then V8 will have computed an inferred name that we can
538 	 * include in the stack trace.
539 	 */
540 	APPEND_CHR8('(','a','n','o','n',')',' ','a');
541 	APPEND_CHR('s');
542 	APPEND_CHR(' ');
543 
544 	this->funcrawnamestr = COPYIN_PTR(this->shared + V8_OFF_SHARED_IDENT);
545 	LOAD_STRFIELDS(this->funcrawnamestr, this->funcnamelen,
546 	    this->funcnameattrs);
547 }
548 
549 dtrace:helper:ustack:
550 /!this->done && this->funcnamelen == 0/
551 {
552 	APPEND_CHR('(');
553 	APPEND_CHR4('a','n','o','n');
554 	APPEND_CHR(')');
555 }
556 
557 APPEND_V8STR(this->funcrawnamestr, this->funcnamelen, this->funcnameattrs)
558 
559 /*
560  * Now look for the name of the script where the function was defined.  The
561  * "script" itself may be undefined for special functions like "RegExp".
562  */
563 dtrace:helper:ustack:
564 /!this->done/
565 {
566 	this->script = COPYIN_PTR(this->shared + V8_OFF_SHARED_SCRIPT);
567 	this->map = V8_MAP_PTR(COPYIN_PTR(this->script + V8_OFF_HEAPOBJ_MAP));
568 	this->scriptattrs = COPYIN_UINT8(this->map + V8_OFF_MAP_ATTRS);
569 }
570 
571 dtrace:helper:ustack:
572 /!this->done && !V8_TYPE_SCRIPT(this->scriptattrs)/
573 {
574 	APPEND_CHR('\0');
575 	this->done = 1;
576 	stringof(this->buf);
577 }
578 
579 
580 dtrace:helper:ustack:
581 /!this->done/
582 {
583 	this->scriptnamestr = COPYIN_PTR(this->script + V8_OFF_SCRIPT_NAME);
584 	LOAD_STRFIELDS(this->scriptnamestr, this->scriptnamelen,
585 	    this->scriptnameattrs);
586 }
587 
588 dtrace:helper:ustack:
589 /!this->done && this->scriptnamelen != 0/
590 {
591 	APPEND_CHR4(' ','a','t',' ');
592 }
593 
594 APPEND_V8STR(this->scriptnamestr, this->scriptnamelen, this->scriptnameattrs)
595 
596 /*
597  * Now look for file position and line number information.
598  */
599 dtrace:helper:ustack:
600 /!this->done/
601 {
602 	this->position = COPYIN_UINT32(this->shared + V8_OFF_SHARED_FUNIDENT);
603 	this->line_ends = COPYIN_PTR(this->script + V8_OFF_SCRIPT_LENDS);
604 	this->map = V8_MAP_PTR(COPYIN_PTR(this->line_ends + V8_OFF_HEAPOBJ_MAP));
605 	this->le_attrs = COPYIN_UINT8(this->map + V8_OFF_MAP_ATTRS);
606 }
607 
608 dtrace:helper:ustack:
609 /!this->done && this->le_attrs != V8_IT_FIXEDARRAY && this->position == 0/
610 {
611 	APPEND_CHR('\0');
612 	this->done = 1;
613 	stringof(this->buf);
614 }
615 
616 dtrace:helper:ustack:
617 /!this->done && this->le_attrs != V8_IT_FIXEDARRAY/
618 {
619 	/*
620 	 * If the line number array was not a valid FixedArray, it's probably
621 	 * undefined because V8 has not had to compute it yet.  In this case we
622 	 * just show the raw position and call it a day.
623 	 */
624 	APPEND_CHR4(' ','p','o','s');
625 	APPEND_CHR(' ');
626 	APPEND_NUM(SMI_VALUE(this->position));
627 	APPEND_CHR('\0');
628 	this->done = 1;
629 	stringof(this->buf);
630 }
631 
632 /*
633  * At this point, we've got both a position in the script and an array
634  * describing where each line of the file ends.  We can use this to compute the
635  * line number by binary searching the array.  (This is also what V8 does when
636  * computing stack traces.)
637  */
638 dtrace:helper:ustack:
639 /!this->done/
640 {
641 	/* initialize binary search */
642 	this->bsearch_line = this->position <
643 	SMI_VALUE(COPYIN_PTR(this->line_ends + V8_OFF_FA_DATA)) ? 1 : 0;
644 	this->bsearch_min = 0;
645 	this->bsearch_max = this->bsearch_line != 0 ? 0 :
646 	SMI_VALUE(COPYIN_PTR(this->line_ends + V8_OFF_FA_SIZE)) - 1;
647 }
648 
649 /*
650  * Of course, we can't iterate the binary search indefinitely, so we hardcode 15
651  * iterations.  That's enough to precisely identify the line number in files up
652  * to 32768 lines of code.
653  */
654 #define	BSEARCH_LOOP							\
655     dtrace:helper:ustack:	\
656     /!this->done && this->bsearch_max >= 1/	\
657     {	\
658 	this->ii = (this->bsearch_min + this->bsearch_max) >> 1;	\
659     }	\
660 									\
661     dtrace:helper:ustack:	\
662     /!this->done && this->bsearch_max >= 1 &&	\
663      this->position > SMI_VALUE(	\
664          COPYIN_PTR(this->line_ends + V8_OFF_FA_DATA +	\
665                     this->ii * sizeof (uint32_t)))/	\
666     {	\
667 	this->bsearch_min = this->ii + 1;				\
668     }	\
669 									\
670     dtrace:helper:ustack:	\
671     /!this->done && this->bsearch_max >= 1 &&	\
672      this->position <= SMI_VALUE(	\
673          COPYIN_PTR(this->line_ends + V8_OFF_FA_DATA +	\
674                     (this->ii - 1) * sizeof (uint32_t)))/	\
675     {	\
676 	this->bsearch_max = this->ii - 1;				\
677     }
678 
679 BSEARCH_LOOP
680 BSEARCH_LOOP
681 BSEARCH_LOOP
682 BSEARCH_LOOP
683 BSEARCH_LOOP
684 BSEARCH_LOOP
685 BSEARCH_LOOP
686 BSEARCH_LOOP
687 BSEARCH_LOOP
688 BSEARCH_LOOP
689 BSEARCH_LOOP
690 BSEARCH_LOOP
691 BSEARCH_LOOP
692 BSEARCH_LOOP
693 BSEARCH_LOOP
694 
695 dtrace:helper:ustack:
696 /!this->done && !this->bsearch_line/
697 {
698 	this->bsearch_line = this->ii + 1;
699 }
700 
701 dtrace:helper:ustack:
702 /!this->done/
703 {
704 	APPEND_CHR(' ');
705 	APPEND_CHR4('l','i','n','e');
706 	APPEND_CHR(' ');
707 	APPEND_NUM(this->bsearch_line);
708 	APPEND_CHR('\0');
709 	this->done = 1;
710 	stringof(this->buf);
711 }
712 
713 /* vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
714