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