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
36namespace JSC {
37
38StackVisitor::StackVisitor(CallFrame* startFrame)
39{
40    m_frame.m_index = 0;
41    readFrame(startFrame);
42}
43
44void 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
57void 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
103void 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)
120static int inlinedFrameOffset(CodeOrigin* codeOrigin)
121{
122    InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
123    int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0;
124    return frameOffset;
125}
126
127void 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
161StackVisitor::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
178String 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
201String 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
221String 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
244Arguments* 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
264Arguments* 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
286void 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
306void 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
313void 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
323static 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
336static void printIndents(int levels)
337{
338    while (levels--)
339        dataLogFString("   ");
340}
341
342static 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
365void 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
430using JSC::StackVisitor;
431
432// For debugging use
433JS_EXPORT_PRIVATE void debugPrintCallFrame(JSC::CallFrame*);
434JS_EXPORT_PRIVATE void debugPrintStack(JSC::CallFrame* topCallFrame);
435
436class DebugPrintFrameFunctor {
437public:
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
454private:
455    Action m_action;
456};
457
458void debugPrintCallFrame(JSC::CallFrame* callFrame)
459{
460    if (!callFrame)
461        return;
462    DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintOne);
463    callFrame->iterate(functor);
464}
465
466void debugPrintStack(JSC::CallFrame* topCallFrame)
467{
468    if (!topCallFrame)
469        return;
470    DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintAll);
471    topCallFrame->iterate(functor);
472}
473
474#endif // !NDEBUG
475