/ interpreter / StackVisitor.cpp
StackVisitor.cpp
  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 "JSCInlines.h"
 33  #include "RegisterAtOffsetList.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 = nullptr;
 61          topFrame = nullptr;
 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 = nullptr;
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).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(isWebAssemblyModule(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 = nullptr;
432  #if ENABLE(DFG_JIT)
433      m_inlineCallFrame = nullptr;
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