diff --git a/stat-collector-src/Main.hs b/stat-collector-src/Main.hs index e9447e9..64098f5 100644 --- a/stat-collector-src/Main.hs +++ b/stat-collector-src/Main.hs @@ -9,7 +9,7 @@ module Main (main) where -- import Control.Concurrent (threadDelay) import Control.Monad.IO.Class (MonadIO(..)) import Data.Function ((&)) -import Data.List (foldl', findIndex, sortBy) +import Data.List (foldl', findIndex, sortBy, find) import Data.Map (Map) import Data.Maybe (fromJust) import Data.Word (Word8) @@ -48,7 +48,13 @@ data Counter type Tag = String type Value = Double -type EventId = (Tag, Counter) +data EventId = + EventId + { evTid :: Int + , evTag :: Tag + , evCounter :: Counter + } + deriving (Eq, Ord, Show) data Event = Event EventId Value @@ -111,7 +117,7 @@ range n = Fold step initial extract -------------------------------------------------------------------------------- -- Event format: --- STAT/// +-- STAT//// errorString :: String -> String -> String errorString line reason = [str|Error: @@ -126,14 +132,13 @@ parseLineToEvent line = do & Stream.foldMany (Fold.takeEndBy_ (== '/') Fold.toList) & Stream.toList case res of - ["STAT", counter, tag, val] -> - case readMaybe counter :: Maybe Counter of - Just x -> - case readMaybe val :: Maybe Double of - Just y -> pure $ Right $ Event (tag, x) y - Nothing -> - pure $ Left $ errorString line "Not a valid value" - Nothing -> pure $ Left $ errorString line "Not a valid counter" + ["STAT", tid, counter, tag, val] -> + case ( readMaybe counter :: Maybe Counter + , readMaybe val :: Maybe Double + , readMaybe tid :: Maybe Int) of + (Just x, Just y, Just z) -> + pure $ Right $ Event (EventId z tag x) y + _ -> pure $ Left $ errorString line "Not valid" _ -> pure $ Left $ errorString line "Chunks /= 4" parseInputToEventStream :: MonadIO m => Stream m (Array Word8) -> Stream m Event @@ -149,10 +154,24 @@ parseInputToEventStream inp = -- Processing stats -------------------------------------------------------------------------------- +normalizeFreqList :: String -> Int -> [(EventId, Int)] -> [(EventId, Int)] +normalizeFreqList _ _ [] = [] +normalizeFreqList mainTag winSize freqList = + case find ((== mainTag) . evTag . fst) freqList of + Nothing -> error "normalizeFreqList: mainTag does not exist" + Just (_, c) -> mapper c <$> freqList + + where + + mapper baseFreq (evId, freq) = (evId, max (freq `div` baseFreq) 1 * winSize) + + keyCounterFold :: MonadIO m => Fold m Event [(EventId, Int)] keyCounterFold = Fold.lmap getEventId (Fold.foldl' updateCount []) + where + updateCount [] k = (k, 1) : [] updateCount (x@(v, c):xs) k = if k == v @@ -165,15 +184,28 @@ statCollector winSize = winSize (Fold.tee Fold.windowMean (Fold.lmap fst (range winSize))) -eventCollector :: MonadIO m => Int -> Fold m Event (Map EventId StatResult) -eventCollector winSize = - Fold.toMap getEventId (Fold.lmap getEventVal (statCollector winSize)) +eventCollector :: + MonadIO m => [(EventId, Int)] -> Fold m Event (Map EventId StatResult) +eventCollector winSizeList = + Fold.demuxToMap getEventId deriveFold + + where + + deriveFold ev = do + let evId = getEventId ev + winSize = + case lookup evId winSizeList of + Nothing -> error "eventCollector: Key not found" + Just x -> x + pure (Fold.lmap getEventVal (statCollector winSize)) scanStats :: MonadIO m - => Int -> Stream m Event -> Stream m ([(EventId, Int)], Map EventId StatResult) -scanStats winSize = - Stream.postscan (Fold.tee keyCounterFold (eventCollector winSize)) + => [(EventId, Int)] + -> Stream m Event + -> Stream m ([(EventId, Int)], Map EventId StatResult) +scanStats winSizeList = + Stream.postscan (Fold.tee keyCounterFold (eventCollector winSizeList)) -------------------------------------------------------------------------------- -- Printing stats @@ -197,19 +229,25 @@ printTable rows = do separatorRow = map (\n -> replicate n '-') maxLengths fillRow r = zipWith (\n x -> fill n x) maxLengths r -statsToTable :: Counter -> [(EventId, Int)] -> Map EventId StatResult -> [[String]] -statsToTable counter keyTracker mp = - ["Tag", "Mean", "Min", "Max", "Count"] +statsToTable + :: [(EventId, Int)] + -> Counter + -> [(EventId, Int)] + -> Map EventId StatResult + -> [[String]] +statsToTable winSizeList counter keyTracker mp = + ["Tag", "Mean", "Min", "Max", "Count", "Window"] : map - (\(evId@(t, _), (me, rg)) -> + (\(evId@(EventId _ t _), (me, rg)) -> [ t , showFFloat (Just 2) me "" , showMaybe (fmap fst rg) , showMaybe (fmap snd rg) , show $ fromJust $ lookup evId keyTracker + , show $ fromJust $ lookup evId winSizeList ]) (sortBy sorterFunc - $ filter ((== counter) . snd . fst) (Map.toList mp)) + $ filter ((== counter) . evCounter . fst) (Map.toList mp)) where @@ -222,14 +260,20 @@ statsToTable counter keyTracker mp = (Just i, Just j) -> compare (-i) (-j) _ -> error "Key not found" -printSlidingStats :: - Counter -> Int -> Stream IO ([(EventId, Int)], Map EventId StatResult) -> IO () -printSlidingStats counter rowsOnPage strm = +printSlidingStats + :: [(EventId, Int)] + -> Counter + -> Int + -> Stream IO ([(EventId, Int)], Map EventId StatResult) + -> IO () +printSlidingStats winSizeList counter rowsOnPage strm = Stream.fold (Fold.drainMapM (\(order, mp) -> do ANSI.setCursorPosition 0 0 - printTable (take rowsOnPage (statsToTable counter order mp)) + printTable + (take rowsOnPage + (statsToTable winSizeList counter order mp)) hFlush stdout )) strm @@ -241,13 +285,30 @@ printSlidingStats counter rowsOnPage strm = main :: IO () main = do let rowsOnPage = 50 - -- XXX This should be different for different windows. - windowSize = 100 + windowSize = 30 counter = CpuTime + freqMeasurementWindow = 30 + tidToInstrument = 21 + mainTag = "server" + lastCounter = Allocated ANSI.hideCursor ANSI.clearScreen - Stream.unfold Handle.chunkReader stdin + (winSizeList, rest) <- Stream.unfold Handle.chunkReader stdin & parseInputToEventStream - & scanStats windowSize + & Stream.filter ((== tidToInstrument) . evTid . getEventId) + & Stream.foldBreak + (Fold.postscan + keyCounterFold + (Fold.takeEndBy + (\x -> + case find ((== EventId + tidToInstrument + mainTag + lastCounter) . fst) x of + Nothing -> False + Just (_, c) -> c <= freqMeasurementWindow) + (normalizeFreqList mainTag windowSize . fromJust + <$> Fold.latest))) + scanStats winSizeList rest & Stream.sampleIntervalEnd 2 - & printSlidingStats counter rowsOnPage + & printSlidingStats winSizeList counter rowsOnPage