Skip to content

Commit 86a4d11

Browse files
committed
add TraverseState debug utility module to generate reachability graphs
1 parent 81c142a commit 86a4d11

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

external-stg-interpreter/external-stg-interpreter.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ library
3030
Stg.Interpreter.Debugger.Internal
3131
Stg.Interpreter.Debugger.Region
3232
Stg.Interpreter.Debugger.UI
33+
Stg.Interpreter.Debugger.TraverseState
3334
Stg.Interpreter.GC
3435
Stg.Interpreter.GC.GCRef
3536
Stg.Interpreter.GC.LiveDataAnalysis
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
{-# LANGUAGE RecordWildCards, LambdaCase, OverloadedStrings #-}
2+
module Stg.Interpreter.Debugger.TraverseState
3+
( exportReachableGraph
4+
) where
5+
6+
import Control.Monad.State
7+
import Data.Set (Set)
8+
import qualified Data.Set as Set
9+
import qualified Data.IntMap as IntMap
10+
import qualified Data.ByteString.Char8 as BS8
11+
import System.IO
12+
import Stg.Interpreter.Base
13+
import Stg.Interpreter.GC.GCRef
14+
15+
{-
16+
export GCSymbol's reachability graph as gephi compatible .tsv file
17+
-}
18+
19+
type ExportM = StateT (Set GCSymbol) IO
20+
21+
exportReachableGraph :: FilePath -> FilePath -> StgState -> GCSymbol -> IO ()
22+
exportReachableGraph nodesFname edgesFname stgState root = do
23+
withFile edgesFname WriteMode $ \hEdge -> do
24+
withFile nodesFname WriteMode $ \hNode -> do
25+
BS8.hPutStrLn hNode $ BS8.intercalate "\t"
26+
[ "Id"
27+
, "Label"
28+
, "partition2"
29+
]
30+
BS8.hPutStrLn hEdge $ BS8.intercalate "\t"
31+
[ "Source"
32+
, "Target"
33+
, "partition2"
34+
]
35+
evalStateT (addEdgesFrom hNode hEdge stgState root True) Set.empty
36+
{-
37+
special case: if gcsymbol has no children then emit one node only
38+
otherwise: leaves will always be included
39+
40+
OR:
41+
the graph should be empty if the object has no internal structure
42+
-}
43+
44+
mark :: GCSymbol -> ExportM Bool
45+
mark symbol = state $ \visitedSet ->
46+
let wasVisited = Set.member symbol visitedSet
47+
in (not wasVisited, if wasVisited then visitedSet else Set.insert symbol visitedSet)
48+
49+
addEdgesFrom :: Handle -> Handle -> StgState -> GCSymbol -> Bool -> ExportM ()
50+
addEdgesFrom hNode hEdge stgState@StgState{..} source isRoot = do
51+
firstTimeVisit <- mark source
52+
when firstTimeVisit $ do
53+
liftIO $ print source
54+
55+
let (ns, idx) = decodeRef source
56+
(nodeLabel, nodeCategory) = case ns of
57+
NS_HeapPtr
58+
| Just ho <- IntMap.lookup idx ssHeap
59+
-> (getHeapObjectSummary ho, getHeapObjectCategory ho)
60+
_ -> (drop 3 $ show ns, drop 3 $ show ns)
61+
62+
-- HINT: write line to node .tsv
63+
liftIO $ do
64+
BS8.hPut hNode $ unGCSymbol source
65+
BS8.hPut hNode "\t"
66+
hPutStr hNode $ if isRoot then "Root " ++ nodeLabel else nodeLabel
67+
BS8.hPut hNode "\t"
68+
hPutStr hNode nodeCategory
69+
BS8.hPut hNode "\n"
70+
71+
-- TODO: generate Source node attributes ; or get
72+
let emitEdge :: VisitGCRef a => Maybe a -> ExportM ()
73+
emitEdge Nothing = error $ "missing StgState item: " ++ show (ns, idx)
74+
emitEdge obj = flip visitGCRef obj $ \target -> do
75+
-- HINT: write line to edge .tsv
76+
liftIO $ do
77+
BS8.hPut hEdge $ unGCSymbol source
78+
BS8.hPut hEdge "\t"
79+
BS8.hPut hEdge $ unGCSymbol target
80+
BS8.hPut hEdge "\t"
81+
BS8.hPut hEdge "green"
82+
BS8.hPut hEdge "\n"
83+
addEdgesFrom hNode hEdge stgState target False
84+
85+
case ns of
86+
NS_Array -> emitEdge $ IntMap.lookup idx ssArrays
87+
NS_ArrayArray -> emitEdge $ IntMap.lookup idx ssArrayArrays
88+
NS_HeapPtr -> emitEdge $ IntMap.lookup idx ssHeap
89+
NS_MutableArray -> emitEdge $ IntMap.lookup idx ssMutableArrays
90+
NS_MutableArrayArray -> emitEdge $ IntMap.lookup idx ssMutableArrayArrays
91+
NS_MutableByteArray -> pure () -- IntMap.lookup idx ssMutableByteArrays
92+
NS_MutVar -> emitEdge $ IntMap.lookup idx ssMutVars
93+
NS_TVar -> emitEdge $ IntMap.lookup idx ssTVars
94+
NS_MVar -> emitEdge $ IntMap.lookup idx ssMVars
95+
NS_SmallArray -> emitEdge $ IntMap.lookup idx ssSmallArrays
96+
NS_SmallMutableArray -> emitEdge $ IntMap.lookup idx ssSmallMutableArrays
97+
{-
98+
NS_StableName
99+
| Just obj <- IntMap.lookup idx -- TODO
100+
-}
101+
NS_StablePointer -> emitEdge $ IntMap.lookup idx ssStablePointers
102+
NS_WeakPointer -> emitEdge $ IntMap.lookup idx ssWeakPointers
103+
NS_Thread -> emitEdge $ IntMap.lookup idx ssThreads
104+
105+
_ -> error $ "unknown StgState item: " ++ show (ns, idx)
106+
107+
getHeapObjectSummary :: HeapObject -> String
108+
getHeapObjectSummary = \case
109+
Con{..} -> "Con: " ++ show hoCon
110+
Closure{..} -> if hoCloMissing == 0
111+
then "Thunk: " ++ show hoName
112+
else "Closure: " ++ show hoName
113+
BlackHole{} -> "BlackHole"
114+
ApStack{} -> "ApStack"
115+
RaiseException{} -> "RaiseException"
116+
117+
getHeapObjectCategory :: HeapObject -> String
118+
getHeapObjectCategory = \case
119+
Con{} -> "Constructor"
120+
Closure{..} -> if hoCloMissing == 0 then "Thunk" else "Closure"
121+
BlackHole{} -> "BlackHole"
122+
ApStack{} -> "ApStack"
123+
RaiseException{} -> "Exception"

0 commit comments

Comments
 (0)