xref: /third_party/node/src/v8ustack.d (revision 1cb0ef41)
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 */
359dtrace: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 * Check for other common frame types for which we also have nothing to add.
397 */
398dtrace:helper:ustack:
399/!this->done/
400{
401	this->marker = COPYIN_PTR(this->fp + V8_OFF_FP_CONTEXT);
402}
403
404dtrace:helper:ustack:
405/!this->done && IS_SMI(this->marker) &&
406 SMI_VALUE(this->marker) == V8_FT_ENTRY/
407{
408	this->done = 1;
409	APPEND_CHR8('<','<',' ','e','n','t','r','y');
410	APPEND_CHR4(' ','>','>','\0');
411	stringof(this->buf);
412}
413
414dtrace:helper:ustack:
415/!this->done && IS_SMI(this->marker) &&
416 SMI_VALUE(this->marker) == V8_FT_ENTRYCONSTRUCT/
417{
418	this->done = 1;
419	APPEND_CHR8('<','<',' ','e','n','t','r','y');
420	APPEND_CHR8('_','c','o','n','s','t','r','u');
421	APPEND_CHR4('t',' ','>','>');
422	APPEND_CHR('\0');
423	stringof(this->buf);
424}
425
426dtrace:helper:ustack:
427/!this->done && IS_SMI(this->marker) &&
428 SMI_VALUE(this->marker) == V8_FT_EXIT/
429{
430	this->done = 1;
431	APPEND_CHR8('<','<',' ','e','x','i','t',' ');
432	APPEND_CHR4('>','>','\0','\0');
433	stringof(this->buf);
434}
435
436dtrace:helper:ustack:
437/!this->done && IS_SMI(this->marker) &&
438 SMI_VALUE(this->marker) == V8_FT_INTERNAL/
439{
440	this->done = 1;
441	APPEND_CHR8('<','<',' ','i','n','t','e','r');
442	APPEND_CHR8('n','a','l',' ','>','>','\0','\0');
443	stringof(this->buf);
444}
445
446dtrace:helper:ustack:
447/!this->done && IS_SMI(this->marker) &&
448 SMI_VALUE(this->marker) == V8_FT_CONSTRUCT/
449{
450	this->done = 1;
451	APPEND_CHR8('<','<',' ','c','o','n','s','t');
452	APPEND_CHR8('r','u','c','t','o','r',' ','>');
453	APPEND_CHR4('>','\0','\0','\0');
454	stringof(this->buf);
455}
456
457dtrace:helper:ustack:
458/!this->done && IS_SMI(this->marker) &&
459 SMI_VALUE(this->marker) == V8_FT_STUB/
460{
461	this->done = 1;
462	APPEND_CHR8('<','<',' ','s','t','u','b',' ');
463	APPEND_CHR4('>','>','\0','\0');
464	stringof(this->buf);
465}
466
467/*
468 * Now check for internal frames that we can only identify by seeing that
469 * there's a Code object where there would be a JSFunction object for a
470 * JavaScriptFrame.
471 */
472dtrace:helper:ustack:
473/!this->done/
474{
475	this->func = COPYIN_PTR(this->fp + V8_OFF_FP_FUNC);
476	this->map = V8_MAP_PTR(COPYIN_PTR(this->func + V8_OFF_HEAPOBJ_MAP));
477	this->attrs = COPYIN_UINT8(this->map + V8_OFF_MAP_ATTRS);
478}
479
480dtrace:helper:ustack:
481/!this->done && this->attrs == V8_IT_CODE/
482{
483	this->done = 1;
484	APPEND_CHR8('<','<',' ','i','n','t','e','r');
485	APPEND_CHR8('n','a','l',' ','c','o','d','e');
486	APPEND_CHR4(' ','>','>','\0');
487	stringof(this->buf);
488}
489
490/*
491 * At this point, we're either looking at a JavaScriptFrame or an
492 * OptimizedFrame.  For now, we assume JavaScript and start by grabbing the
493 * function name.
494 */
495dtrace:helper:ustack:
496/!this->done/
497{
498	this->map = 0;
499	this->attrs = 0;
500
501	this->shared = COPYIN_PTR(this->func + V8_OFF_FUNC_SHARED);
502	this->funcrawnamestr = COPYIN_PTR(this->shared + V8_OFF_RAW_NAME);
503	this->hassharedname = this->funcrawnamestr !=
504		NO_SHARED_FUNCTION_NAME_SENTINEL;
505}
506
507dtrace:helper:ustack:
508/!this->done && this->hassharedname/
509{
510	LOAD_STRFIELDS(this->funcrawnamestr, this->funcnamelen,
511		this->funcnameattrs);
512}
513
514dtrace:helper:ustack:
515/!this->done && this->funcnamelen == 0/
516{
517	/*
518	 * This is an anonymous function, but if it was invoked as a method of
519	 * some object then V8 will have computed an inferred name that we can
520	 * include in the stack trace.
521	 */
522	APPEND_CHR8('(','a','n','o','n',')',' ','a');
523	APPEND_CHR('s');
524	APPEND_CHR(' ');
525
526	this->funcrawnamestr = COPYIN_PTR(this->shared + V8_OFF_SHARED_IDENT);
527	LOAD_STRFIELDS(this->funcrawnamestr, this->funcnamelen,
528	    this->funcnameattrs);
529}
530
531dtrace:helper:ustack:
532/!this->done && this->funcnamelen == 0/
533{
534	APPEND_CHR('(');
535	APPEND_CHR4('a','n','o','n');
536	APPEND_CHR(')');
537}
538
539APPEND_V8STR(this->funcrawnamestr, this->funcnamelen, this->funcnameattrs)
540
541/*
542 * Now look for the name of the script where the function was defined.  The
543 * "script" itself may be undefined for special functions like "RegExp".
544 */
545dtrace:helper:ustack:
546/!this->done/
547{
548	this->script = COPYIN_PTR(this->shared + V8_OFF_SHARED_SCRIPT);
549	this->map = V8_MAP_PTR(COPYIN_PTR(this->script + V8_OFF_HEAPOBJ_MAP));
550	this->scriptattrs = COPYIN_UINT8(this->map + V8_OFF_MAP_ATTRS);
551}
552
553dtrace:helper:ustack:
554/!this->done && !V8_TYPE_SCRIPT(this->scriptattrs)/
555{
556	APPEND_CHR('\0');
557	this->done = 1;
558	stringof(this->buf);
559}
560
561
562dtrace:helper:ustack:
563/!this->done/
564{
565	this->scriptnamestr = COPYIN_PTR(this->script + V8_OFF_SCRIPT_NAME);
566	LOAD_STRFIELDS(this->scriptnamestr, this->scriptnamelen,
567	    this->scriptnameattrs);
568}
569
570dtrace:helper:ustack:
571/!this->done && this->scriptnamelen != 0/
572{
573	APPEND_CHR4(' ','a','t',' ');
574}
575
576APPEND_V8STR(this->scriptnamestr, this->scriptnamelen, this->scriptnameattrs)
577
578/*
579 * Now look for file position and line number information.
580 */
581dtrace:helper:ustack:
582/!this->done/
583{
584	this->position = COPYIN_UINT32(this->shared + V8_OFF_SHARED_FUNIDENT);
585	this->line_ends = COPYIN_PTR(this->script + V8_OFF_SCRIPT_LENDS);
586	this->map = V8_MAP_PTR(COPYIN_PTR(this->line_ends + V8_OFF_HEAPOBJ_MAP));
587	this->le_attrs = COPYIN_UINT8(this->map + V8_OFF_MAP_ATTRS);
588}
589
590dtrace:helper:ustack:
591/!this->done && this->le_attrs != V8_IT_FIXEDARRAY && this->position == 0/
592{
593	APPEND_CHR('\0');
594	this->done = 1;
595	stringof(this->buf);
596}
597
598dtrace:helper:ustack:
599/!this->done && this->le_attrs != V8_IT_FIXEDARRAY/
600{
601	/*
602	 * If the line number array was not a valid FixedArray, it's probably
603	 * undefined because V8 has not had to compute it yet.  In this case we
604	 * just show the raw position and call it a day.
605	 */
606	APPEND_CHR4(' ','p','o','s');
607	APPEND_CHR(' ');
608	APPEND_NUM(SMI_VALUE(this->position));
609	APPEND_CHR('\0');
610	this->done = 1;
611	stringof(this->buf);
612}
613
614/*
615 * At this point, we've got both a position in the script and an array
616 * describing where each line of the file ends.  We can use this to compute the
617 * line number by binary searching the array.  (This is also what V8 does when
618 * computing stack traces.)
619 */
620dtrace:helper:ustack:
621/!this->done/
622{
623	/* initialize binary search */
624	this->bsearch_line = this->position <
625	SMI_VALUE(COPYIN_PTR(this->line_ends + V8_OFF_FA_DATA)) ? 1 : 0;
626	this->bsearch_min = 0;
627	this->bsearch_max = this->bsearch_line != 0 ? 0 :
628	SMI_VALUE(COPYIN_PTR(this->line_ends + V8_OFF_FA_SIZE)) - 1;
629}
630
631/*
632 * Of course, we can't iterate the binary search indefinitely, so we hardcode 15
633 * iterations.  That's enough to precisely identify the line number in files up
634 * to 32768 lines of code.
635 */
636#define	BSEARCH_LOOP							\
637    dtrace:helper:ustack:	\
638    /!this->done && this->bsearch_max >= 1/	\
639    {	\
640	this->ii = (this->bsearch_min + this->bsearch_max) >> 1;	\
641    }	\
642									\
643    dtrace:helper:ustack:	\
644    /!this->done && this->bsearch_max >= 1 &&	\
645     this->position > SMI_VALUE(	\
646         COPYIN_PTR(this->line_ends + V8_OFF_FA_DATA +	\
647                    this->ii * sizeof (uint32_t)))/	\
648    {	\
649	this->bsearch_min = this->ii + 1;				\
650    }	\
651									\
652    dtrace:helper:ustack:	\
653    /!this->done && this->bsearch_max >= 1 &&	\
654     this->position <= SMI_VALUE(	\
655         COPYIN_PTR(this->line_ends + V8_OFF_FA_DATA +	\
656                    (this->ii - 1) * sizeof (uint32_t)))/	\
657    {	\
658	this->bsearch_max = this->ii - 1;				\
659    }
660
661BSEARCH_LOOP
662BSEARCH_LOOP
663BSEARCH_LOOP
664BSEARCH_LOOP
665BSEARCH_LOOP
666BSEARCH_LOOP
667BSEARCH_LOOP
668BSEARCH_LOOP
669BSEARCH_LOOP
670BSEARCH_LOOP
671BSEARCH_LOOP
672BSEARCH_LOOP
673BSEARCH_LOOP
674BSEARCH_LOOP
675BSEARCH_LOOP
676
677dtrace:helper:ustack:
678/!this->done && !this->bsearch_line/
679{
680	this->bsearch_line = this->ii + 1;
681}
682
683dtrace:helper:ustack:
684/!this->done/
685{
686	APPEND_CHR(' ');
687	APPEND_CHR4('l','i','n','e');
688	APPEND_CHR(' ');
689	APPEND_NUM(this->bsearch_line);
690	APPEND_CHR('\0');
691	this->done = 1;
692	stringof(this->buf);
693}
694
695/* vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
696