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 | |
40 | namespace JSC { |
41 | |
42 | StackVisitor::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 | |
73 | void 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 | |
96 | void 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 | |
108 | void 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 | |
158 | void 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) |
193 | static int inlinedFrameOffset(CodeOrigin* codeOrigin) |
194 | { |
195 | InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame(); |
196 | int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0; |
197 | return frameOffset; |
198 | } |
199 | |
200 | void 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 | |
235 | StackVisitor::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) |
258 | Optional<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 | |
289 | String 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 | |
319 | String 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 | |
343 | String 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 | |
366 | intptr_t StackVisitor::Frame::sourceID() |
367 | { |
368 | if (CodeBlock* codeBlock = this->codeBlock()) |
369 | return codeBlock->ownerExecutable()->sourceID(); |
370 | return noSourceID; |
371 | } |
372 | |
373 | ClonedArguments* 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 | |
393 | bool StackVisitor::Frame::hasLineAndColumnInfo() const |
394 | { |
395 | return !!codeBlock(); |
396 | } |
397 | |
398 | void 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 | |
421 | void 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 | |
428 | void 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 | |
437 | void StackVisitor::Frame::dump(PrintStream& out, Indenter indent) const |
438 | { |
439 | dump(out, indent, [] (PrintStream&) { }); |
440 | } |
441 | |
442 | void 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 | |