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.
    image


    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 SetGoroutineNameor 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 SetGoroutineNameyou 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.Stackand 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.


    https://github.com/CossackPyra/pyradebug


    Also popular now: