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