forked from davecheney/gcvis
-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser.go
118 lines (98 loc) · 2.74 KB
/
parser.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
package main
import (
"bufio"
"io"
"regexp"
"runtime"
"strconv"
)
const (
GCRegexpGo14 = `gc\d+\(\d+\): ([\d.]+\+?)+ us, \d+ -> (?P<Heap1>\d+) MB, \d+ \(\d+-\d+\) objects,( \d+ goroutines,)? \d+\/\d+\/\d+ sweeps, \d+\(\d+\) handoff, \d+\(\d+\) steal, \d+\/\d+\/\d+ yields`
GCRegexpGo15 = `gc \d+ @[\d.]+s \d+%: [\d.+/]+ ms clock, [\d.+/]+ ms cpu, \d+->\d+->\d+ MB, (?P<Heap1>\d+) MB goal, \d+ P`
SCVGRegexp = `scvg\d+: inuse: (?P<inuse>\d+), idle: (?P<idle>\d+), sys: (?P<sys>\d+), released: (?P<released>\d+), consumed: (?P<consumed>\d+) \(MB\)`
)
var (
gcrego14 = regexp.MustCompile(GCRegexpGo14)
gcrego15 = regexp.MustCompile(GCRegexpGo15)
scvgre = regexp.MustCompile(SCVGRegexp)
)
type Parser struct {
reader io.Reader
GcChan chan *gctrace
ScvgChan chan *scvgtrace
NoMatchChan chan string
done chan bool
Err error
gcRegexp *regexp.Regexp
scvgRegexp *regexp.Regexp
}
func NewParser(r io.Reader) *Parser {
return &Parser{
reader: r,
GcChan: make(chan *gctrace, 1),
ScvgChan: make(chan *scvgtrace, 1),
NoMatchChan: make(chan string, 1),
done: make(chan bool),
}
}
func (p *Parser) Run() {
sc := bufio.NewScanner(p.reader)
if p.gcRegexp == nil {
// Set regexp based on Golang version
if runtime.Version() == "go1.5" {
p.gcRegexp = gcrego15
} else {
p.gcRegexp = gcrego14
}
}
for sc.Scan() {
line := sc.Text()
if result := p.gcRegexp.FindStringSubmatch(line); result != nil {
p.GcChan <- parseGCTrace(p.gcRegexp, result)
continue
}
if result := scvgre.FindStringSubmatch(line); result != nil {
p.ScvgChan <- parseSCVGTrace(result)
continue
}
p.NoMatchChan <- line
}
p.Err = sc.Err()
close(p.done)
}
func parseGCTrace(gcre *regexp.Regexp, matches []string) *gctrace {
matchMap := getMatchMap(gcre, matches)
return &gctrace{
Heap1: matchMap["Heap1"],
}
}
func parseSCVGTrace(matches []string) *scvgtrace {
matchMap := getMatchMap(scvgre, matches)
return &scvgtrace{
inuse: matchMap["inuse"],
idle: matchMap["idle"],
sys: matchMap["sys"],
released: matchMap["released"],
consumed: matchMap["consumed"],
}
}
// Transform our matches in a readable hash map.
//
// The resulting hash map will be something like { "Heap1": 123 }
func getMatchMap(re *regexp.Regexp, matches []string) map[string]int64 {
matchingNames := re.SubexpNames()
matchMap := map[string]int64{}
for i, value := range matches {
intVal, err := strconv.ParseInt(value, 10, 64)
if err != nil {
// Happens on first element of range and any matching parenthesis
// that includes non-parseable string
//
// For example a matching array would contain:
// [ "scvg1: inuse:3 ..." "3" ]
continue
}
matchMap[matchingNames[i]] = intVal
}
return matchMap
}