{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE BangPatterns #-} -- | Functions to support the constant space traversal of a heap. module GHC.Debug.Trace ( traceFromM, TraceFunctions(..) ) where import GHC.Debug.Types import GHC.Debug.Client.Monad import GHC.Debug.Client.Query import qualified Data.IntMap as IM import Data.Array.BitArray.IO import Control.Monad.Reader import Data.IORef import Data.Word import System.IO newtype VisitedSet = VisitedSet (IM.IntMap (IOBitArray Word16)) data TraceState = TraceState { visited :: !VisitedSet, n :: !Int } getKeyPair :: ClosurePtr -> (Int, Word16) getKeyPair cp = let BlockPtr raw_bk = applyBlockMask cp bk = fromIntegral raw_bk `div` 8 offset = getBlockOffset cp `div` 8 in (bk, fromIntegral offset) checkVisit :: ClosurePtr -> IORef TraceState -> IO Bool checkVisit cp mref = do st <- readIORef mref let VisitedSet v = visited st num_visited = n st (bk, offset) = getKeyPair cp case IM.lookup bk v of Nothing -> do na <- newArray (0, fromIntegral (blockMask `div` 8)) False writeArray na offset True writeIORef mref (TraceState (VisitedSet (IM.insert bk na v)) (num_visited + 1)) when (num_visited `mod` 10_000 == 0) $ hPutStrLn stderr ("Traced: " ++ show num_visited) return False Just bm -> do res <- readArray bm offset unless res (writeArray bm offset True) return res data TraceFunctions m = TraceFunctions { papTrace :: !(GenPapPayload ClosurePtr -> m DebugM ()) , stackTrace :: !(GenStackFrames ClosurePtr -> m DebugM ()) , closTrace :: !(ClosurePtr -> SizedClosure -> m DebugM () -> m DebugM ()) , visitedVal :: !(ClosurePtr -> (m DebugM) ()) , conDescTrace :: !(ConstrDesc -> m DebugM ()) } type C m = (MonadTrans m, Monad (m DebugM)) -- | A generic heap traversal function which will use a small amount of -- memory linear in the heap size. Using this function with appropiate -- accumulation functions you should be able to traverse quite big heaps in -- not a huge amount of memory. traceFromM :: C m => TraceFunctions m-> [ClosurePtr] -> m DebugM () traceFromM k cps = do st <- lift (unsafeLiftIO (newIORef (TraceState (VisitedSet IM.empty) 1))) runReaderT (mapM_ (traceClosureFromM k) cps) st {-# INLINE traceFromM #-} {-# INLINE traceClosureFromM #-} traceClosureFromM :: C m => TraceFunctions m -> ClosurePtr -> ReaderT (IORef TraceState) (m DebugM) () traceClosureFromM !k = go where go cp = do mref <- ask b <- lift $ lift $ unsafeLiftIO (checkVisit cp mref) if b then lift $ visitedVal k cp else do sc <- lift $ lift $ dereferenceClosure cp ReaderT $ \st -> closTrace k cp sc (runReaderT (() <$ quadtraverse gop gocd gos go sc) st) gos st = do st' <- lift $ lift $ dereferenceStack st lift $ stackTrace k st' () <$ traverse go st' gocd d = do cd <- lift $ lift $ dereferenceConDesc d lift $ conDescTrace k cd gop p = do p' <- lift $ lift $ dereferencePapPayload p lift $ papTrace k p' () <$ traverse go p'