{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE MultiWayIf #-}
module GHC.RTS.Events.Incremental
(
Decoder(..)
, decodeHeader
, decodeEvents
, decodeEventLog
, readHeader
, readEvents
, readEventLog
) where
import Control.Monad
import Data.Either
import Data.Maybe
import Prelude
import qualified Data.Binary.Get as G
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Internal as BL
import qualified Data.IntMap.Strict as IM
import GHC.RTS.EventParserUtils
import GHC.RTS.EventTypes
import GHC.RTS.Events.Binary
#define EVENTLOG_CONSTANTS_ONLY
#include "EventLogFormat.h"
data Decoder a
= Consume (B.ByteString -> Decoder a)
| Produce !a (Decoder a)
| Done B.ByteString
| Error B.ByteString String
pushChunk :: Decoder a -> B.ByteString -> Decoder a
pushChunk decoder chunk = case decoder of
Consume k -> k chunk
Produce a decoder' -> Produce a $ decoder' `pushChunk` chunk
Done leftover -> Done $ leftover `B.append` chunk
Error leftover err -> Error (leftover `B.append` chunk) err
withHeader
:: (Header -> B.ByteString -> Decoder r)
-> Decoder r
withHeader f = go $ G.runGetIncremental getHeader
where
go decoder = case decoder of
G.Done leftover _ header -> f header leftover
G.Partial k -> Consume $ \chunk -> go $ k $ Just chunk
G.Fail leftover _ err -> Error leftover err
decodeHeader :: Decoder Header
decodeHeader = withHeader $ \header leftover -> Produce header $ Done leftover
decodeEvents :: Header -> Decoder Event
decodeEvents header = go (0 :: Int) Nothing decoder0
where
decoder0 = mkEventDecoder header
go !remaining !blockCap decoder = case decoder of
G.Done leftover consumed r -> do
let !decoder' = decoder0 `G.pushChunk` leftover
case r of
Just event -> case evSpec event of
EventBlock {..} ->
go (fromIntegral block_size) (mkCap cap) decoder'
_ -> do
let
!remaining' = remaining - fromIntegral consumed
!blockCap' = if remaining' > 0 then blockCap else Nothing
!event' = event { evCap = blockCap }
Produce event' $ go remaining' blockCap' decoder'
Nothing -> go remaining blockCap decoder'
G.Partial k ->
Consume $ \chunk -> go remaining blockCap $ k $ Just chunk
G.Fail leftover _ err ->
Error leftover err
decodeEventLog :: Decoder Event
decodeEventLog = withHeader $ \header leftover ->
decodeEvents header `pushChunk` leftover
readHeader :: BL.ByteString -> Either String (Header, BL.ByteString)
readHeader = go $ Left decodeHeader
where
go r bytes = case r of
Left decoder -> case decoder of
Produce header decoder' -> case decoder' of
Done leftover -> Right (header, BL.Chunk leftover bytes)
_ -> Left "readHeader: unexpected decoder"
Consume k -> case bytes of
BL.Empty -> Left "readHeader: not enough bytes"
BL.Chunk chunk chunks -> go (Left $! k chunk) chunks
Done _ -> Left "readHeader: unexpected termination"
Error _ err -> Left err
Right header -> Right (header, bytes)
readEvents :: Header -> BL.ByteString -> ([Event], Maybe String)
readEvents header = f . break isLeft . go (decodeEvents header)
where
f (rs, ls) = (rights rs, listToMaybe (lefts ls))
#if !MIN_VERSION_base(4, 7, 0)
isLeft (Left _) = True
isLeft _ = False
#endif
go :: Decoder Event -> BL.ByteString -> [Either String Event]
go decoder bytes = case decoder of
Produce event decoder' -> Right event : go decoder' bytes
Consume k -> case bytes of
BL.Empty -> []
BL.Chunk chunk chunks -> go (k chunk) chunks
Done {} -> []
Error _ err -> [Left err]
readEventLog :: BL.ByteString -> Either String (EventLog, Maybe String)
readEventLog bytes = do
(header, bytes') <- readHeader bytes
case readEvents header bytes' of
(events, err) -> return (EventLog header (Data events), err)
mkEventDecoder :: Header -> G.Decoder (Maybe Event)
mkEventDecoder header = G.runGetIncremental $ getEvent parsers
where
imap = IM.fromList [(fromIntegral (num t), t) | t <- eventTypes header]
is_ghc_6 = Just sz_old_tid == do
create_et <- IM.lookup EVENT_CREATE_THREAD imap
size create_et
is_pre77 = IM.notMember EVENT_USER_MARKER imap
is_ghc782 = IM.member EVENT_USER_MARKER imap
&& IM.notMember EVENT_HACK_BUG_T9003 imap
stopParsers
| is_pre77 = pre77StopParsers
| is_ghc782 = [ghc782StopParser]
| otherwise = [post782StopParser]
event_parsers
| is_ghc_6 = concat
[ standardParsers
, ghc6Parsers
, parRTSParsers sz_old_tid
]
| otherwise = concat
[ standardParsers
, ghc7Parsers
, stopParsers
, parRTSParsers sz_tid
, mercuryParsers
, perfParsers
, heapProfParsers
, timeProfParsers
]
parsers = EventParsers $ mkEventTypeParsers imap event_parsers