{-# LANGUAGE FlexibleContexts #-} module Main (main) where import Language.Haskell.Exts import Data.Generics.Uniplate.Data import Control.Monad.State import Control.Applicative import Control.Arrow import qualified Control.Exception as Exc import qualified Pretty as P import qualified Language.Haskell.Exts.Pretty as PP import ParseProfile import System.Process import System.Directory import System.Environment import System.IO.Error import Data.Maybe import GraphUtils import Data.Char import qualified Data.IntMap as IMap import Text.RegexPR import Debug.Trace import Numeric import Data.List type Exp_ = Exp pprint tm = "
"
      ++ P.prettyPrint 120 tm ++ "
" assignSCC :: Module -> Module assignSCC m = evalState (transformBiM f m) 0 where f e = do st <- get put (st + 1) return $ addSCC st $ strip e strip :: Exp_ -> Exp_ strip (SCCPragma _ e) = e strip e = e addSCC :: Int -> Exp_ -> Exp_ addSCC _ e@(Var _) = e addSCC _ e@(Lit _) = e addSCC num e = Paren $ SCCPragma (show num) e addColour m s = gsubRegexPRBy (pref ++ ".*?" ++ suf) (\ str -> pref ++ sccNumToColour str ++ suf) s where sccNumToColour :: String -> String sccNumToColour str = maybe transparentColour toColour $ lookup (readSCCNumber str) m readSCCNumber :: String -> Int readSCCNumber str = fst $ head $ reads $ drop (length pref) str pref = "color: #" suf = "\">" transparentColour = "00ffffff" toColour :: Float -> String toColour fl | 0 <= fl && fl < 1.0e-2 = transparentColour | 1.0e-2 <= fl && fl < yellow = (pad $ showHex (truncate $ fl * 255 * (1 / yellow)) "") ++ "ff00" | yellow <= fl && fl <= 1 = "ff" ++ (pad $ showHex (255 - (truncate $ (fl - yellow) * 255 / (1 - yellow))) "") ++ "00" where pad :: String -> String pad str = if length str == 1 then '0':str else str yellow = 0.1 parseModuleFromFile :: FilePath -> IO Module parseModuleFromFile path = fromParseResult <$> parseFile path computeTicksMap :: Profile -> [(Int, Float)] computeTicksMap prof = map (\ n -> (read $ nodeName n :: Int, ticksFraction n)) $ filter (isNumber . head . nodeName) $ IMap.elems graph where totalTicks = profileTicks prof graph = profileGraph prof ticksFraction n = (fromInteger $ snd3 $ totalCost $ parentNodes n) / (fromInteger totalTicks) computeAllocMap :: Profile -> [(Int, Float)] computeAllocMap prof = allocFraction $ map (\ n -> (read $ nodeName n :: Int, alloc n)) $ filter (isNumber . head . nodeName) $ IMap.elems graph where graph = profileGraph prof alloc :: Node -> Float alloc n = fromInteger $ trd3 $ totalCost $ parentNodes n allocFraction l = map (second (/ sumSnd l)) l sumSnd :: [(Int, Float)] -> Float sumSnd l = sum $ map snd l snd3 :: (Integer, Integer, Integer) -> Integer snd3 (_, x, _) = x trd3 :: (Integer, Integer, Integer) -> Integer trd3 (_, _, x) = x ind l n = if length l > n then Just $ l !! n else Nothing main :: IO () main = do args <- getArgs let mode = args !! 0 unless ("-h" `isPrefixOf` mode || "-px" `isPrefixOf` mode) (error "mode should be -h or -px") let file = args !! 1 let run = args !! 2 let programArgs = ind args 3 let inpFile = ind args 4 let bak = file ++ ".bak" (do removeFile bak `catch` (\ e -> if isDoesNotExistError e then return () else ioError e) renameFile file bak putStrLn "started parsing original file" m <- parseModuleFromFile bak let tm = assignSCC m putStrLn "writing modified file" writeFile file $ PP.prettyPrint tm let buildStr = "ghc -prof -O2 --make " ++ run ++ ".hs && ./" ++ run ++ " +RTS " ++ mode ++ " -RTS" let buildStrArgs = buildStr ++ " " ++ fromMaybe "" programArgs ++ " " let buildStrInp = maybe buildStrArgs ((buildStrArgs ++ " < ") ++) inpFile putStrLn buildStrInp buildCommand <- runCommand buildStrInp waitForProcess buildCommand (when ("-px" `isPrefixOf` mode) $ do putStrLn "parsing profiling results" profFile <- readFile $ run ++ ".prof" let prof = fromJust $ parseProfile (run ++ ".prof") profFile let profMap = computeTicksMap prof putStrLn "printing output html file" let html = addColour profMap $ pprint tm writeFile (file ++ ".html") html) renameFile file (file ++ ".scc")) `Exc.finally` (do copyFile bak file removeFile bak)