Skip to content

Expose primop to atomically "clone" a thread stack

Motivation

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 (closed)).

Proposal

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 Message type), 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

StackFrame would

Edited by Matthew Pickering
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information