/ interpreter / StackVisitor.cpp
StackVisitor.cpp
1 /* 2 * Copyright (C) 2013-2019 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "StackVisitor.h" 28 29 #include "ClonedArguments.h" 30 #include "DebuggerPrimitives.h" 31 #include "InlineCallFrame.h" 32 #include "JSCInlines.h" 33 #include "RegisterAtOffsetList.h" 34 #include "WasmCallee.h" 35 #include "WasmIndexOrName.h" 36 #include "WebAssemblyFunction.h" 37 #include <wtf/text/StringBuilder.h> 38 39 namespace JSC { 40 41 StackVisitor::StackVisitor(CallFrame* startFrame, VM& vm) 42 { 43 m_frame.m_index = 0; 44 m_frame.m_isWasmFrame = false; 45 CallFrame* topFrame; 46 if (startFrame) { 47 ASSERT(!vm.topCallFrame || reinterpret_cast<void*>(vm.topCallFrame) != vm.topEntryFrame); 48 49 m_frame.m_entryFrame = vm.topEntryFrame; 50 topFrame = vm.topCallFrame; 51 52 if (topFrame && topFrame->isStackOverflowFrame()) { 53 topFrame = topFrame->callerFrame(m_frame.m_entryFrame); 54 m_topEntryFrameIsEmpty = (m_frame.m_entryFrame != vm.topEntryFrame); 55 if (startFrame == vm.topCallFrame) 56 startFrame = topFrame; 57 } 58 59 } else { 60 m_frame.m_entryFrame = nullptr; 61 topFrame = nullptr; 62 } 63 m_frame.m_callerIsEntryFrame = false; 64 readFrame(topFrame); 65 66 // Find the frame the caller wants to start unwinding from. 67 while (m_frame.callFrame() && m_frame.callFrame() != startFrame) 68 gotoNextFrame(); 69 } 70 71 void StackVisitor::gotoNextFrame() 72 { 73 m_frame.m_index++; 74 #if ENABLE(DFG_JIT) 75 if (m_frame.isInlinedFrame()) { 76 InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame(); 77 CodeOrigin* callerCodeOrigin = inlineCallFrame->getCallerSkippingTailCalls(); 78 if (!callerCodeOrigin) { 79 while (inlineCallFrame) { 80 readInlinedFrame(m_frame.callFrame(), &inlineCallFrame->directCaller); 81 inlineCallFrame = m_frame.inlineCallFrame(); 82 } 83 m_frame.m_entryFrame = m_frame.m_callerEntryFrame; 84 readFrame(m_frame.callerFrame()); 85 } else 86 readInlinedFrame(m_frame.callFrame(), callerCodeOrigin); 87 return; 88 } 89 #endif // ENABLE(DFG_JIT) 90 m_frame.m_entryFrame = m_frame.m_callerEntryFrame; 91 readFrame(m_frame.callerFrame()); 92 } 93 94 void StackVisitor::unwindToMachineCodeBlockFrame() 95 { 96 #if ENABLE(DFG_JIT) 97 if (m_frame.isInlinedFrame()) { 98 CodeOrigin codeOrigin = m_frame.inlineCallFrame()->directCaller; 99 while (codeOrigin.inlineCallFrame()) 100 codeOrigin = codeOrigin.inlineCallFrame()->directCaller; 101 readNonInlinedFrame(m_frame.callFrame(), &codeOrigin); 102 } 103 #endif 104 } 105 106 void StackVisitor::readFrame(CallFrame* callFrame) 107 { 108 if (!callFrame) { 109 m_frame.setToEnd(); 110 return; 111 } 112 113 if (callFrame->isAnyWasmCallee()) { 114 readNonInlinedFrame(callFrame); 115 return; 116 } 117 118 #if !ENABLE(DFG_JIT) 119 readNonInlinedFrame(callFrame); 120 121 #else // !ENABLE(DFG_JIT) 122 // If the frame doesn't have a code block, then it's not a DFG frame. 123 // Hence, we're not at an inlined frame. 124 CodeBlock* codeBlock = callFrame->codeBlock(); 125 if (!codeBlock) { 126 readNonInlinedFrame(callFrame); 127 return; 128 } 129 130 // If the code block does not have any code origins, then there's no 131 // inlining. Hence, we're not at an inlined frame. 132 if (!codeBlock->hasCodeOrigins()) { 133 readNonInlinedFrame(callFrame); 134 return; 135 } 136 137 CallSiteIndex index = callFrame->callSiteIndex(); 138 ASSERT(codeBlock->canGetCodeOrigin(index)); 139 if (!codeBlock->canGetCodeOrigin(index)) { 140 // See assertion above. In release builds, we try to protect ourselves 141 // from crashing even though stack walking will be goofed up. 142 m_frame.setToEnd(); 143 return; 144 } 145 146 CodeOrigin codeOrigin = codeBlock->codeOrigin(index); 147 if (!codeOrigin.inlineCallFrame()) { 148 readNonInlinedFrame(callFrame, &codeOrigin); 149 return; 150 } 151 152 readInlinedFrame(callFrame, &codeOrigin); 153 #endif // !ENABLE(DFG_JIT) 154 } 155 156 void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin) 157 { 158 m_frame.m_callFrame = callFrame; 159 m_frame.m_argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); 160 m_frame.m_callerEntryFrame = m_frame.m_entryFrame; 161 m_frame.m_callerFrame = callFrame->callerFrame(m_frame.m_callerEntryFrame); 162 m_frame.m_callerIsEntryFrame = m_frame.m_callerEntryFrame != m_frame.m_entryFrame; 163 m_frame.m_isWasmFrame = false; 164 165 CalleeBits callee = callFrame->callee(); 166 m_frame.m_callee = callee; 167 168 if (callFrame->isAnyWasmCallee()) { 169 m_frame.m_isWasmFrame = true; 170 m_frame.m_codeBlock = nullptr; 171 m_frame.m_bytecodeIndex = BytecodeIndex(); 172 #if ENABLE(WEBASSEMBLY) 173 CalleeBits bits = callFrame->callee(); 174 if (bits.isWasm()) 175 m_frame.m_wasmFunctionIndexOrName = bits.asWasmCallee()->indexOrName(); 176 #endif 177 } else { 178 m_frame.m_codeBlock = callFrame->codeBlock(); 179 m_frame.m_bytecodeIndex = !m_frame.codeBlock() ? BytecodeIndex(0) 180 : codeOrigin ? codeOrigin->bytecodeIndex() 181 : callFrame->bytecodeIndex(); 182 183 } 184 185 #if ENABLE(DFG_JIT) 186 m_frame.m_inlineCallFrame = nullptr; 187 #endif 188 } 189 190 #if ENABLE(DFG_JIT) 191 static int inlinedFrameOffset(CodeOrigin* codeOrigin) 192 { 193 InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame(); 194 int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0; 195 return frameOffset; 196 } 197 198 void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin) 199 { 200 ASSERT(codeOrigin); 201 m_frame.m_isWasmFrame = false; 202 203 int frameOffset = inlinedFrameOffset(codeOrigin); 204 bool isInlined = !!frameOffset; 205 if (isInlined) { 206 InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame(); 207 208 m_frame.m_callFrame = callFrame; 209 m_frame.m_inlineCallFrame = inlineCallFrame; 210 if (inlineCallFrame->argumentCountRegister.isValid()) 211 m_frame.m_argumentCountIncludingThis = callFrame->r(inlineCallFrame->argumentCountRegister).unboxedInt32(); 212 else 213 m_frame.m_argumentCountIncludingThis = inlineCallFrame->argumentCountIncludingThis; 214 m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock.get(); 215 m_frame.m_bytecodeIndex = codeOrigin->bytecodeIndex(); 216 217 JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame); 218 m_frame.m_callee = callee; 219 ASSERT(!!m_frame.callee().rawPtr()); 220 221 // The callerFrame just needs to be non-null to indicate that we 222 // haven't reached the last frame yet. Setting it to the root 223 // frame (i.e. the callFrame that this inlined frame is called from) 224 // would work just fine. 225 m_frame.m_callerFrame = callFrame; 226 return; 227 } 228 229 readNonInlinedFrame(callFrame, codeOrigin); 230 } 231 #endif // ENABLE(DFG_JIT) 232 233 StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const 234 { 235 if (isWasmFrame()) 236 return CodeType::Wasm; 237 238 if (!codeBlock()) 239 return CodeType::Native; 240 241 switch (codeBlock()->codeType()) { 242 case EvalCode: 243 return CodeType::Eval; 244 case ModuleCode: 245 return CodeType::Module; 246 case FunctionCode: 247 return CodeType::Function; 248 case GlobalCode: 249 return CodeType::Global; 250 } 251 RELEASE_ASSERT_NOT_REACHED(); 252 return CodeType::Global; 253 } 254 255 #if ENABLE(ASSEMBLER) 256 Optional<RegisterAtOffsetList> StackVisitor::Frame::calleeSaveRegistersForUnwinding() 257 { 258 if (!NUMBER_OF_CALLEE_SAVES_REGISTERS) 259 return WTF::nullopt; 260 261 if (isInlinedFrame()) 262 return WTF::nullopt; 263 264 #if ENABLE(WEBASSEMBLY) 265 if (isWasmFrame()) { 266 if (callee().isCell()) { 267 RELEASE_ASSERT(isWebAssemblyModule(callee().asCell())); 268 return WTF::nullopt; 269 } 270 Wasm::Callee* wasmCallee = callee().asWasmCallee(); 271 return *wasmCallee->calleeSaveRegisters(); 272 } 273 274 if (callee().isCell()) { 275 if (auto* jsToWasmICCallee = jsDynamicCast<JSToWasmICCallee*>(callee().asCell()->vm(), callee().asCell())) 276 return jsToWasmICCallee->function()->usedCalleeSaveRegisters(); 277 } 278 #endif // ENABLE(WEBASSEMBLY) 279 280 if (CodeBlock* codeBlock = this->codeBlock()) 281 return *codeBlock->calleeSaveRegisters(); 282 283 return WTF::nullopt; 284 } 285 #endif // ENABLE(ASSEMBLER) 286 287 String StackVisitor::Frame::functionName() const 288 { 289 String traceLine; 290 291 switch (codeType()) { 292 case CodeType::Wasm: 293 traceLine = makeString(m_wasmFunctionIndexOrName); 294 break; 295 case CodeType::Eval: 296 traceLine = "eval code"_s; 297 break; 298 case CodeType::Module: 299 traceLine = "module code"_s; 300 break; 301 case CodeType::Native: { 302 JSCell* callee = this->callee().asCell(); 303 if (callee) 304 traceLine = getCalculatedDisplayName(callFrame()->deprecatedVM(), jsCast<JSObject*>(callee)).impl(); 305 break; 306 } 307 case CodeType::Function: 308 traceLine = getCalculatedDisplayName(callFrame()->deprecatedVM(), jsCast<JSObject*>(this->callee().asCell())).impl(); 309 break; 310 case CodeType::Global: 311 traceLine = "global code"_s; 312 break; 313 } 314 return traceLine.isNull() ? emptyString() : traceLine; 315 } 316 317 String StackVisitor::Frame::sourceURL() const 318 { 319 String traceLine; 320 321 switch (codeType()) { 322 case CodeType::Eval: 323 case CodeType::Module: 324 case CodeType::Function: 325 case CodeType::Global: { 326 String sourceURL = codeBlock()->ownerExecutable()->sourceURL(); 327 if (!sourceURL.isEmpty()) 328 traceLine = sourceURL.impl(); 329 break; 330 } 331 case CodeType::Native: 332 traceLine = "[native code]"_s; 333 break; 334 case CodeType::Wasm: 335 traceLine = "[wasm code]"_s; 336 break; 337 } 338 return traceLine.isNull() ? emptyString() : traceLine; 339 } 340 341 String StackVisitor::Frame::toString() const 342 { 343 StringBuilder traceBuild; 344 String functionName = this->functionName(); 345 String sourceURL = this->sourceURL(); 346 traceBuild.append(functionName); 347 if (!sourceURL.isEmpty()) { 348 if (!functionName.isEmpty()) 349 traceBuild.append('@'); 350 traceBuild.append(sourceURL); 351 if (hasLineAndColumnInfo()) { 352 unsigned line = 0; 353 unsigned column = 0; 354 computeLineAndColumn(line, column); 355 traceBuild.append(':'); 356 traceBuild.appendNumber(line); 357 traceBuild.append(':'); 358 traceBuild.appendNumber(column); 359 } 360 } 361 return traceBuild.toString().impl(); 362 } 363 364 intptr_t StackVisitor::Frame::sourceID() 365 { 366 if (CodeBlock* codeBlock = this->codeBlock()) 367 return codeBlock->ownerExecutable()->sourceID(); 368 return noSourceID; 369 } 370 371 ClonedArguments* StackVisitor::Frame::createArguments(VM& vm) 372 { 373 ASSERT(m_callFrame); 374 CallFrame* physicalFrame = m_callFrame; 375 // FIXME: Revisit JSGlobalObject. 376 // https://bugs.webkit.org/show_bug.cgi?id=203204 377 JSGlobalObject* globalObject = physicalFrame->lexicalGlobalObject(vm); 378 ClonedArguments* arguments; 379 ArgumentsMode mode; 380 if (Options::useFunctionDotArguments()) 381 mode = ArgumentsMode::Cloned; 382 else 383 mode = ArgumentsMode::FakeValues; 384 #if ENABLE(DFG_JIT) 385 if (isInlinedFrame()) { 386 ASSERT(m_inlineCallFrame); 387 arguments = ClonedArguments::createWithInlineFrame(globalObject, physicalFrame, m_inlineCallFrame, mode); 388 } else 389 #endif 390 arguments = ClonedArguments::createWithMachineFrame(globalObject, physicalFrame, mode); 391 return arguments; 392 } 393 394 bool StackVisitor::Frame::hasLineAndColumnInfo() const 395 { 396 return !!codeBlock(); 397 } 398 399 void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) const 400 { 401 CodeBlock* codeBlock = this->codeBlock(); 402 if (!codeBlock) { 403 line = 0; 404 column = 0; 405 return; 406 } 407 408 int divot = 0; 409 int unusedStartOffset = 0; 410 int unusedEndOffset = 0; 411 unsigned divotLine = 0; 412 unsigned divotColumn = 0; 413 retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn); 414 415 line = divotLine + codeBlock->ownerExecutable()->firstLine(); 416 column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset()); 417 418 if (Optional<int> overrideLineNumber = codeBlock->ownerExecutable()->overrideLineNumber(codeBlock->vm())) 419 line = overrideLineNumber.value(); 420 } 421 422 void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) const 423 { 424 CodeBlock* codeBlock = this->codeBlock(); 425 codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeIndex(bytecodeIndex(), divot, startOffset, endOffset, line, column); 426 divot += codeBlock->sourceOffset(); 427 } 428 429 void StackVisitor::Frame::setToEnd() 430 { 431 m_callFrame = nullptr; 432 #if ENABLE(DFG_JIT) 433 m_inlineCallFrame = nullptr; 434 #endif 435 m_isWasmFrame = false; 436 } 437 438 void StackVisitor::Frame::dump(PrintStream& out, Indenter indent) const 439 { 440 dump(out, indent, [] (PrintStream&) { }); 441 } 442 443 void StackVisitor::Frame::dump(PrintStream& out, Indenter indent, WTF::Function<void(PrintStream&)> prefix) const 444 { 445 if (!this->callFrame()) { 446 out.print(indent, "frame 0x0\n"); 447 return; 448 } 449 450 CodeBlock* codeBlock = this->codeBlock(); 451 out.print(indent); 452 prefix(out); 453 out.print("frame ", RawPointer(this->callFrame()), " {\n"); 454 455 { 456 indent++; 457 458 CallFrame* callFrame = m_callFrame; 459 CallFrame* callerFrame = this->callerFrame(); 460 const void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr; 461 462 out.print(indent, "name: ", functionName(), "\n"); 463 out.print(indent, "sourceURL: ", sourceURL(), "\n"); 464 465 bool isInlined = false; 466 #if ENABLE(DFG_JIT) 467 isInlined = isInlinedFrame(); 468 out.print(indent, "isInlinedFrame: ", isInlinedFrame(), "\n"); 469 if (isInlinedFrame()) 470 out.print(indent, "InlineCallFrame: ", RawPointer(m_inlineCallFrame), "\n"); 471 #endif 472 473 out.print(indent, "callee: ", RawPointer(callee().rawPtr()), "\n"); 474 out.print(indent, "returnPC: ", RawPointer(returnPC), "\n"); 475 out.print(indent, "callerFrame: ", RawPointer(callerFrame), "\n"); 476 uintptr_t locationRawBits = callFrame->callSiteAsRawBits(); 477 out.print(indent, "rawLocationBits: ", locationRawBits, 478 " ", RawPointer(reinterpret_cast<void*>(locationRawBits)), "\n"); 479 out.print(indent, "codeBlock: ", RawPointer(codeBlock)); 480 if (codeBlock) 481 out.print(" ", *codeBlock); 482 out.print("\n"); 483 if (codeBlock && !isInlined) { 484 indent++; 485 486 if (callFrame->callSiteBitsAreBytecodeOffset()) { 487 BytecodeIndex bytecodeIndex = callFrame->bytecodeIndex(); 488 out.print(indent, bytecodeIndex, " of ", codeBlock->instructions().size(), "\n"); 489 #if ENABLE(DFG_JIT) 490 } else { 491 out.print(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n"); 492 if (codeBlock->hasCodeOrigins()) { 493 CallSiteIndex callSiteIndex = callFrame->callSiteIndex(); 494 out.print(indent, "callSiteIndex: ", callSiteIndex.bits(), " of ", codeBlock->codeOrigins().size(), "\n"); 495 496 JITType jitType = codeBlock->jitType(); 497 if (jitType != JITType::FTLJIT) { 498 JITCode* jitCode = codeBlock->jitCode().get(); 499 out.print(indent, "jitCode: ", RawPointer(jitCode), 500 " start ", RawPointer(jitCode->start()), 501 " end ", RawPointer(jitCode->end()), "\n"); 502 } 503 } 504 #endif 505 } 506 unsigned line = 0; 507 unsigned column = 0; 508 computeLineAndColumn(line, column); 509 out.print(indent, "line: ", line, "\n"); 510 out.print(indent, "column: ", column, "\n"); 511 512 indent--; 513 } 514 out.print(indent, "EntryFrame: ", RawPointer(m_entryFrame), "\n"); 515 indent--; 516 } 517 out.print(indent, "}\n"); 518 } 519 520 } // namespace JSC