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 }