2 * Copyright (C) 2013, 2015 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 "CallFrameInlines.h"
30 #include "ClonedArguments.h"
31 #include "Executable.h"
32 #include "Interpreter.h"
33 #include "JSCInlines.h"
34 #include <wtf/DataLog.h>
38 StackVisitor::StackVisitor(CallFrame
* startFrame
)
43 m_frame
.m_VMEntryFrame
= startFrame
->vm().topVMEntryFrame
;
44 topFrame
= startFrame
->vm().topCallFrame
;
46 m_frame
.m_VMEntryFrame
= 0;
49 m_frame
.m_callerIsVMEntryFrame
= false;
52 // Find the frame the caller wants to start unwinding from.
53 while (m_frame
.callFrame() && m_frame
.callFrame() != startFrame
)
57 void StackVisitor::gotoNextFrame()
60 if (m_frame
.isInlinedFrame()) {
61 InlineCallFrame
* inlineCallFrame
= m_frame
.inlineCallFrame();
62 CodeOrigin
* callerCodeOrigin
= &inlineCallFrame
->caller
;
63 readInlinedFrame(m_frame
.callFrame(), callerCodeOrigin
);
66 #endif // ENABLE(DFG_JIT)
67 m_frame
.m_VMEntryFrame
= m_frame
.m_CallerVMEntryFrame
;
68 readFrame(m_frame
.callerFrame());
71 void StackVisitor::readFrame(CallFrame
* callFrame
)
79 readNonInlinedFrame(callFrame
);
81 #else // !ENABLE(DFG_JIT)
82 // If the frame doesn't have a code block, then it's not a DFG frame.
83 // Hence, we're not at an inlined frame.
84 CodeBlock
* codeBlock
= callFrame
->codeBlock();
86 readNonInlinedFrame(callFrame
);
90 // If the code block does not have any code origins, then there's no
91 // inlining. Hence, we're not at an inlined frame.
92 if (!codeBlock
->hasCodeOrigins()) {
93 readNonInlinedFrame(callFrame
);
97 unsigned index
= callFrame
->locationAsCodeOriginIndex();
98 ASSERT(codeBlock
->canGetCodeOrigin(index
));
99 if (!codeBlock
->canGetCodeOrigin(index
)) {
100 // See assertion above. In release builds, we try to protect ourselves
101 // from crashing even though stack walking will be goofed up.
106 CodeOrigin codeOrigin
= codeBlock
->codeOrigin(index
);
107 if (!codeOrigin
.inlineCallFrame
) {
108 readNonInlinedFrame(callFrame
, &codeOrigin
);
112 readInlinedFrame(callFrame
, &codeOrigin
);
113 #endif // !ENABLE(DFG_JIT)
116 void StackVisitor::readNonInlinedFrame(CallFrame
* callFrame
, CodeOrigin
* codeOrigin
)
118 m_frame
.m_callFrame
= callFrame
;
119 m_frame
.m_argumentCountIncludingThis
= callFrame
->argumentCountIncludingThis();
120 m_frame
.m_CallerVMEntryFrame
= m_frame
.m_VMEntryFrame
;
121 m_frame
.m_callerFrame
= callFrame
->callerFrame(m_frame
.m_CallerVMEntryFrame
);
122 m_frame
.m_callerIsVMEntryFrame
= m_frame
.m_CallerVMEntryFrame
!= m_frame
.m_VMEntryFrame
;
123 m_frame
.m_callee
= callFrame
->callee();
124 m_frame
.m_codeBlock
= callFrame
->codeBlock();
125 m_frame
.m_bytecodeOffset
= !m_frame
.codeBlock() ? 0
126 : codeOrigin
? codeOrigin
->bytecodeIndex
127 : callFrame
->locationAsBytecodeOffset();
129 m_frame
.m_inlineCallFrame
= 0;
134 static int inlinedFrameOffset(CodeOrigin
* codeOrigin
)
136 InlineCallFrame
* inlineCallFrame
= codeOrigin
->inlineCallFrame
;
137 int frameOffset
= inlineCallFrame
? inlineCallFrame
->stackOffset
: 0;
141 void StackVisitor::readInlinedFrame(CallFrame
* callFrame
, CodeOrigin
* codeOrigin
)
145 int frameOffset
= inlinedFrameOffset(codeOrigin
);
146 bool isInlined
= !!frameOffset
;
148 InlineCallFrame
* inlineCallFrame
= codeOrigin
->inlineCallFrame
;
150 m_frame
.m_callFrame
= callFrame
;
151 m_frame
.m_inlineCallFrame
= inlineCallFrame
;
152 if (inlineCallFrame
->argumentCountRegister
.isValid())
153 m_frame
.m_argumentCountIncludingThis
= callFrame
->r(inlineCallFrame
->argumentCountRegister
.offset()).unboxedInt32();
155 m_frame
.m_argumentCountIncludingThis
= inlineCallFrame
->arguments
.size();
156 m_frame
.m_codeBlock
= inlineCallFrame
->baselineCodeBlock();
157 m_frame
.m_bytecodeOffset
= codeOrigin
->bytecodeIndex
;
159 JSFunction
* callee
= inlineCallFrame
->calleeForCallFrame(callFrame
);
160 m_frame
.m_callee
= callee
;
161 ASSERT(m_frame
.callee());
163 // The callerFrame just needs to be non-null to indicate that we
164 // haven't reached the last frame yet. Setting it to the root
165 // frame (i.e. the callFrame that this inlined frame is called from)
166 // would work just fine.
167 m_frame
.m_callerFrame
= callFrame
;
171 readNonInlinedFrame(callFrame
, codeOrigin
);
173 #endif // ENABLE(DFG_JIT)
175 StackVisitor::Frame::CodeType
StackVisitor::Frame::codeType() const
178 return CodeType::Native
;
180 switch (codeBlock()->codeType()) {
182 return CodeType::Eval
;
184 return CodeType::Function
;
186 return CodeType::Global
;
188 RELEASE_ASSERT_NOT_REACHED();
189 return CodeType::Global
;
192 String
StackVisitor::Frame::functionName()
195 JSObject
* callee
= this->callee();
197 switch (codeType()) {
199 traceLine
= ASCIILiteral("eval code");
201 case CodeType::Native
:
203 traceLine
= getCalculatedDisplayName(callFrame(), callee
).impl();
205 case CodeType::Function
:
206 traceLine
= getCalculatedDisplayName(callFrame(), callee
).impl();
208 case CodeType::Global
:
209 traceLine
= ASCIILiteral("global code");
212 return traceLine
.isNull() ? emptyString() : traceLine
;
215 String
StackVisitor::Frame::sourceURL()
219 switch (codeType()) {
221 case CodeType::Function
:
222 case CodeType::Global
: {
223 String sourceURL
= codeBlock()->ownerExecutable()->sourceURL();
224 if (!sourceURL
.isEmpty())
225 traceLine
= sourceURL
.impl();
228 case CodeType::Native
:
229 traceLine
= ASCIILiteral("[native code]");
232 return traceLine
.isNull() ? emptyString() : traceLine
;
235 String
StackVisitor::Frame::toString()
237 StringBuilder traceBuild
;
238 String functionName
= this->functionName();
239 String sourceURL
= this->sourceURL();
240 traceBuild
.append(functionName
);
241 if (!sourceURL
.isEmpty()) {
242 if (!functionName
.isEmpty())
243 traceBuild
.append('@');
244 traceBuild
.append(sourceURL
);
248 computeLineAndColumn(line
, column
);
249 traceBuild
.append(':');
250 traceBuild
.appendNumber(line
);
251 traceBuild
.append(':');
252 traceBuild
.appendNumber(column
);
255 return traceBuild
.toString().impl();
258 ClonedArguments
* StackVisitor::Frame::createArguments()
261 CallFrame
* physicalFrame
= m_callFrame
;
262 ClonedArguments
* arguments
;
264 if (Options::enableFunctionDotArguments())
265 mode
= ArgumentsMode::Cloned
;
267 mode
= ArgumentsMode::FakeValues
;
269 if (isInlinedFrame()) {
270 ASSERT(m_inlineCallFrame
);
271 arguments
= ClonedArguments::createWithInlineFrame(physicalFrame
, physicalFrame
, m_inlineCallFrame
, mode
);
274 arguments
= ClonedArguments::createWithMachineFrame(physicalFrame
, physicalFrame
, mode
);
278 void StackVisitor::Frame::computeLineAndColumn(unsigned& line
, unsigned& column
)
280 CodeBlock
* codeBlock
= this->codeBlock();
288 int unusedStartOffset
= 0;
289 int unusedEndOffset
= 0;
290 unsigned divotLine
= 0;
291 unsigned divotColumn
= 0;
292 retrieveExpressionInfo(divot
, unusedStartOffset
, unusedEndOffset
, divotLine
, divotColumn
);
294 line
= divotLine
+ codeBlock
->ownerExecutable()->firstLine();
295 column
= divotColumn
+ (divotLine
? 1 : codeBlock
->firstLineColumnOffset());
297 if (codeBlock
->ownerExecutable()->hasOverrideLineNumber())
298 line
= codeBlock
->ownerExecutable()->overrideLineNumber();
301 void StackVisitor::Frame::retrieveExpressionInfo(int& divot
, int& startOffset
, int& endOffset
, unsigned& line
, unsigned& column
)
303 CodeBlock
* codeBlock
= this->codeBlock();
304 codeBlock
->unlinkedCodeBlock()->expressionRangeForBytecodeOffset(bytecodeOffset(), divot
, startOffset
, endOffset
, line
, column
);
305 divot
+= codeBlock
->sourceOffset();
308 void StackVisitor::Frame::setToEnd()
312 m_inlineCallFrame
= 0;
316 static void printIndents(int levels
)
322 template<typename
... Types
>
323 void log(unsigned indent
, const Types
&... values
)
325 printIndents(indent
);
329 template<typename
... Types
>
330 void logF(unsigned indent
, const char* format
, const Types
&... values
)
332 printIndents(indent
);
334 #if COMPILER(CLANG) || COMPILER(GCC)
335 #pragma GCC diagnostic push
336 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
337 #pragma GCC diagnostic ignored "-Wmissing-format-attribute"
340 dataLogF(format
, values
...);
342 #if COMPILER(CLANG) || COMPILER(GCC)
343 #pragma GCC diagnostic pop
347 void StackVisitor::Frame::print(int indent
)
349 if (!this->callFrame()) {
350 log(indent
, "frame 0x0\n");
354 CodeBlock
* codeBlock
= this->codeBlock();
355 logF(indent
, "frame %p {\n", this->callFrame());
360 CallFrame
* callFrame
= m_callFrame
;
361 CallFrame
* callerFrame
= this->callerFrame();
362 void* returnPC
= callFrame
->hasReturnPC() ? callFrame
->returnPC().value() : nullptr;
364 log(indent
, "name: ", functionName(), "\n");
365 log(indent
, "sourceURL: ", sourceURL(), "\n");
367 bool isInlined
= false;
369 isInlined
= isInlinedFrame();
370 log(indent
, "isInlinedFrame: ", isInlinedFrame(), "\n");
371 if (isInlinedFrame())
372 logF(indent
, "InlineCallFrame: %p\n", m_inlineCallFrame
);
375 logF(indent
, "callee: %p\n", callee());
376 logF(indent
, "returnPC: %p\n", returnPC
);
377 logF(indent
, "callerFrame: %p\n", callerFrame
);
378 unsigned locationRawBits
= callFrame
->locationAsRawBits();
379 logF(indent
, "rawLocationBits: %u 0x%x\n", locationRawBits
, locationRawBits
);
380 logF(indent
, "codeBlock: %p ", codeBlock
);
384 if (codeBlock
&& !isInlined
) {
387 if (callFrame
->hasLocationAsBytecodeOffset()) {
388 unsigned bytecodeOffset
= callFrame
->locationAsBytecodeOffset();
389 log(indent
, "bytecodeOffset: ", bytecodeOffset
, " of ", codeBlock
->instructions().size(), "\n");
392 log(indent
, "hasCodeOrigins: ", codeBlock
->hasCodeOrigins(), "\n");
393 if (codeBlock
->hasCodeOrigins()) {
394 unsigned codeOriginIndex
= callFrame
->locationAsCodeOriginIndex();
395 log(indent
, "codeOriginIndex: ", codeOriginIndex
, " of ", codeBlock
->codeOrigins().size(), "\n");
397 JITCode::JITType jitType
= codeBlock
->jitType();
398 if (jitType
!= JITCode::FTLJIT
) {
399 JITCode
* jitCode
= codeBlock
->jitCode().get();
400 logF(indent
, "jitCode: %p start %p end %p\n", jitCode
, jitCode
->start(), jitCode
->end());
407 computeLineAndColumn(line
, column
);
408 log(indent
, "line: ", line
, "\n");
409 log(indent
, "column: ", column
, "\n");