aboutsummaryrefslogtreecommitdiff
path: root/internal/lsp/debug
diff options
context:
space:
mode:
authorHeschi Kreinick <heschi@google.com>2020-02-10 14:34:33 -0500
committerHeschi Kreinick <heschi@google.com>2020-02-10 22:37:59 +0000
commitb0390335f4a1d5d66207ac0d9a1bf258ba3a9bef (patch)
tree9a1d2d51b3a7de6b7ef420c3f17b70302e9d2184 /internal/lsp/debug
parent1ace956b0e17ff85a6f9bdf6973af28a26234113 (diff)
downloadgolang-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.go44
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) {