Last active
April 29, 2021 01:02
-
-
Save kennyyu/f91b49a5ce8f92cb8aaf1973e641a375 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment