blob: 75f17723b14a47586d9ecd6636edfa469817b032 [file] [log] [blame]
/*
* 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 "InlineCallFrame.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;
}
CallSiteIndex index = callFrame->callSiteIndex();
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->bytecodeOffset();
#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 ModuleCode:
return CodeType::Module;
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::Module:
traceLine = ASCIILiteral("module 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::Module:
case CodeType::Function:
case CodeType::Global: {
String sourceURL = codeBlock()->ownerScriptExecutable()->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->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)
{
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->callSiteAsRawBits();
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->callSiteBitsAreBytecodeOffset()) {
unsigned bytecodeOffset = callFrame->bytecodeOffset();
log(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n");
#if ENABLE(DFG_JIT)
} else {
log(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n");
if (codeBlock->hasCodeOrigins()) {
CallSiteIndex callSiteIndex = callFrame->callSiteIndex();
log(indent, "callSiteIndex: ", callSiteIndex.bits(), " 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