diff options
Diffstat (limited to 'Source/JavaScriptCore/interpreter/StackVisitor.cpp')
-rw-r--r-- | Source/JavaScriptCore/interpreter/StackVisitor.cpp | 433 |
1 files changed, 232 insertions, 201 deletions
diff --git a/Source/JavaScriptCore/interpreter/StackVisitor.cpp b/Source/JavaScriptCore/interpreter/StackVisitor.cpp index d922e7f8f..aae6daa78 100644 --- a/Source/JavaScriptCore/interpreter/StackVisitor.cpp +++ b/Source/JavaScriptCore/interpreter/StackVisitor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013, 2015-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,42 +26,88 @@ #include "config.h" #include "StackVisitor.h" -#include "Arguments.h" -#include "CallFrameInlines.h" -#include "Executable.h" +#include "ClonedArguments.h" +#include "DebuggerPrimitives.h" +#include "InlineCallFrame.h" #include "Interpreter.h" -#include "Operations.h" -#include <wtf/DataLog.h> +#include "JSCInlines.h" +#include "JSWebAssemblyCallee.h" +#include <wtf/text/StringBuilder.h> namespace JSC { StackVisitor::StackVisitor(CallFrame* startFrame) { m_frame.m_index = 0; - readFrame(startFrame); + m_frame.m_isWasmFrame = false; + CallFrame* topFrame; + if (startFrame) { + m_frame.m_VMEntryFrame = startFrame->vm().topVMEntryFrame; + topFrame = startFrame->vm().topCallFrame; + + if (topFrame && static_cast<void*>(m_frame.m_VMEntryFrame) == static_cast<void*>(topFrame)) { + topFrame = vmEntryRecord(m_frame.m_VMEntryFrame)->m_prevTopCallFrame; + m_frame.m_VMEntryFrame = vmEntryRecord(m_frame.m_VMEntryFrame)->m_prevTopVMEntryFrame; + } + } else { + m_frame.m_VMEntryFrame = 0; + topFrame = 0; + } + m_frame.m_callerIsVMEntryFrame = false; + readFrame(topFrame); + + // Find the frame the caller wants to start unwinding from. + while (m_frame.callFrame() && m_frame.callFrame() != startFrame) + gotoNextFrame(); } void StackVisitor::gotoNextFrame() { + m_frame.m_index++; #if ENABLE(DFG_JIT) if (m_frame.isInlinedFrame()) { InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame(); - CodeOrigin* callerCodeOrigin = &inlineCallFrame->caller; - readInlinedFrame(m_frame.callFrame(), callerCodeOrigin); - - } else + CodeOrigin* callerCodeOrigin = inlineCallFrame->getCallerSkippingTailCalls(); + if (!callerCodeOrigin) { + while (inlineCallFrame) { + readInlinedFrame(m_frame.callFrame(), &inlineCallFrame->directCaller); + inlineCallFrame = m_frame.inlineCallFrame(); + } + m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame; + readFrame(m_frame.callerFrame()); + } else + readInlinedFrame(m_frame.callFrame(), callerCodeOrigin); + return; + } #endif // ENABLE(DFG_JIT) - readFrame(m_frame.callerFrame()); + m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame; + readFrame(m_frame.callerFrame()); +} + +void StackVisitor::unwindToMachineCodeBlockFrame() +{ +#if ENABLE(DFG_JIT) + if (m_frame.isInlinedFrame()) { + CodeOrigin codeOrigin = m_frame.inlineCallFrame()->directCaller; + while (codeOrigin.inlineCallFrame) + codeOrigin = codeOrigin.inlineCallFrame->directCaller; + readNonInlinedFrame(m_frame.callFrame(), &codeOrigin); + } +#endif } void StackVisitor::readFrame(CallFrame* callFrame) { - ASSERT(!callFrame->isVMEntrySentinel()); if (!callFrame) { m_frame.setToEnd(); return; } + if (callFrame->callee()->isAnyWasmCallee(callFrame->vm())) { + readNonInlinedFrame(callFrame); + return; + } + #if !ENABLE(DFG_JIT) readNonInlinedFrame(callFrame); @@ -81,7 +127,7 @@ void StackVisitor::readFrame(CallFrame* callFrame) return; } - unsigned index = callFrame->locationAsCodeOriginIndex(); + CallSiteIndex index = callFrame->callSiteIndex(); ASSERT(codeBlock->canGetCodeOrigin(index)); if (!codeBlock->canGetCodeOrigin(index)) { // See assertion above. In release builds, we try to protect ourselves @@ -104,13 +150,26 @@ void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOri { m_frame.m_callFrame = callFrame; m_frame.m_argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); - m_frame.m_callerFrame = callFrame->callerFrameSkippingVMEntrySentinel(); - m_frame.m_callee = callFrame->callee(); - m_frame.m_scope = callFrame->scope(); - m_frame.m_codeBlock = callFrame->codeBlock(); - m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0 - : codeOrigin ? codeOrigin->bytecodeIndex - : callFrame->locationAsBytecodeOffset(); + m_frame.m_CallerVMEntryFrame = m_frame.m_VMEntryFrame; + m_frame.m_callerFrame = callFrame->callerFrame(m_frame.m_CallerVMEntryFrame); + m_frame.m_callerIsVMEntryFrame = m_frame.m_CallerVMEntryFrame != m_frame.m_VMEntryFrame; + m_frame.m_isWasmFrame = false; + + JSCell* callee = callFrame->callee(); + m_frame.m_callee = callee; + + if (callee->isAnyWasmCallee(*callee->vm())) { + m_frame.m_isWasmFrame = true; + m_frame.m_codeBlock = nullptr; + m_frame.m_bytecodeOffset = 0; + } else { + m_frame.m_codeBlock = callFrame->codeBlock(); + m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0 + : codeOrigin ? codeOrigin->bytecodeIndex + : callFrame->bytecodeOffset(); + + } + #if ENABLE(DFG_JIT) m_frame.m_inlineCallFrame = 0; #endif @@ -127,7 +186,7 @@ static int inlinedFrameOffset(CodeOrigin* codeOrigin) void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin) { ASSERT(codeOrigin); - ASSERT(!callFrame->isVMEntrySentinel()); + m_frame.m_isWasmFrame = false; int frameOffset = inlinedFrameOffset(codeOrigin); bool isInlined = !!frameOffset; @@ -136,14 +195,15 @@ void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin m_frame.m_callFrame = callFrame; m_frame.m_inlineCallFrame = inlineCallFrame; - m_frame.m_argumentCountIncludingThis = inlineCallFrame->arguments.size(); - m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock(); + if (inlineCallFrame->argumentCountRegister.isValid()) + m_frame.m_argumentCountIncludingThis = callFrame->r(inlineCallFrame->argumentCountRegister.offset()).unboxedInt32(); + else + m_frame.m_argumentCountIncludingThis = inlineCallFrame->arguments.size(); + m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock.get(); m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex; JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame); - m_frame.m_scope = callee->scope(); m_frame.m_callee = callee; - ASSERT(m_frame.scope()); ASSERT(m_frame.callee()); // The callerFrame just needs to be non-null to indicate that we @@ -158,14 +218,24 @@ void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin } #endif // ENABLE(DFG_JIT) +bool StackVisitor::Frame::isWasmFrame() const +{ + return m_isWasmFrame; +} + StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const { - if (!isJSFrame()) + if (isWasmFrame()) + return CodeType::Wasm; + + if (!codeBlock()) return CodeType::Native; switch (codeBlock()->codeType()) { case EvalCode: return CodeType::Eval; + case ModuleCode: + return CodeType::Module; case FunctionCode: return CodeType::Function; case GlobalCode: @@ -175,50 +245,88 @@ StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const return CodeType::Global; } -String StackVisitor::Frame::functionName() +RegisterAtOffsetList* StackVisitor::Frame::calleeSaveRegisters() +{ + if (isInlinedFrame()) + return nullptr; + +#if ENABLE(JIT) && NUMBER_OF_CALLEE_SAVES_REGISTERS > 0 + +#if ENABLE(WEBASSEMBLY) + if (isWasmFrame()) { + if (JSCell* callee = this->callee()) { + if (JSWebAssemblyCallee* wasmCallee = jsDynamicCast<JSWebAssemblyCallee*>(*callee->vm(), callee)) + return wasmCallee->calleeSaveRegisters(); + // Other wasm callees (e.g, stubs) don't use callee save registers, so nothing needs + // to be restored for them. + } + + return nullptr; + } +#endif // ENABLE(WEBASSEMBLY) + + if (CodeBlock* codeBlock = this->codeBlock()) + return codeBlock->calleeSaveRegisters(); + +#endif // ENABLE(JIT) && NUMBER_OF_CALLEE_SAVES_REGISTERS > 0 + + return nullptr; +} + +String StackVisitor::Frame::functionName() const { String traceLine; - JSObject* callee = this->callee(); + JSCell* callee = this->callee(); switch (codeType()) { + case CodeType::Wasm: + traceLine = ASCIILiteral("wasm code"); + break; case CodeType::Eval: - traceLine = "eval code"; + traceLine = ASCIILiteral("eval code"); + break; + case CodeType::Module: + traceLine = ASCIILiteral("module code"); break; case CodeType::Native: if (callee) - traceLine = getCalculatedDisplayName(callFrame(), callee).impl(); + traceLine = getCalculatedDisplayName(callFrame()->vm(), jsCast<JSObject*>(callee)).impl(); break; case CodeType::Function: - traceLine = getCalculatedDisplayName(callFrame(), callee).impl(); + traceLine = getCalculatedDisplayName(callFrame()->vm(), jsCast<JSObject*>(callee)).impl(); break; case CodeType::Global: - traceLine = "global code"; + traceLine = ASCIILiteral("global code"); break; } return traceLine.isNull() ? emptyString() : traceLine; } -String StackVisitor::Frame::sourceURL() +String StackVisitor::Frame::sourceURL() const { String traceLine; switch (codeType()) { case CodeType::Eval: + case CodeType::Module: case CodeType::Function: case CodeType::Global: { - String sourceURL = codeBlock()->ownerExecutable()->sourceURL(); + String sourceURL = codeBlock()->ownerScriptExecutable()->sourceURL(); if (!sourceURL.isEmpty()) traceLine = sourceURL.impl(); break; } case CodeType::Native: - traceLine = "[native code]"; + traceLine = ASCIILiteral("[native code]"); + break; + case CodeType::Wasm: + traceLine = ASCIILiteral("[wasm code]"); break; } return traceLine.isNull() ? emptyString() : traceLine; } -String StackVisitor::Frame::toString() +String StackVisitor::Frame::toString() const { StringBuilder traceBuild; String functionName = this->functionName(); @@ -228,7 +336,7 @@ String StackVisitor::Frame::toString() if (!functionName.isEmpty()) traceBuild.append('@'); traceBuild.append(sourceURL); - if (isJSFrame()) { + if (hasLineAndColumnInfo()) { unsigned line = 0; unsigned column = 0; computeLineAndColumn(line, column); @@ -241,49 +349,39 @@ String StackVisitor::Frame::toString() return traceBuild.toString().impl(); } -Arguments* StackVisitor::Frame::createArguments() +intptr_t StackVisitor::Frame::sourceID() +{ + if (CodeBlock* codeBlock = this->codeBlock()) + return codeBlock->ownerScriptExecutable()->sourceID(); + return noSourceID; +} + +ClonedArguments* StackVisitor::Frame::createArguments() { ASSERT(m_callFrame); CallFrame* physicalFrame = m_callFrame; - VM& vm = physicalFrame->vm(); - Arguments* arguments; + ClonedArguments* arguments; + ArgumentsMode mode; + if (Options::useFunctionDotArguments()) + mode = ArgumentsMode::Cloned; + else + mode = ArgumentsMode::FakeValues; #if ENABLE(DFG_JIT) if (isInlinedFrame()) { ASSERT(m_inlineCallFrame); - arguments = Arguments::create(vm, physicalFrame, m_inlineCallFrame); - arguments->tearOff(physicalFrame, m_inlineCallFrame); + arguments = ClonedArguments::createWithInlineFrame(physicalFrame, physicalFrame, m_inlineCallFrame, mode); } else #endif - { - arguments = Arguments::create(vm, physicalFrame); - arguments->tearOff(physicalFrame); - } + arguments = ClonedArguments::createWithMachineFrame(physicalFrame, physicalFrame, mode); return arguments; } -Arguments* StackVisitor::Frame::existingArguments() +bool StackVisitor::Frame::hasLineAndColumnInfo() const { - if (codeBlock()->codeType() != FunctionCode) - return 0; - if (!codeBlock()->usesArguments()) - return 0; - - VirtualRegister reg; - -#if ENABLE(DFG_JIT) - if (isInlinedFrame()) - reg = inlineCallFrame()->argumentsRegister; - else -#endif // ENABLE(DFG_JIT) - reg = codeBlock()->argumentsRegister(); - - JSValue result = callFrame()->r(unmodifiedArgumentsRegister(reg).offset()).jsValue(); - if (!result) - return 0; - return jsCast<Arguments*>(result); + return !!codeBlock(); } -void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) +void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) const { CodeBlock* codeBlock = this->codeBlock(); if (!codeBlock) { @@ -299,11 +397,14 @@ void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) unsigned divotColumn = 0; retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn); - line = divotLine + codeBlock->ownerExecutable()->lineNo(); + line = divotLine + codeBlock->ownerScriptExecutable()->firstLine(); column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset()); + + if (codeBlock->ownerScriptExecutable()->hasOverrideLineNumber()) + line = codeBlock->ownerScriptExecutable()->overrideLineNumber(); } -void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) +void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) const { CodeBlock* codeBlock = this->codeBlock(); codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeOffset(bytecodeOffset(), divot, startOffset, endOffset, line, column); @@ -316,159 +417,89 @@ void StackVisitor::Frame::setToEnd() #if ENABLE(DFG_JIT) m_inlineCallFrame = 0; #endif + m_isWasmFrame = false; } -#ifndef NDEBUG - -static const char* jitTypeName(JITCode::JITType jitType) +void StackVisitor::Frame::dump(PrintStream& out, Indenter indent) const { - switch (jitType) { - case JITCode::None: return "None"; - case JITCode::HostCallThunk: return "HostCallThunk"; - case JITCode::InterpreterThunk: return "InterpreterThunk"; - case JITCode::BaselineJIT: return "BaselineJIT"; - case JITCode::DFGJIT: return "DFGJIT"; - case JITCode::FTLJIT: return "FTLJIT"; - } - return "<unknown>"; + dump(out, indent, [] (PrintStream&) { }); } -static void printIndents(int levels) +void StackVisitor::Frame::dump(PrintStream& out, Indenter indent, std::function<void(PrintStream&)> prefix) const { - while (levels--) - dataLogFString(" "); -} - -static void printif(int indentLevels, const char* format, ...) -{ - va_list argList; - va_start(argList, format); - - if (indentLevels) - printIndents(indentLevels); - -#if COMPILER(CLANG) || (COMPILER(GCC) && GCC_VERSION_AT_LEAST(4, 6, 0)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" -#pragma GCC diagnostic ignored "-Wmissing-format-attribute" -#endif - - WTF::dataLogFV(format, argList); - -#if COMPILER(CLANG) || (COMPILER(GCC) && GCC_VERSION_AT_LEAST(4, 6, 0)) -#pragma GCC diagnostic pop -#endif - - va_end(argList); -} - -void StackVisitor::Frame::print(int indentLevel) -{ - int i = indentLevel; - if (!this->callFrame()) { - printif(i, "frame 0x0\n"); + out.print(indent, "frame 0x0\n"); return; } CodeBlock* codeBlock = this->codeBlock(); - printif(i, "frame %p {\n", this->callFrame()); + out.print(indent); + prefix(out); + out.print("frame ", RawPointer(this->callFrame()), " {\n"); - CallFrame* callFrame = m_callFrame; - CallFrame* callerFrame = this->callerFrame(); - void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr; + { + indent++; + + CallFrame* callFrame = m_callFrame; + CallFrame* callerFrame = this->callerFrame(); + void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr; - printif(i, " name '%s'\n", functionName().utf8().data()); - printif(i, " sourceURL '%s'\n", sourceURL().utf8().data()); - printif(i, " isVMEntrySentinel %d\n", callerFrame->isVMEntrySentinel()); + out.print(indent, "name: ", functionName(), "\n"); + out.print(indent, "sourceURL: ", sourceURL(), "\n"); + bool isInlined = false; #if ENABLE(DFG_JIT) - printif(i, " isInlinedFrame %d\n", isInlinedFrame()); - if (isInlinedFrame()) - printif(i, " InlineCallFrame %p\n", m_inlineCallFrame); + isInlined = isInlinedFrame(); + out.print(indent, "isInlinedFrame: ", isInlinedFrame(), "\n"); + if (isInlinedFrame()) + out.print(indent, "InlineCallFrame: ", RawPointer(m_inlineCallFrame), "\n"); #endif - printif(i, " callee %p\n", callee()); - printif(i, " returnPC %p\n", returnPC); - printif(i, " callerFrame %p\n", callerFrame); - unsigned locationRawBits = callFrame->locationAsRawBits(); - printif(i, " rawLocationBits %u 0x%x\n", locationRawBits, locationRawBits); - printif(i, " codeBlock %p\n", codeBlock); - if (codeBlock) { - JITCode::JITType jitType = codeBlock->jitType(); - if (callFrame->hasLocationAsBytecodeOffset()) { - unsigned bytecodeOffset = callFrame->locationAsBytecodeOffset(); - printif(i, " bytecodeOffset %u %p / %zu\n", bytecodeOffset, reinterpret_cast<void*>(bytecodeOffset), codeBlock->instructions().size()); + out.print(indent, "callee: ", RawPointer(callee()), "\n"); + out.print(indent, "returnPC: ", RawPointer(returnPC), "\n"); + out.print(indent, "callerFrame: ", RawPointer(callerFrame), "\n"); + unsigned locationRawBits = callFrame->callSiteAsRawBits(); + out.print(indent, "rawLocationBits: ", static_cast<uintptr_t>(locationRawBits), + " ", RawPointer(reinterpret_cast<void*>(locationRawBits)), "\n"); + out.print(indent, "codeBlock: ", RawPointer(codeBlock)); + if (codeBlock) + out.print(" ", *codeBlock); + out.print("\n"); + if (codeBlock && !isInlined) { + indent++; + + if (callFrame->callSiteBitsAreBytecodeOffset()) { + unsigned bytecodeOffset = callFrame->bytecodeOffset(); + out.print(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n"); #if ENABLE(DFG_JIT) - } else { - unsigned codeOriginIndex = callFrame->locationAsCodeOriginIndex(); - printif(i, " codeOriginIdex %u %p / %zu\n", codeOriginIndex, reinterpret_cast<void*>(codeOriginIndex), codeBlock->codeOrigins().size()); + } else { + out.print(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n"); + if (codeBlock->hasCodeOrigins()) { + CallSiteIndex callSiteIndex = callFrame->callSiteIndex(); + out.print(indent, "callSiteIndex: ", callSiteIndex.bits(), " of ", codeBlock->codeOrigins().size(), "\n"); + + JITCode::JITType jitType = codeBlock->jitType(); + if (jitType != JITCode::FTLJIT) { + JITCode* jitCode = codeBlock->jitCode().get(); + out.print(indent, "jitCode: ", RawPointer(jitCode), + " start ", RawPointer(jitCode->start()), + " end ", RawPointer(jitCode->end()), "\n"); + } + } #endif + } + unsigned line = 0; + unsigned column = 0; + computeLineAndColumn(line, column); + out.print(indent, "line: ", line, "\n"); + out.print(indent, "column: ", column, "\n"); + + indent--; } - unsigned line = 0; - unsigned column = 0; - computeLineAndColumn(line, column); - printif(i, " line %d\n", line); - printif(i, " column %d\n", column); - printif(i, " jitType %d <%s> isOptimizingJIT %d\n", jitType, jitTypeName(jitType), JITCode::isOptimizingJIT(jitType)); -#if ENABLE(DFG_JIT) - printif(i, " hasCodeOrigins %d\n", codeBlock->hasCodeOrigins()); - if (codeBlock->hasCodeOrigins()) { - JITCode* jitCode = codeBlock->jitCode().get(); - printif(i, " jitCode %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end()); - } -#endif + out.print(indent, "vmEntryFrame: ", RawPointer(vmEntryFrame()), "\n"); + indent--; } - printif(i, "}\n"); + out.print(indent, "}\n"); } -#endif // NDEBUG - } // namespace JSC - -#ifndef NDEBUG -using JSC::StackVisitor; - -// For debugging use -JS_EXPORT_PRIVATE void debugPrintCallFrame(JSC::CallFrame*); -JS_EXPORT_PRIVATE void debugPrintStack(JSC::CallFrame* topCallFrame); - -class DebugPrintFrameFunctor { -public: - enum Action { - PrintOne, - PrintAll - }; - - DebugPrintFrameFunctor(Action action) - : m_action(action) - { - } - - StackVisitor::Status operator()(StackVisitor& visitor) - { - visitor->print(2); - return m_action == PrintAll ? StackVisitor::Continue : StackVisitor::Done; - } - -private: - Action m_action; -}; - -void debugPrintCallFrame(JSC::CallFrame* callFrame) -{ - if (!callFrame) - return; - DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintOne); - callFrame->iterate(functor); -} - -void debugPrintStack(JSC::CallFrame* topCallFrame) -{ - if (!topCallFrame) - return; - DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintAll); - topCallFrame->iterate(functor); -} - -#endif // !NDEBUG |