ghc-debug-client-0.1.0.0: src/GHC/Debug/Client/BlockCache.hs
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE BinaryLiterals #-}
{-# LANGUAGE RankNTypes #-}
-- The BlockCache stores the currently fetched blocks
-- and is consulted first to avoid requesting too much
-- from the debuggee. The BlockCache can either be populated
-- via a call to RequestBlocks or on demand on a cache miss.
module GHC.Debug.Client.BlockCache(BlockCache, BlockCacheRequest(..)
, handleBlockReq, emptyBlockCache, bcSize, addBlocks) where
import GHC.Debug.Types.Ptr
import GHC.Debug.Types
import qualified Data.HashMap.Strict as HM
import GHC.Word
import Data.Hashable
import Data.IORef
import Data.Bits
import Data.List (sort)
import Data.Binary
newtype BlockCache = BlockCache (HM.HashMap Word64 RawBlock)
instance Binary BlockCache where
get = BlockCache . HM.fromList <$> get
put (BlockCache hm) = put (HM.toList hm)
emptyBlockCache :: BlockCache
emptyBlockCache = BlockCache HM.empty
addBlock :: RawBlock -> BlockCache -> BlockCache
addBlock rb@(RawBlock (BlockPtr bp) _ _) (BlockCache bc) =
BlockCache (HM.insert bp rb bc)
addBlocks :: [RawBlock] -> BlockCache -> BlockCache
addBlocks bc bs = Prelude.foldr addBlock bs bc
lookupClosure :: ClosurePtr -> BlockCache -> Maybe RawBlock
lookupClosure (ClosurePtr cp) (BlockCache b) =
HM.lookup (cp .&. complement blockMask) b
bcSize :: BlockCache -> Int
bcSize (BlockCache b) = HM.size b
_bcKeys :: BlockCache -> [ClosurePtr]
_bcKeys (BlockCache b) = sort $ map mkClosurePtr (HM.keys b)
data BlockCacheRequest a where
LookupClosure :: ClosurePtr -> BlockCacheRequest RawClosure
PopulateBlockCache :: BlockCacheRequest [RawBlock]
deriving instance Show (BlockCacheRequest a)
deriving instance Eq (BlockCacheRequest a)
instance Hashable (BlockCacheRequest a) where
hashWithSalt s (LookupClosure cpt) = s `hashWithSalt` (1 :: Int) `hashWithSalt` cpt
hashWithSalt s PopulateBlockCache = s `hashWithSalt` (2 :: Int)
handleBlockReq :: (forall a . Request a -> IO a) -> IORef BlockCache -> BlockCacheRequest resp -> IO resp
handleBlockReq do_req ref (LookupClosure cp) = do
bc <- readIORef ref
let mrb = lookupClosure cp bc
rb <- case mrb of
Nothing -> do
rb <- do_req (RequestBlock cp)
atomicModifyIORef' ref (\bc' -> (addBlock rb bc', ()))
return rb
Just rb -> do
return rb
return (extractFromBlock cp rb)
handleBlockReq do_req ref PopulateBlockCache = do
blocks <- do_req RequestAllBlocks
-- mapM_ (\rb -> print ("NEW", rawBlockAddr rb)) blocks
print ("CACHING", length blocks)
atomicModifyIORef' ref ((,()) . addBlocks blocks)
return blocks