packages feed

ghc-build-stats-0.1.0.0: src/Lib.hs

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedRecordDot #-}
module Lib where
import Prelude hiding (writeFile)
import Control.Arrow (first)
import Data.Aeson (FromJSON(..))
import Data.Aeson.Encoding (encodingToLazyByteString)
import Data.ByteString.Lazy (writeFile)
import Data.Foldable (traverse_, for_)
import Data.Function (on)
import Data.List (sortBy)
import Data.Maybe
import Data.Text (pack)
import GHC.Generics
import Text.Printf
import Options.Applicative
import qualified Data.Aeson.Encoding as Encoding
import qualified Data.Aeson.Decoding as Aeson
import qualified Data.Map as M
import qualified Data.ByteString.Char8 as BS

import WeighedNode
import Simulate (simulate, critPath, ModEvent (..))
import ChromeTracingExport

data JSONNode = JSONNode { name :: ModName, duration :: Double, deps :: [ModName]}
  deriving (Generic, FromJSON)

data Options =
  Options
  { simulatedCores :: Int
  , inputFile :: FilePath
  }

optionsParser :: Parser Options
optionsParser =
  Options
  <$> option auto
       ( long "cores"
       <> help "The amount of simulated cores"
       <> showDefault
       <> value 8
       <> metavar "CORES")
  <*> argument str (metavar "INPUT")

main :: IO ()
main = do
  options <- execParser $ info (optionsParser <**> helper) (progDesc "Analyse the build statistics of INPUT")
  input <- BS.readFile options.inputFile
  let
    jsonNodes :: [JSONNode]
    jsonNodes = mapMaybe Aeson.decodeStrict $ BS.lines input
  let
    wgraph = M.fromList [(node.name, WeighedNode node.deps (node.duration / 1000) 0) | node <- jsonNodes]
    timePath = critPath wgraph nodeEopTime
  putStrLn "10 slowest modules to compile:"
  let slowMods = zip [(1 ::Int)..] . take 10 . sortBy (flip compare `on` (nodeEopTime . snd)) . M.toList $ wgraph
  for_ slowMods $ \(ix, (modName, node)) ->
    putStrLn $ printf "%2d, %-5.2fs, %s" ix (node.nodeEopTime / 1000) modName
  putStrLn $ printf "Critical path, %-5.2fs total, composed of %d modules:" (fst timePath/1000) (length $ snd timePath)
  traverse_ (uncurry (printf "%-5.2fs, %s\n") . first (/1000)) $ snd timePath
  putStrLn $ "TOTAL: " <> show ((/1000) . sum . map nodeEopTime . M.elems $ wgraph)
  writeFile (options.inputFile ++ ".trace.json")
    $ encodingToLazyByteString
    $ Encoding.list (\ModEvent{..} -> mkCompleteEvent (pack eventModName) "module" (eventStart * 1000) ((eventEnd - eventStart) * 1000) 1 eventThreadId)
    $ simulate options.simulatedCores wgraph