+++ /dev/null
-Started 29/11/93:
-
-> module Main where
-> import PreludeGlaST
-> import LibSystem
-
-Program to draw a graph of last @n@ pieces of data from standard input
-continuously.
-
-> n :: Int
-> n = 40
-
-> max_sample :: Int
-> max_sample = 100
-
-> screen_size :: Int
-> screen_size = 200
-
-Version of grapher that can handle the output of ghc's @+RTS -Sstderr@
-option.
-
-Nice variant would be to take a list of numbers from the commandline
-and display several graphs at once.
-
-> main :: IO ()
-> main =
-> getArgs >>= \ r ->
-> case r of
-> [select] ->
-> let selection = read select
-> in
-> xInitialise [] screen_size screen_size >>
-> hGetContents stdin >>= \ input ->
-> graphloop2 (parseGCData selection input) []
-> _ ->
-> error "usage: graph <number in range 0..17>\n"
-
-The format of glhc18's stderr stuff is:
-
--- start of example (view in 120 column window)
-graph +RTS -Sstderr -H500
-
-Collector: APPEL HeapSize: 500 (bytes)
-
- Alloc Collect Live Resid GC GC TOT TOT Page Flts No of Roots Caf Mut- Old Collec Resid
- bytes bytes bytes ency user elap user elap GC MUT Astk Bstk Reg No able Gen tion %heap
- 248 248 60 24.2% 0.00 0.04 0.05 0.23 1 1 1 0 0 1 0 0 Minor
--- end of example
- 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
-
-That is: 6 header lines followed by 17-18 columns of integers,
-percentages, floats and text.
-
-The scaling in the following is largely based on guesses about likely
-values - needs tuned.
-
-@gcParsers@ is a list of functions which parse the corresponding
-column and attempts to scale the numbers into the range $0.0 .. 1.0$.
-(But may return a number avove $1.0$ which graphing part will scale to
-fit screen...)
-
-(Obvious optimisation - replace by list of scaling information!)
-
-(Obvious improvement - return (x,y) pair based on elapsed (or user) time.)
-
-> gcParsers :: [ String -> Float ]
-> gcParsers = [ heap, heap, heap, percent, time, time, time, time, flts, flts, stk, stk, reg, caf, caf, heap, text, percent ]
-> where
-> heap = scale 100000.0 . fromInt . check 0 . readDec
-> stk = scale 25000.0 . fromInt . check 0 . readDec
-> int = scale 1000.0 . fromInt . check 0 . readDec
-> reg = scale 10.0 . fromInt . check 0 . readDec
-> caf = scale 100.0 . fromInt . check 0 . readDec
-> flts = scale 100.0 . fromInt . check 0 . readDec
-> percent = scale 100.0 . check 0.0 . readFloat
-> time = scale 20.0 . check 0.0 . readFloat
-> text s = 0.0
-
-> check :: a -> [(a,String)] -> a
-> check error_value parses =
-> case parses of
-> [] -> error_value
-> ((a,s):_) -> a
-
-> scale :: Float -> Float -> Float
-> scale max n = n / max
-
-> parseGCData :: Int -> String -> [Float]
-> parseGCData column input =
-> map ((gcParsers !! column) . (!! column) . words) (drop 6 (lines input))
-
-Hmmm, how to add logarithmic scaling neatly? Do I still need to?
-
-Note: unpleasant as it is, the code cannot be simplified to something
-like the following. The problem is that the graph won't start to be
-drawn until the first @n@ values are available. (Is there also a
-danger of clearing the screen while waiting for the next input value?)
-A possible alternative solution is to keep count of how many values
-have actually been received.
-
-< graphloop2 :: [Float] -> [Float] -> IO ()
-< graphloop2 [] =
-< return ()
-< graphloop2 ys =
-< let ys' = take n ys
-< m = maximum ys'
-< y_scale = (floor m) + 1
-< y_scale' = fromInt y_scale
-< in
-< xCls >>
-< drawScales y_scale >>
-< draw x_coords [ x / y_scale' | x <- ys' ] >>
-< xHandleEvent >>
-< graphloop2 (tail ys)
-
-
-> graphloop2 :: [Float] -> [Float] -> IO ()
-> graphloop2 (y:ys) xs =
-> let xs' = take n (y:xs)
-> m = maximum xs'
-> y_scale = (floor m) + 1
-> y_scale' = fromInt y_scale
-> in
-> xCls >>
-> drawScales y_scale >>
-> draw x_coords [ x / y_scale' | x <- xs' ] >>
-> xHandleEvent >>
-> graphloop2 ys xs'
-> graphloop2 [] xs =
-> return ()
-
-> x_coords :: [Float]
-> x_coords = [ 0.0, 1 / (fromInt n) .. ]
-
-Draw lines specified by coordinates in range (0.0 .. 1.0) onto screen.
-
-> draw :: [Float] -> [Float] -> IO ()
-> draw xs ys = drawPoly (zip xs' (reverse ys'))
-> where
-> xs' = [ floor (x * sz) | x <- xs ]
-> ys' = [ floor ((1.0 - y) * sz) | y <- ys ]
-> sz = fromInt screen_size
-
-> drawPoly :: [(Int, Int)] -> IO ()
-> drawPoly ((x1,y1):(x2,y2):poly) =
-> xDrawLine x1 y1 x2 y2 >>
-> drawPoly ((x2,y2):poly)
-> drawPoly _ = return ()
-
-Draw horizontal line at major points on y-axis.
-
-> drawScales :: Int -> IO ()
-> drawScales y_scale =
-> sequence (map drawScale ys) >>
-> return ()
-> where
-> ys = [ (fromInt i) / (fromInt y_scale) | i <- [1 .. y_scale - 1] ]
-
-> drawScale :: Float -> IO ()
-> drawScale y =
-> let y' = floor ((1.0 - y) * (fromInt screen_size))
-> in
-> xDrawLine 0 y' screen_size y'
-
->#include "common-bits"