
SetGoroutineName
More than a million goroutine can be launched in Go, and this mechanism is delighted until there is a problem. For example, a memory leak or dead-lock.
The first thing I want to do is figure it out, look at what is happening.
It can be done like this.
package main
import (
"bytes"
"fmt"
"runtime/pprof"
)
func main() {
profiler := pprof.Lookup("goroutine")
var buf bytes.Buffer
profiler.WriteTo(&buf, 1)
fmt.Println(buf.String())
}
goroutine profile: total 1
1 @ 0xb1620 0xb1340 0xac9c0 0x200c0 0x59f80 0x98d61
# 0xb161f runtime/pprof.writeRuntimeProfile+0xdf /usr/local/go/src/runtime/pprof/pprof.go:614
# 0xb133f runtime/pprof.writeGoroutine+0x9f /usr/local/go/src/runtime/pprof/pprof.go:576
# 0xac9bf runtime/pprof.(*Profile).WriteTo+0xff /usr/local/go/src/runtime/pprof/pprof.go:298
# 0x200bf main.main+0xbf /tmp/sandbox627252447/main.go:12
# 0x59f7f runtime.main+0x39f /usr/local/go/src/runtime/proc.go:183
https://play.golang.org/p/ysl_avotLS
Or so:
package main
import (
"fmt"
"runtime"
)
func main() {
b1 := make([]byte, 1<<20)
runtime.Stack(b1, true)
s1 := string(b1)
fmt.Println(s1)
}
goroutine 1 [running]:
main.main()
/tmp/sandbox952340390/main.go:10 +0x80
https://play.golang.org/p/FCbzn2_DlQ
We see that in these examples one gorutin and its stack trace are launched, but if we have 20-30 identical goroutines, then the question arises: who do they serve? If this is a site, then there may be a user who has left, but goroutine has not ceased to exist, and as a result does not free memory?
There is a desire to give the gourutins names and write in the name a useful useful info, such as the Id of the user she serves. But Go isn’t easy so there is no function SetGoroutineName
or GetGoroutineId
. Such features are evil according to the creators of the Go language. It's hard to disagree with people like Rob and Ken. But evil is not because these functions are evil in general, but because programmers will begin to use these functions incorrectly. Not for debugging applications at the time of a memory leak, but just like goroutine local storage. That is, in the logic of the program it is bad to use such things, but if they messed up, then you still need to somehow find the error.
Pyradebug
So you need to give names to the goroutines, but whatever the application logic does not depend on it. And so was bornpyradebug
go get -u github.com/CossackPyra/pyradebug
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/CossackPyra/pyradebug"
)
var pyraDebug *pyradebug.PyraDebug
func main() {
pyraDebug = pyradebug.InitPyraDebug()
pyraDebug.Enable = true
for i := 0; i < 10; i++ {
go func(i int) {
pyraDebug.SetGoroutineName(fmt.Sprintf("sleep func %d", i))
for {
time.Sleep(time.Second)
}
}(i)
}
http.HandleFunc("/goroutine", handle_goroutine)
log.Fatal(http.ListenAndServe(":8765", nil))
}
func handle_goroutine(w http.ResponseWriter, r *http.Request) {
agi := pyraDebug.ListGoroutines(1<<20, true)
w.Header().Set("Content-Type", "text/plain")
for i, gi := range agi {
fmt.Fprintf(w, "%d\t%s\t%s\t%s\n", i, gi.Id, gi.Name, gi.Status)
}
}
# http://127.0.0.1:8765/goroutine
0 goroutine 31 running
1 goroutine 1 IO wait
2 goroutine 17 syscall, locked to thread
3 goroutine 20 sleep func 0 sleep
4 goroutine 21 sleep func 1 sleep
5 goroutine 22 sleep func 2 sleep
6 goroutine 23 sleep func 3 sleep
7 goroutine 24 sleep func 4 sleep
8 goroutine 25 sleep func 5 sleep
9 goroutine 26 sleep func 6 sleep
10 goroutine 27 sleep func 7 sleep
11 goroutine 28 sleep func 8 sleep
12 goroutine 29 sleep func 9 sleep
An important feature of the library is pyraDebug.Enable = true
, that is, after debugging, you simply turn it off and it stops doing anything and therefore SetGoroutineName
you cannot rely on logic.
Initially, I also wanted to indicate the name of the gorutin, which gave birth to it, but in practice, I only need the name, the source code of 127 lines. It is easy to fork, understand and modify.
The library depends on the text format that it gives out runtime.Stack
and may stop working in the future, which is also good if developers don’t rely on this library thoughtlessly and use it only for debugging.
PS. It's always interesting to find out what the bike did.