ghc-debug-brick-0.8.0.0: src/GHC/Debug/Brick/Action/Retainers.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NamedFieldPuns #-}
module GHC.Debug.Brick.Action.Retainers (
searchWithCurrentFiltersIncremental,
RetainerMap,
retainerTreeMode,
topRetainersFromRetainerMap,
retainerTree,
retainerTreeM,
) where
import Brick.BChan
import Brick.Types
import Brick.Widgets.IOTree
import Control.Monad.IO.Class (liftIO)
import Data.IORef
import qualified Data.List as List
import qualified Data.Map.Strict as Map
import qualified Data.Text as T
import Lens.Micro
import Lens.Micro.Extras
import GHC.Debug.Brick.ClosureDetails
import GHC.Debug.Brick.IOTree (mkIOTree)
import GHC.Debug.Brick.Lib
import GHC.Debug.Brick.Model
import GHC.Debug.Brick.Render.Closure
import GHC.Debug.Brick.UI.Async
import GHC.Debug.Retainers (SearchLimit)
import GHC.Debug.Brick.UI.Filter
searchWithCurrentFiltersIncremental :: Debuggee -> [UIFilter] -> EventM n OperationalState ()
searchWithCurrentFiltersIncremental dbg uifilters = do
outside_os <- get
let mClosFilter = uiFiltersToFilter uifilters
info_map_ref <- liftIO $ newIORef Map.empty
let tree = retainerTreeM dbg (readIORef info_map_ref)
let
add_new_sample :: [ClosureDetails] -> IO ()
add_new_sample new_retainer_stack_sample = do
-- We expect this retainer stack to be non-empty.
-- The first entry is the "Leaf" node in the tree, while the tail of '[ClosureDetails]'
-- is the stack of retainers of the first 'ClosureDetails'.
case List.uncons new_retainer_stack_sample of
Just (k, v) -> do
let kk = toPtr (_closure k)
modifyIORef info_map_ref (Map.insert kk (k, v))
Nothing -> return ()
ret_add_sample cps = do
let cps' = zipWith (\n cp -> (T.pack (show n),cp)) [0 :: Int ..] cps
res <- mapM (completeClosureDetails dbg) cps'
-- 1. Add the sample to the IOTree
add_new_sample res
-- 2. Update the IOTree
writeBChan (view event_chan outside_os) (NewSampleEvent $ RetainerSample res)
let searchLimit = _resultSize outside_os
put $ outside_os
& treeMode .~ retainerTreeMode searchLimit tree []
asyncAction "Searching for closures" (liftIO $ retainersOfIncremental searchLimit mClosFilter Nothing dbg ret_add_sample) $ \() -> do
-- We finished the traversal, finalise the search results
result <- liftIO $ readIORef info_map_ref
sendEvent $ CacheSearch (DoRetainerSearch searchLimit uifilters) result
os <- get
put (os & resetFooter)
retainerTreeMode :: SearchLimit -> IOTree RetainerNode Name -> [RetainerNode] -> TreeMode
retainerTreeMode _limit tree retainers =
Retainer (renderClosureDetails . retainerNodeClosure) (setIOTreeRoots retainers tree)
topRetainersFromRetainerMap :: RetainerMap -> [RetainerNode]
topRetainersFromRetainerMap retainerMap =
fmap (RetainerStackRoot . fst) $ Map.elems retainerMap
retainerTree :: Debuggee -> RetainerMap -> IOTree RetainerNode Name
retainerTree dbg retainerStacks =
retainerTreeM dbg (pure retainerStacks)
retainerTreeM :: Debuggee -> IO RetainerMap -> IOTree RetainerNode Name
retainerTreeM dbg get_info_map_ref =
let
lookup_c dbg' (RetainerStackRoot dc'@(ClosureDetails dc _ _)) = do
let ptr = toPtr dc
info_map <- get_info_map_ref
let results = maybe [] snd $ Map.lookup ptr info_map
-- We are also looking up the children of the object we are retaining,
-- and displaying them prior to the retainer stack
cs <- getChildren dbg' dc'
results' <- liftIO $ mapM (\(l, c) -> getClosureDetails dbg' l (ListFullClosure c)) (withIndex results)
return $ map RetainerStackChild (cs ++ results')
lookup_c dbg' (RetainerStackRoot dc) = fmap (map RetainerStackChild) $ getChildren dbg' dc
lookup_c dbg' (RetainerStackChild dc) = fmap (map RetainerStackChild) $ getChildren dbg' dc
in
mkIOTree dbg [] lookup_c (renderInlineClosureDesc . retainerNodeClosure) id
where
withIndex vs = zipWith (\ n v -> (T.show n, _closure v) ) [(1 :: Int) .. ] vs