summaryrefslogtreecommitdiff
path: root/utils/heap-view/Graph.lhs
diff options
context:
space:
mode:
Diffstat (limited to 'utils/heap-view/Graph.lhs')
-rw-r--r--utils/heap-view/Graph.lhs165
1 files changed, 165 insertions, 0 deletions
diff --git a/utils/heap-view/Graph.lhs b/utils/heap-view/Graph.lhs
new file mode 100644
index 0000000000..b8e08dbb9b
--- /dev/null
+++ b/utils/heap-view/Graph.lhs
@@ -0,0 +1,165 @@
+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"