module GHC.Debug.GML (writeTpfToGML, addSourceInfo, typePointsFromToGML) where
import GHC.Debug.TypePointsFrom as TPF
import GHC.Debug.Types (SourceInformation(..))
import GHC.Debug.Types.Closures (Size(..))
import GHC.Debug.Profile.Types (CensusStats(..), Count(..))
import GHC.Debug.Client.Monad
import GHC.Debug.Client.Query (getSourceInfo, gcRoots)
import Data.Map as Map
import Data.Int (Int32)
import Data.Semigroup
import qualified Data.Map.Monoidal.Strict as MMap
import qualified Data.Foldable as F
import System.IO
type SourceInfoMap = Map.Map TPF.Key SourceInformation
typePointsFromToGML :: FilePath -> Debuggee -> IO ()
typePointsFromToGML path e = do
(tpf, infoMap) <- run e $ do
roots <- gcRoots
tpf <- TPF.typePointsFrom roots
si <- addSourceInfo tpf
return (tpf, si)
writeTpfToGML path tpf infoMap
-- | Exports TypePointsFrom graph to a GML file
addSourceInfo :: TypePointsFrom -> DebugM SourceInfoMap
addSourceInfo tpf = do
-- Generate a map of InfoTablePtr to SourceInformation pairs
let ptrs = MMap.keys . TPF.nodes $ tpf
infos <- mapM getSourceInfo ptrs
let kvPairs :: [(TPF.Key, SourceInformation)]
kvPairs = do
(k, Just si) <- zip ptrs infos
return (k, si)
infoMap :: SourceInfoMap
infoMap = Map.fromList kvPairs
return (infoMap)
writeTpfToGML :: FilePath -> TPF.TypePointsFrom -> SourceInfoMap -> IO ()
writeTpfToGML path tpf infoMap = do
outHandle <- openFile path WriteMode
writeGML outHandle
hClose outHandle
where
ixMap :: Map.Map TPF.Key Int32
ixMap = Map.fromList $ zip ((MMap.keys . nodes) tpf) [1..]
lookupId :: TPF.Key -> Int32
lookupId key' = case Map.lookup key' ixMap of
Nothing -> error "This shouldn't happen, see function ixMap"
Just i -> i
writeGML :: Handle -> IO ()
writeGML outHandle = do
let nodesKvPairs = MMap.assocs . TPF.nodes $ tpf
edgesKvPairs = MMap.assocs . TPF.edges $ tpf
-- Beginning of GML file
hPutStrLn stderr $ "Writing to file " <> path <> "..."
writeOpenGML
F.forM_ nodesKvPairs (uncurry writeNode)
F.forM_ edgesKvPairs (uncurry writeEdge)
writeCloseGML
hPutStrLn stderr $ "Finished writing to GML file..."
-- End of GML file
where
write = hPutStr outHandle
writeOpenGML =
write $ "graph[\n"
<> "comment \"this is a graph in GML format\"\n"
<> "directed 1\n"
writeCloseGML =
write $ "]\n"
writeNode :: TPF.Key -> CensusStats -> IO ()
writeNode key' cs =
write $ "node [\n"
<> "id " <> showPtr key' <> "\n"
<> gmlShowCensus cs
<> gmlShowSourceInfo key'
<> "]\n"
writeEdge :: TPF.Edge -> CensusStats -> IO ()
writeEdge edge cs =
write $ "edge [\n"
<> "source " <> (showPtr . TPF.edgeSource) edge <> "\n"
<> "target " <> (showPtr . TPF.edgeTarget) edge <> "\n"
<> gmlShowCensus cs
<> "]\n"
gmlShowCensus :: CensusStats -> String
gmlShowCensus (CS (Count c) (Size s) (Max (Size m)) _) =
"count " <> show c <> "\n"
<> "size " <> show s <> "\n"
<> "max " <> show m <> "\n"
gmlShowSourceInfo :: TPF.Key -> String
gmlShowSourceInfo key = case Map.lookup key infoMap of
Nothing -> mempty
Just si -> "infoName \"" <> infoName si <> "\"\n"
<> "infoClosureType \"" <> (show . infoClosureType) si <> "\"\n"
<> "infoType \"" <> infoType si <> "\"\n"
<> "infoLabel \"" <> infoLabel si <> "\"\n"
<> "infoModule \"" <> infoModule si <> "\"\n"
<> "infoPosition \"" <> infoPosition si <> "\"\n"
showPtr :: TPF.Key -> String
showPtr = show . lookupId