1
0
mirror of https://github.com/DataDog/go-profiler-notes.git synced 2025-07-12 23:50:13 +02:00
Files
Felix Geisendörfer 6fde1a9f35 Note about goroutines
2021-01-17 19:21:02 +01:00

187 lines
3.6 KiB
Go

package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
_ "net/http/pprof"
"runtime"
"runtime/pprof"
"time"
)
type Profile struct {
Name string
WriteTo func(w io.Writer) error
}
var profiles = []Profile{
{
Name: "runtime.stack.txt",
WriteTo: func(w io.Writer) error {
buf := make([]byte, 1024*1024)
n := runtime.Stack(buf, true)
buf = buf[:n]
_, err := w.Write(buf)
return err
},
},
{
Name: "runtime.goroutineprofile.json",
WriteTo: func(w io.Writer) error {
p := make([]runtime.StackRecord, 1000)
n, ok := runtime.GoroutineProfile(p)
if !ok {
return errors.New("runtime.GoroutineProfile: not ok")
}
p = p[0:n]
e := json.NewEncoder(w)
e.SetIndent("", " ")
return e.Encode(p)
},
},
{
Name: "pprof.lookup.goroutine.debug0.pb.gz",
WriteTo: func(w io.Writer) error {
profile := pprof.Lookup("goroutine")
return profile.WriteTo(w, 0)
},
},
{
Name: "pprof.lookup.goroutine.debug1.txt",
WriteTo: func(w io.Writer) error {
profile := pprof.Lookup("goroutine")
return profile.WriteTo(w, 1)
},
},
{
Name: "pprof.lookup.goroutine.debug2.txt",
WriteTo: func(w io.Writer) error {
profile := pprof.Lookup("goroutine")
return profile.WriteTo(w, 2)
},
},
{
Name: "net.http.pprof.goroutine.debug0.pb.gz",
WriteTo: func(w io.Writer) error {
return writeHttpProfile(w, 0)
},
},
{
Name: "net.http.pprof.goroutine.debug1.txt",
WriteTo: func(w io.Writer) error {
return writeHttpProfile(w, 1)
},
},
{
Name: "net.http.pprof.goroutine.debug2.txt",
WriteTo: func(w io.Writer) error {
return writeHttpProfile(w, 2)
},
},
}
func writeHttpProfile(w io.Writer, debug int) error {
url := fmt.Sprintf("http://%s/debug/pprof/goroutine?debug=%d", listenAddr, debug)
res, err := http.Get(url)
if err != nil {
return err
}
defer res.Body.Close()
_, err = io.Copy(w, res.Body)
return err
}
func writeProfiles(n int) error {
for _, profile := range profiles {
buf := &bytes.Buffer{}
filename := fmt.Sprintf("%d.%s", n, profile.Name)
if err := profile.WriteTo(buf); err != nil {
return err
} else if err := ioutil.WriteFile(filename, buf.Bytes(), 0666); err != nil {
return err
}
}
return nil
}
var listenAddr = "127.0.0.1:8080"
func main() {
flag.Parse()
errCh := make(chan error, 1)
go func() {
fmt.Printf("Listening for pprof requests on %s\n", listenAddr)
errCh <- http.ListenAndServe(listenAddr, nil)
}()
labels := pprof.Labels("test_label", "test_value")
ctx := pprof.WithLabels(context.Background(), labels)
pprof.SetGoroutineLabels(ctx)
go shortSleepLoop()
go sleepLoop(time.Hour)
go chanReceiveForever()
go indirectShortSleepLoop()
sleep := time.Second
fmt.Printf("Sleeping for %s followed by gc\n", sleep)
time.Sleep(time.Second)
runtime.GC()
fmt.Printf("Dump 1\n")
if err := writeProfiles(1); err != nil {
panic(err)
}
sleep = 1*time.Minute + 10*time.Second
fmt.Printf("Sleeping for %s followed by gc\n", sleep)
time.Sleep(sleep)
runtime.GC()
fmt.Printf("Dump 2\n")
if err := writeProfiles(2); err != nil {
panic(err)
}
fmt.Printf("Waiting forever\n")
//fmt.Printf("waiting indefinitely so you can press ctrl+\\ to compare the output\n")
//runtime.GC()
<-errCh
}
func shortSleepLoop() {
for {
time.Sleep(time.Second)
}
}
func sleepLoop(d time.Duration) {
for {
time.Sleep(d)
}
}
func chanReceiveForever() {
forever := make(chan struct{})
<-forever
}
func indirectShortSleepLoop() {
indirectShortSleepLoop2()
}
func indirectShortSleepLoop2() {
go shortSleepLoop()
}