]> git.saurik.com Git - apple/javascriptcore.git/blob - interpreter/StackVisitor.cpp
19f95b92b1ebfa7e7927a94a33fdd902100c3f39
[apple/javascriptcore.git] / interpreter / StackVisitor.cpp
1 /*
2 * Copyright (C) 2013 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 "Arguments.h"
30 #include "CallFrameInlines.h"
31 #include "Executable.h"
32 #include "Interpreter.h"
33 #include "JSCInlines.h"
34 #include <wtf/DataLog.h>
35
36 namespace JSC {
37
38 StackVisitor::StackVisitor(CallFrame* startFrame)
39 {
40 m_frame.m_index = 0;
41 readFrame(startFrame);
42 }
43
44 void StackVisitor::gotoNextFrame()
45 {
46 #if ENABLE(DFG_JIT)
47 if (m_frame.isInlinedFrame()) {
48 InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame();
49 CodeOrigin* callerCodeOrigin = &inlineCallFrame->caller;
50 readInlinedFrame(m_frame.callFrame(), callerCodeOrigin);
51
52 } else
53 #endif // ENABLE(DFG_JIT)
54 readFrame(m_frame.callerFrame());
55 }
56
57 void StackVisitor::readFrame(CallFrame* callFrame)
58 {
59 ASSERT(!callFrame->isVMEntrySentinel());
60 if (!callFrame) {
61 m_frame.setToEnd();
62 return;
63 }
64
65 #if !ENABLE(DFG_JIT)
66 readNonInlinedFrame(callFrame);
67
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();
72 if (!codeBlock) {
73 readNonInlinedFrame(callFrame);
74 return;
75 }
76
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);
81 return;
82 }
83
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.
89 m_frame.setToEnd();
90 return;
91 }
92
93 CodeOrigin codeOrigin = codeBlock->codeOrigin(index);
94 if (!codeOrigin.inlineCallFrame) {
95 readNonInlinedFrame(callFrame, &codeOrigin);
96 return;
97 }
98
99 readInlinedFrame(callFrame, &codeOrigin);
100 #endif // !ENABLE(DFG_JIT)
101 }
102
103 void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
104 {
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();
114 #if ENABLE(DFG_JIT)
115 m_frame.m_inlineCallFrame = 0;
116 #endif
117 }
118
119 #if ENABLE(DFG_JIT)
120 static int inlinedFrameOffset(CodeOrigin* codeOrigin)
121 {
122 InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
123 int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0;
124 return frameOffset;
125 }
126
127 void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
128 {
129 ASSERT(codeOrigin);
130 ASSERT(!callFrame->isVMEntrySentinel());
131
132 int frameOffset = inlinedFrameOffset(codeOrigin);
133 bool isInlined = !!frameOffset;
134 if (isInlined) {
135 InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
136
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;
142
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());
148
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;
154 return;
155 }
156
157 readNonInlinedFrame(callFrame, codeOrigin);
158 }
159 #endif // ENABLE(DFG_JIT)
160
161 StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const
162 {
163 if (!isJSFrame())
164 return CodeType::Native;
165
166 switch (codeBlock()->codeType()) {
167 case EvalCode:
168 return CodeType::Eval;
169 case FunctionCode:
170 return CodeType::Function;
171 case GlobalCode:
172 return CodeType::Global;
173 }
174 RELEASE_ASSERT_NOT_REACHED();
175 return CodeType::Global;
176 }
177
178 String StackVisitor::Frame::functionName()
179 {
180 String traceLine;
181 JSObject* callee = this->callee();
182
183 switch (codeType()) {
184 case CodeType::Eval:
185 traceLine = "eval code";
186 break;
187 case CodeType::Native:
188 if (callee)
189 traceLine = getCalculatedDisplayName(callFrame(), callee).impl();
190 break;
191 case CodeType::Function:
192 traceLine = getCalculatedDisplayName(callFrame(), callee).impl();
193 break;
194 case CodeType::Global:
195 traceLine = "global code";
196 break;
197 }
198 return traceLine.isNull() ? emptyString() : traceLine;
199 }
200
201 String StackVisitor::Frame::sourceURL()
202 {
203 String traceLine;
204
205 switch (codeType()) {
206 case CodeType::Eval:
207 case CodeType::Function:
208 case CodeType::Global: {
209 String sourceURL = codeBlock()->ownerExecutable()->sourceURL();
210 if (!sourceURL.isEmpty())
211 traceLine = sourceURL.impl();
212 break;
213 }
214 case CodeType::Native:
215 traceLine = "[native code]";
216 break;
217 }
218 return traceLine.isNull() ? emptyString() : traceLine;
219 }
220
221 String StackVisitor::Frame::toString()
222 {
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);
231 if (isJSFrame()) {
232 unsigned line = 0;
233 unsigned column = 0;
234 computeLineAndColumn(line, column);
235 traceBuild.append(':');
236 traceBuild.appendNumber(line);
237 traceBuild.append(':');
238 traceBuild.appendNumber(column);
239 }
240 }
241 return traceBuild.toString().impl();
242 }
243
244 Arguments* StackVisitor::Frame::createArguments()
245 {
246 ASSERT(m_callFrame);
247 CallFrame* physicalFrame = m_callFrame;
248 VM& vm = physicalFrame->vm();
249 Arguments* arguments;
250 #if ENABLE(DFG_JIT)
251 if (isInlinedFrame()) {
252 ASSERT(m_inlineCallFrame);
253 arguments = Arguments::create(vm, physicalFrame, m_inlineCallFrame);
254 arguments->tearOff(physicalFrame, m_inlineCallFrame);
255 } else
256 #endif
257 {
258 arguments = Arguments::create(vm, physicalFrame);
259 arguments->tearOff(physicalFrame);
260 }
261 return arguments;
262 }
263
264 Arguments* StackVisitor::Frame::existingArguments()
265 {
266 if (codeBlock()->codeType() != FunctionCode)
267 return 0;
268 if (!codeBlock()->usesArguments())
269 return 0;
270
271 VirtualRegister reg;
272
273 #if ENABLE(DFG_JIT)
274 if (isInlinedFrame())
275 reg = inlineCallFrame()->argumentsRegister;
276 else
277 #endif // ENABLE(DFG_JIT)
278 reg = codeBlock()->argumentsRegister();
279
280 JSValue result = callFrame()->r(unmodifiedArgumentsRegister(reg).offset()).jsValue();
281 if (!result || !result.isCell()) // Protect against Undefined in case we throw in op_enter.
282 return 0;
283 return jsCast<Arguments*>(result);
284 }
285
286 void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column)
287 {
288 CodeBlock* codeBlock = this->codeBlock();
289 if (!codeBlock) {
290 line = 0;
291 column = 0;
292 return;
293 }
294
295 int divot = 0;
296 int unusedStartOffset = 0;
297 int unusedEndOffset = 0;
298 unsigned divotLine = 0;
299 unsigned divotColumn = 0;
300 retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn);
301
302 line = divotLine + codeBlock->ownerExecutable()->lineNo();
303 column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset());
304 }
305
306 void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column)
307 {
308 CodeBlock* codeBlock = this->codeBlock();
309 codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeOffset(bytecodeOffset(), divot, startOffset, endOffset, line, column);
310 divot += codeBlock->sourceOffset();
311 }
312
313 void StackVisitor::Frame::setToEnd()
314 {
315 m_callFrame = 0;
316 #if ENABLE(DFG_JIT)
317 m_inlineCallFrame = 0;
318 #endif
319 }
320
321 #ifndef NDEBUG
322
323 static const char* jitTypeName(JITCode::JITType jitType)
324 {
325 switch (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";
332 }
333 return "<unknown>";
334 }
335
336 static void printIndents(int levels)
337 {
338 while (levels--)
339 dataLogFString(" ");
340 }
341
342 static void printif(int indentLevels, const char* format, ...)
343 {
344 va_list argList;
345 va_start(argList, format);
346
347 if (indentLevels)
348 printIndents(indentLevels);
349
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"
354 #endif
355
356 WTF::dataLogFV(format, argList);
357
358 #if COMPILER(CLANG) || COMPILER(GCC)
359 #pragma GCC diagnostic pop
360 #endif
361
362 va_end(argList);
363 }
364
365 void StackVisitor::Frame::print(int indentLevel)
366 {
367 int i = indentLevel;
368
369 if (!this->callFrame()) {
370 printif(i, "frame 0x0\n");
371 return;
372 }
373
374 CodeBlock* codeBlock = this->codeBlock();
375 printif(i, "frame %p {\n", this->callFrame());
376
377 CallFrame* callFrame = m_callFrame;
378 CallFrame* callerFrame = this->callerFrame();
379 void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr;
380
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());
384
385 #if ENABLE(DFG_JIT)
386 printif(i, " isInlinedFrame %d\n", isInlinedFrame());
387 if (isInlinedFrame())
388 printif(i, " InlineCallFrame %p\n", m_inlineCallFrame);
389 #endif
390
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);
397 if (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());
402 #if ENABLE(DFG_JIT)
403 } else {
404 unsigned codeOriginIndex = callFrame->locationAsCodeOriginIndex();
405 printif(i, " codeOriginIdex %u %p / %zu\n", codeOriginIndex, reinterpret_cast<void*>(codeOriginIndex), codeBlock->codeOrigins().size());
406 #endif
407 }
408 unsigned line = 0;
409 unsigned column = 0;
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));
414 #if ENABLE(DFG_JIT)
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());
419 }
420 #endif
421 }
422 printif(i, "}\n");
423 }
424
425 #endif // NDEBUG
426
427 } // namespace JSC
428
429 #ifndef NDEBUG
430 using JSC::StackVisitor;
431
432 // For debugging use
433 JS_EXPORT_PRIVATE void debugPrintCallFrame(JSC::CallFrame*);
434 JS_EXPORT_PRIVATE void debugPrintStack(JSC::CallFrame* topCallFrame);
435
436 class DebugPrintFrameFunctor {
437 public:
438 enum Action {
439 PrintOne,
440 PrintAll
441 };
442
443 DebugPrintFrameFunctor(Action action)
444 : m_action(action)
445 {
446 }
447
448 StackVisitor::Status operator()(StackVisitor& visitor)
449 {
450 visitor->print(2);
451 return m_action == PrintAll ? StackVisitor::Continue : StackVisitor::Done;
452 }
453
454 private:
455 Action m_action;
456 };
457
458 void debugPrintCallFrame(JSC::CallFrame* callFrame)
459 {
460 if (!callFrame)
461 return;
462 DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintOne);
463 callFrame->iterate(functor);
464 }
465
466 void debugPrintStack(JSC::CallFrame* topCallFrame)
467 {
468 if (!topCallFrame)
469 return;
470 DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintAll);
471 topCallFrame->iterate(functor);
472 }
473
474 #endif // !NDEBUG