{-# LANGUAGE RecordWildCards, LambdaCase #-} module Clash.GHCi.Leak ( LeakIndicators , getLeakIndicators , checkLeakIndicators ) where import Control.Monad import Data.Bits import Foreign.Ptr (ptrToIntPtr, intPtrToPtr) import GHC import GHC.Ptr (Ptr (..)) import Clash.GHCi.Util import HscTypes import Outputable import GHC.Platform (target32Bit) import Prelude import System.Mem import System.Mem.Weak import UniqDFM -- Checking for space leaks in GHCi. See #15111, and the -- -fghci-leak-check flag. data LeakIndicators = LeakIndicators [LeakModIndicators] data LeakModIndicators = LeakModIndicators { leakMod :: Weak HomeModInfo , leakIface :: Weak ModIface , leakDetails :: Weak ModDetails , leakLinkable :: Maybe (Weak Linkable) } -- | Grab weak references to some of the data structures representing -- the currently loaded modules. getLeakIndicators :: HscEnv -> IO LeakIndicators getLeakIndicators HscEnv{..} = fmap LeakIndicators $ forM (eltsUDFM hsc_HPT) $ \hmi@HomeModInfo{..} -> do leakMod <- mkWeakPtr hmi Nothing leakIface <- mkWeakPtr hm_iface Nothing leakDetails <- mkWeakPtr hm_details Nothing leakLinkable <- mapM (`mkWeakPtr` Nothing) hm_linkable return $ LeakModIndicators{..} -- | Look at the LeakIndicators collected by an earlier call to -- `getLeakIndicators`, and print messasges if any of them are still -- alive. checkLeakIndicators :: DynFlags -> LeakIndicators -> IO () checkLeakIndicators dflags (LeakIndicators leakmods) = do performGC forM_ leakmods $ \LeakModIndicators{..} -> do deRefWeak leakMod >>= \case Nothing -> return () Just hmi -> report ("HomeModInfo for " ++ showSDoc dflags (ppr (mi_module (hm_iface hmi)))) (Just hmi) deRefWeak leakIface >>= report "ModIface" deRefWeak leakDetails >>= report "ModDetails" forM_ leakLinkable $ \l -> deRefWeak l >>= report "Linkable" where report :: String -> Maybe a -> IO () report _ Nothing = return () report msg (Just a) = do addr <- anyToPtr a putStrLn ("-fghci-leak-check: " ++ msg ++ " is still alive at " ++ show (maskTagBits addr)) tagBits | target32Bit (targetPlatform dflags) = 2 | otherwise = 3 maskTagBits :: Ptr a -> Ptr a maskTagBits p = intPtrToPtr (ptrToIntPtr p .&. complement (shiftL 1 tagBits - 1))