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