-
Notifications
You must be signed in to change notification settings - Fork 199
/
counter.go
85 lines (69 loc) · 2.27 KB
/
counter.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
package goroutines
import (
"runtime"
"sync"
)
const oneHundredMBSize = 2 << 20 * 100
type goCounter struct {
mutSnapshots sync.RWMutex
snapshots []*snapshot
goRoutinesFetcher func() string
filter func(goRoutineData string) bool
}
// NewGoCounter returns an instance of the go counter
func NewGoCounter(filterFunc func(goRoutineData string) bool) *goCounter {
if filterFunc == nil {
panic("NewGoCounter can not work with a nil filter")
}
return &goCounter{
snapshots: make([]*snapshot, 0),
filter: filterFunc,
goRoutinesFetcher: func() string {
buff := make([]byte, oneHundredMBSize)
charsRead := runtime.Stack(buff, true)
return string(buff[:charsRead])
},
}
}
// Reset will clean all stored snapshots
func (gc *goCounter) Reset() {
gc.mutSnapshots.Lock()
gc.snapshots = make([]*snapshot, 0)
gc.mutSnapshots.Unlock()
}
// DiffGoRoutines makes an absolute difference* between first and second snapshots indexes.
// If one of the index is invalid (-1, > maximum snapshots) the difference will be done against a nil snapshot
// this difference will return any stored routines info from the non-nil instance.
// If both the indexes do not have a corresponding snapshot, this function will return an empty slice
// * absolute difference = (snapshot1 - snapshot2) U (snapshot2 - snapshot1)
func (gc *goCounter) DiffGoRoutines(firstSnapshotIdx, secondSnapshotIdx int) []*RoutineInfo {
gc.mutSnapshots.RLock()
defer gc.mutSnapshots.RUnlock()
snapshot1 := gc.getSnapshot(firstSnapshotIdx)
snapshot2 := gc.getSnapshot(secondSnapshotIdx)
if snapshot1 == nil && snapshot2 == nil {
return make([]*RoutineInfo, 0)
}
if snapshot1 != nil {
return snapshot1.diff(snapshot2)
}
return snapshot2.diff(snapshot1)
}
func (gc *goCounter) getSnapshot(idx int) *snapshot {
if idx < 0 || idx >= len(gc.snapshots) {
return nil
}
return gc.snapshots[idx]
}
// Snapshot will make a snapshot of the go routines that will pass through the filter function
func (gc *goCounter) Snapshot() (int, error) {
goRoutinesData := gc.goRoutinesFetcher()
s, err := newSnapshot(goRoutinesData, gc.filter)
if err != nil {
return 0, err
}
gc.mutSnapshots.Lock()
defer gc.mutSnapshots.Unlock()
gc.snapshots = append(gc.snapshots, s)
return len(gc.snapshots) - 1, nil
}