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