unioil-loyalty-rn-app/ios/Pods/Flipper-Folly/folly/tracing/AsyncStack.h

469 lines
20 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <atomic>
#include <cassert>
#include <cstdint>
#include <folly/CPortability.h>
#include <folly/CppAttributes.h>
#include <folly/Portability.h>
#include <folly/experimental/coro/Coroutine.h>
namespace folly {
// Gets the instruction pointer of the return-address of the current function.
//
// Generally a function that uses this macro should be declared FOLLY_NOINLINE
// to prevent this returning surprising results in cases where the function
// is inlined.
#if FOLLY_HAS_BUILTIN(__builtin_return_address)
#define FOLLY_ASYNC_STACK_RETURN_ADDRESS() __builtin_return_address(0)
#else
#define FOLLY_ASYNC_STACK_RETURN_ADDRESS() static_cast<void*>(nullptr)
#endif
// Gets pointer to the current function invocation's stack-frame.
//
// Generally a function that uses this macro should be declared FOLLY_NOINLINE
// to prevent this returning surprising results in cases where the function
// is inlined.
#if FOLLY_HAS_BUILTIN(__builtin_frame_address)
#define FOLLY_ASYNC_STACK_FRAME_POINTER() __builtin_frame_address(0)
#else
#define FOLLY_ASYNC_STACK_FRAME_POINTER() static_cast<void*>(nullptr)
#endif
// This header defines data-structures used to represent an async stack-trace.
//
// These data-structures are intended for use by coroutines (and possibly other
// representations of async operations) to allow the current program to record
// an async-stack as a linked-list of async-stack-frames in a similar way to
// how a normal thread represents the stack as a linked-list of stack frames.
//
// From a high-level, just looking at the AsyncStackRoot/AsyncStackFrame
// data-structures, each thread maintains a linked-list of active AsyncStack
// chains that looks a bit like this.
//
// Stack Register
// |
// V
// Stack Frame currentStackRoot (TLS)
// | |
// V V
// Stack Frame <- AsyncStackRoot -> AsyncStackFrame -> AsyncStackFrame -> ...
// | |
// V |
// Stack Frame |
// : |
// V V
// Stack Frame <- AsyncStackRoot -> AsyncStackFrame -> AsyncStackFrame -> ...
// | |
// V X
// Stack Frame
// :
// V
//
// Whenever a thread enters an event loop or is about to execute some
// asynchronus callback/continuation the current thread registers an
// AsyncStackRoot and records the stack-frame of the normal thread
// stack that corresponds to that call so that each AsyncStackRoot
// can be later interleaved with a normal thread stack-trace at
// the appropriate location.
//
// Each AsyncStackRoot contains a pointer to the currently active
// AsyncStackFrame (if any). This AsyncStackFrame forms the head
// of a linked-list of AsyncStackFrame objects that represent the
// async stack-trace. Each non-head AsyncStackFrame is a suspended
// asynchronous operation, which is typically suspended waiting for
// the previous operation to complete.
//
//
// The following diagram shows in more detail how each of the fields
// in these data-structures relate to each other and how the
// async-stack interleaves with the normal thread-stack.
//
// Current Thread Stack
// ====================
// +------------------------------------+ <--- current top of stack
// | Normal Stack Frame |
// | - stack-base-pointer ---. |
// | - return-address | | Thread Local Storage
// | | | ====================
// +--------------------------V---------+
// | ... | +-------------------------+
// | : | | - currentStackRoot -. |
// | : | | | |
// +--------------------------V---------+ +----------------------|--+
// | Normal Stack Frame | |
// | - stack-base-pointer ---. | |
// | - return-address | .-------------------------------`
// | | | |
// +--------------------------V------|--+
// | Active Async Operation | |
// | (Callback or Coroutine) | | Heap Allocated
// | - stack-base-pointer ---. | | ==============
// | - return-address | | |
// | - pointer to async state | --------------> +-------------------------+
// | (e.g. coro frame or | | | | Coroutine Frame |
// | future core) | | | | +---------------------+ |
// | | | | | | Promise | |
// +--------------------------V------|--+ | | +-----------------+ | |
// | Event / Callback | | .------>| AsyncStackFrame | | |
// | Loop Callsite | | | | | | - parentFrame --------.
// | - stack-base-pointer ---. | | | | | | - instructionPtr| | | |
// | - return-address | | | | | | | - stackRoot -. | | | |
// | | | | | | | +--------------|--+ | | |
// | +--------------------+ | | | | | | ... | | | |
// | | AsyncStackRoot |<--------` | | | +----------------|----+ | |
// | | - topFrame -----------------------` | ... | | |
// | | - stackFramePtr -. |<---------------, +------------------|------+ |
// | | - nextRoot --. | | | | | | |
// | +--------------|---|-+ | | '----------------------` |
// +-----------------|---V----V---------+ +-------------------------+ |
// | ... | | | Coroutine Frame | |
// | | : | | | |
// | | : | | +-------------------+ | |
// +-----------------|--------V---------+ | | AsyncStackFrame |<----`
// | Async Operation | | | | - parentFrame --------.
// | (Callback/Coro) | | | | - instructionPtr | | |
// | | : | | | - stackRoot | | |
// | | : | | +-------------------+ | |
// +-----------------|--------V---------+ +-------------------------+ |
// | Event Loop / | | :
// | Callback Call | | :
// | - frame-pointer | -------. | V
// | - return-address| | |
// | | | | Another chain of potentially
// | +--------------V-----+ | | unrelated AsyncStackFrame
// | | AsyncStackRoot | | | +---------------------+
// | | - topFrame ---------------- - - - - > | AsyncStackFrame |
// | | - stackFramePtr -. | | | | - parentFrame -. |
// | | - nextRoot -. | | | | +----------------|----+
// | +-------------|----|-+ | | :
// | | | | | V
// +----------------|----V----V---------+
// | ... : |
// | V |
// | |
// +------------------------------------+
//
//
// This data-structure can be inspected from within the current process
// if desired, but is also intended to allow tools such as debuggers or
// profilers that are inspecting the memory of this process remotely.
struct AsyncStackRoot;
struct AsyncStackFrame;
namespace detail {
class ScopedAsyncStackRoot;
}
// Get access to the current thread's top-most AsyncStackRoot.
//
// Returns nullptr if there is no active AsyncStackRoot.
FOLLY_NODISCARD AsyncStackRoot* tryGetCurrentAsyncStackRoot() noexcept;
// Get access to the current thread's top-most AsyncStackRoot.
//
// Assumes that there is a current AsyncStackRoot.
FOLLY_NODISCARD AsyncStackRoot& getCurrentAsyncStackRoot() noexcept;
// Exchange the current thread's active AsyncStackRoot with the
// specified AsyncStackRoot pointer, returning the old AsyncStackRoot
// pointer.
//
// This is intended to be used to update the thread-local pointer
// when context-switching fiber stacks.
FOLLY_NODISCARD AsyncStackRoot* exchangeCurrentAsyncStackRoot(
AsyncStackRoot* newRoot) noexcept;
// Perform some consistency checks on the specified AsyncStackFrame,
// assuming that it is the currently active AsyncStackFrame.
void checkAsyncStackFrameIsActive(const folly::AsyncStackFrame& frame) noexcept;
// Activate the specified AsyncStackFrame on the specified AsyncStackRoot,
// setting it as the current 'topFrame'.
//
// The AsyncStackRoot must be the current thread's top-most AsyncStackRoot
// and it must not currently have an active 'topFrame'.
//
// This is typically called immediately prior to executing a callback that
// resumes the async operation represented by 'frame'.
void activateAsyncStackFrame(
folly::AsyncStackRoot& root, folly::AsyncStackFrame& frame) noexcept;
// Deactivate the specified AsyncStackFrame, clearing the current 'topFrame'.
//
// Typically called when the current async operation completes or is suspended
// and execution is about to return from the callback to the executor's event
// loop.
void deactivateAsyncStackFrame(folly::AsyncStackFrame& frame) noexcept;
// Push the 'callee' frame onto the current thread's async stack, deactivating
// the 'caller' frame and setting up the 'caller' to be the parent-frame of
// the 'callee'.
//
// The 'caller' frame must be the current thread's active frame.
//
// After this call, the 'callee' frame will be the current thread's active
// frame.
//
// This is typically used when one async operation is about to transfer
// execution to a child async operation. e.g. via a coroutine symmetric
// transfer.
void pushAsyncStackFrameCallerCallee(
folly::AsyncStackFrame& callerFrame,
folly::AsyncStackFrame& calleeFrame) noexcept;
// Pop the 'callee' frame off the stack, restoring the parent frame as the
// current frame.
//
// This is typically used when the current async operation completes and
// you are about to call/resume the caller. e.g. performing a symmetric
// transfer to the calling coroutine in final_suspend().
//
// If calleeFrame.getParentFrame() is null then this method is equivalent
// to deactivateAsyncStackFrame(), leaving no active AsyncStackFrame on
// the current AsyncStackRoot.
void popAsyncStackFrameCallee(folly::AsyncStackFrame& calleeFrame) noexcept;
// Get a pointer to a special frame that can be used as the root-frame
// for a chain of AsyncStackFrame that does not chain onto a normal
// call-stack.
//
// The caller should never modify this frame as it will be shared across
// many frames and threads. The implication of this restriction is that
// you should also never activate this frame.
AsyncStackFrame& getDetachedRootAsyncStackFrame() noexcept;
// Given an initial AsyncStackFrame, this will write `addresses` with
// the return addresses of the frames in this async stack trace, up to
// `maxAddresses` written.
// This assumes `addresses` has `maxAddresses` allocated space available.
// Returns the number of frames written.
size_t getAsyncStackTraceFromInitialFrame(
folly::AsyncStackFrame* initialFrame,
std::uintptr_t* addresses,
size_t maxAddresses);
#if FOLLY_HAS_COROUTINES
// Resume the specified coroutine after installing a new AsyncStackRoot
// on the current thread and setting the specified AsyncStackFrame as
// the current async frame.
FOLLY_NOINLINE void resumeCoroutineWithNewAsyncStackRoot(
coro::coroutine_handle<> h, AsyncStackFrame& frame) noexcept;
// Resume the specified coroutine after installing a new AsyncStackRoot
// on the current thread and setting the coroutine's associated
// AsyncStackFrame, obtained by calling promise.getAsyncFrame(), as the
// current async frame.
template <typename Promise>
void resumeCoroutineWithNewAsyncStackRoot(
coro::coroutine_handle<Promise> h) noexcept;
#endif // FOLLY_HAS_COROUTINES
// An async stack frame contains information about a particular
// invocation of an asynchronous operation.
//
// For example, asynchronous operations implemented using coroutines
// would have each coroutine-frame contain an instance of AsyncStackFrame
// to record async-stack trace information for that coroutine invocation.
struct AsyncStackFrame {
public:
AsyncStackFrame() = default;
// The parent frame is the frame of the async operation that is logically
// the caller of this frame.
AsyncStackFrame* getParentFrame() noexcept;
const AsyncStackFrame* getParentFrame() const noexcept;
void setParentFrame(AsyncStackFrame& frame) noexcept;
// Get access to the current stack-root.
//
// This is only valid for either the root or leaf AsyncStackFrame
// in a chain of frames.
//
// In the case of an active leaf-frame it is used as a cache to
// avoid accessing the thread-local when pushing/popping frames.
// In the case of the root frame (which has a null parent frame)
// it points to an AsyncStackRoot that contains information about
// the normal-stack caller.
AsyncStackRoot* getStackRoot() noexcept;
// The return address is generallty the address of the code in the
// caller that will be executed when the operation owning the current
// frame completes.
void setReturnAddress(void* p = FOLLY_ASYNC_STACK_RETURN_ADDRESS()) noexcept;
void* getReturnAddress() const noexcept;
private:
friend AsyncStackRoot;
friend AsyncStackFrame& getDetachedRootAsyncStackFrame() noexcept;
friend void activateAsyncStackFrame(
folly::AsyncStackRoot&, folly::AsyncStackFrame&) noexcept;
friend void deactivateAsyncStackFrame(folly::AsyncStackFrame&) noexcept;
friend void pushAsyncStackFrameCallerCallee(
folly::AsyncStackFrame&, folly::AsyncStackFrame&) noexcept;
friend void checkAsyncStackFrameIsActive(
const folly::AsyncStackFrame&) noexcept;
friend void popAsyncStackFrameCallee(folly::AsyncStackFrame&) noexcept;
// Pointer to the async caller's stack-frame info.
//
// This forms a linked-list of frames that make up a stack.
// The list is terminated by a null pointer which indicates
// the top of the async stack - either because the operation
// is detached or because the next frame is a thread that is
// blocked waiting for the async stack to complete.
AsyncStackFrame* parentFrame = nullptr;
// Instruction pointer of the caller of this frame.
// This will typically be either the address of the continuation
// of this asynchronous operation, or the address of the code
// that launched this asynchronous operation. May be null
// if the address is not known.
//
// Typically initialised with the result of a call to
// FOLLY_ASYNC_STACK_RETURN_ADDRESS().
void* instructionPointer = nullptr;
// Pointer to the stack-root for the current thread.
// Cache this in each async-stack frame so we don't have to
// read from a thread-local to get the pointer.
//
// This pointer is only valid for the top-most stack frame.
// When a frame is pushed or popped it should be copied to
// the next frame, etc.
//
// The exception is for the bottom-most frame (ie. where
// parentFrame == null). In this case, if stackRoot is non-null
// then it points to a root that is currently blocked on some
// thread waiting for the async work to complete. In this case
// you can find the information about the stack-frame for that
// thread in the AsyncStackRoot and can use it to continue
// walking the stack-frames.
AsyncStackRoot* stackRoot = nullptr;
};
// A stack-root represents the context of an event loop
// that is running some asynchronous work. The current async
// operation that is being executed by the event loop (if any)
// is pointed to by the 'topFrame'.
//
// If the current event loop is running nested inside some other
// event loop context then the 'nextRoot' points to the AsyncStackRoot
// context for the next event loop up the stack on the current thread.
//
// The 'stackFramePtr' holds a pointer to the normal stack-frame
// that is currently executing this event loop. This allows
// reconciliation of the parts between a normal stack-trace and
// the start of the async-stack trace.
//
// The current thread's top-most context (the head of the linked
// list of contexts) is obtained by calling getCurrentAsyncStackRoot().
struct AsyncStackRoot {
public:
// Sets the top-frame to be 'frame' and also updates the cached
// 'frame.stackRoot' to be 'this'.
//
// The current stack root must not currently have any active
// frame.
void setTopFrame(AsyncStackFrame& frame) noexcept;
AsyncStackFrame* getTopFrame() const noexcept;
// Initialises this stack root with information about the context
// in which the stack-root was declared. This records information
// about where the async-stack-trace should be spliced into the
// normal stack-trace.
void setStackFrameContext(
void* framePtr = FOLLY_ASYNC_STACK_FRAME_POINTER(),
void* ip = FOLLY_ASYNC_STACK_RETURN_ADDRESS()) noexcept;
void* getStackFramePointer() const noexcept;
void* getReturnAddress() const noexcept;
const AsyncStackRoot* getNextRoot() const noexcept;
void setNextRoot(AsyncStackRoot* next) noexcept;
private:
friend class detail::ScopedAsyncStackRoot;
friend void activateAsyncStackFrame(
folly::AsyncStackRoot&, folly::AsyncStackFrame&) noexcept;
friend void deactivateAsyncStackFrame(folly::AsyncStackFrame&) noexcept;
friend void pushAsyncStackFrameCallerCallee(
folly::AsyncStackFrame&, folly::AsyncStackFrame&) noexcept;
friend void checkAsyncStackFrameIsActive(
const folly::AsyncStackFrame&) noexcept;
friend void popAsyncStackFrameCallee(folly::AsyncStackFrame&) noexcept;
// Pointer to the currently-active AsyncStackFrame for this event
// loop or callback invocation. May be null if this event loop is
// not currently executing any async operations.
//
// This is atomic primarily to enforce visibility of writes to the
// AsyncStackFrame that occur before the topFrame in other processes,
// such as profilers/debuggers that may be running concurrently
// with the current thread.
std::atomic<AsyncStackFrame*> topFrame{nullptr};
// Pointer to the next event loop context lower on the current
// thread's stack.
// This is nullptr if this is not a nested call to an event loop.
AsyncStackRoot* nextRoot = nullptr;
// Pointer to the stack-frame and return-address of the function
// call that registered this AsyncStackRoot on the current thread.
// This is generally the stack-frame responsible for executing async
// callbacks (typically an event-loop).
// Anything prior to this frame on the stack in the current thread
// is potentially unrelated to the call-chain of the current async-stack.
//
// Typically initialised with FOLLY_ASYNC_STACK_FRAME_POINTER() or
// setStackFrameContext().
void* stackFramePtr = nullptr;
// Typically initialise with FOLLY_ASYNC_STACK_RETURN_ADDRESS() or
// setStackFrameContext().
void* returnAddress = nullptr;
};
namespace detail {
class ScopedAsyncStackRoot {
public:
explicit ScopedAsyncStackRoot(
void* framePointer = FOLLY_ASYNC_STACK_FRAME_POINTER(),
void* returnAddress = FOLLY_ASYNC_STACK_RETURN_ADDRESS()) noexcept;
~ScopedAsyncStackRoot();
void activateFrame(AsyncStackFrame& frame) noexcept {
folly::activateAsyncStackFrame(root_, frame);
}
private:
AsyncStackRoot root_;
};
} // namespace detail
} // namespace folly
#include <folly/tracing/AsyncStack-inl.h>