diff options
author | Heschi Kreinick <heschi@google.com> | 2020-02-10 14:34:33 -0500 |
---|---|---|
committer | Heschi Kreinick <heschi@google.com> | 2020-02-10 22:37:59 +0000 |
commit | b0390335f4a1d5d66207ac0d9a1bf258ba3a9bef (patch) | |
tree | 9a1d2d51b3a7de6b7ef420c3f17b70302e9d2184 /internal/lsp/debug | |
parent | 1ace956b0e17ff85a6f9bdf6973af28a26234113 (diff) | |
download | golang-x-tools-b0390335f4a1d5d66207ac0d9a1bf258ba3a9bef.tar.gz |
internal/lsp/debug: write debug info for large goplses
We've had various reports of high memory usage in gopls. Catching it in
the act can be difficult, so check every second and write relevant
profiles to os.TempDir every time the heap reaches a new watermark.
Note that the logging in this package is broken. I didn't fix it, so
nobody will know this is happening until we tell them. So it goes.
Change-Id: I972d7ccfe5308658f86dde717465f0e0151b396d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/218858
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Diffstat (limited to 'internal/lsp/debug')
-rw-r--r-- | internal/lsp/debug/serve.go | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/internal/lsp/debug/serve.go b/internal/lsp/debug/serve.go index f9d871cfc..3316817ae 100644 --- a/internal/lsp/debug/serve.go +++ b/internal/lsp/debug/serve.go @@ -21,6 +21,7 @@ import ( "path/filepath" "reflect" "runtime" + rpprof "runtime/pprof" "strconv" "strings" "sync" @@ -326,6 +327,49 @@ func (i *Instance) Serve(ctx context.Context) error { return nil } +func (i *Instance) MonitorMemory(ctx context.Context) { + tick := time.NewTicker(time.Second) + nextThresholdGiB := uint64(5) + go func() { + for { + <-tick.C + var mem runtime.MemStats + runtime.ReadMemStats(&mem) + if mem.HeapAlloc < nextThresholdGiB*1<<30 { + continue + } + i.writeMemoryDebug(nextThresholdGiB) + log.Print(ctx, fmt.Sprintf("Wrote memory usage debug info to %v", os.TempDir())) + nextThresholdGiB++ + } + }() +} + +func (i *Instance) writeMemoryDebug(threshold uint64) error { + fname := func(t string) string { + return fmt.Sprintf("gopls.%d-%dGiB-%s", os.Getpid(), threshold, t) + } + + f, err := os.Create(filepath.Join(os.TempDir(), fname("heap.pb.gz"))) + if err != nil { + return err + } + defer f.Close() + if err := rpprof.Lookup("heap").WriteTo(f, 0); err != nil { + return err + } + + f, err = os.Create(filepath.Join(os.TempDir(), fname("goroutines.txt"))) + if err != nil { + return err + } + defer f.Close() + if err := rpprof.Lookup("goroutine").WriteTo(f, 1); err != nil { + return err + } + return nil +} + type dataFunc func(*http.Request) interface{} func render(tmpl *template.Template, fun dataFunc) func(http.ResponseWriter, *http.Request) { |