diff options
author | Simon Marlow <marlowsd@gmail.com> | 2018-05-02 11:01:13 +0100 |
---|---|---|
committer | Simon Marlow <marlowsd@gmail.com> | 2018-05-09 13:43:32 +0100 |
commit | 5fe6aaa3756cda654374ebfd883fa8f064ff64a4 (patch) | |
tree | 3b667e7100f20864a2133c3330543d74afa88ebf /ghc/GHCi/Leak.hs | |
parent | ba6e445e1cf31957f2a327a73f9f66cfa7f24e26 (diff) | |
download | haskell-5fe6aaa3756cda654374ebfd883fa8f064ff64a4.tar.gz |
Add -fghci-leak-check to check for space leaks
Summary:
Space leaks in GHCi emerge from time to time and tend to come back again
after they get fixed. This is an attempt to limit regressions by
* adding a reliable detection for some classes of space leaks in GHCi
* turning on leak checking for all GHCi tests in the test suite, so that
we'll notice if the leak appears again.
The idea for detecting space leaks is quite simple:
* find some data that we expect to be GC'd later, make a weak pointer to it
* when we expect the data to be dead, do a `performGC` and then check
the status of the weak pointer.
It would be nice to apply this trick to lots of things in GHC,
e.g. ensuring that HsSyn is not retained after the desugarer, or
ensuring that CoreSyn from the previous simplifier pass is not retained.
Test Plan: validate
Reviewers: bgamari, simonpj, erikd, niteria
Subscribers: thomie, carter
GHC Trac Issues: #15111
Differential Revision: https://phabricator.haskell.org/D4658
Diffstat (limited to 'ghc/GHCi/Leak.hs')
-rw-r--r-- | ghc/GHCi/Leak.hs | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/ghc/GHCi/Leak.hs b/ghc/GHCi/Leak.hs new file mode 100644 index 0000000000..3f64b5dcf0 --- /dev/null +++ b/ghc/GHCi/Leak.hs @@ -0,0 +1,59 @@ +{-# LANGUAGE RecordWildCards, LambdaCase #-} +module GHCi.Leak + ( LeakIndicators + , getLeakIndicators + , checkLeakIndicators + ) where + +import Control.Monad +import GHC +import Outputable +import HscTypes +import UniqDFM +import System.Mem +import System.Mem.Weak + +-- 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 _) = + putStrLn ("-fghci-leak-check: " ++ msg ++ " is still alive!") |