module ReadEvents (
registerEventsFromFile, registerEventsFromTrace
) where
import EventTree
import State
import TestEvents
import EventDuration
import Timeline
import Traces
import Utils
import Graphics.UI.Gtk hiding (on)
import qualified GHC.RTS.Events as GHCEvents
import GHC.RTS.Events hiding (Event)
import System.IO
import Data.Array
import qualified Data.Function
import Data.IORef
import Data.List
import Text.Printf
import System.FilePath
import Control.Monad
import Data.Function
import Control.Concurrent
import Control.Exception
-------------------------------------------------------------------------------
-- The GHC.RTS.Events library returns the profile information
-- in a data-streucture which contains a list data structure
-- representing the events i.e. [GHCEvents.Event]
-- ThreadScope transforms this list into an alternative representation
-- which (for each HEC) records event *durations* which are ordered in time.
-- The durations represent the run-lengths for thread execution and
-- run-lengths for garbage colleciton. This data-structure is called
-- EventDuration.
-- ThreadScope then transformations this data-structure into another
-- data-structure which gives a binary-tree view of the event information
-- by performing a binary split on the time domain i.e. the EventTree
-- data structure.
-- GHCEvents.Event => [EventDuration] => EventTree
-------------------------------------------------------------------------------
rawEventsToHECs :: [(Maybe Int, [GHCEvents.Event])] -> Timestamp
-> [(DurationTree,EventTree)]
rawEventsToHECs eventList endTime
= map (toTree . flip lookup heclists) [0 .. maximum0 (map fst heclists)]
where
heclists = [ (h,events) | (Just h,events) <- eventList ]
toTree Nothing = (DurationTreeEmpty, EventTree 0 0 (EventTreeLeaf []))
toTree (Just evs) =
( mkDurationTree (eventsToDurations nondiscrete) endTime,
mkEventTree discrete endTime )
where (discrete,nondiscrete) = partition isDiscreteEvent evs
-------------------------------------------------------------------------------
-- XXX: what's this for?
maximum0 :: (Num a, Ord a) => [a] -> a
maximum0 [] = -1
maximum0 x = maximum x
-------------------------------------------------------------------------------
registerEventsFromFile :: String -> ViewerState -> IO ()
registerEventsFromFile filename state = registerEvents (Left filename) state
registerEventsFromTrace :: String -> ViewerState -> IO ()
registerEventsFromTrace traceName state = registerEvents (Right traceName) state
registerEvents :: Either FilePath String
-> ViewerState
-> IO ()
registerEvents from state@ViewerState{..} = do
let msg = case from of
Left filename -> filename
Right test -> test
-- dialog <- messageDialogNew Nothing [DialogModal] MessageInfo ButtonsCancel msg
dialog <- dialogNew
dialogAddButton dialog "gtk-cancel" ResponseCancel
widgetSetSizeRequest dialog 400 (-1)
upper <- dialogGetUpper dialog
hbox <- hBoxNew True 0
label <- labelNew Nothing
miscSetAlignment label 0 0.5
miscSetPadding label 20 0
labelSetMarkup label $
printf "<big><b>Loading %s</b></big>" (takeFileName msg)
boxPackStart upper label PackGrow 10
boxPackStart upper hbox PackNatural 10
progress <- progressBarNew
boxPackStart hbox progress PackGrow 20
widgetShowAll upper
progressBarSetText progress msg
set dialog [ dialogHasSeparator := False ]
timeout <- timeoutAdd (do progressBarPulse progress; return True) 50
windowSetTitle dialog "ThreadScope"
withBackgroundProcessing $ do
t <- forkIO $ buildEventLog from dialog progress state
`onException` dialogResponse dialog (ResponseUser 1)
r <- dialogRun dialog
case r of
ResponseUser 1 -> return ()
_ -> killThread t
widgetDestroy dialog
timeoutRemove timeout
-------------------------------------------------------------------------------
-- NB. Runs in a background thread, can call GUI functions only with
-- postGUI.
--
buildEventLog :: DialogClass dialog => Either FilePath String
-> dialog
-> ProgressBar -> ViewerState -> IO ()
buildEventLog from dialog progress state@ViewerState{..} =
case from of
Right test -> build test (testTrace test)
Left filename -> do
postGUISync $ progressBarSetText progress $ "Reading " ++ filename
fmt <- readEventLogFromFile filename
case fmt of
Left err -> hPutStr stderr err
Right evs -> build filename evs
where
build name evs = do
let
eventBlockEnd e | EventBlock{ end_time=t } <- spec e = t
eventBlockEnd e = time e
lastTx = maximum (0 : map eventBlockEnd (events (dat evs)))
groups = groupEvents (events (dat evs))
trees = rawEventsToHECs groups lastTx
-- sort the events by time and put them in an array
sorted = sortGroups groups
n_events = length sorted
event_arr = listArray (0, n_events-1) sorted
hec_count = length trees
hecs = HECs {
hecCount = hec_count,
hecTrees = trees,
hecEventArray = event_arr,
hecLastEventTime = lastTx
}
treeProgress :: ProgressBar -> Int -> (DurationTree,EventTree) -> IO ()
treeProgress progress hec (tree1,tree2) = do
postGUISync $ progressBarSetText progress $
printf "Building HEC %d/%d" (hec+1) hec_count
progressBarSetFraction progress $
fromIntegral hec / fromIntegral hec_count
evaluate tree1
evaluate (eventTreeMaxDepth tree2)
return ()
zipWithM_ (treeProgress progress) [0..] trees
postGUISync $ do
windowSetTitle mainWindow ("ThreadScope - " ++ takeFileName name)
ctx <- statusbarGetContextId statusBar "file"
statusbarPush statusBar ctx $
printf "%s (%d events, %.3fs)" name n_events
((fromIntegral lastTx :: Double) * 1.0e-9)
newHECs state hecs
timelineParamsChanged state
when debug $ zipWithM_ reportDurationTree [0..] (map fst trees)
when debug $ zipWithM_ reportEventTree [0..] (map snd trees)
writeIORef hecsIORef (Just hecs)
writeIORef scaleIORef defaultScaleValue
dialogResponse dialog (ResponseUser 1)
-------------------------------------------------------------------------------