tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

JSJitFrameIter.cpp (22674B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "jit/JSJitFrameIter-inl.h"
      8 
      9 #include "jit/CalleeToken.h"
     10 #include "jit/IonScript.h"
     11 #include "jit/JitcodeMap.h"
     12 #include "jit/JitFrames.h"
     13 #include "jit/JitRuntime.h"
     14 #include "jit/JitScript.h"
     15 #include "jit/MacroAssembler.h"  // js::jit::Assembler::GetPointer
     16 #include "jit/SafepointIndex.h"
     17 #include "jit/Safepoints.h"
     18 #include "jit/ScriptFromCalleeToken.h"
     19 #include "jit/VMFunctions.h"
     20 #include "js/friend/DumpFunctions.h"  // js::DumpObject, js::DumpValue
     21 #include "vm/JitActivation.h"
     22 
     23 #include "vm/JSScript-inl.h"
     24 
     25 using namespace js;
     26 using namespace js::jit;
     27 
     28 JSJitFrameIter::JSJitFrameIter(const JitActivation* activation)
     29    : current_(activation->jsExitFP()),
     30      type_(FrameType::Exit),
     31      activation_(activation) {
     32  // If we're currently performing a bailout, we have to use the activation's
     33  // bailout data when we start iterating over the activation's frames.
     34  if (activation_->bailoutData()) {
     35    current_ = activation_->bailoutData()->fp();
     36    type_ = FrameType::Bailout;
     37  }
     38  MOZ_ASSERT(!TlsContext.get()->inUnsafeCallWithABI);
     39 }
     40 
     41 JSJitFrameIter::JSJitFrameIter(const JitActivation* activation, uint8_t* fp,
     42                               bool unwinding)
     43    : current_(fp), type_(FrameType::Exit), activation_(activation) {
     44  // This constructor is only used when resuming iteration after iterating Wasm
     45  // frames in the same JitActivation so ignore activation_->bailoutData().
     46  if (unwinding) {
     47    MOZ_ASSERT(fp == activation->jsExitFP());
     48  } else {
     49    MOZ_ASSERT(fp > activation->jsOrWasmExitFP());
     50  }
     51  MOZ_ASSERT(!TlsContext.get()->inUnsafeCallWithABI);
     52 }
     53 
     54 bool JSJitFrameIter::checkInvalidation() const {
     55  IonScript* dummy;
     56  return checkInvalidation(&dummy);
     57 }
     58 
     59 bool JSJitFrameIter::checkInvalidation(IonScript** ionScriptOut) const {
     60  JSScript* script = this->script();
     61  if (isBailoutJS()) {
     62    *ionScriptOut = activation_->bailoutData()->ionScript();
     63    return !script->hasIonScript() || script->ionScript() != *ionScriptOut;
     64  }
     65 
     66  uint8_t* returnAddr = resumePCinCurrentFrame();
     67  // N.B. the current IonScript is not the same as the frame's
     68  // IonScript if the frame has since been invalidated.
     69  bool invalidated = !script->hasIonScript() ||
     70                     !script->ionScript()->containsReturnAddress(returnAddr);
     71  if (!invalidated) {
     72    return false;
     73  }
     74 
     75  int32_t invalidationDataOffset = ((int32_t*)returnAddr)[-1];
     76  uint8_t* ionScriptDataOffset = returnAddr + invalidationDataOffset;
     77  IonScript* ionScript = (IonScript*)Assembler::GetPointer(ionScriptDataOffset);
     78  MOZ_ASSERT(ionScript->containsReturnAddress(returnAddr));
     79  *ionScriptOut = ionScript;
     80  return true;
     81 }
     82 
     83 CalleeToken JSJitFrameIter::calleeToken() const {
     84  return ((JitFrameLayout*)current_)->calleeToken();
     85 }
     86 
     87 JSFunction* JSJitFrameIter::callee() const {
     88  MOZ_ASSERT(isScripted() || isTrampolineNative());
     89  MOZ_ASSERT(isFunctionFrame());
     90  return CalleeTokenToFunction(calleeToken());
     91 }
     92 
     93 JSFunction* JSJitFrameIter::maybeCallee() const {
     94  if (isScripted() && isFunctionFrame()) {
     95    return callee();
     96  }
     97  return nullptr;
     98 }
     99 
    100 bool JSJitFrameIter::isBareExit() const {
    101  if (type_ != FrameType::Exit) {
    102    return false;
    103  }
    104  return exitFrame()->isBareExit();
    105 }
    106 
    107 bool JSJitFrameIter::isUnwoundJitExit() const {
    108  if (type_ != FrameType::Exit) {
    109    return false;
    110  }
    111  return exitFrame()->isUnwoundJitExit();
    112 }
    113 
    114 bool JSJitFrameIter::isFunctionFrame() const {
    115  return CalleeTokenIsFunction(calleeToken());
    116 }
    117 
    118 JSScript* JSJitFrameIter::script() const {
    119  MOZ_ASSERT(isScripted());
    120  JSScript* script = MaybeForwardedScriptFromCalleeToken(calleeToken());
    121  MOZ_ASSERT(script);
    122  return script;
    123 }
    124 
    125 JSScript* JSJitFrameIter::maybeForwardedScript() const {
    126  MOZ_ASSERT(isScripted());
    127  if (isBaselineJS()) {
    128    return MaybeForwardedScriptFromCalleeToken(baselineFrame()->calleeToken());
    129  }
    130  JSScript* script = MaybeForwardedScriptFromCalleeToken(calleeToken());
    131  MOZ_ASSERT(script);
    132  return script;
    133 }
    134 
    135 void JSJitFrameIter::baselineScriptAndPc(JSScript** scriptRes,
    136                                         jsbytecode** pcRes) const {
    137  MOZ_ASSERT(isBaselineJS());
    138  JSScript* script = this->script();
    139  if (scriptRes) {
    140    *scriptRes = script;
    141  }
    142 
    143  MOZ_ASSERT(pcRes);
    144 
    145  // The Baseline Interpreter stores the bytecode pc in the frame.
    146  if (baselineFrame()->runningInInterpreter()) {
    147    MOZ_ASSERT(baselineFrame()->interpreterScript() == script);
    148    *pcRes = baselineFrame()->interpreterPC();
    149    return;
    150  }
    151 
    152  // There must be a BaselineScript with a RetAddrEntry for the current return
    153  // address.
    154  uint8_t* retAddr = resumePCinCurrentFrame();
    155  const RetAddrEntry& entry =
    156      script->baselineScript()->retAddrEntryFromReturnAddress(retAddr);
    157  *pcRes = entry.pc(script);
    158 }
    159 
    160 Value* JSJitFrameIter::actualArgs() const { return jsFrame()->actualArgs(); }
    161 
    162 uint8_t* JSJitFrameIter::prevFp() const { return current()->callerFramePtr(); }
    163 
    164 // Compute the size of a Baseline frame excluding pushed VMFunction arguments or
    165 // callee frame headers. This is used to calculate the number of Value slots in
    166 // the frame. The caller asserts this matches BaselineFrame::debugFrameSize.
    167 static uint32_t ComputeBaselineFrameSize(const JSJitFrameIter& frame) {
    168  MOZ_ASSERT(frame.prevType() == FrameType::BaselineJS);
    169 
    170  uint32_t frameSize = frame.current()->callerFramePtr() - frame.fp();
    171 
    172  if (frame.isBaselineStub()) {
    173    return frameSize - BaselineStubFrameLayout::Size();
    174  }
    175 
    176  // Note: an UnwoundJit exit frame is a JitFrameLayout that was turned into an
    177  // ExitFrameLayout by EnsureUnwoundJitExitFrame. We have to use the original
    178  // header size here because that's what we have on the stack.
    179  if (frame.isScripted() || frame.isUnwoundJitExit()) {
    180    return frameSize - JitFrameLayout::Size();
    181  }
    182 
    183  if (frame.isExitFrame()) {
    184    frameSize -= ExitFrameLayout::Size();
    185    if (frame.exitFrame()->isWrapperExit()) {
    186      VMFunctionId id = frame.exitFrame()->footer()->functionId();
    187      const VMFunctionData& data = GetVMFunction(id);
    188      frameSize -= data.explicitStackSlots() * sizeof(void*);
    189    }
    190    return frameSize;
    191  }
    192 
    193  MOZ_CRASH("Unexpected frame");
    194 }
    195 
    196 void JSJitFrameIter::operator++() {
    197  MOZ_ASSERT(!isEntry());
    198 
    199  // Compute BaselineFrame size. In debug builds this is equivalent to
    200  // BaselineFrame::debugFrameSize_. This is asserted at the end of this method.
    201  if (current()->prevType() == FrameType::BaselineJS) {
    202    uint32_t frameSize = ComputeBaselineFrameSize(*this);
    203    baselineFrameSize_ = mozilla::Some(frameSize);
    204  } else {
    205    baselineFrameSize_ = mozilla::Nothing();
    206  }
    207 
    208  cachedSafepointIndex_ = nullptr;
    209 
    210  // If the next frame is the entry frame, just exit. Don't update current_,
    211  // since the entry and first frames overlap.
    212  if (isEntry(current()->prevType())) {
    213    type_ = current()->prevType();
    214    return;
    215  }
    216 
    217  type_ = current()->prevType();
    218  resumePCinCurrentFrame_ = current()->returnAddress();
    219  current_ = prevFp();
    220 
    221  MOZ_ASSERT_IF(isBaselineJS(),
    222                baselineFrame()->debugFrameSize() == *baselineFrameSize_);
    223 }
    224 
    225 uintptr_t* JSJitFrameIter::spillBase() const {
    226  MOZ_ASSERT(isIonJS());
    227 
    228  // Get the base address to where safepoint registers are spilled.
    229  // Out-of-line calls do not unwind the extra padding space used to
    230  // aggregate bailout tables, so we use frameSize instead of frameLocals,
    231  // which would only account for local stack slots.
    232  return reinterpret_cast<uintptr_t*>(fp() - ionScript()->frameSize());
    233 }
    234 
    235 MachineState JSJitFrameIter::machineState() const {
    236  MOZ_ASSERT(isIonScripted());
    237 
    238  // The MachineState is used by GCs for tracing call-sites.
    239  if (MOZ_UNLIKELY(isBailoutJS())) {
    240    return *activation_->bailoutData()->machineState();
    241  }
    242 
    243  SafepointReader reader(ionScript(), safepoint());
    244 
    245  FloatRegisterSet fregs = reader.allFloatSpills().set().reduceSetForPush();
    246  GeneralRegisterSet regs = reader.allGprSpills().set();
    247 
    248  uintptr_t* spill = spillBase();
    249  uint8_t* spillAlign =
    250      alignDoubleSpill(reinterpret_cast<uint8_t*>(spill - regs.size()));
    251  char* floatSpill = reinterpret_cast<char*>(spillAlign);
    252 
    253  return MachineState::FromSafepoint(fregs, regs, floatSpill, spill);
    254 }
    255 
    256 JitFrameLayout* JSJitFrameIter::jsFrame() const {
    257  MOZ_ASSERT(isScripted());
    258  if (isBailoutJS()) {
    259    return (JitFrameLayout*)activation_->bailoutData()->fp();
    260  }
    261 
    262  return (JitFrameLayout*)fp();
    263 }
    264 
    265 IonScript* JSJitFrameIter::ionScript() const {
    266  MOZ_ASSERT(isIonScripted());
    267  if (isBailoutJS()) {
    268    return activation_->bailoutData()->ionScript();
    269  }
    270 
    271  IonScript* ionScript = nullptr;
    272  if (checkInvalidation(&ionScript)) {
    273    return ionScript;
    274  }
    275  return ionScriptFromCalleeToken();
    276 }
    277 
    278 IonScript* JSJitFrameIter::ionScriptFromCalleeToken() const {
    279  MOZ_ASSERT(isIonJS());
    280  MOZ_ASSERT(!checkInvalidation());
    281  return script()->ionScript();
    282 }
    283 
    284 const SafepointIndex* JSJitFrameIter::safepoint() const {
    285  MOZ_ASSERT(isIonJS());
    286  if (!cachedSafepointIndex_) {
    287    cachedSafepointIndex_ =
    288        ionScript()->getSafepointIndex(resumePCinCurrentFrame());
    289  }
    290  return cachedSafepointIndex_;
    291 }
    292 
    293 SnapshotOffset JSJitFrameIter::snapshotOffset() const {
    294  MOZ_ASSERT(isIonScripted());
    295  if (isBailoutJS()) {
    296    return activation_->bailoutData()->snapshotOffset();
    297  }
    298  return osiIndex()->snapshotOffset();
    299 }
    300 
    301 const OsiIndex* JSJitFrameIter::osiIndex() const {
    302  MOZ_ASSERT(isIonJS());
    303  SafepointReader reader(ionScript(), safepoint());
    304  return ionScript()->getOsiIndex(reader.osiReturnPointOffset());
    305 }
    306 
    307 bool JSJitFrameIter::isConstructing() const {
    308  return CalleeTokenIsConstructing(calleeToken());
    309 }
    310 
    311 unsigned JSJitFrameIter::numActualArgs() const {
    312  if (isScripted()) {
    313    return jsFrame()->numActualArgs();
    314  }
    315 
    316  MOZ_ASSERT(isExitFrameLayout<NativeExitFrameLayout>());
    317  return exitFrame()->as<NativeExitFrameLayout>()->argc();
    318 }
    319 
    320 void JSJitFrameIter::dumpBaseline() const {
    321  MOZ_ASSERT(isBaselineJS());
    322 
    323  fprintf(stderr, " JS Baseline frame\n");
    324  if (isFunctionFrame()) {
    325    fprintf(stderr, "  callee fun: ");
    326 #if defined(DEBUG) || defined(JS_JITSPEW)
    327    DumpObject(callee());
    328 #else
    329    fprintf(stderr, "?\n");
    330 #endif
    331  } else {
    332    fprintf(stderr, "  global frame, no callee\n");
    333  }
    334 
    335  fprintf(stderr, "  file %s line %u\n", script()->filename(),
    336          script()->lineno());
    337 
    338  JSContext* cx = TlsContext.get();
    339  RootedScript script(cx);
    340  jsbytecode* pc;
    341  baselineScriptAndPc(script.address(), &pc);
    342 
    343  fprintf(stderr, "  script = %p, pc = %p (offset %u)\n", (void*)script, pc,
    344          uint32_t(script->pcToOffset(pc)));
    345  fprintf(stderr, "  current op: %s\n", CodeName(JSOp(*pc)));
    346 
    347  fprintf(stderr, "  actual args: %u\n", numActualArgs());
    348 
    349  for (unsigned i = 0; i < baselineFrameNumValueSlots(); i++) {
    350    fprintf(stderr, "  slot %u: ", i);
    351 #if defined(DEBUG) || defined(JS_JITSPEW)
    352    Value* v = baselineFrame()->valueSlot(i);
    353    DumpValue(*v);
    354 #else
    355    fprintf(stderr, "?\n");
    356 #endif
    357  }
    358 }
    359 
    360 void JSJitFrameIter::dump() const {
    361  switch (type_) {
    362    case FrameType::CppToJSJit:
    363      fprintf(stderr, " Entry frame\n");
    364      fprintf(stderr, "  Caller frame ptr: %p\n", current()->callerFramePtr());
    365      break;
    366    case FrameType::BaselineJS:
    367      dumpBaseline();
    368      break;
    369    case FrameType::BaselineStub:
    370      fprintf(stderr, " Baseline stub frame\n");
    371      fprintf(stderr, "  Caller frame ptr: %p\n", current()->callerFramePtr());
    372      break;
    373    case FrameType::Bailout:
    374    case FrameType::IonJS: {
    375      InlineFrameIterator frames(TlsContext.get(), this);
    376      for (;;) {
    377        frames.dump();
    378        if (!frames.more()) {
    379          break;
    380        }
    381        ++frames;
    382      }
    383      break;
    384    }
    385    case FrameType::BaselineInterpreterEntry:
    386      fprintf(stderr, " Baseline Interpreter Entry frame\n");
    387      fprintf(stderr, "  Caller frame ptr: %p\n", current()->callerFramePtr());
    388      break;
    389    case FrameType::TrampolineNative:
    390      fprintf(stderr, " TrampolineNative frame\n");
    391      fprintf(stderr, "  Caller frame ptr: %p\n", current()->callerFramePtr());
    392      break;
    393    case FrameType::IonICCall:
    394      fprintf(stderr, " Ion IC call\n");
    395      fprintf(stderr, "  Caller frame ptr: %p\n", current()->callerFramePtr());
    396      break;
    397    case FrameType::WasmToJSJit:
    398      fprintf(stderr, " Fast wasm-to-JS entry frame\n");
    399      fprintf(stderr, "  Caller frame ptr: %p\n", current()->callerFramePtr());
    400      break;
    401    case FrameType::Exit:
    402      fprintf(stderr, " Exit frame\n");
    403      break;
    404  };
    405  fputc('\n', stderr);
    406 }
    407 
    408 JSJitProfilingFrameIterator::JSJitProfilingFrameIterator(JSContext* cx,
    409                                                         void* pc, void* sp) {
    410  // If no profilingActivation is live, initialize directly to
    411  // end-of-iteration state.
    412  if (!cx->profilingActivation()) {
    413    type_ = FrameType::CppToJSJit;
    414    fp_ = nullptr;
    415    resumePCinCurrentFrame_ = nullptr;
    416    return;
    417  }
    418 
    419  MOZ_ASSERT(cx->profilingActivation()->isJit());
    420 
    421  JitActivation* act = cx->profilingActivation()->asJit();
    422 
    423  // If the top JitActivation has a null lastProfilingFrame, assume that
    424  // it's a trivially empty activation, and initialize directly
    425  // to end-of-iteration state.
    426  if (!act->lastProfilingFrame()) {
    427    type_ = FrameType::CppToJSJit;
    428    fp_ = nullptr;
    429    resumePCinCurrentFrame_ = nullptr;
    430    return;
    431  }
    432 
    433  // Get the fp from the current profilingActivation
    434  fp_ = (uint8_t*)act->lastProfilingFrame();
    435 
    436  // Use fp_ as endStackAddress_. For cases below where we know we're currently
    437  // executing JIT code, we use the current stack pointer instead.
    438  endStackAddress_ = fp_;
    439 
    440  // Profiler sampling must NOT be suppressed if we are here.
    441  MOZ_ASSERT(cx->isProfilerSamplingEnabled());
    442 
    443  // Try initializing with sampler pc
    444  if (tryInitWithPC(pc)) {
    445    endStackAddress_ = sp;
    446    return;
    447  }
    448 
    449  if (!IsPortableBaselineInterpreterEnabled()) {
    450    // Try initializing with sampler pc using native=>bytecode table.
    451    JitcodeGlobalTable* table =
    452        cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
    453    if (tryInitWithTable(table, pc, /* forLastCallSite = */ false)) {
    454      endStackAddress_ = sp;
    455      return;
    456    }
    457 
    458    // Try initializing with lastProfilingCallSite pc
    459    void* lastCallSite = act->lastProfilingCallSite();
    460    if (lastCallSite) {
    461      if (tryInitWithPC(lastCallSite)) {
    462        return;
    463      }
    464 
    465      // Try initializing with lastProfilingCallSite pc using native=>bytecode
    466      // table.
    467      if (tryInitWithTable(table, lastCallSite, /* forLastCallSite = */ true)) {
    468        return;
    469      }
    470    }
    471  }
    472 
    473  // If nothing matches, for now just assume we are at the start of the last
    474  // frame's baseline jit code or interpreter code.
    475  type_ = FrameType::BaselineJS;
    476  if (frameScript()->hasBaselineScript()) {
    477    resumePCinCurrentFrame_ = frameScript()->baselineScript()->method()->raw();
    478  } else if (!IsPortableBaselineInterpreterEnabled()) {
    479    MOZ_ASSERT(IsBaselineInterpreterEnabled());
    480    resumePCinCurrentFrame_ =
    481        cx->runtime()->jitRuntime()->baselineInterpreter().codeRaw();
    482  } else {
    483    resumePCinCurrentFrame_ = nullptr;
    484  }
    485 }
    486 
    487 template <typename ReturnType = CommonFrameLayout*>
    488 static inline ReturnType GetPreviousRawFrame(CommonFrameLayout* frame) {
    489  return ReturnType(frame->callerFramePtr());
    490 }
    491 
    492 JSJitProfilingFrameIterator::JSJitProfilingFrameIterator(
    493    CommonFrameLayout* fp) {
    494  endStackAddress_ = fp;
    495  moveToNextFrame(fp);
    496 }
    497 
    498 bool JSJitProfilingFrameIterator::tryInitWithPC(void* pc) {
    499  JSScript* callee = frameScript();
    500 
    501  // Check for Ion first, since it's more likely for hot code.
    502  if (callee->hasIonScript() &&
    503      callee->ionScript()->method()->containsNativePC(pc)) {
    504    type_ = FrameType::IonJS;
    505    resumePCinCurrentFrame_ = pc;
    506    return true;
    507  }
    508 
    509  // Check for containment in Baseline jitcode second.
    510  if (callee->hasBaselineScript() &&
    511      callee->baselineScript()->method()->containsNativePC(pc)) {
    512    type_ = FrameType::BaselineJS;
    513    resumePCinCurrentFrame_ = pc;
    514    return true;
    515  }
    516 
    517  return false;
    518 }
    519 
    520 bool JSJitProfilingFrameIterator::tryInitWithTable(JitcodeGlobalTable* table,
    521                                                   void* pc,
    522                                                   bool forLastCallSite) {
    523  if (!pc) {
    524    return false;
    525  }
    526 
    527  const JitcodeGlobalEntry* entry = table->lookup(pc);
    528  if (!entry) {
    529    return false;
    530  }
    531 
    532  JSScript* callee = frameScript();
    533 
    534  MOZ_ASSERT(entry->isIon() || entry->isIonIC() || entry->isBaseline() ||
    535             entry->isBaselineInterpreter() || entry->isDummy() ||
    536             entry->isRealmIndependentShared());
    537 
    538  // Treat dummy lookups as an empty frame sequence.
    539  if (entry->isDummy()) {
    540    type_ = FrameType::CppToJSJit;
    541    fp_ = nullptr;
    542    resumePCinCurrentFrame_ = nullptr;
    543    return true;
    544  }
    545 
    546  // For IonICEntry, use the corresponding IonEntry.
    547  if (entry->isIonIC()) {
    548    entry = table->lookup(entry->asIonIC().rejoinAddr());
    549    MOZ_ASSERT(entry);
    550    MOZ_RELEASE_ASSERT(entry->isIon());
    551  }
    552 
    553  if (entry->isIon()) {
    554    // If looked-up callee doesn't match frame callee, don't accept
    555    // lastProfilingCallSite
    556    if (!entry->asIon().getScriptSource(0).matches(callee)) {
    557      return false;
    558    }
    559 
    560    type_ = FrameType::IonJS;
    561    resumePCinCurrentFrame_ = pc;
    562    return true;
    563  }
    564 
    565  if (entry->isBaseline()) {
    566    // If looked-up callee doesn't match frame callee, don't accept
    567    // lastProfilingCallSite
    568    if (forLastCallSite &&
    569        !entry->asBaseline().scriptSource().matches(callee)) {
    570      return false;
    571    }
    572 
    573    type_ = FrameType::BaselineJS;
    574    resumePCinCurrentFrame_ = pc;
    575    return true;
    576  }
    577 
    578  if (entry->isRealmIndependentShared()) {
    579    // Shared entries don't track who the callee is, so we can't check
    580    // lastProfilingCallSite
    581    type_ = FrameType::BaselineJS;
    582    resumePCinCurrentFrame_ = pc;
    583    return true;
    584  }
    585 
    586  if (entry->isBaselineInterpreter()) {
    587    type_ = FrameType::BaselineJS;
    588    resumePCinCurrentFrame_ = pc;
    589    return true;
    590  }
    591 
    592  return false;
    593 }
    594 
    595 const char* JSJitProfilingFrameIterator::baselineInterpreterLabel() const {
    596  MOZ_ASSERT(type_ == FrameType::BaselineJS);
    597  return frameScript()->jitScript()->profileString();
    598 }
    599 
    600 void JSJitProfilingFrameIterator::baselineInterpreterScriptPC(
    601    JSScript** script, jsbytecode** pc, uint64_t* realmID,
    602    uint32_t* sourceId) const {
    603  MOZ_ASSERT(type_ == FrameType::BaselineJS);
    604  BaselineFrame* blFrame = (BaselineFrame*)(fp_ - BaselineFrame::Size());
    605  *script = frameScript();
    606  *pc = (*script)->code();
    607 
    608  if (blFrame->runningInInterpreter() &&
    609      blFrame->interpreterScript() == *script) {
    610    jsbytecode* interpPC = blFrame->interpreterPC();
    611    if ((*script)->containsPC(interpPC)) {
    612      *pc = interpPC;
    613    }
    614 
    615    *realmID = (*script)->realm()->creationOptions().profilerRealmID();
    616    *sourceId = (*script)->scriptSource()->id();
    617  }
    618 }
    619 
    620 void JSJitProfilingFrameIterator::operator++() {
    621  JitFrameLayout* frame = framePtr();
    622  moveToNextFrame(frame);
    623 }
    624 
    625 void JSJitProfilingFrameIterator::moveToNextFrame(CommonFrameLayout* frame) {
    626  /*
    627   * fp_ points to a Baseline or Ion frame.  The possible call-stacks
    628   * patterns occurring between this frame and a previous Ion, Baseline or Entry
    629   * frame are as follows:
    630   *
    631   * <Baseline-Or-Ion>
    632   * ^
    633   * |
    634   * ^--- Ion (or Baseline JSOp::Resume)
    635   * |
    636   * ^--- Baseline Stub <---- Baseline
    637   * |
    638   * ^--- IonICCall <---- Ion
    639   * |
    640   * ^--- WasmToJSJit <---- (other wasm frames, not handled by this iterator)
    641   * |
    642   * ^--- Entry Frame (BaselineInterpreter) (unwrapped)
    643   * |
    644   * ^--- Trampoline Native (unwrapped)
    645   * |
    646   * ^--- Entry Frame (CppToJSJit)
    647   *
    648   * NOTE: Keep this in sync with JitRuntime::generateProfilerExitFrameTailStub!
    649   */
    650 
    651  while (true) {
    652    // Unwrap baseline interpreter entry frame.
    653    if (frame->prevType() == FrameType::BaselineInterpreterEntry) {
    654      frame = GetPreviousRawFrame<BaselineInterpreterEntryFrameLayout*>(frame);
    655      continue;
    656    }
    657 
    658    // Unwrap TrampolineNative frames.
    659    if (frame->prevType() == FrameType::TrampolineNative) {
    660      frame = GetPreviousRawFrame<TrampolineNativeFrameLayout*>(frame);
    661      MOZ_ASSERT(frame->prevType() == FrameType::IonJS ||
    662                 frame->prevType() == FrameType::BaselineStub ||
    663                 frame->prevType() == FrameType::WasmToJSJit ||
    664                 frame->prevType() == FrameType::CppToJSJit);
    665      continue;
    666    }
    667 
    668    break;
    669  }
    670 
    671  FrameType prevType = frame->prevType();
    672  switch (prevType) {
    673    case FrameType::IonJS:
    674    case FrameType::BaselineJS:
    675      resumePCinCurrentFrame_ = frame->returnAddress();
    676      fp_ = GetPreviousRawFrame<uint8_t*>(frame);
    677      type_ = prevType;
    678      return;
    679 
    680    case FrameType::BaselineStub:
    681    case FrameType::IonICCall: {
    682      FrameType stubPrevType = (prevType == FrameType::BaselineStub)
    683                                   ? FrameType::BaselineJS
    684                                   : FrameType::IonJS;
    685      auto* stubFrame = GetPreviousRawFrame<CommonFrameLayout*>(frame);
    686      MOZ_ASSERT(stubFrame->prevType() == stubPrevType);
    687      resumePCinCurrentFrame_ = stubFrame->returnAddress();
    688      fp_ = GetPreviousRawFrame<uint8_t*>(stubFrame);
    689      type_ = stubPrevType;
    690      return;
    691    }
    692 
    693    case FrameType::WasmToJSJit:
    694      // No previous JS JIT frame. Set fp_ to nullptr to indicate the
    695      // JSJitProfilingFrameIterator is done(). Also set wasmCallerFP_ so that
    696      // the caller can pass it to a Wasm frame iterator.
    697      resumePCinCurrentFrame_ = nullptr;
    698      fp_ = nullptr;
    699      type_ = FrameType::WasmToJSJit;
    700      MOZ_ASSERT(!wasmCallerFP_);
    701      wasmCallerFP_ = GetPreviousRawFrame<uint8_t*>(frame);
    702      MOZ_ASSERT(wasmCallerFP_);
    703      MOZ_ASSERT(done());
    704      return;
    705 
    706    case FrameType::CppToJSJit:
    707      // No previous JS JIT frame. Set fp_ to nullptr to indicate the
    708      // JSJitProfilingFrameIterator is done().
    709      resumePCinCurrentFrame_ = nullptr;
    710      fp_ = nullptr;
    711      type_ = FrameType::CppToJSJit;
    712      MOZ_ASSERT(!wasmCallerFP_);
    713      MOZ_ASSERT(done());
    714      return;
    715 
    716    case FrameType::BaselineInterpreterEntry:
    717    case FrameType::TrampolineNative:
    718    case FrameType::Exit:
    719    case FrameType::Bailout:
    720      // Baseline Interpreter entry frames are handled before this switch. The
    721      // other frame types can't call JS functions directly.
    722      break;
    723  }
    724 
    725  MOZ_CRASH("Bad frame type.");
    726 }