-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathhtml.go
133 lines (109 loc) · 2.82 KB
/
html.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package main
import (
"bufio"
"encoding/json"
"fmt"
"html/template"
"regexp"
"strings"
"github.com/burntcarrot/heaputil"
"github.com/burntcarrot/heaputil/record"
)
type templateData struct {
RecordTypes []RecordInfo
Records []heaputil.RecordData
GraphData string
}
func GenerateHTML(records []heaputil.RecordData, graphContent string) (string, error) {
tmpl, err := template.ParseFiles("index.html")
if err != nil {
return "", err
}
data := templateData{
RecordTypes: GetUniqueRecordTypes(records),
Records: records,
GraphData: graphContent,
}
var htmlBuilder strings.Builder
err = tmpl.Execute(&htmlBuilder, data)
if err != nil {
return "", err
}
return htmlBuilder.String(), nil
}
func GenerateGraph(rd *bufio.Reader) (string, error) {
err := record.ReadHeader(rd)
if err != nil {
return "", err
}
nodes := []map[string]interface{}{}
links := []map[string]interface{}{}
nodeMap := make(map[uint64]int)
var dumpParams *record.DumpParamsRecord
counter := 0
for {
r, err := record.ReadRecord(rd)
if err != nil {
break
}
_, isEOF := r.(*record.EOFRecord)
if isEOF {
break
}
dp, isDumpParams := r.(*record.DumpParamsRecord)
if isDumpParams {
dumpParams = dp
}
obj, isObj := r.(*record.ObjectRecord)
if !isObj {
continue
}
name, address := ParseNameAndAddress(r.Repr())
nodeLabel := fmt.Sprintf("[%s] %s", name, address)
if _, exists := nodeMap[obj.Address]; !exists {
nodeMap[obj.Address] = counter
nodes = append(nodes, map[string]interface{}{
"id": counter,
"label": nodeLabel,
"address": obj.Address,
})
counter++
}
p, isParent := r.(record.ParentGuard)
if isParent {
_, outgoing := record.ParsePointers(p, dumpParams)
for i := 0; i < len(outgoing); i++ {
if outgoing[i] != 0 {
if targetIndex, exists := nodeMap[outgoing[i]]; exists {
links = append(links, map[string]interface{}{
"source": nodeMap[obj.Address],
"target": targetIndex,
})
}
}
}
}
}
graphData := map[string]interface{}{
"nodes": nodes,
"links": links,
}
jsonData, err := json.Marshal(graphData)
if err != nil {
return "", err
}
return string(jsonData), nil
}
func ParseNameAndAddress(input string) (name, address string) {
// Define a regular expression pattern to match the desired format
// The pattern captures the node name (before " at address") and the address.
re := regexp.MustCompile(`^(.*?) at address (0x[0-9a-fA-F]+).*?$`)
// Find the submatches in the input string
matches := re.FindStringSubmatch(input)
// If there are no matches, return empty strings for both name and address
if len(matches) != 3 {
return "", ""
}
// The first submatch (matches[1]) contains the node name, and the second submatch (matches[2]) contains the address.
return matches[1], matches[2]
}