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;
}