ssize_t getAsyncStackTraceSafe(uintptr_t* addresses, size_t maxAddresses) { size_t numFrames = 0; const auto* asyncStackRoot = tryGetCurrentAsyncStackRoot(); if (asyncStackRoot == nullptr) { // No async operation in progress return numFrames; } // Start by walking the normal stack until we get to the frame right before // the frame that holds the async root. auto* normalStackFrame = (StackFrame*)FOLLY_ASYNC_STACK_FRAME_POINTER(); auto* normalStackFrameStop = (StackFrame*)asyncStackRoot->getStackFramePointer(); AsyncStackFrame* asyncStackFrame = nullptr; bool isAsync = false; while (numFrames < maxAddresses) { if (isAsync) { // Currently walking async stack if (asyncStackFrame == nullptr) { break; } auto* asyncStackFrameNext = asyncStackFrame->getParentFrame(); addresses[numFrames++] = reinterpret_cast<std::uintptr_t>(asyncStackFrame->getReturnAddress()); if (asyncStackFrameNext == nullptr) { // Reached end of async-stack. // Check if there is an AsyncStackRoot and if so, whether there // is an associated stack frame ptr that indicates the normal stack // frame we should continue walking at. asyncStackRoot = asyncStackFrame->getStackRoot(); if (asyncStackRoot == nullptr) { // This is a detached async stack. We are done break; } // Get the normal stack-frame pointer for this async-root normalStackFrame = (StackFrame*)asyncStackRoot->getStackFramePointer(); if (normalStackFrame == nullptr) { // No associated normal stack frame for this async stack root. // This means we should treat this as a top-level/detached // stack and not try to walk any further. break; } // Skip to the parent stack-frame pointer normalStackFrame = normalStackFrame->parentFrame; // Now check if there is a higher-level AsyncStackRoot that defines // the stop point we should stop walking stack frames at. // If there is no higher stack root then we will walk to the // top of the normal stack (indicated by // normalStackFrameStop == nullptr). // Otherwise we record the frame pointer that we should stop // at and walk stack frames until we hit that frame. asyncStackRoot = asyncStackRoot->getNextRoot(); if (asyncStackRoot != nullptr) { normalStackFrameStop = (StackFrame*)asyncStackRoot->getStackFramePointer(); } else { normalStackFrameStop = nullptr; } isAsync = false; } asyncStackFrame = asyncStackFrameNext; } else { // Currently walking normal-stack if (normalStackFrame == nullptr) { break; } // We want to make sure we don't include the return-address in the // stack trace that points to the frame that registered the // AsyncStackRoot if we are to stop at the AsyncStackRoot. auto* normalStackFrameNext = normalStackFrame->parentFrame; if (normalStackFrameStop != nullptr && normalStackFrameNext == normalStackFrameStop) { // Reached end of normal stack, need to transition to the async stack asyncStackFrame = asyncStackRoot->getTopFrame(); if (asyncStackFrame == nullptr) { break; } isAsync = true; // When normalStackFrameNext is the same as normalStackFrameStop // (aka. asyncRoot->stackFramePtr) then we know that we should use the // instructionPointer from the AsyncStackFrame as the current frame's // return-address rather than the return-address from the normal // stack-frame, which would be the address of the executor function that // invoked the callback. addresses[numFrames++] = reinterpret_cast<std::uintptr_t>( asyncStackFrame->getReturnAddress()); asyncStackFrame = asyncStackFrame->getParentFrame(); } else { addresses[numFrames++] = reinterpret_cast<std::uintptr_t>(normalStackFrame->returnAddress); } normalStackFrame = normalStackFrameNext; } } return numFrames; }