{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad
import Data.Maybe
import Data.List (sortBy)
import Data.Monoid
import Data.Ord
import Options.Applicative
import Text.PrettyPrint.ANSI.Leijen hiding ((<$>), (<>))
import qualified Text.PrettyPrint.ANSI.Leijen as PP
import Text.Regex.TDFA
import Text.Regex.TDFA.Common (Regex)
import Text.Regex.TDFA.Text
import GhcDump.Pretty
import GhcDump.Util
import GhcDump.Ast
data Column a = Col { colWidth :: Int, colHeader :: String, colGet :: (a -> Doc) }
type Table a = [Column a]
renderTable :: forall a. Table a -> [a] -> Doc
renderTable cols xs =
row (PP.bold . text . colHeader)
<$$> vcat [ row (flip colGet x) | x <- xs ]
where
row :: (Column a -> Doc) -> Doc
row toCell = go cols
where
go :: [Column a] -> Doc
go [] = PP.empty
go [col] = align $ toCell col
go (col : rest) = fillBreak (colWidth col) (align $ toCell col) PP.<+> go rest
filterBindings :: Regex -> Module -> Module
filterBindings re m =
m { moduleTopBindings = mapMaybe filterTopBinding $ moduleTopBindings m }
where
filterTopBinding b'@(NonRecTopBinding b _ _)
| nameMatches b = Just b'
| otherwise = Nothing
filterTopBinding (RecTopBinding bs)
| not $ null bs' = Just $ RecTopBinding bs'
| otherwise = Nothing
where bs' = filter (\(b,_,_) -> nameMatches b) bs
nameMatches :: Binder -> Bool
nameMatches b = matchTest re (binderUniqueName b)
modes :: Parser (IO ())
modes = subparser
$ mode "show" showMode (progDesc "print Core")
<> mode "list-bindings" listBindingsMode (progDesc "list top-level bindings, their sizes, and types")
<> mode "summarize" summarizeMode (progDesc "summarize multiple dump files")
where
mode name f opts = command name (info (helper <*> f) opts)
dumpFile :: Parser FilePath
dumpFile = argument str (metavar "DUMP FILE" <> help "CBOR dump file")
filterCond :: Parser (Module -> Module)
filterCond =
fmap (maybe id filterBindings)
$ option (str >>= fmap Just . makeRegexM')
(short 'f' <> long "filter" <> value Nothing <> help "filter bindings by name")
where
makeRegexM' = makeRegexM :: String -> ReadM Regex
prettyOpts :: Parser PrettyOpts
prettyOpts =
PrettyOpts
<$> switch (short 'u' <> long "show-uniques" <> help "Show binder uniques")
<*> switch (short 'i' <> long "show-idinfo" <> help "Show IdInfo of bindings")
<*> switch (short 'T' <> long "show-let-types" <> help "Show type signatures for let-bound binders")
<*> switch (short 'U' <> long "show-unfoldings" <> help "Show unfolding templates")
showMode =
run <$> filterCond <*> prettyOpts <*> dumpFile
where
run filterFn opts fname = do
dump <- filterFn <$> GhcDump.Util.readDump fname
print $ pprModule opts dump
listBindingsMode =
run <$> filterCond <*> sortField <*> prettyOpts <*> dumpFile
where
sortField =
option (str >>= readSortField)
(long "sort" <> short 's' <> value id
<> help "Sort by (accepted values: terms, types, coercions, type)")
where
readSortField "terms" = return $ sortBy (flip $ comparing $ csTerms . getStats)
readSortField "types" = return $ sortBy (flip $ comparing $ csTypes . getStats)
readSortField "coercions" = return $ sortBy (flip $ comparing $ csCoercions . getStats)
readSortField "type" = return $ sortBy (comparing $ binderType . unBndr . getBinder)
readSortField f = fail $ "unknown sort field "++f
run filterFn sortBindings opts fname = do
dump <- filterFn <$> GhcDump.Util.readDump fname
let table = [ Col 20 "Name" (pprBinder opts . getBinder)
, Col 6 "Terms" (pretty . csTerms . getStats)
, Col 6 "Types" (pretty . csTypes . getStats)
, Col 6 "Coerc." (pretty . csCoercions . getStats)
, Col 300 "Type" (pprType opts . binderType . unBndr . getBinder)
]
print $ renderTable table (sortBindings $ moduleBindings dump)
summarizeMode =
run <$> some dumpFile
where
run fnames = do
mods <- mapM (\fname -> do mod <- readDump fname
return (fname, mod)) fnames
let totalSize :: Module -> CoreStats
totalSize = foldMap getStats . moduleBindings
let table = [ Col 35 "Name" (text . fst)
, Col 8 "Terms" (pretty . csTerms . totalSize . snd)
, Col 8 "Types" (pretty . csTypes . totalSize . snd)
, Col 8 "Coerc." (pretty . csCoercions . totalSize . snd)
, Col 35 "Previous phase" (pretty . modulePhase . snd)
]
print $ renderTable table mods
getBinder (b,_,_) = b
getStats (_,s,_) = s
getRHS (_,_,e) = e
main :: IO ()
main = join $ execParser $ info (helper <*> modes) mempty