WebAssembly: improve stack trace
[WebKit-https.git] / Source / JavaScriptCore / interpreter / StackVisitor.cpp
1 /*
2  * Copyright (C) 2013, 2015-2017 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 "DebuggerPrimitives.h"
31 #include "InlineCallFrame.h"
32 #include "Interpreter.h"
33 #include "JSCInlines.h"
34 #include "WasmCallee.h"
35 #include "WasmIndexOrName.h"
36 #include <wtf/text/StringBuilder.h>
37
38 namespace JSC {
39
40 StackVisitor::StackVisitor(CallFrame* startFrame, VM* vm)
41 {
42     m_frame.m_index = 0;
43     m_frame.m_isWasmFrame = false;
44     CallFrame* topFrame;
45     if (startFrame) {
46         ASSERT(vm);
47         m_frame.m_entryFrame = vm->topEntryFrame;
48         topFrame = vm->topCallFrame;
49         
50         if (topFrame && static_cast<void*>(m_frame.m_entryFrame) == static_cast<void*>(topFrame)) {
51             topFrame = vmEntryRecord(m_frame.m_entryFrame)->m_prevTopCallFrame;
52             m_frame.m_entryFrame = vmEntryRecord(m_frame.m_entryFrame)->m_prevTopEntryFrame;
53         }
54     } else {
55         m_frame.m_entryFrame = 0;
56         topFrame = 0;
57     }
58     m_frame.m_callerIsEntryFrame = false;
59     readFrame(topFrame);
60
61     // Find the frame the caller wants to start unwinding from.
62     while (m_frame.callFrame() && m_frame.callFrame() != startFrame)
63         gotoNextFrame();
64 }
65
66 void StackVisitor::gotoNextFrame()
67 {
68     m_frame.m_index++;
69 #if ENABLE(DFG_JIT)
70     if (m_frame.isInlinedFrame()) {
71         InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame();
72         CodeOrigin* callerCodeOrigin = inlineCallFrame->getCallerSkippingTailCalls();
73         if (!callerCodeOrigin) {
74             while (inlineCallFrame) {
75                 readInlinedFrame(m_frame.callFrame(), &inlineCallFrame->directCaller);
76                 inlineCallFrame = m_frame.inlineCallFrame();
77             }
78             m_frame.m_entryFrame = m_frame.m_callerEntryFrame;
79             readFrame(m_frame.callerFrame());
80         } else
81             readInlinedFrame(m_frame.callFrame(), callerCodeOrigin);
82         return;
83     }
84 #endif // ENABLE(DFG_JIT)
85     m_frame.m_entryFrame = m_frame.m_callerEntryFrame;
86     readFrame(m_frame.callerFrame());
87 }
88
89 void StackVisitor::unwindToMachineCodeBlockFrame()
90 {
91 #if ENABLE(DFG_JIT)
92     if (m_frame.isInlinedFrame()) {
93         CodeOrigin codeOrigin = m_frame.inlineCallFrame()->directCaller;
94         while (codeOrigin.inlineCallFrame)
95             codeOrigin = codeOrigin.inlineCallFrame->directCaller;
96         readNonInlinedFrame(m_frame.callFrame(), &codeOrigin);
97     }
98 #endif
99 }
100
101 void StackVisitor::readFrame(CallFrame* callFrame)
102 {
103     if (!callFrame) {
104         m_frame.setToEnd();
105         return;
106     }
107
108     if (callFrame->isAnyWasmCallee()) {
109         readNonInlinedFrame(callFrame);
110         return;
111     }
112
113 #if !ENABLE(DFG_JIT)
114     readNonInlinedFrame(callFrame);
115
116 #else // !ENABLE(DFG_JIT)
117     // If the frame doesn't have a code block, then it's not a DFG frame.
118     // Hence, we're not at an inlined frame.
119     CodeBlock* codeBlock = callFrame->codeBlock();
120     if (!codeBlock) {
121         readNonInlinedFrame(callFrame);
122         return;
123     }
124
125     // If the code block does not have any code origins, then there's no
126     // inlining. Hence, we're not at an inlined frame.
127     if (!codeBlock->hasCodeOrigins()) {
128         readNonInlinedFrame(callFrame);
129         return;
130     }
131
132     CallSiteIndex index = callFrame->callSiteIndex();
133     ASSERT(codeBlock->canGetCodeOrigin(index));
134     if (!codeBlock->canGetCodeOrigin(index)) {
135         // See assertion above. In release builds, we try to protect ourselves
136         // from crashing even though stack walking will be goofed up.
137         m_frame.setToEnd();
138         return;
139     }
140
141     CodeOrigin codeOrigin = codeBlock->codeOrigin(index);
142     if (!codeOrigin.inlineCallFrame) {
143         readNonInlinedFrame(callFrame, &codeOrigin);
144         return;
145     }
146
147     readInlinedFrame(callFrame, &codeOrigin);
148 #endif // !ENABLE(DFG_JIT)
149 }
150
151 void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
152 {
153     m_frame.m_callFrame = callFrame;
154     m_frame.m_argumentCountIncludingThis = callFrame->argumentCountIncludingThis();
155     m_frame.m_callerEntryFrame = m_frame.m_entryFrame;
156     m_frame.m_callerFrame = callFrame->callerFrame(m_frame.m_callerEntryFrame);
157     m_frame.m_callerIsEntryFrame = m_frame.m_callerEntryFrame != m_frame.m_entryFrame;
158     m_frame.m_isWasmFrame = false;
159
160     CalleeBits callee = callFrame->callee();
161     m_frame.m_callee = callee;
162
163     if (callFrame->isAnyWasmCallee()) {
164         m_frame.m_isWasmFrame = true;
165         m_frame.m_codeBlock = nullptr;
166         m_frame.m_bytecodeOffset = 0;
167 #if ENABLE(WEBASSEMBLY)
168         CalleeBits bits = callFrame->callee();
169         if (bits.isWasm())
170             m_frame.m_wasmFunctionIndexOrName = bits.asWasmCallee()->indexOrName();
171 #endif
172     } else {
173         m_frame.m_codeBlock = callFrame->codeBlock();
174         m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0
175             : codeOrigin ? codeOrigin->bytecodeIndex
176             : callFrame->bytecodeOffset();
177
178     }
179
180 #if ENABLE(DFG_JIT)
181     m_frame.m_inlineCallFrame = 0;
182 #endif
183 }
184
185 #if ENABLE(DFG_JIT)
186 static int inlinedFrameOffset(CodeOrigin* codeOrigin)
187 {
188     InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
189     int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0;
190     return frameOffset;
191 }
192
193 void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
194 {
195     ASSERT(codeOrigin);
196     m_frame.m_isWasmFrame = false;
197
198     int frameOffset = inlinedFrameOffset(codeOrigin);
199     bool isInlined = !!frameOffset;
200     if (isInlined) {
201         InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
202
203         m_frame.m_callFrame = callFrame;
204         m_frame.m_inlineCallFrame = inlineCallFrame;
205         if (inlineCallFrame->argumentCountRegister.isValid())
206             m_frame.m_argumentCountIncludingThis = callFrame->r(inlineCallFrame->argumentCountRegister.offset()).unboxedInt32();
207         else
208             m_frame.m_argumentCountIncludingThis = inlineCallFrame->argumentCountIncludingThis;
209         m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock.get();
210         m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex;
211
212         JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame);
213         m_frame.m_callee = callee;
214         ASSERT(!!m_frame.callee().rawPtr());
215
216         // The callerFrame just needs to be non-null to indicate that we
217         // haven't reached the last frame yet. Setting it to the root
218         // frame (i.e. the callFrame that this inlined frame is called from)
219         // would work just fine.
220         m_frame.m_callerFrame = callFrame;
221         return;
222     }
223
224     readNonInlinedFrame(callFrame, codeOrigin);
225 }
226 #endif // ENABLE(DFG_JIT)
227
228 StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const
229 {
230     if (isWasmFrame())
231         return CodeType::Wasm;
232
233     if (!codeBlock())
234         return CodeType::Native;
235
236     switch (codeBlock()->codeType()) {
237     case EvalCode:
238         return CodeType::Eval;
239     case ModuleCode:
240         return CodeType::Module;
241     case FunctionCode:
242         return CodeType::Function;
243     case GlobalCode:
244         return CodeType::Global;
245     }
246     RELEASE_ASSERT_NOT_REACHED();
247     return CodeType::Global;
248 }
249
250 RegisterAtOffsetList* StackVisitor::Frame::calleeSaveRegisters()
251 {
252     if (isInlinedFrame())
253         return nullptr;
254
255 #if ENABLE(JIT) && NUMBER_OF_CALLEE_SAVES_REGISTERS > 0
256
257 #if ENABLE(WEBASSEMBLY)
258     if (isWasmFrame()) {
259         if (callee().isCell()) {
260             RELEASE_ASSERT(isWebAssemblyToJSCallee(callee().asCell()));
261             return nullptr;
262         }
263         Wasm::Callee* wasmCallee = callee().asWasmCallee();
264         return wasmCallee->calleeSaveRegisters();
265     }
266 #endif // ENABLE(WEBASSEMBLY)
267
268     if (CodeBlock* codeBlock = this->codeBlock())
269         return codeBlock->calleeSaveRegisters();
270
271 #endif // ENABLE(JIT) && NUMBER_OF_CALLEE_SAVES_REGISTERS > 0
272
273     return nullptr;
274 }
275
276 String StackVisitor::Frame::functionName() const
277 {
278     String traceLine;
279
280     switch (codeType()) {
281     case CodeType::Wasm:
282         traceLine = makeString(m_wasmFunctionIndexOrName);
283         break;
284     case CodeType::Eval:
285         traceLine = ASCIILiteral("eval code");
286         break;
287     case CodeType::Module:
288         traceLine = ASCIILiteral("module code");
289         break;
290     case CodeType::Native: {
291         JSCell* callee = this->callee().asCell();
292         if (callee)
293             traceLine = getCalculatedDisplayName(callFrame()->vm(), jsCast<JSObject*>(callee)).impl();
294         break;
295     }
296     case CodeType::Function: 
297         traceLine = getCalculatedDisplayName(callFrame()->vm(), jsCast<JSObject*>(this->callee().asCell())).impl();
298         break;
299     case CodeType::Global:
300         traceLine = ASCIILiteral("global code");
301         break;
302     }
303     return traceLine.isNull() ? emptyString() : traceLine;
304 }
305
306 String StackVisitor::Frame::sourceURL() const
307 {
308     String traceLine;
309
310     switch (codeType()) {
311     case CodeType::Eval:
312     case CodeType::Module:
313     case CodeType::Function:
314     case CodeType::Global: {
315         String sourceURL = codeBlock()->ownerScriptExecutable()->sourceURL();
316         if (!sourceURL.isEmpty())
317             traceLine = sourceURL.impl();
318         break;
319     }
320     case CodeType::Native:
321         traceLine = ASCIILiteral("[native code]");
322         break;
323     case CodeType::Wasm:
324         traceLine = ASCIILiteral("[wasm code]");
325         break;
326     }
327     return traceLine.isNull() ? emptyString() : traceLine;
328 }
329
330 String StackVisitor::Frame::toString() const
331 {
332     StringBuilder traceBuild;
333     String functionName = this->functionName();
334     String sourceURL = this->sourceURL();
335     traceBuild.append(functionName);
336     if (!sourceURL.isEmpty()) {
337         if (!functionName.isEmpty())
338             traceBuild.append('@');
339         traceBuild.append(sourceURL);
340         if (hasLineAndColumnInfo()) {
341             unsigned line = 0;
342             unsigned column = 0;
343             computeLineAndColumn(line, column);
344             traceBuild.append(':');
345             traceBuild.appendNumber(line);
346             traceBuild.append(':');
347             traceBuild.appendNumber(column);
348         }
349     }
350     return traceBuild.toString().impl();
351 }
352
353 intptr_t StackVisitor::Frame::sourceID()
354 {
355     if (CodeBlock* codeBlock = this->codeBlock())
356         return codeBlock->ownerScriptExecutable()->sourceID();
357     return noSourceID;
358 }
359
360 ClonedArguments* StackVisitor::Frame::createArguments()
361 {
362     ASSERT(m_callFrame);
363     CallFrame* physicalFrame = m_callFrame;
364     ClonedArguments* arguments;
365     ArgumentsMode mode;
366     if (Options::useFunctionDotArguments())
367         mode = ArgumentsMode::Cloned;
368     else
369         mode = ArgumentsMode::FakeValues;
370 #if ENABLE(DFG_JIT)
371     if (isInlinedFrame()) {
372         ASSERT(m_inlineCallFrame);
373         arguments = ClonedArguments::createWithInlineFrame(physicalFrame, physicalFrame, m_inlineCallFrame, mode);
374     } else 
375 #endif
376         arguments = ClonedArguments::createWithMachineFrame(physicalFrame, physicalFrame, mode);
377     return arguments;
378 }
379
380 bool StackVisitor::Frame::hasLineAndColumnInfo() const
381 {
382     return !!codeBlock();
383 }
384
385 void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) const
386 {
387     CodeBlock* codeBlock = this->codeBlock();
388     if (!codeBlock) {
389         line = 0;
390         column = 0;
391         return;
392     }
393
394     int divot = 0;
395     int unusedStartOffset = 0;
396     int unusedEndOffset = 0;
397     unsigned divotLine = 0;
398     unsigned divotColumn = 0;
399     retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn);
400
401     line = divotLine + codeBlock->ownerScriptExecutable()->firstLine();
402     column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset());
403
404     if (codeBlock->ownerScriptExecutable()->hasOverrideLineNumber())
405         line = codeBlock->ownerScriptExecutable()->overrideLineNumber();
406 }
407
408 void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) const
409 {
410     CodeBlock* codeBlock = this->codeBlock();
411     codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeOffset(bytecodeOffset(), divot, startOffset, endOffset, line, column);
412     divot += codeBlock->sourceOffset();
413 }
414
415 void StackVisitor::Frame::setToEnd()
416 {
417     m_callFrame = 0;
418 #if ENABLE(DFG_JIT)
419     m_inlineCallFrame = 0;
420 #endif
421     m_isWasmFrame = false;
422 }
423
424 void StackVisitor::Frame::dump(PrintStream& out, Indenter indent) const
425 {
426     dump(out, indent, [] (PrintStream&) { });
427 }
428
429 void StackVisitor::Frame::dump(PrintStream& out, Indenter indent, std::function<void(PrintStream&)> prefix) const
430 {
431     if (!this->callFrame()) {
432         out.print(indent, "frame 0x0\n");
433         return;
434     }
435
436     CodeBlock* codeBlock = this->codeBlock();
437     out.print(indent);
438     prefix(out);
439     out.print("frame ", RawPointer(this->callFrame()), " {\n");
440
441     {
442         indent++;
443
444         CallFrame* callFrame = m_callFrame;
445         CallFrame* callerFrame = this->callerFrame();
446         void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr;
447
448         out.print(indent, "name: ", functionName(), "\n");
449         out.print(indent, "sourceURL: ", sourceURL(), "\n");
450
451         bool isInlined = false;
452 #if ENABLE(DFG_JIT)
453         isInlined = isInlinedFrame();
454         out.print(indent, "isInlinedFrame: ", isInlinedFrame(), "\n");
455         if (isInlinedFrame())
456             out.print(indent, "InlineCallFrame: ", RawPointer(m_inlineCallFrame), "\n");
457 #endif
458
459         out.print(indent, "callee: ", RawPointer(callee().rawPtr()), "\n");
460         out.print(indent, "returnPC: ", RawPointer(returnPC), "\n");
461         out.print(indent, "callerFrame: ", RawPointer(callerFrame), "\n");
462         unsigned locationRawBits = callFrame->callSiteAsRawBits();
463         out.print(indent, "rawLocationBits: ", static_cast<uintptr_t>(locationRawBits),
464             " ", RawPointer(reinterpret_cast<void*>(locationRawBits)), "\n");
465         out.print(indent, "codeBlock: ", RawPointer(codeBlock));
466         if (codeBlock)
467             out.print(" ", *codeBlock);
468         out.print("\n");
469         if (codeBlock && !isInlined) {
470             indent++;
471
472             if (callFrame->callSiteBitsAreBytecodeOffset()) {
473                 unsigned bytecodeOffset = callFrame->bytecodeOffset();
474                 out.print(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n");
475 #if ENABLE(DFG_JIT)
476             } else {
477                 out.print(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n");
478                 if (codeBlock->hasCodeOrigins()) {
479                     CallSiteIndex callSiteIndex = callFrame->callSiteIndex();
480                     out.print(indent, "callSiteIndex: ", callSiteIndex.bits(), " of ", codeBlock->codeOrigins().size(), "\n");
481
482                     JITCode::JITType jitType = codeBlock->jitType();
483                     if (jitType != JITCode::FTLJIT) {
484                         JITCode* jitCode = codeBlock->jitCode().get();
485                         out.print(indent, "jitCode: ", RawPointer(jitCode),
486                             " start ", RawPointer(jitCode->start()),
487                             " end ", RawPointer(jitCode->end()), "\n");
488                     }
489                 }
490 #endif
491             }
492             unsigned line = 0;
493             unsigned column = 0;
494             computeLineAndColumn(line, column);
495             out.print(indent, "line: ", line, "\n");
496             out.print(indent, "column: ", column, "\n");
497
498             indent--;
499         }
500         out.print(indent, "EntryFrame: ", RawPointer(m_entryFrame), "\n");
501         indent--;
502     }
503     out.print(indent, "}\n");
504 }
505
506 } // namespace JSC