1 /* 2 * Copyright (c) Facebook, Inc. and its affiliates. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <atomic> 20 #include <cassert> 21 #include <cstdint> 22 23 #include <folly/CPortability.h> 24 #include <folly/CppAttributes.h> 25 #include <folly/Portability.h> 26 #include <folly/experimental/coro/Coroutine.h> 27 28 namespace folly { 29 30 // Gets the instruction pointer of the return-address of the current function. 31 // 32 // Generally a function that uses this macro should be declared FOLLY_NOINLINE 33 // to prevent this returning surprising results in cases where the function 34 // is inlined. 35 #if FOLLY_HAS_BUILTIN(__builtin_return_address) 36 #define FOLLY_ASYNC_STACK_RETURN_ADDRESS() __builtin_return_address(0) 37 #else 38 #define FOLLY_ASYNC_STACK_RETURN_ADDRESS() static_cast<void*>(nullptr) 39 #endif 40 41 // Gets pointer to the current function invocation's stack-frame. 42 // 43 // Generally a function that uses this macro should be declared FOLLY_NOINLINE 44 // to prevent this returning surprising results in cases where the function 45 // is inlined. 46 #if FOLLY_HAS_BUILTIN(__builtin_frame_address) 47 #define FOLLY_ASYNC_STACK_FRAME_POINTER() __builtin_frame_address(0) 48 #else 49 #define FOLLY_ASYNC_STACK_FRAME_POINTER() static_cast<void*>(nullptr) 50 #endif 51 52 // This header defines data-structures used to represent an async stack-trace. 53 // 54 // These data-structures are intended for use by coroutines (and possibly other 55 // representations of async operations) to allow the current program to record 56 // an async-stack as a linked-list of async-stack-frames in a similar way to 57 // how a normal thread represents the stack as a linked-list of stack frames. 58 // 59 // From a high-level, just looking at the AsyncStackRoot/AsyncStackFrame 60 // data-structures, each thread maintains a linked-list of active AsyncStack 61 // chains that looks a bit like this. 62 // 63 // Stack Register 64 // | 65 // V 66 // Stack Frame currentStackRoot (TLS) 67 // | | 68 // V V 69 // Stack Frame <- AsyncStackRoot -> AsyncStackFrame -> AsyncStackFrame -> ... 70 // | | 71 // V | 72 // Stack Frame | 73 // : | 74 // V V 75 // Stack Frame <- AsyncStackRoot -> AsyncStackFrame -> AsyncStackFrame -> ... 76 // | | 77 // V X 78 // Stack Frame 79 // : 80 // V 81 // 82 // Whenever a thread enters an event loop or is about to execute some 83 // asynchronus callback/continuation the current thread registers an 84 // AsyncStackRoot and records the stack-frame of the normal thread 85 // stack that corresponds to that call so that each AsyncStackRoot 86 // can be later interleaved with a normal thread stack-trace at 87 // the appropriate location. 88 // 89 // Each AsyncStackRoot contains a pointer to the currently active 90 // AsyncStackFrame (if any). This AsyncStackFrame forms the head 91 // of a linked-list of AsyncStackFrame objects that represent the 92 // async stack-trace. Each non-head AsyncStackFrame is a suspended 93 // asynchronous operation, which is typically suspended waiting for 94 // the previous operation to complete. 95 // 96 // 97 // The following diagram shows in more detail how each of the fields 98 // in these data-structures relate to each other and how the 99 // async-stack interleaves with the normal thread-stack. 100 // 101 // Current Thread Stack 102 // ==================== 103 // +------------------------------------+ <--- current top of stack 104 // | Normal Stack Frame | 105 // | - stack-base-pointer ---. | 106 // | - return-address | | Thread Local Storage 107 // | | | ==================== 108 // +--------------------------V---------+ 109 // | ... | +-------------------------+ 110 // | : | | - currentStackRoot -. | 111 // | : | | | | 112 // +--------------------------V---------+ +----------------------|--+ 113 // | Normal Stack Frame | | 114 // | - stack-base-pointer ---. | | 115 // | - return-address | .-------------------------------` 116 // | | | | 117 // +--------------------------V------|--+ 118 // | Active Async Operation | | 119 // | (Callback or Coroutine) | | Heap Allocated 120 // | - stack-base-pointer ---. | | ============== 121 // | - return-address | | | 122 // | - pointer to async state | --------------> +-------------------------+ 123 // | (e.g. coro frame or | | | | Coroutine Frame | 124 // | future core) | | | | +---------------------+ | 125 // | | | | | | Promise | | 126 // +--------------------------V------|--+ | | +-----------------+ | | 127 // | Event / Callback | | .------>| AsyncStackFrame | | | 128 // | Loop Callsite | | | | | | - parentFrame --------. 129 // | - stack-base-pointer ---. | | | | | | - instructionPtr| | | | 130 // | - return-address | | | | | | | - stackRoot -. | | | | 131 // | | | | | | | +--------------|--+ | | | 132 // | +--------------------+ | | | | | | ... | | | | 133 // | | AsyncStackRoot |<--------` | | | +----------------|----+ | | 134 // | | - topFrame -----------------------` | ... | | | 135 // | | - stackFramePtr -. |<---------------, +------------------|------+ | 136 // | | - nextRoot --. | | | | | | | 137 // | +--------------|---|-+ | | '----------------------` | 138 // +-----------------|---V----V---------+ +-------------------------+ | 139 // | ... | | | Coroutine Frame | | 140 // | | : | | | | 141 // | | : | | +-------------------+ | | 142 // +-----------------|--------V---------+ | | AsyncStackFrame |<----` 143 // | Async Operation | | | | - parentFrame --------. 144 // | (Callback/Coro) | | | | - instructionPtr | | | 145 // | | : | | | - stackRoot | | | 146 // | | : | | +-------------------+ | | 147 // +-----------------|--------V---------+ +-------------------------+ | 148 // | Event Loop / | | : 149 // | Callback Call | | : 150 // | - frame-pointer | -------. | V 151 // | - return-address| | | 152 // | | | | Another chain of potentially 153 // | +--------------V-----+ | | unrelated AsyncStackFrame 154 // | | AsyncStackRoot | | | +---------------------+ 155 // | | - topFrame ---------------- - - - - > | AsyncStackFrame | 156 // | | - stackFramePtr -. | | | | - parentFrame -. | 157 // | | - nextRoot -. | | | | +----------------|----+ 158 // | +-------------|----|-+ | | : 159 // | | | | | V 160 // +----------------|----V----V---------+ 161 // | ... : | 162 // | V | 163 // | | 164 // +------------------------------------+ 165 // 166 // 167 // This data-structure can be inspected from within the current process 168 // if desired, but is also intended to allow tools such as debuggers or 169 // profilers that are inspecting the memory of this process remotely. 170 171 struct AsyncStackRoot; 172 struct AsyncStackFrame; 173 namespace detail { 174 class ScopedAsyncStackRoot; 175 } 176 177 // Get access to the current thread's top-most AsyncStackRoot. 178 // 179 // Returns nullptr if there is no active AsyncStackRoot. 180 FOLLY_NODISCARD AsyncStackRoot* tryGetCurrentAsyncStackRoot() noexcept; 181 182 // Get access to the current thread's top-most AsyncStackRoot. 183 // 184 // Assumes that there is a current AsyncStackRoot. 185 FOLLY_NODISCARD AsyncStackRoot& getCurrentAsyncStackRoot() noexcept; 186 187 // Exchange the current thread's active AsyncStackRoot with the 188 // specified AsyncStackRoot pointer, returning the old AsyncStackRoot 189 // pointer. 190 // 191 // This is intended to be used to update the thread-local pointer 192 // when context-switching fiber stacks. 193 FOLLY_NODISCARD AsyncStackRoot* exchangeCurrentAsyncStackRoot( 194 AsyncStackRoot* newRoot) noexcept; 195 196 // Perform some consistency checks on the specified AsyncStackFrame, 197 // assuming that it is the currently active AsyncStackFrame. 198 void checkAsyncStackFrameIsActive(const folly::AsyncStackFrame& frame) noexcept; 199 200 // Activate the specified AsyncStackFrame on the specified AsyncStackRoot, 201 // setting it as the current 'topFrame'. 202 // 203 // The AsyncStackRoot must be the current thread's top-most AsyncStackRoot 204 // and it must not currently have an active 'topFrame'. 205 // 206 // This is typically called immediately prior to executing a callback that 207 // resumes the async operation represented by 'frame'. 208 void activateAsyncStackFrame( 209 folly::AsyncStackRoot& root, folly::AsyncStackFrame& frame) noexcept; 210 211 // Deactivate the specified AsyncStackFrame, clearing the current 'topFrame'. 212 // 213 // Typically called when the current async operation completes or is suspended 214 // and execution is about to return from the callback to the executor's event 215 // loop. 216 void deactivateAsyncStackFrame(folly::AsyncStackFrame& frame) noexcept; 217 218 // Push the 'callee' frame onto the current thread's async stack, deactivating 219 // the 'caller' frame and setting up the 'caller' to be the parent-frame of 220 // the 'callee'. 221 // 222 // The 'caller' frame must be the current thread's active frame. 223 // 224 // After this call, the 'callee' frame will be the current thread's active 225 // frame. 226 // 227 // This is typically used when one async operation is about to transfer 228 // execution to a child async operation. e.g. via a coroutine symmetric 229 // transfer. 230 void pushAsyncStackFrameCallerCallee( 231 folly::AsyncStackFrame& callerFrame, 232 folly::AsyncStackFrame& calleeFrame) noexcept; 233 234 // Pop the 'callee' frame off the stack, restoring the parent frame as the 235 // current frame. 236 // 237 // This is typically used when the current async operation completes and 238 // you are about to call/resume the caller. e.g. performing a symmetric 239 // transfer to the calling coroutine in final_suspend(). 240 // 241 // If calleeFrame.getParentFrame() is null then this method is equivalent 242 // to deactivateAsyncStackFrame(), leaving no active AsyncStackFrame on 243 // the current AsyncStackRoot. 244 void popAsyncStackFrameCallee(folly::AsyncStackFrame& calleeFrame) noexcept; 245 246 // Get a pointer to a special frame that can be used as the root-frame 247 // for a chain of AsyncStackFrame that does not chain onto a normal 248 // call-stack. 249 // 250 // The caller should never modify this frame as it will be shared across 251 // many frames and threads. The implication of this restriction is that 252 // you should also never activate this frame. 253 AsyncStackFrame& getDetachedRootAsyncStackFrame() noexcept; 254 255 // Given an initial AsyncStackFrame, this will write `addresses` with 256 // the return addresses of the frames in this async stack trace, up to 257 // `maxAddresses` written. 258 // This assumes `addresses` has `maxAddresses` allocated space available. 259 // Returns the number of frames written. 260 size_t getAsyncStackTraceFromInitialFrame( 261 folly::AsyncStackFrame* initialFrame, 262 std::uintptr_t* addresses, 263 size_t maxAddresses); 264 265 #if FOLLY_HAS_COROUTINES 266 267 // Resume the specified coroutine after installing a new AsyncStackRoot 268 // on the current thread and setting the specified AsyncStackFrame as 269 // the current async frame. 270 FOLLY_NOINLINE void resumeCoroutineWithNewAsyncStackRoot( 271 coro::coroutine_handle<> h, AsyncStackFrame& frame) noexcept; 272 273 // Resume the specified coroutine after installing a new AsyncStackRoot 274 // on the current thread and setting the coroutine's associated 275 // AsyncStackFrame, obtained by calling promise.getAsyncFrame(), as the 276 // current async frame. 277 template <typename Promise> 278 void resumeCoroutineWithNewAsyncStackRoot( 279 coro::coroutine_handle<Promise> h) noexcept; 280 281 #endif // FOLLY_HAS_COROUTINES 282 283 // An async stack frame contains information about a particular 284 // invocation of an asynchronous operation. 285 // 286 // For example, asynchronous operations implemented using coroutines 287 // would have each coroutine-frame contain an instance of AsyncStackFrame 288 // to record async-stack trace information for that coroutine invocation. 289 struct AsyncStackFrame { 290 public: 291 AsyncStackFrame() = default; 292 293 // The parent frame is the frame of the async operation that is logically 294 // the caller of this frame. 295 AsyncStackFrame* getParentFrame() noexcept; 296 const AsyncStackFrame* getParentFrame() const noexcept; 297 void setParentFrame(AsyncStackFrame& frame) noexcept; 298 299 // Get access to the current stack-root. 300 // 301 // This is only valid for either the root or leaf AsyncStackFrame 302 // in a chain of frames. 303 // 304 // In the case of an active leaf-frame it is used as a cache to 305 // avoid accessing the thread-local when pushing/popping frames. 306 // In the case of the root frame (which has a null parent frame) 307 // it points to an AsyncStackRoot that contains information about 308 // the normal-stack caller. 309 AsyncStackRoot* getStackRoot() noexcept; 310 311 // The return address is generallty the address of the code in the 312 // caller that will be executed when the operation owning the current 313 // frame completes. 314 void setReturnAddress(void* p = FOLLY_ASYNC_STACK_RETURN_ADDRESS()) noexcept; 315 void* getReturnAddress() const noexcept; 316 317 private: 318 friend AsyncStackRoot; 319 320 friend AsyncStackFrame& getDetachedRootAsyncStackFrame() noexcept; 321 friend void activateAsyncStackFrame( 322 folly::AsyncStackRoot&, folly::AsyncStackFrame&) noexcept; 323 friend void deactivateAsyncStackFrame(folly::AsyncStackFrame&) noexcept; 324 friend void pushAsyncStackFrameCallerCallee( 325 folly::AsyncStackFrame&, folly::AsyncStackFrame&) noexcept; 326 friend void checkAsyncStackFrameIsActive( 327 const folly::AsyncStackFrame&) noexcept; 328 friend void popAsyncStackFrameCallee(folly::AsyncStackFrame&) noexcept; 329 330 // Pointer to the async caller's stack-frame info. 331 // 332 // This forms a linked-list of frames that make up a stack. 333 // The list is terminated by a null pointer which indicates 334 // the top of the async stack - either because the operation 335 // is detached or because the next frame is a thread that is 336 // blocked waiting for the async stack to complete. 337 AsyncStackFrame* parentFrame = nullptr; 338 339 // Instruction pointer of the caller of this frame. 340 // This will typically be either the address of the continuation 341 // of this asynchronous operation, or the address of the code 342 // that launched this asynchronous operation. May be null 343 // if the address is not known. 344 // 345 // Typically initialised with the result of a call to 346 // FOLLY_ASYNC_STACK_RETURN_ADDRESS(). 347 void* instructionPointer = nullptr; 348 349 // Pointer to the stack-root for the current thread. 350 // Cache this in each async-stack frame so we don't have to 351 // read from a thread-local to get the pointer. 352 // 353 // This pointer is only valid for the top-most stack frame. 354 // When a frame is pushed or popped it should be copied to 355 // the next frame, etc. 356 // 357 // The exception is for the bottom-most frame (ie. where 358 // parentFrame == null). In this case, if stackRoot is non-null 359 // then it points to a root that is currently blocked on some 360 // thread waiting for the async work to complete. In this case 361 // you can find the information about the stack-frame for that 362 // thread in the AsyncStackRoot and can use it to continue 363 // walking the stack-frames. 364 AsyncStackRoot* stackRoot = nullptr; 365 }; 366 367 // A stack-root represents the context of an event loop 368 // that is running some asynchronous work. The current async 369 // operation that is being executed by the event loop (if any) 370 // is pointed to by the 'topFrame'. 371 // 372 // If the current event loop is running nested inside some other 373 // event loop context then the 'nextRoot' points to the AsyncStackRoot 374 // context for the next event loop up the stack on the current thread. 375 // 376 // The 'stackFramePtr' holds a pointer to the normal stack-frame 377 // that is currently executing this event loop. This allows 378 // reconciliation of the parts between a normal stack-trace and 379 // the start of the async-stack trace. 380 // 381 // The current thread's top-most context (the head of the linked 382 // list of contexts) is obtained by calling getCurrentAsyncStackRoot(). 383 struct AsyncStackRoot { 384 public: 385 // Sets the top-frame to be 'frame' and also updates the cached 386 // 'frame.stackRoot' to be 'this'. 387 // 388 // The current stack root must not currently have any active 389 // frame. 390 void setTopFrame(AsyncStackFrame& frame) noexcept; 391 AsyncStackFrame* getTopFrame() const noexcept; 392 393 // Initialises this stack root with information about the context 394 // in which the stack-root was declared. This records information 395 // about where the async-stack-trace should be spliced into the 396 // normal stack-trace. 397 void setStackFrameContext( 398 void* framePtr = FOLLY_ASYNC_STACK_FRAME_POINTER(), 399 void* ip = FOLLY_ASYNC_STACK_RETURN_ADDRESS()) noexcept; 400 void* getStackFramePointer() const noexcept; 401 void* getReturnAddress() const noexcept; 402 403 const AsyncStackRoot* getNextRoot() const noexcept; 404 void setNextRoot(AsyncStackRoot* next) noexcept; 405 406 private: 407 friend class detail::ScopedAsyncStackRoot; 408 friend void activateAsyncStackFrame( 409 folly::AsyncStackRoot&, folly::AsyncStackFrame&) noexcept; 410 friend void deactivateAsyncStackFrame(folly::AsyncStackFrame&) noexcept; 411 friend void pushAsyncStackFrameCallerCallee( 412 folly::AsyncStackFrame&, folly::AsyncStackFrame&) noexcept; 413 friend void checkAsyncStackFrameIsActive( 414 const folly::AsyncStackFrame&) noexcept; 415 friend void popAsyncStackFrameCallee(folly::AsyncStackFrame&) noexcept; 416 417 // Pointer to the currently-active AsyncStackFrame for this event 418 // loop or callback invocation. May be null if this event loop is 419 // not currently executing any async operations. 420 // 421 // This is atomic primarily to enforce visibility of writes to the 422 // AsyncStackFrame that occur before the topFrame in other processes, 423 // such as profilers/debuggers that may be running concurrently 424 // with the current thread. 425 std::atomic<AsyncStackFrame*> topFrame{nullptr}; 426 427 // Pointer to the next event loop context lower on the current 428 // thread's stack. 429 // This is nullptr if this is not a nested call to an event loop. 430 AsyncStackRoot* nextRoot = nullptr; 431 432 // Pointer to the stack-frame and return-address of the function 433 // call that registered this AsyncStackRoot on the current thread. 434 // This is generally the stack-frame responsible for executing async 435 // callbacks (typically an event-loop). 436 // Anything prior to this frame on the stack in the current thread 437 // is potentially unrelated to the call-chain of the current async-stack. 438 // 439 // Typically initialised with FOLLY_ASYNC_STACK_FRAME_POINTER() or 440 // setStackFrameContext(). 441 void* stackFramePtr = nullptr; 442 443 // Typically initialise with FOLLY_ASYNC_STACK_RETURN_ADDRESS() or 444 // setStackFrameContext(). 445 void* returnAddress = nullptr; 446 }; 447 448 namespace detail { 449 450 class ScopedAsyncStackRoot { 451 public: 452 explicit ScopedAsyncStackRoot( 453 void* framePointer = FOLLY_ASYNC_STACK_FRAME_POINTER(), 454 void* returnAddress = FOLLY_ASYNC_STACK_RETURN_ADDRESS()) noexcept; 455 ~ScopedAsyncStackRoot(); 456 activateFrame(AsyncStackFrame & frame)457 void activateFrame(AsyncStackFrame& frame) noexcept { 458 folly::activateAsyncStackFrame(root_, frame); 459 } 460 461 private: 462 AsyncStackRoot root_; 463 }; 464 465 } // namespace detail 466 } // namespace folly 467 468 #include <folly/tracing/AsyncStack-inl.h> 469