1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
|
module Events.EventTree (
DurationTree(..),
mkDurationTree,
runTimeOf, gcTimeOf,
reportDurationTree,
durationTreeCountNodes,
durationTreeMaxDepth,
EventTree(..), EventNode(..),
mkEventTree,
reportEventTree, eventTreeMaxDepth,
) where
import Events.EventDuration
import qualified GHC.RTS.Events as GHC
import GHC.RTS.Events hiding (Event)
import Text.Printf
import Control.Exception (assert)
-------------------------------------------------------------------------------
-- We map the events onto a binary search tree, so that we can easily
-- find the events that correspond to a particular view of the
-- timeline. Additionally, each node of the tree contains a summary
-- of the information below it, so that we can render views at various
-- levels of resolution. For example, if a tree node would represent
-- less than one pixel on the display, there is no point is descending
-- the tree further.
-- We only split at event boundaries; we never split an event into
-- multiple pieces. Therefore, the binary tree is only roughly split
-- by time, the actual split depends on the distribution of events
-- below it.
data DurationTree
= DurationSplit
{-#UNPACK#-}!Timestamp -- The start time of this run-span
{-#UNPACK#-}!Timestamp -- The time used to split the events into two parts
{-#UNPACK#-}!Timestamp -- The end time of this run-span
DurationTree -- The LHS split; all events lie completely between
-- start and split
DurationTree -- The RHS split; all events lie completely between
-- split and end
{-#UNPACK#-}!Timestamp -- The total amount of time spent running a thread
{-#UNPACK#-}!Timestamp -- The total amount of time spend in GC
| DurationTreeLeaf
EventDuration
| DurationTreeEmpty
deriving Show
-------------------------------------------------------------------------------
mkDurationTree :: [EventDuration] -> Timestamp -> DurationTree
mkDurationTree es endTime =
-- trace (show tree) $
tree
where
tree = splitDurations es endTime
splitDurations :: [EventDuration] -- events
-> Timestamp -- end time of last event in the list
-> DurationTree
splitDurations [] _endTime =
-- if len /= 0 then error "splitDurations0" else
DurationTreeEmpty -- The case for an empty list of events.
splitDurations [e] _entTime =
DurationTreeLeaf e
splitDurations es endTime
| null rhs
= splitDurations es lhs_end
| null lhs
= error $
printf "splitDurations: null lhs: len = %d, startTime = %d, endTime = %d\n"
(length es) startTime endTime
++ '\n': show es
| otherwise
= -- trace (printf "len = %d, startTime = %d, endTime = %d, lhs_len = %d\n" len startTime endTime lhs_len) $
assert (length lhs + length rhs == length es) $
DurationSplit startTime
lhs_end
endTime
ltree
rtree
runTime
gcTime
where
startTime = startTimeOf (head es)
splitTime = startTime + (endTime - startTime) `div` 2
(lhs, lhs_end, rhs) = splitDurationList es [] splitTime 0
ltree = splitDurations lhs lhs_end
rtree = splitDurations rhs endTime
runTime = runTimeOf ltree + runTimeOf rtree
gcTime = gcTimeOf ltree + gcTimeOf rtree
splitDurationList :: [EventDuration]
-> [EventDuration]
-> Timestamp
-> Timestamp
-> ([EventDuration], Timestamp, [EventDuration])
splitDurationList [] acc !_tsplit !tmax
= (reverse acc, tmax, [])
splitDurationList [e] acc !_tsplit !tmax
-- Just one event left: put it on the right. This ensures that we
-- have at least one event on each side of the split.
= (reverse acc, tmax, [e])
splitDurationList (e:es) acc !tsplit !tmax
| tstart <= tsplit -- pick all events that start at or before the split
= splitDurationList es (e:acc) tsplit (max tmax tend)
| otherwise
= (reverse acc, tmax, e:es)
where
tstart = startTimeOf e
tend = endTimeOf e
-------------------------------------------------------------------------------
runTimeOf :: DurationTree -> Timestamp
runTimeOf (DurationSplit _ _ _ _ _ runTime _) = runTime
runTimeOf (DurationTreeLeaf e) | ThreadRun{} <- e = durationOf e
runTimeOf _ = 0
-------------------------------------------------------------------------------
gcTimeOf :: DurationTree -> Timestamp
gcTimeOf (DurationSplit _ _ _ _ _ _ gcTime) = gcTime
gcTimeOf (DurationTreeLeaf e) | isGCDuration e = durationOf e
gcTimeOf _ = 0
-------------------------------------------------------------------------------
reportDurationTree :: Int -> DurationTree -> IO ()
reportDurationTree hecNumber eventTree
= putStrLn ("HEC " ++ show hecNumber ++ reportText)
where
reportText = " nodes = " ++ show (durationTreeCountNodes eventTree) ++
" max depth = " ++ show (durationTreeMaxDepth eventTree)
-------------------------------------------------------------------------------
durationTreeCountNodes :: DurationTree -> Int
durationTreeCountNodes (DurationSplit _ _ _ lhs rhs _ _)
= 1 + durationTreeCountNodes lhs + durationTreeCountNodes rhs
durationTreeCountNodes _ = 1
-------------------------------------------------------------------------------
durationTreeMaxDepth :: DurationTree -> Int
durationTreeMaxDepth (DurationSplit _ _ _ lhs rhs _ _)
= 1 + durationTreeMaxDepth lhs `max` durationTreeMaxDepth rhs
durationTreeMaxDepth _ = 1
-------------------------------------------------------------------------------
data EventTree
= EventTree
{-#UNPACK#-}!Timestamp -- The start time of this run-span
{-#UNPACK#-}!Timestamp -- The end time of this run-span
EventNode
data EventNode
= EventSplit
{-#UNPACK#-}!Timestamp -- The time used to split the events into two parts
EventNode -- The LHS split; all events lie completely between
-- start and split
EventNode -- The RHS split; all events lie completely between
-- split and end
| EventTreeLeaf [GHC.Event]
-- sometimes events happen "simultaneously" (at the same time
-- given the resolution of our clock source), so we can't
-- separate them.
| EventTreeOne GHC.Event
-- This is a space optimisation for the common case of
-- EventTreeLeaf [e].
mkEventTree :: [GHC.Event] -> Timestamp -> EventTree
mkEventTree es endTime =
EventTree s e $
-- trace (show tree) $
tree
where
tree = splitEvents es endTime
(s,e) = if null es then (0,0) else (time (head es), endTime)
splitEvents :: [GHC.Event] -- events
-> Timestamp -- end time of last event in the list
-> EventNode
splitEvents [] !_endTime =
-- if len /= 0 then error "splitEvents0" else
EventTreeLeaf [] -- The case for an empty list of events
splitEvents [e] !_endTime =
EventTreeOne e
splitEvents es !endTime
| duration == 0
= EventTreeLeaf es
| null rhs
= splitEvents es lhs_end
| null lhs
= error $
printf "splitEvents: null lhs: len = %d, startTime = %d, endTime = %d\n"
(length es) startTime endTime
++ '\n': show es
| otherwise
= -- trace (printf "len = %d, startTime = %d, endTime = %d, lhs_len = %d\n" len startTime endTime lhs_len) $
assert (length lhs + length rhs == length es) $
EventSplit (time (head rhs))
ltree
rtree
where
-- | Integer division, rounding up.
divUp :: Timestamp -> Timestamp -> Timestamp
divUp n k = (n + k - 1) `div` k
startTime = time (head es)
splitTime = startTime + (endTime - startTime) `divUp` 2
duration = endTime - startTime
(lhs, lhs_end, rhs) = splitEventList es [] splitTime 0
ltree = splitEvents lhs lhs_end
rtree = splitEvents rhs endTime
splitEventList :: [GHC.Event]
-> [GHC.Event]
-> Timestamp
-> Timestamp
-> ([GHC.Event], Timestamp, [GHC.Event])
splitEventList [] acc !_tsplit !tmax
= (reverse acc, tmax, [])
splitEventList [e] acc !_tsplit !tmax
-- Just one event left: put it on the right. This ensures that we
-- have at least one event on each side of the split.
= (reverse acc, tmax, [e])
splitEventList (e:es) acc !tsplit !tmax
| t <= tsplit -- pick all events that start at or before the split
= splitEventList es (e:acc) tsplit (max tmax t)
| otherwise
= (reverse acc, tmax, e:es)
where
t = time e
-------------------------------------------------------------------------------
reportEventTree :: Int -> EventTree -> IO ()
reportEventTree hecNumber (EventTree _ _ eventTree)
= putStrLn ("HEC " ++ show hecNumber ++ reportText)
where
reportText = " nodes = " ++ show (eventTreeCountNodes eventTree) ++
" max depth = " ++ show (eventNodeMaxDepth eventTree)
-------------------------------------------------------------------------------
eventTreeCountNodes :: EventNode -> Int
eventTreeCountNodes (EventSplit _ lhs rhs)
= 1 + eventTreeCountNodes lhs + eventTreeCountNodes rhs
eventTreeCountNodes _ = 1
-------------------------------------------------------------------------------
eventTreeMaxDepth :: EventTree -> Int
eventTreeMaxDepth (EventTree _ _ t) = eventNodeMaxDepth t
eventNodeMaxDepth :: EventNode -> Int
eventNodeMaxDepth (EventSplit _ lhs rhs)
= 1 + eventNodeMaxDepth lhs `max` eventNodeMaxDepth rhs
eventNodeMaxDepth _ = 1
|