packages feed

grapesy-1.0.0: test-stress/Test/Stress/Driver/Summary.hs

{-# LANGUAGE OverloadedStrings #-}

module Test.Stress.Driver.Summary
  ( createSummaryPlots
  , eventlogToSvg
  ) where

import Control.Exception
import Data.ByteString.Lazy.Char8 qualified as BS.Lazy
import Data.List
import Data.Maybe
import Data.Word
import GHC.RTS.Events
import GHC.RTS.Events.Incremental
import Graphics.Rendering.Chart.Backend.Diagrams
import Graphics.Rendering.Chart.Easy hiding ((<.>))
import System.Directory
import System.Exit
import System.FilePath

import Test.Stress.Common

createSummaryPlots :: Bool -> Maybe FilePath -> IO ()
createSummaryPlots v mwd = do
    cwd <- getCurrentDirectory
    let wd = fromMaybe cwd mwd
    putStrLn $ "Creating summary plots in " ++ wd ++ "..."
    wdFiles <- map (wd </>) <$> listDirectory wd
    let wdElFiles = filter (".eventlog" `isSuffixOf`) wdFiles
    say' v $ "found event logs:"
    mapM_ (say' v) $ map ("  " ++) wdElFiles
    mapM_ (\e -> handleFailure e $ eventlogToSvg v e) wdElFiles
  where
    handleFailure :: FilePath -> IO () -> IO ()
    handleFailure f =
        handle $ \case
          e | Just UserInterrupt <- fromException e ->
              exitFailure
            | otherwise -> do
              putStrLn $ "failed to generate summary plot for " ++ f
              print e

eventlogToSvg :: Bool -> FilePath -> IO ()
eventlogToSvg v elFile = do
    say' v $ "generating plot file for " ++ elFile
    elBytes <- BS.Lazy.readFile elFile
    case readEventLog elBytes of
      Right (EventLog _ (Data events), _merr) -> do
        samples <- goEvents emptySamples events
        toFile def (elFile <.> "svg") $ do
          layout_title .= elFile
          layout_x_axis . laxis_title .= "Time (seconds)"
          layout_y_axis . laxis_title .= "Size (megabytes)"
          plot (line "live bytes" [samplesLiveBytes samples])
          plot (line "blocks size" [samplesBlocksSize samples])
          plot (line "heap size" [samplesHeapSize samples])
        say' v $ "finished plot for " ++ elFile
      Left err ->
        putStrLn $
          "Failed to create summary plot from " ++ elFile ++ ": " ++ err
  where
    goEvents :: Samples -> [Event] -> IO Samples
    goEvents acc [] =
        return acc
    goEvents acc (Event t ei _:es) = do
        acc' <- addEvent acc (t,ei)
          `catch` (\(_e :: SomeException) -> return acc)
        goEvents acc' es

    addEvent :: Samples -> (Timestamp, EventInfo) -> IO Samples
    addEvent acc (t, ei) =
        case ei of
          HeapLive _ s -> return $ acc {
              samplesLiveBytes =
                insertBy byFirst
                  (timeConv t, sizeConv s) (samplesLiveBytes acc)
            }
          BlocksSize _ s -> return $ acc {
              samplesBlocksSize =
                insertBy byFirst
                  (timeConv t, sizeConv s) (samplesBlocksSize acc)
            }
          HeapSize _ s -> return $ acc {
              samplesHeapSize =
                insertBy byFirst
                  (timeConv t, sizeConv s) (samplesHeapSize acc)
            }
          _ -> return acc
      where
        timeConv :: Word64 -> Double
        sizeConv :: Word64 -> Double
        timeConv = (/ 1_000_000_000) . fromIntegral -- nanoseconds to seconds
        sizeConv = (/ 1_000_000)     . fromIntegral -- bytes to megabytes

say' :: Bool -> String -> IO ()
say' v = say v . ("(summary) " ++)

byFirst :: Ord a => (a, b) -> (a, b) -> Ordering
byFirst (x1, _) (x2, _) = compare x1 x2

-------------------------------------------------------------------------------
-- Internal auxiliary
-------------------------------------------------------------------------------

data Samples = Samples {
      samplesLiveBytes  :: [(Double, Double)]
    , samplesBlocksSize :: [(Double, Double)]
    , samplesHeapSize   :: [(Double, Double)]
    }

emptySamples :: Samples
emptySamples = Samples [] [] []