packages feed

eventlog2html 0.2.0 → 0.3.0

raw patch · 8 files changed

+119/−59 lines, 8 filesdep +vectordep ~hvegaPVP ok

version bump matches the API change (PVP)

Dependencies added: vector

Dependency ranges changed: hvega

API changes (from Hackage documentation)

- Eventlog.Types: type PartialHeader = Int -> Header
+ Eventlog.Events: instance GHC.Show.Show Eventlog.Events.CostCentre
- Eventlog.Events: chunk :: FilePath -> IO (PartialHeader, [Frame], [Trace])
+ Eventlog.Events: chunk :: FilePath -> IO (Header, Map Text (Double, Double), [Frame], [Trace])
- Eventlog.HeapProf: chunk :: FilePath -> IO (PartialHeader, [Frame], [Trace])
+ Eventlog.HeapProf: chunk :: FilePath -> IO (Header, Map Text (Double, Double), [Frame], [Trace])
- Eventlog.Total: total :: PartialHeader -> [Frame] -> (Header, Map Text (Double, Double))
+ Eventlog.Total: total :: [Frame] -> (Int, Map Text (Double, Double))

Files

CHANGELOG view
@@ -1,3 +1,9 @@+0.3, released 2019-09-08++* Added warnings if eventlog2html is used on eventlogs generated by GHC Version without profiling support.+* Moved to version 0.4 of HVega.+* HeapProfCostCentre and HeapProfSampleCostCentre Events are included  in the generated output.+ 0.2, released 2019-07-05  * Added the commandline option '-o OUTFILE' which writes the output to
eventlog2html.cabal view
@@ -1,5 +1,5 @@ Name:                eventlog2html-Version:             0.2.0+Version:             0.3.0 Synopsis:            Visualise an eventlog Description:         eventlog2html is a library for visualising eventlogs.                      At the moment, the intended use is to visualise eventlogs@@ -46,12 +46,13 @@     filepath             >= 1.4.2 && < 1.5,     ghc-events           >= 0.8.0 && < 0.10,     hashtables           >= 1.2.3 && < 1.3,-    hvega                >= 0.2.0 && < 0.3,+    hvega                >= 0.4.0 && < 0.5,     mtl                  >= 2.2.2 && < 2.3,     optparse-applicative >= 0.14.3 && < 0.15,     semigroups           >= 0.18 && < 0.20,     text                 >= 1.2.3 && < 1.3,-    time                 >= 1.8.0 && < 2.0+    time                 >= 1.8.0 && < 2.0,+    vector               >= 0.11    GHC-options:         -Wall   default-language:    Haskell2010
src/Eventlog/Data.hs view
@@ -10,7 +10,6 @@ import qualified Eventlog.Events as E import qualified Eventlog.HeapProf as H import Eventlog.Prune (prune, cmpName, cmpSize, cmpStdDev)-import Eventlog.Total (total) import Eventlog.Vega import Eventlog.Types (Header) @@ -23,9 +22,7 @@         Size -> cmpSize         StdDev -> cmpStdDev       reversing' = if reversing a then swap else id-  (ph, fs, traces) <- chunk file-  putStrLn (show fs)-  let (h, totals) = total ph fs+  (h,totals, fs, traces) <- chunk file   let keeps = prune cmp 0 (bound $ nBands a) totals   let combinedJson = object [           "samples" .= bandsToVega keeps (bands h keeps fs)
src/Eventlog/Events.hs view
@@ -3,58 +3,100 @@ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE StrictData #-} module Eventlog.Events(chunk) where  import GHC.RTS.Events hiding (Header, header) import Prelude hiding (init, lookup) import qualified Data.Text as T+import Data.Text (Text)+import Data.Map (Map)  import Eventlog.Types+import Eventlog.Total import Data.List import Data.Function import Data.Word import Data.Time import Data.Time.Clock.POSIX+import qualified Data.Map as Map+import Data.Vector.Unboxed (Vector, (!?))+import Data.Maybe+import Data.Version+import Text.ParserCombinators.ReadP+import Control.Monad+import Data.Char+import System.IO +type PartialHeader = Int -> Header+ fromNano :: Word64 -> Double fromNano e = fromIntegral e * 1e-9 -chunk :: FilePath -> IO (PartialHeader, [Frame], [Trace])-chunk f = eventlogToHP . either error id =<< readEventLogFromFile f+chunk :: FilePath -> IO (Header,Map Text (Double, Double), [Frame], [Trace])+chunk f = do+  (EventLog _ e) <- either error id <$> readEventLogFromFile f+  (ph, frames, traces) <- eventsToHP e+  let (counts, totals) = total frames+  return $ (ph counts, totals, frames, traces) -eventlogToHP :: EventLog -> IO (PartialHeader, [Frame], [Trace])-eventlogToHP (EventLog _h e) = do-  eventsToHP e+checkGHCVersion :: EL -> Maybe String+checkGHCVersion EL { ident = Just (version,_)}+  | version <= makeVersion [8,4,4]  =+      Just $ "Warning: The eventlog has been generated with GHC version "+           ++ show version+           ++ ", which does not support profiling events in the eventlog."+checkGHCVersion EL { pargs = Just args, ident = Just (version,_)}+  | version > makeVersion [8,4,4] &&+    version <= makeVersion [8,9,0] &&+    ("-hr" `elem` args || "-hb" `elem` args) =+     Just $ "Warning: The eventlog has been generated with GHC version"+            ++ show version+            ++ ", which does not support biographical or retainer profiling."+checkGHCVersion _ = Nothing + eventsToHP :: Data -> IO (PartialHeader, [Frame], [Trace]) eventsToHP (Data es) = do   let       el@EL{..} = foldEvents es       fir = Frame (fromNano start) []       las = Frame (fromNano end) []+  mapM_ (hPutStrLn stderr) (checkGHCVersion el)   return $ (elHeader el, fir : reverse (las: normalise frames) , traces)  normalise :: [(Word64, [Sample])] -> [Frame] normalise fs = map (\(t, ss) -> Frame (fromNano t) ss) fs + data EL = EL-  { pargs :: Maybe [String]-  , clocktimeSec :: Word64-  , samples :: Maybe (Word64, [Sample])-  , frames :: [(Word64, [Sample])]-  , traces :: [Trace]-  , start :: Word64-  , end :: Word64 } deriving Show+  { pargs :: !(Maybe [String])+  , ident :: Maybe (Version, String)+  , ccMap :: !(Map.Map Word32 CostCentre)+  , clocktimeSec :: !Word64+  , samples :: !(Maybe (Word64, [Sample]))+  , frames :: ![(Word64, [Sample])]+  , traces :: ![Trace]+  , start :: !Word64+  , end :: !Word64 } deriving Show +data CostCentre = CC { cid :: Word32+                     , label :: Text+                     , modul :: Text+                     , loc :: Text } deriving Show+ initEL :: Word64 -> EL initEL t = EL   { pargs = Nothing+  , ident = Nothing   , clocktimeSec = 0   , samples = Nothing   , frames = []   , traces = []   , start = t   , end = 0+  , ccMap = Map.empty   }  foldEvents :: [Event] -> EL@@ -68,16 +110,44 @@   updateLast t .     case e of       -- Traces+      RtsIdentifier _ ident -> addIdent ident       Message s -> addTrace (Trace (fromNano t) (T.pack s))       UserMessage s -> addTrace (Trace (fromNano t) (T.pack s))       HeapProfBegin {} -> addFrame t-      --HeapProfCostCentre {} -> False+      HeapProfCostCentre cid l m loc _  -> addCostCentre cid (CC cid l m loc)       HeapProfSampleBegin {} -> addFrame t-      --HeapProfSampleCostCentre {} -> True+      HeapProfSampleCostCentre _hid r d s -> addCCSample r d s       HeapProfSampleString _hid res k -> addSample (Sample k (fromIntegral res))       ProgramArgs _ as -> addArgs as       WallClockTime _ s _ -> addClocktime s       _ -> id++addIdent :: String -> EL -> EL+addIdent s el = el { ident = parseIdent s }++parseIdent :: String -> Maybe (Version, String)+parseIdent s = listToMaybe $ flip readP_to_S s $ do+  void $ string "GHC-"+  [v1, v2, v3] <- replicateM 3 (intP <* optional (char '.'))+  skipSpaces+  return (makeVersion [v1,v2,v3])+  where+    intP = do+      x <- munch1 isDigit+      return $ read x+++addCostCentre :: Word32 -> CostCentre -> EL -> EL+addCostCentre s cc el = el { ccMap = Map.insert s cc (ccMap el) }++addCCSample :: Word64 -> Word8 -> Vector Word32 -> EL -> EL+addCCSample res _sd st el =+  fromMaybe (addSample (Sample "NONE" (fromIntegral res)) el) $ do+  cid <- st !? 0+  CC{label, modul} <- Map.lookup cid (ccMap el)+  let fmtl = modul <> "." <> label+  return $ addSample (Sample fmtl (fromIntegral res)) el+  addClocktime :: Word64 -> EL -> EL addClocktime s el = el { clocktimeSec = s }
src/Eventlog/HeapProf.hs view
@@ -5,16 +5,19 @@ import Data.Text (Text, lines, init, drop, length, isPrefixOf, unpack, words, pack) import Data.Text.IO (readFile) import Data.Attoparsec.Text (parseOnly, double)+import Data.Map (Map) +import Eventlog.Total import Eventlog.Types -chunk :: FilePath -> IO (PartialHeader, [Frame], [Trace])+chunk :: FilePath -> IO (Header, Map Text (Double, Double), [Frame], [Trace]) chunk f = do   (ph, fs) <- chunkT <$> readFile f+  let (counts, totals) = total fs   -- Heap profiles do not support traces-  return (ph, fs, [])+  return (ph counts, totals, fs, []) -chunkT :: Text -> (PartialHeader, [Frame])+chunkT :: Text -> (Int -> Header, [Frame]) chunkT s =   let ls = lines s       (hs, ss) = splitAt 4 ls
src/Eventlog/Total.hs view
@@ -19,11 +19,11 @@ parse0 :: Parse parse0 = Parse{ symbols = empty, totals = empty, count = 0 } -total :: PartialHeader -> [Frame] -> (Header, Map Text (Double, Double))-total ph fs =+total :: [Frame] -> (Int, Map Text (Double, Double))+total fs =   let parse1 = flip execState parse0 . mapM_ parseFrame $ fs   in  (-       ph (count parse1)+       count parse1       , fmap (stddev $ fromIntegral (count parse1)) (totals parse1)       ) 
src/Eventlog/Types.hs view
@@ -11,8 +11,6 @@   , hCount       :: Int   } -type PartialHeader = Int -> Header- data Sample = Sample Text Double deriving Show  data Frame = Frame Double [Sample] deriving Show
src/Eventlog/VegaTemplate.hs view
@@ -21,9 +21,6 @@ injectJSON :: Text -> Value -> BuildLabelledSpecs injectJSON t val = \x -> x ++ [(t,val)] --- injectJSONs :: [(Text, Value)] -> BuildLabelledSpecs--- injectJSONs ts = \x -> x ++ ts- data AreaChartType   = Stacked   | Normalized@@ -109,7 +106,7 @@     dataFromSource "data_json_samples" [],     VL.mark Line [],     encodingLineLayer c [],-    transformLineLayer+    transformLineLayer []   ]  encodingLineLayer :: ChartConfig -> [LabelledSpec] -> (VLProperty, VLSpec)@@ -120,21 +117,12 @@                   PScale [SDomain (DSelection "brush")]]     . position Y [PName "norm_y", PmType Quantitative, PAxis [AxTitle "Allocation", AxFormat ".1f"]] -transformLineLayer :: (VLProperty, VLSpec)+transformLineLayer :: [LabelledSpec] -> (VLProperty, VLSpec) transformLineLayer =-  -- We need to get the `VLTransform` data constructor but it's not-  -- exported-  let (label, _vs) = transform . filter (FSelection "legend") $ []-  in (label,-  toJSON [object ["window" .= [object ["field" .= String "y"-                                      , "op" .= String "max"-                                      , "as" .= String "max_y"]]-                              , "frame" .= toJSON [Null, Null]-                              , "groupby" .= toJSON [String "k"]]-         , object ["calculate" .= String "datum.y / datum.max_y"-                          , "as" .= String "norm_y"]-         , object ["filter" .= object ["selection" .= String "legend"]]])-+  transform+  . window [([WField "y", WAggregateOp Max], "max_y")] [WFrame Nothing Nothing, WGroupBy ["k"]]+  . calculateAs "datum.y / datum.max_y" "norm_y"+  . filter (FSelection "legend")  ----------------------------------------------------------------------------------- -- The Selection Chart@@ -196,8 +184,10 @@   encoding     . order [OName "k", OmType Quantitative]     . color [MName "c", MmType Nominal, MScale [colourProperty c], MLegend []]-    . injectJSON "tooltip" (toJSON [object ["field" .= String "y", "type" .= String "quantitative", "format" .= String "s", "title" .= String "Allocation"],-                             object ["field" .= String "c", "type" .= String "nominal", "title" .= String "Type"]])+    . tooltips+        [ [TName "y", TmType Quantitative, TFormat "s", TTitle "Allocation"]+        , [TName "c", TmType Nominal, TTitle "Type"]+        ]     . position X [PName "x", PmType Quantitative, PAxis [AxTitle ""]                  , PScale [SDomain (DSelection "brush")]]     . position Y [PName "y"@@ -259,15 +249,10 @@ encodingRight =   encoding   . injectJSON "tooltip" Null-  . injectJSON "color" (object [-                    ("value", String "lightgray")-                    , ("condition", object [-                                           ("aggregate", String "min")-                                           ,("field", String "c")-                                           ,("legend", Null)-                                           ,("selection", String "legend")-                                           ,("type", String "nominal")])-                    ])+  . color+     [+       MSelectionCondition (SelectionName "legend") [MName "c", MmType Nominal, MAggregate Min, MLegend []] [MString "lightgray"]+     ]   . position Y [PName "c"                , PmType Nominal                , PAxis [ AxOrient SRight@@ -276,7 +261,7 @@                        , AxGrid False                        , AxMinExtent 100                        , AxMaxExtent 100]-               , PSort [(ByField "k"), Descending]]+               , PSort [(ByFieldOp "k" Mean), Descending]]  selectionRight :: [LabelledSpec] -> (VLProperty, VLSpec) selectionRight =