module GHC.Vis.View.Graph.Parser (
xDotParse
)
where
import Data.Monoid
import qualified Data.Foldable as F
import qualified Data.IntMap as M
import qualified Data.IntSet as S
import qualified Data.Text.Lazy as B
import Data.Graph.Inductive hiding (edges, newNodes)
import Data.GraphViz hiding (toNode, Ellipse, Polygon, parse)
import Data.GraphViz.Attributes.Complete
import Data.GraphViz.Commands.IO
import GHC.HeapView hiding (name)
import GHC.Vis.Internal (showClosureFields)
import GHC.Vis.Types
import GHC.Vis.View.Common
import Graphics.XDot.Types hiding (name, h, Style, Color)
import Graphics.XDot.Parser
fontName :: B.Text
fontName = "Sans"
graphFontSize :: Double
graphFontSize = 24
nodeFontSize :: Double
nodeFontSize = 24
edgeFontSize :: Double
edgeFontSize = 24
graphvizCommand :: GraphvizCommand
graphvizCommand = Dot
reachableSubgraph :: DynGraph gr => [Node] => gr a b => gr a b
reachableSubgraph roots graph = flip delNodes graph $
filter (`S.notMember` reachableNodes) $
nodes graph
where
trGraph = trc graph
reachableNodes = S.unions $ map (S.fromList . suc trGraph) roots
convertGraph :: HeapGraph Identifier -> Gr ([String], Int) (String, Int)
convertGraph hg = appEndo (removeGarbage <> addNames <> addEdges <> addNodes) empty
where
HeapGraph hgm = hg
addNodes = mconcat [
Endo (insNode (i, toNode hge))
| (i,hge) <- M.toList hgm
]
deref = fmap $ hgeClosure . (M.!) hgm
toNode hge | Just byteCode <- disassembleBCO deref (hgeClosure hge)
= (["BCO"], length (concatMap F.toList byteCode))
| otherwise
= (showClosureFields (hgeClosure hge), length $ allPtrs (hgeClosure hge))
addEdges = mconcat [
Endo (insEdge (i, t, ("",n)))
| (i, hge) <- M.toList hgm
, (t,n) <- toEdges hge
]
toEdges hge = [ (t, n) | (Just t, n) <- zip myPtrs [0..] ]
where myPtrs | Just byteCode <- disassembleBCO deref (hgeClosure hge)
= concatMap F.toList byteCode
| otherwise
= allPtrs (hgeClosure hge)
addNameList = zip [1,2..] $ reverse
[ (i,name)
| (i, hge) <- M.toList hgm
, name <- hgeData hge
]
addNames = mconcat $ map addName addNameList
addName (n,(i,name)) = Endo (insEdge (n, i, (name, 0))) <> Endo (insNode (n, ([""],0)))
removeGarbage = Endo (reachableSubgraph (map fst addNameList))
removeOld :: Eq a => [a] -> Maybe a -> Maybe a
removeOld keys (Just x)
| x `elem` keys = Just x
| otherwise = Nothing
removeOld _ x = x
xDotParse :: [Box] -> IO ([(Object Node, Operation)], [Box], [(Object Node, Rectangle)], Rectangle)
xDotParse hidden = do
(HeapGraph hg'', _) <- getHeapGraph
let hg' = M.filter (\(HeapGraphEntry b _ _ _) -> not $ b `elem` hidden) hg''
let hg = HeapGraph $ M.map (\hge -> hge{hgeClosure = fmap (removeOld $ M.keys hg') (hgeClosure hge)}) hg'
xDot <- graphvizWithHandle graphvizCommand (defaultVis $ convertGraph hg) XDot hGetDot
return (getOperations xDot, getBoxes (HeapGraph hg''), getDimensions xDot, getSize xDot)
getBoxes :: HeapGraph a -> [Box]
getBoxes (HeapGraph hg) = map (\(HeapGraphEntry b _ _ _) -> b) $ M.elems hg
defaultVis :: (Graph gr) => gr ([String], Int) (String, Int) -> DotGraph Node
defaultVis = graphToDot nonClusteredParams
{ globalAttributes = [GraphAttrs [BgColor [toWC $ RGBA 0 0 0 0], FontName fontName, FontSize graphFontSize]]
, fmtNode = \(x,(l,i)) -> if x >= 0 then [
nodeLabel l i,
Shape Record, FontName fontName, FontSize nodeFontSize]
else [Shape PointShape, Style [SItem Invisible []]]
, fmtEdge = \ (_,_,(l,i)) -> [
TailPort (LabelledPort (PN (B.pack (show i))) Nothing),
toLabel l,
FontName fontName, FontSize edgeFontSize]
}
where
nodeLabel ls i = Label $ RecordLabel $
[FieldLabel (B.pack l) | l <- ls] ++
[ PortName (PN (B.pack (show j))) | j <- [0..i1]]