2 * Copyright (C) 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "StackVisitor.h"
29 #include "Arguments.h"
30 #include "CallFrameInlines.h"
31 #include "Executable.h"
32 #include "Interpreter.h"
33 #include "JSCInlines.h"
34 #include <wtf/DataLog.h>
38 StackVisitor::StackVisitor(CallFrame
* startFrame
)
41 readFrame(startFrame
);
44 void StackVisitor::gotoNextFrame()
47 if (m_frame
.isInlinedFrame()) {
48 InlineCallFrame
* inlineCallFrame
= m_frame
.inlineCallFrame();
49 CodeOrigin
* callerCodeOrigin
= &inlineCallFrame
->caller
;
50 readInlinedFrame(m_frame
.callFrame(), callerCodeOrigin
);
53 #endif // ENABLE(DFG_JIT)
54 readFrame(m_frame
.callerFrame());
57 void StackVisitor::readFrame(CallFrame
* callFrame
)
59 ASSERT(!callFrame
->isVMEntrySentinel());
66 readNonInlinedFrame(callFrame
);
68 #else // !ENABLE(DFG_JIT)
69 // If the frame doesn't have a code block, then it's not a DFG frame.
70 // Hence, we're not at an inlined frame.
71 CodeBlock
* codeBlock
= callFrame
->codeBlock();
73 readNonInlinedFrame(callFrame
);
77 // If the code block does not have any code origins, then there's no
78 // inlining. Hence, we're not at an inlined frame.
79 if (!codeBlock
->hasCodeOrigins()) {
80 readNonInlinedFrame(callFrame
);
84 unsigned index
= callFrame
->locationAsCodeOriginIndex();
85 ASSERT(codeBlock
->canGetCodeOrigin(index
));
86 if (!codeBlock
->canGetCodeOrigin(index
)) {
87 // See assertion above. In release builds, we try to protect ourselves
88 // from crashing even though stack walking will be goofed up.
93 CodeOrigin codeOrigin
= codeBlock
->codeOrigin(index
);
94 if (!codeOrigin
.inlineCallFrame
) {
95 readNonInlinedFrame(callFrame
, &codeOrigin
);
99 readInlinedFrame(callFrame
, &codeOrigin
);
100 #endif // !ENABLE(DFG_JIT)
103 void StackVisitor::readNonInlinedFrame(CallFrame
* callFrame
, CodeOrigin
* codeOrigin
)
105 m_frame
.m_callFrame
= callFrame
;
106 m_frame
.m_argumentCountIncludingThis
= callFrame
->argumentCountIncludingThis();
107 m_frame
.m_callerFrame
= callFrame
->callerFrameSkippingVMEntrySentinel();
108 m_frame
.m_callee
= callFrame
->callee();
109 m_frame
.m_scope
= callFrame
->scope();
110 m_frame
.m_codeBlock
= callFrame
->codeBlock();
111 m_frame
.m_bytecodeOffset
= !m_frame
.codeBlock() ? 0
112 : codeOrigin
? codeOrigin
->bytecodeIndex
113 : callFrame
->locationAsBytecodeOffset();
115 m_frame
.m_inlineCallFrame
= 0;
120 static int inlinedFrameOffset(CodeOrigin
* codeOrigin
)
122 InlineCallFrame
* inlineCallFrame
= codeOrigin
->inlineCallFrame
;
123 int frameOffset
= inlineCallFrame
? inlineCallFrame
->stackOffset
: 0;
127 void StackVisitor::readInlinedFrame(CallFrame
* callFrame
, CodeOrigin
* codeOrigin
)
130 ASSERT(!callFrame
->isVMEntrySentinel());
132 int frameOffset
= inlinedFrameOffset(codeOrigin
);
133 bool isInlined
= !!frameOffset
;
135 InlineCallFrame
* inlineCallFrame
= codeOrigin
->inlineCallFrame
;
137 m_frame
.m_callFrame
= callFrame
;
138 m_frame
.m_inlineCallFrame
= inlineCallFrame
;
139 m_frame
.m_argumentCountIncludingThis
= inlineCallFrame
->arguments
.size();
140 m_frame
.m_codeBlock
= inlineCallFrame
->baselineCodeBlock();
141 m_frame
.m_bytecodeOffset
= codeOrigin
->bytecodeIndex
;
143 JSFunction
* callee
= inlineCallFrame
->calleeForCallFrame(callFrame
);
144 m_frame
.m_scope
= callee
->scope();
145 m_frame
.m_callee
= callee
;
146 ASSERT(m_frame
.scope());
147 ASSERT(m_frame
.callee());
149 // The callerFrame just needs to be non-null to indicate that we
150 // haven't reached the last frame yet. Setting it to the root
151 // frame (i.e. the callFrame that this inlined frame is called from)
152 // would work just fine.
153 m_frame
.m_callerFrame
= callFrame
;
157 readNonInlinedFrame(callFrame
, codeOrigin
);
159 #endif // ENABLE(DFG_JIT)
161 StackVisitor::Frame::CodeType
StackVisitor::Frame::codeType() const
164 return CodeType::Native
;
166 switch (codeBlock()->codeType()) {
168 return CodeType::Eval
;
170 return CodeType::Function
;
172 return CodeType::Global
;
174 RELEASE_ASSERT_NOT_REACHED();
175 return CodeType::Global
;
178 String
StackVisitor::Frame::functionName()
181 JSObject
* callee
= this->callee();
183 switch (codeType()) {
185 traceLine
= "eval code";
187 case CodeType::Native
:
189 traceLine
= getCalculatedDisplayName(callFrame(), callee
).impl();
191 case CodeType::Function
:
192 traceLine
= getCalculatedDisplayName(callFrame(), callee
).impl();
194 case CodeType::Global
:
195 traceLine
= "global code";
198 return traceLine
.isNull() ? emptyString() : traceLine
;
201 String
StackVisitor::Frame::sourceURL()
205 switch (codeType()) {
207 case CodeType::Function
:
208 case CodeType::Global
: {
209 String sourceURL
= codeBlock()->ownerExecutable()->sourceURL();
210 if (!sourceURL
.isEmpty())
211 traceLine
= sourceURL
.impl();
214 case CodeType::Native
:
215 traceLine
= "[native code]";
218 return traceLine
.isNull() ? emptyString() : traceLine
;
221 String
StackVisitor::Frame::toString()
223 StringBuilder traceBuild
;
224 String functionName
= this->functionName();
225 String sourceURL
= this->sourceURL();
226 traceBuild
.append(functionName
);
227 if (!sourceURL
.isEmpty()) {
228 if (!functionName
.isEmpty())
229 traceBuild
.append('@');
230 traceBuild
.append(sourceURL
);
234 computeLineAndColumn(line
, column
);
235 traceBuild
.append(':');
236 traceBuild
.appendNumber(line
);
237 traceBuild
.append(':');
238 traceBuild
.appendNumber(column
);
241 return traceBuild
.toString().impl();
244 Arguments
* StackVisitor::Frame::createArguments()
247 CallFrame
* physicalFrame
= m_callFrame
;
248 VM
& vm
= physicalFrame
->vm();
249 Arguments
* arguments
;
251 if (isInlinedFrame()) {
252 ASSERT(m_inlineCallFrame
);
253 arguments
= Arguments::create(vm
, physicalFrame
, m_inlineCallFrame
);
254 arguments
->tearOff(physicalFrame
, m_inlineCallFrame
);
258 arguments
= Arguments::create(vm
, physicalFrame
);
259 arguments
->tearOff(physicalFrame
);
264 Arguments
* StackVisitor::Frame::existingArguments()
266 if (codeBlock()->codeType() != FunctionCode
)
268 if (!codeBlock()->usesArguments())
274 if (isInlinedFrame())
275 reg
= inlineCallFrame()->argumentsRegister
;
277 #endif // ENABLE(DFG_JIT)
278 reg
= codeBlock()->argumentsRegister();
280 JSValue result
= callFrame()->r(unmodifiedArgumentsRegister(reg
).offset()).jsValue();
281 if (!result
|| !result
.isCell()) // Protect against Undefined in case we throw in op_enter.
283 return jsCast
<Arguments
*>(result
);
286 void StackVisitor::Frame::computeLineAndColumn(unsigned& line
, unsigned& column
)
288 CodeBlock
* codeBlock
= this->codeBlock();
296 int unusedStartOffset
= 0;
297 int unusedEndOffset
= 0;
298 unsigned divotLine
= 0;
299 unsigned divotColumn
= 0;
300 retrieveExpressionInfo(divot
, unusedStartOffset
, unusedEndOffset
, divotLine
, divotColumn
);
302 line
= divotLine
+ codeBlock
->ownerExecutable()->lineNo();
303 column
= divotColumn
+ (divotLine
? 1 : codeBlock
->firstLineColumnOffset());
306 void StackVisitor::Frame::retrieveExpressionInfo(int& divot
, int& startOffset
, int& endOffset
, unsigned& line
, unsigned& column
)
308 CodeBlock
* codeBlock
= this->codeBlock();
309 codeBlock
->unlinkedCodeBlock()->expressionRangeForBytecodeOffset(bytecodeOffset(), divot
, startOffset
, endOffset
, line
, column
);
310 divot
+= codeBlock
->sourceOffset();
313 void StackVisitor::Frame::setToEnd()
317 m_inlineCallFrame
= 0;
323 static const char* jitTypeName(JITCode::JITType jitType
)
326 case JITCode::None
: return "None";
327 case JITCode::HostCallThunk
: return "HostCallThunk";
328 case JITCode::InterpreterThunk
: return "InterpreterThunk";
329 case JITCode::BaselineJIT
: return "BaselineJIT";
330 case JITCode::DFGJIT
: return "DFGJIT";
331 case JITCode::FTLJIT
: return "FTLJIT";
336 static void printIndents(int levels
)
342 static void printif(int indentLevels
, const char* format
, ...)
345 va_start(argList
, format
);
348 printIndents(indentLevels
);
350 #if COMPILER(CLANG) || COMPILER(GCC)
351 #pragma GCC diagnostic push
352 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
353 #pragma GCC diagnostic ignored "-Wmissing-format-attribute"
356 WTF::dataLogFV(format
, argList
);
358 #if COMPILER(CLANG) || COMPILER(GCC)
359 #pragma GCC diagnostic pop
365 void StackVisitor::Frame::print(int indentLevel
)
369 if (!this->callFrame()) {
370 printif(i
, "frame 0x0\n");
374 CodeBlock
* codeBlock
= this->codeBlock();
375 printif(i
, "frame %p {\n", this->callFrame());
377 CallFrame
* callFrame
= m_callFrame
;
378 CallFrame
* callerFrame
= this->callerFrame();
379 void* returnPC
= callFrame
->hasReturnPC() ? callFrame
->returnPC().value() : nullptr;
381 printif(i
, " name '%s'\n", functionName().utf8().data());
382 printif(i
, " sourceURL '%s'\n", sourceURL().utf8().data());
383 printif(i
, " isVMEntrySentinel %d\n", callerFrame
->isVMEntrySentinel());
386 printif(i
, " isInlinedFrame %d\n", isInlinedFrame());
387 if (isInlinedFrame())
388 printif(i
, " InlineCallFrame %p\n", m_inlineCallFrame
);
391 printif(i
, " callee %p\n", callee());
392 printif(i
, " returnPC %p\n", returnPC
);
393 printif(i
, " callerFrame %p\n", callerFrame
);
394 unsigned locationRawBits
= callFrame
->locationAsRawBits();
395 printif(i
, " rawLocationBits %u 0x%x\n", locationRawBits
, locationRawBits
);
396 printif(i
, " codeBlock %p\n", codeBlock
);
398 JITCode::JITType jitType
= codeBlock
->jitType();
399 if (callFrame
->hasLocationAsBytecodeOffset()) {
400 unsigned bytecodeOffset
= callFrame
->locationAsBytecodeOffset();
401 printif(i
, " bytecodeOffset %u %p / %zu\n", bytecodeOffset
, reinterpret_cast<void*>(bytecodeOffset
), codeBlock
->instructions().size());
404 unsigned codeOriginIndex
= callFrame
->locationAsCodeOriginIndex();
405 printif(i
, " codeOriginIdex %u %p / %zu\n", codeOriginIndex
, reinterpret_cast<void*>(codeOriginIndex
), codeBlock
->codeOrigins().size());
410 computeLineAndColumn(line
, column
);
411 printif(i
, " line %d\n", line
);
412 printif(i
, " column %d\n", column
);
413 printif(i
, " jitType %d <%s> isOptimizingJIT %d\n", jitType
, jitTypeName(jitType
), JITCode::isOptimizingJIT(jitType
));
415 printif(i
, " hasCodeOrigins %d\n", codeBlock
->hasCodeOrigins());
416 if (codeBlock
->hasCodeOrigins()) {
417 JITCode
* jitCode
= codeBlock
->jitCode().get();
418 printif(i
, " jitCode %p start %p end %p\n", jitCode
, jitCode
->start(), jitCode
->end());
430 using JSC::StackVisitor
;
433 JS_EXPORT_PRIVATE
void debugPrintCallFrame(JSC::CallFrame
*);
434 JS_EXPORT_PRIVATE
void debugPrintStack(JSC::CallFrame
* topCallFrame
);
436 class DebugPrintFrameFunctor
{
443 DebugPrintFrameFunctor(Action action
)
448 StackVisitor::Status
operator()(StackVisitor
& visitor
)
451 return m_action
== PrintAll
? StackVisitor::Continue
: StackVisitor::Done
;
458 void debugPrintCallFrame(JSC::CallFrame
* callFrame
)
462 DebugPrintFrameFunctor
functor(DebugPrintFrameFunctor::PrintOne
);
463 callFrame
->iterate(functor
);
466 void debugPrintStack(JSC::CallFrame
* topCallFrame
)
470 DebugPrintFrameFunctor
functor(DebugPrintFrameFunctor::PrintAll
);
471 topCallFrame
->iterate(functor
);