Skip to content

Commit ecb4667

Browse files
authored
QoL improvements: interactive graph and search (#1)
* add interactive graph and search * update screenshot * change link color
1 parent a44851c commit ecb4667

File tree

3 files changed

+238
-59
lines changed

3 files changed

+238
-59
lines changed

html.go

+36-35
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"bufio"
5+
"encoding/json"
56
"fmt"
67
"html/template"
78
"regexp"
@@ -12,9 +13,9 @@ import (
1213
)
1314

1415
type templateData struct {
15-
RecordTypes []RecordInfo
16-
Records []heaputil.RecordData
17-
GraphVizContent string
16+
RecordTypes []RecordInfo
17+
Records []heaputil.RecordData
18+
GraphData string
1819
}
1920

2021
func GenerateHTML(records []heaputil.RecordData, graphContent string) (string, error) {
@@ -24,9 +25,9 @@ func GenerateHTML(records []heaputil.RecordData, graphContent string) (string, e
2425
}
2526

2627
data := templateData{
27-
RecordTypes: GetUniqueRecordTypes(records),
28-
Records: records,
29-
GraphVizContent: graphContent,
28+
RecordTypes: GetUniqueRecordTypes(records),
29+
Records: records,
30+
GraphData: graphContent,
3031
}
3132

3233
var htmlBuilder strings.Builder
@@ -44,23 +45,17 @@ func GenerateGraph(rd *bufio.Reader) (string, error) {
4445
return "", err
4546
}
4647

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)
5651

5752
var dumpParams *record.DumpParamsRecord
5853
counter := 0
5954

6055
for {
6156
r, err := record.ReadRecord(rd)
6257
if err != nil {
63-
return dotContent.String(), err
58+
break
6459
}
6560

6661
_, isEOF := r.(*record.EOFRecord)
@@ -73,45 +68,51 @@ func GenerateGraph(rd *bufio.Reader) (string, error) {
7368
dumpParams = dp
7469
}
7570

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)
7872
if !isObj {
7973
continue
8074
}
8175

82-
// Create a DOT node for each record
83-
nodeName := fmt.Sprintf("Node%d", counter)
84-
counter++
8576
name, address := ParseNameAndAddress(r.Repr())
8677
nodeLabel := fmt.Sprintf("[%s] %s", name, address)
8778

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+
}
9188

92-
// Check if the record has pointers
9389
p, isParent := r.(record.ParentGuard)
9490
if isParent {
9591
_, outgoing := record.ParsePointers(p, dumpParams)
9692
for i := 0; i < len(outgoing); i++ {
9793
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+
}
103100
}
104101
}
105102
}
106103
}
107104

108-
// Close the "heap" cluster
109-
dotContent.WriteString(" }\n")
105+
graphData := map[string]interface{}{
106+
"nodes": nodes,
107+
"links": links,
108+
}
110109

111-
// Write DOT file footer
112-
dotContent.WriteString("}\n")
110+
jsonData, err := json.Marshal(graphData)
111+
if err != nil {
112+
return "", err
113+
}
113114

114-
return dotContent.String(), nil
115+
return string(jsonData), nil
115116
}
116117

117118
func ParseNameAndAddress(input string) (name, address string) {

0 commit comments

Comments
 (0)