1/*
2 * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
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.
24 */
25
26#include "config.h"
27#include "StackVisitor.h"
28
29#include "ClonedArguments.h"
30#include "Executable.h"
31#include "InlineCallFrame.h"
32#include "Interpreter.h"
33#include "JSCInlines.h"
34#include <wtf/DataLog.h>
35
36namespace JSC {
37
38StackVisitor::StackVisitor(CallFrame* startFrame)
39{
40 m_frame.m_index = 0;
41 CallFrame* topFrame;
42 if (startFrame) {
43 m_frame.m_VMEntryFrame = startFrame->vm().topVMEntryFrame;
44 topFrame = startFrame->vm().topCallFrame;
45 } else {
46 m_frame.m_VMEntryFrame = 0;
47 topFrame = 0;
48 }
49 m_frame.m_callerIsVMEntryFrame = false;
50 readFrame(topFrame);
51
52 // Find the frame the caller wants to start unwinding from.
53 while (m_frame.callFrame() && m_frame.callFrame() != startFrame)
54 gotoNextFrame();
55}
56
57void StackVisitor::gotoNextFrame()
58{
59#if ENABLE(DFG_JIT)
60 if (m_frame.isInlinedFrame()) {
61 InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame();
62 CodeOrigin* callerCodeOrigin = inlineCallFrame->getCallerSkippingTailCalls();
63 if (!callerCodeOrigin) {
64 while (inlineCallFrame) {
65 readInlinedFrame(m_frame.callFrame(), &inlineCallFrame->directCaller);
66 inlineCallFrame = m_frame.inlineCallFrame();
67 }
68 m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame;
69 readFrame(m_frame.callerFrame());
70 } else
71 readInlinedFrame(m_frame.callFrame(), callerCodeOrigin);
72 return;
73 }
74#endif // ENABLE(DFG_JIT)
75 m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame;
76 readFrame(m_frame.callerFrame());
77}
78
79void StackVisitor::unwindToMachineCodeBlockFrame()
80{
81#if ENABLE(DFG_JIT)
82 while (m_frame.isInlinedFrame())
83 gotoNextFrame();
84#endif
85}
86
87void StackVisitor::readFrame(CallFrame* callFrame)
88{
89 if (!callFrame) {
90 m_frame.setToEnd();
91 return;
92 }
93
94#if !ENABLE(DFG_JIT)
95 readNonInlinedFrame(callFrame);
96
97#else // !ENABLE(DFG_JIT)
98 // If the frame doesn't have a code block, then it's not a DFG frame.
99 // Hence, we're not at an inlined frame.
100 CodeBlock* codeBlock = callFrame->codeBlock();
101 if (!codeBlock) {
102 readNonInlinedFrame(callFrame);
103 return;
104 }
105
106 // If the code block does not have any code origins, then there's no
107 // inlining. Hence, we're not at an inlined frame.
108 if (!codeBlock->hasCodeOrigins()) {
109 readNonInlinedFrame(callFrame);
110 return;
111 }
112
113 CallSiteIndex index = callFrame->callSiteIndex();
114 ASSERT(codeBlock->canGetCodeOrigin(index));
115 if (!codeBlock->canGetCodeOrigin(index)) {
116 // See assertion above. In release builds, we try to protect ourselves
117 // from crashing even though stack walking will be goofed up.
118 m_frame.setToEnd();
119 return;
120 }
121
122 CodeOrigin codeOrigin = codeBlock->codeOrigin(index);
123 if (!codeOrigin.inlineCallFrame) {
124 readNonInlinedFrame(callFrame, &codeOrigin);
125 return;
126 }
127
128 readInlinedFrame(callFrame, &codeOrigin);
129#endif // !ENABLE(DFG_JIT)
130}
131
132void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
133{
134 m_frame.m_callFrame = callFrame;
135 m_frame.m_argumentCountIncludingThis = callFrame->argumentCountIncludingThis();
136 m_frame.m_CallerVMEntryFrame = m_frame.m_VMEntryFrame;
137 m_frame.m_callerFrame = callFrame->callerFrame(m_frame.m_CallerVMEntryFrame);
138 m_frame.m_callerIsVMEntryFrame = m_frame.m_CallerVMEntryFrame != m_frame.m_VMEntryFrame;
139 m_frame.m_callee = callFrame->callee();
140 m_frame.m_codeBlock = callFrame->codeBlock();
141 m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0
142 : codeOrigin ? codeOrigin->bytecodeIndex
143 : callFrame->bytecodeOffset();
144#if ENABLE(DFG_JIT)
145 m_frame.m_inlineCallFrame = 0;
146#endif
147}
148
149#if ENABLE(DFG_JIT)
150static int inlinedFrameOffset(CodeOrigin* codeOrigin)
151{
152 InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
153 int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0;
154 return frameOffset;
155}
156
157void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
158{
159 ASSERT(codeOrigin);
160
161 int frameOffset = inlinedFrameOffset(codeOrigin);
162 bool isInlined = !!frameOffset;
163 if (isInlined) {
164 InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
165
166 m_frame.m_callFrame = callFrame;
167 m_frame.m_inlineCallFrame = inlineCallFrame;
168 if (inlineCallFrame->argumentCountRegister.isValid())
169 m_frame.m_argumentCountIncludingThis = callFrame->r(inlineCallFrame->argumentCountRegister.offset()).unboxedInt32();
170 else
171 m_frame.m_argumentCountIncludingThis = inlineCallFrame->arguments.size();
172 m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock.get();
173 m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex;
174
175 JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame);
176 m_frame.m_callee = callee;
177 ASSERT(m_frame.callee());
178
179 // The callerFrame just needs to be non-null to indicate that we
180 // haven't reached the last frame yet. Setting it to the root
181 // frame (i.e. the callFrame that this inlined frame is called from)
182 // would work just fine.
183 m_frame.m_callerFrame = callFrame;
184 return;
185 }
186
187 readNonInlinedFrame(callFrame, codeOrigin);
188}
189#endif // ENABLE(DFG_JIT)
190
191StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const
192{
193 if (!isJSFrame())
194 return CodeType::Native;
195
196 switch (codeBlock()->codeType()) {
197 case EvalCode:
198 return CodeType::Eval;
199 case ModuleCode:
200 return CodeType::Module;
201 case FunctionCode:
202 return CodeType::Function;
203 case GlobalCode:
204 return CodeType::Global;
205 }
206 RELEASE_ASSERT_NOT_REACHED();
207 return CodeType::Global;
208}
209
210String StackVisitor::Frame::functionName()
211{
212 String traceLine;
213 JSObject* callee = this->callee();
214
215 switch (codeType()) {
216 case CodeType::Eval:
217 traceLine = ASCIILiteral("eval code");
218 break;
219 case CodeType::Module:
220 traceLine = ASCIILiteral("module code");
221 break;
222 case CodeType::Native:
223 if (callee)
224 traceLine = getCalculatedDisplayName(callFrame(), callee).impl();
225 break;
226 case CodeType::Function:
227 traceLine = getCalculatedDisplayName(callFrame(), callee).impl();
228 break;
229 case CodeType::Global:
230 traceLine = ASCIILiteral("global code");
231 break;
232 }
233 return traceLine.isNull() ? emptyString() : traceLine;
234}
235
236String StackVisitor::Frame::sourceURL()
237{
238 String traceLine;
239
240 switch (codeType()) {
241 case CodeType::Eval:
242 case CodeType::Module:
243 case CodeType::Function:
244 case CodeType::Global: {
245 String sourceURL = codeBlock()->ownerScriptExecutable()->sourceURL();
246 if (!sourceURL.isEmpty())
247 traceLine = sourceURL.impl();
248 break;
249 }
250 case CodeType::Native:
251 traceLine = ASCIILiteral("[native code]");
252 break;
253 }
254 return traceLine.isNull() ? emptyString() : traceLine;
255}
256
257String StackVisitor::Frame::toString()
258{
259 StringBuilder traceBuild;
260 String functionName = this->functionName();
261 String sourceURL = this->sourceURL();
262 traceBuild.append(functionName);
263 if (!sourceURL.isEmpty()) {
264 if (!functionName.isEmpty())
265 traceBuild.append('@');
266 traceBuild.append(sourceURL);
267 if (isJSFrame()) {
268 unsigned line = 0;
269 unsigned column = 0;
270 computeLineAndColumn(line, column);
271 traceBuild.append(':');
272 traceBuild.appendNumber(line);
273 traceBuild.append(':');
274 traceBuild.appendNumber(column);
275 }
276 }
277 return traceBuild.toString().impl();
278}
279
280ClonedArguments* StackVisitor::Frame::createArguments()
281{
282 ASSERT(m_callFrame);
283 CallFrame* physicalFrame = m_callFrame;
284 ClonedArguments* arguments;
285 ArgumentsMode mode;
286 if (Options::useFunctionDotArguments())
287 mode = ArgumentsMode::Cloned;
288 else
289 mode = ArgumentsMode::FakeValues;
290#if ENABLE(DFG_JIT)
291 if (isInlinedFrame()) {
292 ASSERT(m_inlineCallFrame);
293 arguments = ClonedArguments::createWithInlineFrame(physicalFrame, physicalFrame, m_inlineCallFrame, mode);
294 } else
295#endif
296 arguments = ClonedArguments::createWithMachineFrame(physicalFrame, physicalFrame, mode);
297 return arguments;
298}
299
300void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column)
301{
302 CodeBlock* codeBlock = this->codeBlock();
303 if (!codeBlock) {
304 line = 0;
305 column = 0;
306 return;
307 }
308
309 int divot = 0;
310 int unusedStartOffset = 0;
311 int unusedEndOffset = 0;
312 unsigned divotLine = 0;
313 unsigned divotColumn = 0;
314 retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn);
315
316 line = divotLine + codeBlock->ownerScriptExecutable()->firstLine();
317 column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset());
318
319 if (codeBlock->ownerScriptExecutable()->hasOverrideLineNumber())
320 line = codeBlock->ownerScriptExecutable()->overrideLineNumber();
321}
322
323void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column)
324{
325 CodeBlock* codeBlock = this->codeBlock();
326 codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeOffset(bytecodeOffset(), divot, startOffset, endOffset, line, column);
327 divot += codeBlock->sourceOffset();
328}
329
330void StackVisitor::Frame::setToEnd()
331{
332 m_callFrame = 0;
333#if ENABLE(DFG_JIT)
334 m_inlineCallFrame = 0;
335#endif
336}
337
338static void printIndents(int levels)
339{
340 while (levels--)
341 dataLogFString(" ");
342}
343
344template<typename... Types>
345void log(unsigned indent, const Types&... values)
346{
347 printIndents(indent);
348 dataLog(values...);
349}
350
351template<typename... Types>
352void logF(unsigned indent, const char* format, const Types&... values)
353{
354 printIndents(indent);
355
356#if COMPILER(GCC_OR_CLANG)
357#pragma GCC diagnostic push
358#pragma GCC diagnostic ignored "-Wformat-nonliteral"
359#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
360#endif
361
362 dataLogF(format, values...);
363
364#if COMPILER(GCC_OR_CLANG)
365#pragma GCC diagnostic pop
366#endif
367}
368
369void StackVisitor::Frame::print(int indent)
370{
371 if (!this->callFrame()) {
372 log(indent, "frame 0x0\n");
373 return;
374 }
375
376 CodeBlock* codeBlock = this->codeBlock();
377 logF(indent, "frame %p {\n", this->callFrame());
378
379 {
380 indent++;
381
382 CallFrame* callFrame = m_callFrame;
383 CallFrame* callerFrame = this->callerFrame();
384 void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr;
385
386 log(indent, "name: ", functionName(), "\n");
387 log(indent, "sourceURL: ", sourceURL(), "\n");
388
389 bool isInlined = false;
390#if ENABLE(DFG_JIT)
391 isInlined = isInlinedFrame();
392 log(indent, "isInlinedFrame: ", isInlinedFrame(), "\n");
393 if (isInlinedFrame())
394 logF(indent, "InlineCallFrame: %p\n", m_inlineCallFrame);
395#endif
396
397 logF(indent, "callee: %p\n", callee());
398 logF(indent, "returnPC: %p\n", returnPC);
399 logF(indent, "callerFrame: %p\n", callerFrame);
400 unsigned locationRawBits = callFrame->callSiteAsRawBits();
401 logF(indent, "rawLocationBits: %u 0x%x\n", locationRawBits, locationRawBits);
402 logF(indent, "codeBlock: %p ", codeBlock);
403 if (codeBlock)
404 dataLog(*codeBlock);
405 dataLog("\n");
406 if (codeBlock && !isInlined) {
407 indent++;
408
409 if (callFrame->callSiteBitsAreBytecodeOffset()) {
410 unsigned bytecodeOffset = callFrame->bytecodeOffset();
411 log(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n");
412#if ENABLE(DFG_JIT)
413 } else {
414 log(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n");
415 if (codeBlock->hasCodeOrigins()) {
416 CallSiteIndex callSiteIndex = callFrame->callSiteIndex();
417 log(indent, "callSiteIndex: ", callSiteIndex.bits(), " of ", codeBlock->codeOrigins().size(), "\n");
418
419 JITCode::JITType jitType = codeBlock->jitType();
420 if (jitType != JITCode::FTLJIT) {
421 JITCode* jitCode = codeBlock->jitCode().get();
422 logF(indent, "jitCode: %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end());
423 }
424 }
425#endif
426 }
427 unsigned line = 0;
428 unsigned column = 0;
429 computeLineAndColumn(line, column);
430 log(indent, "line: ", line, "\n");
431 log(indent, "column: ", column, "\n");
432
433 indent--;
434 }
435 indent--;
436 }
437 log(indent, "}\n");
438}
439
440} // namespace JSC
441