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 | |
39 | namespace JSC { |
40 | |
41 | StackVisitor::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 | |
71 | void 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 | |
94 | void 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 | |
106 | void 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 | |
156 | void 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) |
191 | static int inlinedFrameOffset(CodeOrigin* codeOrigin) |
192 | { |
193 | InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame(); |
194 | int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0; |
195 | return frameOffset; |
196 | } |
197 | |
198 | void 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 | |
233 | StackVisitor::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) |
256 | Optional<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 | |
287 | String 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 | |
317 | String 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 | |
341 | String 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 | |
364 | intptr_t StackVisitor::Frame::sourceID() |
365 | { |
366 | if (CodeBlock* codeBlock = this->codeBlock()) |
367 | return codeBlock->ownerExecutable()->sourceID(); |
368 | return noSourceID; |
369 | } |
370 | |
371 | ClonedArguments* 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 | |
394 | bool StackVisitor::Frame::hasLineAndColumnInfo() const |
395 | { |
396 | return !!codeBlock(); |
397 | } |
398 | |
399 | void 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 | |
422 | void 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 | |
429 | void 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 | |
438 | void StackVisitor::Frame::dump(PrintStream& out, Indenter indent) const |
439 | { |
440 | dump(out, indent, [] (PrintStream&) { }); |
441 | } |
442 | |
443 | void 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 | |