@@ -2,6 +2,7 @@ package main
2
2
3
3
import (
4
4
"bufio"
5
+ "encoding/json"
5
6
"fmt"
6
7
"html/template"
7
8
"regexp"
@@ -12,9 +13,9 @@ import (
12
13
)
13
14
14
15
type templateData struct {
15
- RecordTypes []RecordInfo
16
- Records []heaputil.RecordData
17
- GraphVizContent string
16
+ RecordTypes []RecordInfo
17
+ Records []heaputil.RecordData
18
+ GraphData string
18
19
}
19
20
20
21
func GenerateHTML (records []heaputil.RecordData , graphContent string ) (string , error ) {
@@ -24,9 +25,9 @@ func GenerateHTML(records []heaputil.RecordData, graphContent string) (string, e
24
25
}
25
26
26
27
data := templateData {
27
- RecordTypes : GetUniqueRecordTypes (records ),
28
- Records : records ,
29
- GraphVizContent : graphContent ,
28
+ RecordTypes : GetUniqueRecordTypes (records ),
29
+ Records : records ,
30
+ GraphData : graphContent ,
30
31
}
31
32
32
33
var htmlBuilder strings.Builder
@@ -44,23 +45,17 @@ func GenerateGraph(rd *bufio.Reader) (string, error) {
44
45
return "" , err
45
46
}
46
47
47
- var dotContent strings.Builder
48
-
49
- // Write DOT file header
50
- dotContent .WriteString ("digraph GoHeapDump {\n " )
51
-
52
- // Create the "heap" node as a cluster
53
- dotContent .WriteString (" subgraph cluster_heap {\n " )
54
- dotContent .WriteString (" label=\" Heap\" ;\n " )
55
- dotContent .WriteString (" style=dotted;\n " )
48
+ nodes := []map [string ]interface {}{}
49
+ links := []map [string ]interface {}{}
50
+ nodeMap := make (map [uint64 ]int )
56
51
57
52
var dumpParams * record.DumpParamsRecord
58
53
counter := 0
59
54
60
55
for {
61
56
r , err := record .ReadRecord (rd )
62
57
if err != nil {
63
- return dotContent . String (), err
58
+ break
64
59
}
65
60
66
61
_ , isEOF := r .(* record.EOFRecord )
@@ -73,45 +68,51 @@ func GenerateGraph(rd *bufio.Reader) (string, error) {
73
68
dumpParams = dp
74
69
}
75
70
76
- // Filter out objects. If the record isn't of the type Object, ignore.
77
- _ , isObj := r .(* record.ObjectRecord )
71
+ obj , isObj := r .(* record.ObjectRecord )
78
72
if ! isObj {
79
73
continue
80
74
}
81
75
82
- // Create a DOT node for each record
83
- nodeName := fmt .Sprintf ("Node%d" , counter )
84
- counter ++
85
76
name , address := ParseNameAndAddress (r .Repr ())
86
77
nodeLabel := fmt .Sprintf ("[%s] %s" , name , address )
87
78
88
- // Write DOT node entry within the "heap" cluster
89
- s := fmt .Sprintf (" %s [label=\" %s\" ];\n " , nodeName , nodeLabel )
90
- dotContent .WriteString (s )
79
+ if _ , exists := nodeMap [obj .Address ]; ! exists {
80
+ nodeMap [obj .Address ] = counter
81
+ nodes = append (nodes , map [string ]interface {}{
82
+ "id" : counter ,
83
+ "label" : nodeLabel ,
84
+ "address" : obj .Address ,
85
+ })
86
+ counter ++
87
+ }
91
88
92
- // Check if the record has pointers
93
89
p , isParent := r .(record.ParentGuard )
94
90
if isParent {
95
91
_ , outgoing := record .ParsePointers (p , dumpParams )
96
92
for i := 0 ; i < len (outgoing ); i ++ {
97
93
if outgoing [i ] != 0 {
98
- childNodeName := fmt .Sprintf ("Pointer0x%x" , outgoing [i ])
99
-
100
- // Create an edge from the current record to the child record
101
- s := fmt .Sprintf (" %s -> %s;\n " , nodeName , childNodeName )
102
- dotContent .WriteString (s )
94
+ if targetIndex , exists := nodeMap [outgoing [i ]]; exists {
95
+ links = append (links , map [string ]interface {}{
96
+ "source" : nodeMap [obj .Address ],
97
+ "target" : targetIndex ,
98
+ })
99
+ }
103
100
}
104
101
}
105
102
}
106
103
}
107
104
108
- // Close the "heap" cluster
109
- dotContent .WriteString (" }\n " )
105
+ graphData := map [string ]interface {}{
106
+ "nodes" : nodes ,
107
+ "links" : links ,
108
+ }
110
109
111
- // Write DOT file footer
112
- dotContent .WriteString ("}\n " )
110
+ jsonData , err := json .Marshal (graphData )
111
+ if err != nil {
112
+ return "" , err
113
+ }
113
114
114
- return dotContent . String ( ), nil
115
+ return string ( jsonData ), nil
115
116
}
116
117
117
118
func ParseNameAndAddress (input string ) (name , address string ) {
0 commit comments