Skip to content

Allow inspection of object blocking graph

Currently debugging "soft" dead-locks in Haskell concurrent programs (e.g. a thread which is blocked on an MVar which should get unblocked but isn't due to logic error) is quite difficult. However, we already have many of the pieces in place to improve this situation. Specifically, we already have

threadStatus :: ThreadId -> IO ThreadStatus

data ThreadStatus
  = ThreadRunning
  | ThreadFinished
  | ThreadBlocked  BlockReason
  | ThreadDied

data BlockReason
  = BlockedOnMVar
  | BlockedOnBlackHole
  | BlockedOnException
  | BlockedOnSTM
  | BlockedOnForeignCall
  | BlockedOnOther

It would be very easy to extend BlockReason to capture the object on which the thread is blocked.

Furthermore, !2816 (merged) puts in place a mechanism for enumerating a program's threads.

All that remains to make this infrastructure useful for diagnosing deadlocks (and loops) is a means of assigning useful names to heap objects.. Cost centers are one way to achieve this in profiled code. In non-profiled code, IPE information helps for thunks but is unhelpful for primitives like MVars.

Sadly, there is no great way of identifying primitives in pure Haskell with the tools that we have today. However, this could be addressed by introducing a new type of primitive object: a finite map keyed on (weak) object references:

-- | A map keyed on object references. Entries keyed on GC'd objects will
-- be automatically dropped.
data WeakMap v

insertWeakMap :: forall (lev :: Levity) (k :: TYPE (BoxedRep lev)).
                 k -> v -> WeakMap v -> WeakMap v

lookupWeakMap :: forall (lev :: Levity) (k :: TYPE (BoxedRep lev)).
                 k -> WeakMap v -> Maybe v

labelledObjects :: IORef (WeakMap String)

labelObject :: a -> String -> IO ()
labelObject x lbl = do
    atomicModifyIORef' labelledObjects (insertWeakMap w lbl)

getObjectLabel :: a -> Maybe String
getObjectLabel x = lookupWeakMap x labelledObjects

In principle one might be able to do implement something like WeakMap as an Array# (Weak# v) with some parts written in Cmm, although efficiency would likely be marginal due to the many Weak# this would produce. It would be better for this to be a new primitive object with GC support. I believe that V8 has an object similar to this, which they call an ephemeron dictionary.

Perhaps even better than String names might be:

data ObjectLabel where
    ObjectLabel :: (Typeable a, Show a) => a -> ObjectLabel

which would make the labels easier to interpret mechanically.

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