LeakDetectメソッドとは別に、leak検出とgoroutine数の推移をグラフ化するLeakGraph{}を作成しました。
start()を呼び出すことでgoroutineの数を定期的に取得しsliceにappendするgoroutineを呼び出し、stop()の呼び出しによってチャネルを通じてstart()を終了させています。goroutineのleakを検出するツールを実装するためにそもそものツール内でもleakをしないようにするのが大変でした笑。
// Detect Goroutine Leak
type LeakGraph struct {
startNumberOfGoroutine int
goroutineData []float64
done chan bool
}
func (l *LeakGraph) start(duration time.Duration) {
l.goroutineData = append(l.goroutineData, float64(runtime.NumGoroutine()))
for {
select {
case <- l.done:
return
case <- time.After(duration):
l.goroutineData = append(l.goroutineData, float64(runtime.NumGoroutine()))
}
}
}
func (l *LeakGraph) stop() {
fmt.Println("=== RUN LeakGraph")
l.done <- true
// show goroutine graph
graph := asciigraph.Plot(l.goroutineData)
fmt.Println(graph)
if l.startNumberOfGoroutine == runtime.NumGoroutine() {
/*
when there are no goroutine leak
*/
fmt.Println("No goroutine leaks")
fmt.Println("=== PASS: LeakGraph")
} else {
/*
when there are some goroutine leak
*/
panic("This code may cause goroutine leak")
}
}
stop()が呼ばれたタイミングでstart()を終了させるために、select caseでtime.After()とstop()が呼ばれたかを確認するdoneチャネル使用しました。