ghc-events-analyze-0.2.9: src/GHC/RTS/Events/Analyze/Reports/Timed.hs
{-# LANGUAGE OverloadedStrings #-}
module GHC.RTS.Events.Analyze.Reports.Timed (
Report
, ReportFragment(..)
, ReportLine(..)
, createReport
, writeReport
) where
import Control.Lens (itoList, (^.), over, each, _3)
import Data.Function (on)
import Data.HashMap.Strict qualified as Map
import Data.IntMap.Strict (IntMap)
import Data.IntMap.Strict qualified as IntMap
import Data.List (group, sortBy)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Text.IO qualified as T
import System.IO (Handle, withFile, IOMode(WriteMode))
import Text.Printf (printf)
import GHC.RTS.Events.Analyze.Analysis
import GHC.RTS.Events.Analyze.Script
import GHC.RTS.Events.Analyze.Types
import GHC.RTS.Events.Analyze.Utils
{-------------------------------------------------------------------------------
Types
-------------------------------------------------------------------------------}
type Report = [ReportFragment]
data ReportFragment =
ReportSection Title
| ReportLine ReportLine
deriving Show
data ReportLine = ReportLineData {
lineHeader :: Text
, lineEventIds :: [EventId]
, lineBackground :: Maybe (Int, Int)
, lineValues :: IntMap Double
}
deriving Show
{-------------------------------------------------------------------------------
Report generation
-------------------------------------------------------------------------------}
createReport :: EventAnalysis -> Quantized -> Script String -> Report
createReport analysis Quantized{..} = concatMap go . fmap (fmap (mkThreadFilter (analysis^.windowThreadInfo)))
where
go :: Command (ThreadId -> Bool)-> [ReportFragment]
go (Section title) =
[ReportSection title]
go (One eid title) =
[ReportLine $ reportLine title (eid, quantTimesForEvent eid)]
go (All f sort) =
map (ReportLine . reportLine Nothing) (sorted sort $ filtered f)
go (Sum f title) =
[ReportLine $ sumLines title $ map (reportLine Nothing) (filtered f)]
quantThreadInfoFlattened = over (each._3) flattenThreadLabels quantThreadInfo
flattenThreadLabels = T.intercalate ":" . map head . group
reportLine :: Maybe Title -> (EventId, IntMap Double) -> ReportLine
reportLine title (eid, qs) = ReportLineData {
lineHeader = showTitle (showEventId quantThreadInfoFlattened eid) title
, lineEventIds = [eid]
, lineBackground = background eid
, lineValues = qs
}
-- For threads we draw a background showing the thread's lifetime
background :: EventId -> Maybe (Int, Int)
background EventGC = Nothing
background (EventUser _ _) = Nothing
background (EventThread tid) =
case Map.lookup tid quantThreadInfo of
Just (start, stop, _) -> Just (start, stop)
Nothing -> error $ "Invalid thread ID " ++ show tid
quantTimesForEvent :: EventId -> IntMap Double
quantTimesForEvent eid =
case Map.lookup eid quantTimes of
Nothing -> mempty -- this event didn't happen in the window
Just times -> times
sorted :: Maybe EventSort -> [(EventId, a)] -> [(EventId, a)]
sorted Nothing = id
sorted (Just sort) = sortBy (compareEventIds analysis sort `on` fst)
filtered :: EventFilter (ThreadId -> Bool) -> [(EventId, IntMap Double)]
filtered f = filter (matchesFilter f . fst) (itoList quantTimes)
sumLines :: Maybe Title -> [ReportLine] -> ReportLine
sumLines title qs = ReportLineData {
lineHeader = showTitle "TOTAL" title
, lineEventIds = concatMap lineEventIds qs
, lineBackground = foldr1 combineBG $ map lineBackground qs
, lineValues = IntMap.unionsWith (+) $ map lineValues qs
}
where
combineBG :: Maybe (Int, Int) -> Maybe (Int, Int) -> Maybe (Int, Int)
combineBG (Just (fr, to)) (Just (fr', to')) = Just (min fr fr', max to to')
combineBG _ _ = Nothing
showTitle :: a -> Maybe a -> a
showTitle _ (Just title) = title
showTitle def Nothing = def
{-------------------------------------------------------------------------------
Write the report in textual form
-------------------------------------------------------------------------------}
writeReport :: Report -> FilePath -> IO ()
writeReport report path = withFile path WriteMode $ writeReport' report
writeReport' :: Report -> Handle -> IO ()
writeReport' report h =
mapM_ writeLine
$ mapEithers id (renderTable (AlignLeft : repeat AlignRight))
$ map reportFragment report
where
writeLine :: Either Text [Text] -> IO ()
writeLine (Left header) = T.hPutStrLn h $ "\n" <> header
writeLine (Right cells) = T.hPutStrLn h $ T.intercalate " " cells
reportFragment :: ReportFragment -> Either Text [Text]
reportFragment (ReportSection title) = Left title
reportFragment (ReportLine line) = Right (reportLine line)
reportLine :: ReportLine -> [Text]
reportLine ReportLineData{..} =
lineHeader : unsparse "0.00" (over each showValue lineValues)
showValue :: Double -> Text
showValue = T.pack . printf "%0.2f"