diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-10-15 09:45:50 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-10-15 09:45:50 +0000 |
commit | e15dd966d523731101f70ccf768bba12435a0208 (patch) | |
tree | ae9cb828a24ded2585a41af3f21411523b47897d /Source/JavaScriptCore/interpreter/StackVisitor.cpp | |
download | WebKitGtk-tarball-e15dd966d523731101f70ccf768bba12435a0208.tar.gz |
webkitgtk-2.10.2webkitgtk-2.10.2
Diffstat (limited to 'Source/JavaScriptCore/interpreter/StackVisitor.cpp')
-rw-r--r-- | Source/JavaScriptCore/interpreter/StackVisitor.cpp | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/interpreter/StackVisitor.cpp b/Source/JavaScriptCore/interpreter/StackVisitor.cpp new file mode 100644 index 000000000..6fe792b7e --- /dev/null +++ b/Source/JavaScriptCore/interpreter/StackVisitor.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StackVisitor.h" + +#include "CallFrameInlines.h" +#include "ClonedArguments.h" +#include "Executable.h" +#include "Interpreter.h" +#include "JSCInlines.h" +#include <wtf/DataLog.h> + +namespace JSC { + +StackVisitor::StackVisitor(CallFrame* startFrame) +{ + m_frame.m_index = 0; + CallFrame* topFrame; + if (startFrame) { + m_frame.m_VMEntryFrame = startFrame->vm().topVMEntryFrame; + topFrame = startFrame->vm().topCallFrame; + } 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() +{ +#if ENABLE(DFG_JIT) + if (m_frame.isInlinedFrame()) { + InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame(); + CodeOrigin* callerCodeOrigin = &inlineCallFrame->caller; + readInlinedFrame(m_frame.callFrame(), callerCodeOrigin); + return; + } +#endif // ENABLE(DFG_JIT) + m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame; + readFrame(m_frame.callerFrame()); +} + +void StackVisitor::readFrame(CallFrame* callFrame) +{ + if (!callFrame) { + m_frame.setToEnd(); + return; + } + +#if !ENABLE(DFG_JIT) + readNonInlinedFrame(callFrame); + +#else // !ENABLE(DFG_JIT) + // If the frame doesn't have a code block, then it's not a DFG frame. + // Hence, we're not at an inlined frame. + CodeBlock* codeBlock = callFrame->codeBlock(); + if (!codeBlock) { + readNonInlinedFrame(callFrame); + return; + } + + // If the code block does not have any code origins, then there's no + // inlining. Hence, we're not at an inlined frame. + if (!codeBlock->hasCodeOrigins()) { + readNonInlinedFrame(callFrame); + return; + } + + unsigned index = callFrame->locationAsCodeOriginIndex(); + ASSERT(codeBlock->canGetCodeOrigin(index)); + if (!codeBlock->canGetCodeOrigin(index)) { + // See assertion above. In release builds, we try to protect ourselves + // from crashing even though stack walking will be goofed up. + m_frame.setToEnd(); + return; + } + + CodeOrigin codeOrigin = codeBlock->codeOrigin(index); + if (!codeOrigin.inlineCallFrame) { + readNonInlinedFrame(callFrame, &codeOrigin); + return; + } + + readInlinedFrame(callFrame, &codeOrigin); +#endif // !ENABLE(DFG_JIT) +} + +void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin) +{ + m_frame.m_callFrame = callFrame; + m_frame.m_argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); + 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_callee = callFrame->callee(); + m_frame.m_codeBlock = callFrame->codeBlock(); + m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0 + : codeOrigin ? codeOrigin->bytecodeIndex + : callFrame->locationAsBytecodeOffset(); +#if ENABLE(DFG_JIT) + m_frame.m_inlineCallFrame = 0; +#endif +} + +#if ENABLE(DFG_JIT) +static int inlinedFrameOffset(CodeOrigin* codeOrigin) +{ + InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame; + int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0; + return frameOffset; +} + +void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin) +{ + ASSERT(codeOrigin); + + int frameOffset = inlinedFrameOffset(codeOrigin); + bool isInlined = !!frameOffset; + if (isInlined) { + InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame; + + m_frame.m_callFrame = callFrame; + m_frame.m_inlineCallFrame = inlineCallFrame; + 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(); + m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex; + + JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame); + m_frame.m_callee = callee; + ASSERT(m_frame.callee()); + + // The callerFrame just needs to be non-null to indicate that we + // haven't reached the last frame yet. Setting it to the root + // frame (i.e. the callFrame that this inlined frame is called from) + // would work just fine. + m_frame.m_callerFrame = callFrame; + return; + } + + readNonInlinedFrame(callFrame, codeOrigin); +} +#endif // ENABLE(DFG_JIT) + +StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const +{ + if (!isJSFrame()) + return CodeType::Native; + + switch (codeBlock()->codeType()) { + case EvalCode: + return CodeType::Eval; + case FunctionCode: + return CodeType::Function; + case GlobalCode: + return CodeType::Global; + } + RELEASE_ASSERT_NOT_REACHED(); + return CodeType::Global; +} + +String StackVisitor::Frame::functionName() +{ + String traceLine; + JSObject* callee = this->callee(); + + switch (codeType()) { + case CodeType::Eval: + traceLine = ASCIILiteral("eval code"); + break; + case CodeType::Native: + if (callee) + traceLine = getCalculatedDisplayName(callFrame(), callee).impl(); + break; + case CodeType::Function: + traceLine = getCalculatedDisplayName(callFrame(), callee).impl(); + break; + case CodeType::Global: + traceLine = ASCIILiteral("global code"); + break; + } + return traceLine.isNull() ? emptyString() : traceLine; +} + +String StackVisitor::Frame::sourceURL() +{ + String traceLine; + + switch (codeType()) { + case CodeType::Eval: + case CodeType::Function: + case CodeType::Global: { + String sourceURL = codeBlock()->ownerExecutable()->sourceURL(); + if (!sourceURL.isEmpty()) + traceLine = sourceURL.impl(); + break; + } + case CodeType::Native: + traceLine = ASCIILiteral("[native code]"); + break; + } + return traceLine.isNull() ? emptyString() : traceLine; +} + +String StackVisitor::Frame::toString() +{ + StringBuilder traceBuild; + String functionName = this->functionName(); + String sourceURL = this->sourceURL(); + traceBuild.append(functionName); + if (!sourceURL.isEmpty()) { + if (!functionName.isEmpty()) + traceBuild.append('@'); + traceBuild.append(sourceURL); + if (isJSFrame()) { + unsigned line = 0; + unsigned column = 0; + computeLineAndColumn(line, column); + traceBuild.append(':'); + traceBuild.appendNumber(line); + traceBuild.append(':'); + traceBuild.appendNumber(column); + } + } + return traceBuild.toString().impl(); +} + +ClonedArguments* StackVisitor::Frame::createArguments() +{ + ASSERT(m_callFrame); + CallFrame* physicalFrame = m_callFrame; + ClonedArguments* arguments; + ArgumentsMode mode; + if (Options::enableFunctionDotArguments()) + mode = ArgumentsMode::Cloned; + else + mode = ArgumentsMode::FakeValues; +#if ENABLE(DFG_JIT) + if (isInlinedFrame()) { + ASSERT(m_inlineCallFrame); + arguments = ClonedArguments::createWithInlineFrame(physicalFrame, physicalFrame, m_inlineCallFrame, mode); + } else +#endif + arguments = ClonedArguments::createWithMachineFrame(physicalFrame, physicalFrame, mode); + return arguments; +} + +void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) +{ + CodeBlock* codeBlock = this->codeBlock(); + if (!codeBlock) { + line = 0; + column = 0; + return; + } + + int divot = 0; + int unusedStartOffset = 0; + int unusedEndOffset = 0; + unsigned divotLine = 0; + unsigned divotColumn = 0; + retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn); + + line = divotLine + codeBlock->ownerExecutable()->firstLine(); + column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset()); + + if (codeBlock->ownerExecutable()->hasOverrideLineNumber()) + line = codeBlock->ownerExecutable()->overrideLineNumber(); +} + +void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) +{ + CodeBlock* codeBlock = this->codeBlock(); + codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeOffset(bytecodeOffset(), divot, startOffset, endOffset, line, column); + divot += codeBlock->sourceOffset(); +} + +void StackVisitor::Frame::setToEnd() +{ + m_callFrame = 0; +#if ENABLE(DFG_JIT) + m_inlineCallFrame = 0; +#endif +} + +static void printIndents(int levels) +{ + while (levels--) + dataLogFString(" "); +} + +template<typename... Types> +void log(unsigned indent, const Types&... values) +{ + printIndents(indent); + dataLog(values...); +} + +template<typename... Types> +void logF(unsigned indent, const char* format, const Types&... values) +{ + printIndents(indent); + +#if COMPILER(GCC_OR_CLANG) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#pragma GCC diagnostic ignored "-Wmissing-format-attribute" +#endif + + dataLogF(format, values...); + +#if COMPILER(GCC_OR_CLANG) +#pragma GCC diagnostic pop +#endif +} + +void StackVisitor::Frame::print(int indent) +{ + if (!this->callFrame()) { + log(indent, "frame 0x0\n"); + return; + } + + CodeBlock* codeBlock = this->codeBlock(); + logF(indent, "frame %p {\n", this->callFrame()); + + { + indent++; + + CallFrame* callFrame = m_callFrame; + CallFrame* callerFrame = this->callerFrame(); + void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr; + + log(indent, "name: ", functionName(), "\n"); + log(indent, "sourceURL: ", sourceURL(), "\n"); + + bool isInlined = false; +#if ENABLE(DFG_JIT) + isInlined = isInlinedFrame(); + log(indent, "isInlinedFrame: ", isInlinedFrame(), "\n"); + if (isInlinedFrame()) + logF(indent, "InlineCallFrame: %p\n", m_inlineCallFrame); +#endif + + logF(indent, "callee: %p\n", callee()); + logF(indent, "returnPC: %p\n", returnPC); + logF(indent, "callerFrame: %p\n", callerFrame); + unsigned locationRawBits = callFrame->locationAsRawBits(); + logF(indent, "rawLocationBits: %u 0x%x\n", locationRawBits, locationRawBits); + logF(indent, "codeBlock: %p ", codeBlock); + if (codeBlock) + dataLog(*codeBlock); + dataLog("\n"); + if (codeBlock && !isInlined) { + indent++; + + if (callFrame->hasLocationAsBytecodeOffset()) { + unsigned bytecodeOffset = callFrame->locationAsBytecodeOffset(); + log(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n"); +#if ENABLE(DFG_JIT) + } else { + log(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n"); + if (codeBlock->hasCodeOrigins()) { + unsigned codeOriginIndex = callFrame->locationAsCodeOriginIndex(); + log(indent, "codeOriginIndex: ", codeOriginIndex, " of ", codeBlock->codeOrigins().size(), "\n"); + + JITCode::JITType jitType = codeBlock->jitType(); + if (jitType != JITCode::FTLJIT) { + JITCode* jitCode = codeBlock->jitCode().get(); + logF(indent, "jitCode: %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end()); + } + } +#endif + } + unsigned line = 0; + unsigned column = 0; + computeLineAndColumn(line, column); + log(indent, "line: ", line, "\n"); + log(indent, "column: ", column, "\n"); + + indent--; + } + indent--; + } + log(indent, "}\n"); +} + +} // namespace JSC |