Skip to content

Instantly share code, notes, and snippets.

@kennyyu
Last active April 29, 2021 01:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kennyyu/f91b49a5ce8f92cb8aaf1973e641a375 to your computer and use it in GitHub Desktop.
Save kennyyu/f91b49a5ce8f92cb8aaf1973e641a375 to your computer and use it in GitHub Desktop.
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