ghc-debug-client-0.6.0.0: src/GHC/Debug/Retainers.hs
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE RecordWildCards #-}
-- | Functions for computing retainers
module GHC.Debug.Retainers
( findRetainers
, findRetainersOf
, findRetainersOfConstructor
, findRetainersOfConstructorExact
, findRetainersOfInfoTable
, addLocationToStack
, displayRetainerStack
, addLocationToStack'
, displayRetainerStack'
, findRetainersOfArrWords
, EraRange(..)
, profHeaderInEraRange
, ClosureFilter(..)
, profHeaderReferencesCCS
, findRetainersOfEra) where
import Prelude hiding (filter)
import GHC.Debug.Client
import Control.Monad.State
import GHC.Debug.Trace
import GHC.Debug.Types.Graph
import Control.Monad
import qualified Data.Set as Set
import Control.Monad.RWS
import Data.Word
addOne :: a -> (Maybe Int, [a]) -> (Maybe Int, [a])
addOne _ (Just 0, cp) = (Just 0, cp)
addOne cp (n, cps) = (subtract 1 <$> n, cp : cps)
data EraRange
= EraRange { startEra :: Word64, endEra :: Word64} -- inclusive
deriving (Eq, Ord, Show)
inEraRange :: Word64 -> Maybe EraRange -> Bool
inEraRange _ Nothing = True
inEraRange n (Just (EraRange s e)) = s <= n && n <= e
profHeaderReferencesCCS :: Maybe ProfHeaderWithPtr -> Set.Set CCSPtr -> Bool
profHeaderReferencesCCS Nothing _ = False
profHeaderReferencesCCS (Just profHeader) f = ccs profHeader `Set.member` f
profHeaderInEraRange :: Maybe (ProfHeader a) -> Maybe EraRange -> Bool
profHeaderInEraRange Nothing _ = True
profHeaderInEraRange (Just ph) eras
= case hp ph of
EraWord w -> w `inEraRange` eras
_ -> True -- Don't filter if no era profiling
data ClosureFilter
= ConstructorDescFilter (ConstrDesc -> Bool)
| InfoFilter (StgInfoTable -> Bool)
| InfoPtrFilter (InfoTablePtr -> Bool)
| InfoSourceFilter (SourceInformation -> Bool)
| SizeFilter (Size -> Bool)
| ProfHeaderFilter (Maybe ProfHeaderWithPtr -> Bool)
| AddressFilter (ClosurePtr -> Bool)
| AndFilter ClosureFilter ClosureFilter
| OrFilter ClosureFilter ClosureFilter
| NotFilter ClosureFilter
| PureFilter Bool
matchesFilter :: ClosureFilter -> ClosurePtr -> SizedClosure -> [ClosurePtr] -> DebugM Bool
matchesFilter filter ptr sc parents = case filter of
ConstructorDescFilter p -> case noSize sc of
ConstrClosure _ _ _ _ cd -> do
cd' <- dereferenceConDesc cd
return $ p cd'
_ -> pure False
InfoFilter p -> pure $ p (decodedTable (info (noSize sc)))
InfoPtrFilter p -> pure $ p (tableId (info (noSize sc)))
InfoSourceFilter p -> do
loc <- getSourceInfo (tableId (info (noSize sc)))
case loc of
Nothing -> return False
Just cur_loc -> pure $ p cur_loc
SizeFilter p -> pure $ p (dcSize sc)
ProfHeaderFilter p -> pure $ p (profHeader $ noSize sc)
AddressFilter p -> pure $ p ptr
AndFilter f1 f2 -> do
r1 <- matchesFilter f1 ptr sc parents
case r1 of
False -> pure False
True -> matchesFilter f2 ptr sc parents
OrFilter f1 f2 -> do
r1 <- matchesFilter f1 ptr sc parents
case r1 of
True -> pure True
False -> matchesFilter f2 ptr sc parents
NotFilter f1 -> do
r1 <- matchesFilter f1 ptr sc parents
pure (not r1)
PureFilter b -> pure b
findRetainersOf :: Maybe Int
-> [ClosurePtr]
-> [ClosurePtr]
-> DebugM [[ClosurePtr]]
findRetainersOf limit cps bads =
findRetainers limit (AddressFilter (`Set.member` bad_set)) cps
where
bad_set = Set.fromList bads
findRetainersOfConstructor :: Maybe Int
-> [ClosurePtr] -> String -> DebugM [[ClosurePtr]]
findRetainersOfConstructor limit rroots con_name =
findRetainers limit (ConstructorDescFilter ((== con_name) . name)) rroots
findRetainersOfConstructorExact
:: Maybe Int
-> [ClosurePtr] -> String -> DebugM [[ClosurePtr]]
findRetainersOfConstructorExact limit rroots clos_name =
findRetainers limit (InfoSourceFilter ((== clos_name) . infoName)) rroots
findRetainersOfEra
:: Maybe Int
-> EraRange
-> [ClosurePtr] -> DebugM [[ClosurePtr]]
findRetainersOfEra limit eras rroots =
findRetainers limit filter rroots
where
filter = ProfHeaderFilter (`profHeaderInEraRange` (Just eras))
findRetainersOfArrWords
:: Maybe Int
-> [ClosurePtr] -> Size -> DebugM [[ClosurePtr]]
findRetainersOfArrWords limit rroots lim =
findRetainers limit filter rroots
where
-- TODO : this is the size of the entire closure, not the size of the ArrWords
filter = AndFilter (InfoFilter ((== ARR_WORDS) . tipe))
(SizeFilter (>= lim))
findRetainersOfInfoTable
:: Maybe Int
-> [ClosurePtr] -> InfoTablePtr -> DebugM [[ClosurePtr]]
findRetainersOfInfoTable limit rroots info_ptr =
findRetainers limit (InfoPtrFilter (== info_ptr)) rroots
-- | From the given roots, find any path to one of the given pointers.
-- Note: This function can be quite slow! The first argument is a limit to
-- how many paths to find. You should normally set this to a small number
-- such as 10.
findRetainers :: Maybe Int
-> ClosureFilter
-> [ClosurePtr] -> DebugM [[ClosurePtr]]
findRetainers limit filter rroots = (\(_, r, _) -> snd r) <$> runRWST (traceFromM funcs rroots) [] (limit, [])
where
funcs = justClosures closAccum
-- Add clos
closAccum :: ClosurePtr
-> SizedClosure
-> RWST [ClosurePtr] () (Maybe Int, [[ClosurePtr]]) DebugM ()
-> RWST [ClosurePtr] () (Maybe Int, [[ClosurePtr]]) DebugM ()
closAccum _ (noSize -> WeakClosure {}) _ = return ()
closAccum cp sc k = do
ctx <- ask
b <- lift $ matchesFilter filter cp sc ctx
if b
then do
modify' (addOne (cp: ctx))
local (cp:) k
else do
(lim, _) <- get
case lim of
Just 0 -> return ()
_ -> local (cp:) k
addLocationToStack :: [ClosurePtr] -> DebugM [(SizedClosureP, Maybe SourceInformation)]
addLocationToStack r = do
cs <- dereferenceClosures r
cs' <- mapM dereferenceToClosurePtr cs
locs <- mapM getSourceLoc cs'
return $ (zip cs' locs)
where
getSourceLoc c = getSourceInfo (tableId (info (noSize c)))
addLocationToStack' :: [ClosurePtr] -> DebugM [(ClosurePtr, SizedClosureP, Maybe SourceInformation)]
addLocationToStack' r = do
cs <- dereferenceClosures r
cs' <- mapM dereferenceToClosurePtr cs
locs <- mapM getSourceLoc cs'
return $ (zip3 r cs' locs)
where
getSourceLoc c = getSourceInfo (tableId (info (noSize c)))
displayRetainerStack :: [(String, [(SizedClosureP, Maybe SourceInformation)])] -> IO ()
displayRetainerStack rs = do
let disp (d, l) =
(ppClosure (\_ -> show) 0 . noSize $ d) ++ " <" ++ maybe "nl" tdisplay l ++ ">"
where
tdisplay sl = infoName sl ++ ":" ++ infoType sl ++ ":" ++ infoModule sl ++ ":" ++ infoPosition sl
do_one k (l, stack) = do
putStrLn (show k ++ "-------------------------------------")
print l
mapM (putStrLn . disp) stack
zipWithM_ do_one [0 :: Int ..] rs
displayRetainerStack' :: [(String, [(ClosurePtr, SizedClosureP, Maybe SourceInformation)])] -> IO ()
displayRetainerStack' rs = do
let disp (p, d, l) =
show p ++ ": " ++ (ppClosure (\_ -> show) 0 . noSize $ d) ++ " <" ++ maybe "nl" tdisplay l ++ ">"
where
tdisplay sl = infoName sl ++ ":" ++ infoType sl ++ ":" ++ infoModule sl ++ ":" ++ infoPosition sl
do_one k (l, stack) = do
putStrLn (show k ++ "-------------------------------------")
print l
mapM (putStrLn . disp) stack
zipWithM_ do_one [0 :: Int ..] rs