Expose primop to atomically "clone" a thread stack
Due to mutation, the stack is a moving target for tools that should analyze it, like
ghc-heap ("stack decoding"),
ghc-debug and the stack unwinder (#18163).
To make those analyses safe, we could add a pair of primops to atomically "clone" a stack:
-- | A snapshot of the state of an evaluation stack. data StackSnapshot# -- | Capture a 'StackSnapshot#' of the state of the current thread's stack. cloneMyStack :: State# RealWorld -> (# State# RealWorld, StackSnapshot# #) -- | Capture a 'StackSnapshot#' of the state of another thread's stack. cloneThreadStack :: ThreadId# -> State# RealWorld -> (# State# RealWorld, StackSnapshot# #)
"Cloning" means in this context to make a deep copy of every closure to non garbage collected memory. "Deep" means that all payload closures are copied, too, and that a copy only references other copies as payload.
With a little help from the RTS (by way of a new
cloneThreadStack would likely be implemented mostly in Haskell as follows:
data StackSnapshot = StackSnapshot StackSnapshot# cloneThreadStack :: ThreadId# -> IO StackSnapshot cloneThreadStack tid# = do resultVar <- newEmptyMVar @StackSnapshot -- Use the RTS's "message" mechanism to request that -- the thread captures its stack, saving the result -- into resultVar. sendCaptureStackMessage tid# resultVar takeMVar resultVar
In addition, we will ultimately expose an interface in
ghc-heap for examining the frames of the stack:
data StackFrame -- | Decode a 'StackSnapshot' into its constituent stack frames. decodeStack :: StackSnapshot -> [StackFrame]
To start out
StackFrame would likely have a simple, abstract structure:
-- | Is a stack frame an update frame? If so, provides a reference to the updatee. stackFrameIsUpdateFrame :: StackFrame -> Maybe Box -- | Enumerate the pointers retained by a 'StackFrame'. stackFramePointers :: StackFrame -> [Box] -- | Is a stack frame a "return" frame of a @case@ analysis? -- The source location may give the source location (assuming either DWARF or -- something like @mpickering's !3469 is available. stackFrameIsReturnFrame :: StackFrame -> Maybe SrcLoc