/
goroutines.go
68 lines (62 loc) · 1.54 KB
/
goroutines.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
// Package goroutines spawns a loop to periodically check for the count of
// goroutines. If the count reaches a limit and is increasing, print the top N
// entries of the goroutine profile.
package goroutines
import (
"bytes"
"runtime"
"runtime/pprof"
"strings"
"time"
"github.com/getlantern/golog"
)
var log = golog.LoggerFor("goroutines")
// Monitor keeps checking the number of goroutines in interval. If the number
// reaching the limit, it logs the topN entries of the goroutine profile
// whenever the number is higher than the previous check.
func Monitor(interval time.Duration, limit int, topN int) (stop func()) {
chStop := make(chan struct{})
go func() {
var lastN int
tk := time.NewTicker(interval)
defer tk.Stop()
for {
select {
case <-tk.C:
num := runtime.NumGoroutine()
if num >= limit && num > lastN {
PrintProfile(topN)
lastN = num
} else {
log.Debugf("goroutine profile: total %v", num)
}
case <-chStop:
return
}
}
}()
return func() { close(chStop) }
}
// PrintProfile logs the topN entries of the goroutine profile
func PrintProfile(topN int) {
var buf bytes.Buffer
p := pprof.Lookup("goroutine")
e := p.WriteTo(&buf, 1) // debug=1
if e != nil {
log.Errorf("Unable to collect goroutine profile: %v", e)
return
}
var lines []string
for count := 0; count < topN; {
line, e := buf.ReadString(byte('\n'))
if e != nil {
break
}
line = strings.TrimSpace(line)
if line == "" {
count++
}
lines = append(lines, line)
}
log.Debug(strings.Join(lines, "\n"))
}