void
AsmJSProfilingFrameIterator::initFromFP(const AsmJSActivation &activation)
{
    uint8_t *fp = activation.fp();

    // If a signal was handled while entering an activation, the frame will
    // still be null.
    if (!fp) {
        JS_ASSERT(done());
        return;
    }

    // Since we don't have the pc for fp, start unwinding at the caller of fp,
    // whose pc we do have via fp->returnAddress. This means that the innermost
    // frame is skipped but this is fine because:
    //  - for FFI calls, the innermost frame is a thunk, so the first frame that
    //    shows up is the function calling the FFI;
    //  - for Math and other builtin calls, when profiling is activated, we
    //    patch all call sites to instead call through a thunk; and
    //  - for interrupts, we just accept that we'll lose the innermost frame.
    void *pc = ReturnAddressFromFP(fp);
    const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(pc);
    JS_ASSERT(codeRange);
    codeRange_ = codeRange;
    stackAddress_ = fp;

    switch (codeRange->kind()) {
      case AsmJSModule::CodeRange::Entry:
        callerPC_ = nullptr;
        callerFP_ = nullptr;
        break;
      case AsmJSModule::CodeRange::Function:
        fp = CallerFPFromFP(fp);
        callerPC_ = ReturnAddressFromFP(fp);
        callerFP_ = CallerFPFromFP(fp);
        AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, fp);
        break;
      case AsmJSModule::CodeRange::IonFFI:
      case AsmJSModule::CodeRange::SlowFFI:
      case AsmJSModule::CodeRange::Interrupt:
      case AsmJSModule::CodeRange::Inline:
      case AsmJSModule::CodeRange::Thunk:
        MOZ_CRASH("Unexpected CodeRange kind");
    }

    // Since, despite the above reasoning for skipping a frame, we do want FFI
    // trampolines and interrupts to show up in the profile (so they can
    // accumulate self time and explain performance faults), an "exit reason" is
    // stored on all the paths leaving asm.js and the iterator logic treats this
    // reason as its own frame. If we have exited asm.js code without setting an
    // exit reason, the reason will be None and this means the code was
    // asynchronously interrupted.
    exitReason_ = activation.exitReason();
    if (exitReason_ == AsmJSExit::None)
        exitReason_ = AsmJSExit::Interrupt;

    JS_ASSERT(!done());
}