diff --git a/go.mod b/go.mod index 850cab54..0984a835 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,7 @@ module golang.org/x/tools +go 1.11 + require ( golang.org/x/net v0.0.0-20190311183353-d8887717615a golang.org/x/sync v0.0.0-20190423024810-112230192c58 diff --git a/internal/lsp/cmd/serve.go b/internal/lsp/cmd/serve.go index 28b7398f..b24c9261 100644 --- a/internal/lsp/cmd/serve.go +++ b/internal/lsp/cmd/serve.go @@ -19,6 +19,7 @@ import ( "golang.org/x/tools/internal/jsonrpc2" "golang.org/x/tools/internal/lsp" + "golang.org/x/tools/internal/lsp/debug" "golang.org/x/tools/internal/tool" ) @@ -30,6 +31,7 @@ type Serve struct { Port int `flag:"port" help:"port on which to run gopls for debugging purposes"` Address string `flag:"listen" help:"address on which to listen for remote connections"` Trace bool `flag:"rpc.trace" help:"Print the full rpc trace in lsp inspector format"` + Debug string `flag:"debug" help:"Serve debug information on the supplied address"` app *Application } @@ -69,6 +71,9 @@ func (s *Serve) Run(ctx context.Context, args ...string) error { log.SetOutput(io.MultiWriter(os.Stderr, f)) out = f } + + debug.Serve(ctx, s.Debug) + if s.app.Remote != "" { return s.forward() } diff --git a/internal/lsp/debug/serve.go b/internal/lsp/debug/serve.go new file mode 100644 index 00000000..bbe34e07 --- /dev/null +++ b/internal/lsp/debug/serve.go @@ -0,0 +1,88 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package debug + +import ( + "context" + "html/template" + "log" + "net" + "net/http" + _ "net/http/pprof" // pull in the standard pprof handlers +) + +func init() { + http.HandleFunc("/", Render(mainTmpl, nil)) + http.HandleFunc("/debug/", Render(debugTmpl, nil)) +} + +// Serve starts and runs a debug server in the background. +// It also logs the port the server starts on, to allow for :0 auto assigned +// ports. +func Serve(ctx context.Context, addr string) error { + if addr == "" { + return nil + } + listener, err := net.Listen("tcp", addr) + if err != nil { + return err + } + log.Printf("Debug serving on port: %d", listener.Addr().(*net.TCPAddr).Port) + go func() { + if err := http.Serve(listener, nil); err != nil { + log.Printf("Debug server failed with %v", err) + return + } + log.Printf("Debug server finished") + }() + return nil +} + +func Render(tmpl *template.Template, fun func(*http.Request) interface{}) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var data interface{} + if fun != nil { + data = fun(r) + } + if err := tmpl.Execute(w, data); err != nil { + log.Print(err) + } + } +} + +var BaseTemplate = template.Must(template.New("").Parse(` + +
+