diff --git a/internal/lsp/cmd/serve.go b/internal/lsp/cmd/serve.go index df99f4ae03..b44d4bd2b3 100644 --- a/internal/lsp/cmd/serve.go +++ b/internal/lsp/cmd/serve.go @@ -62,12 +62,12 @@ func (s *Serve) Run(ctx context.Context, args ...string) error { return tool.CommandLineErrorf("server does not take arguments, got %v", args) } out := os.Stderr - if s.Logfile != "" { - filename := s.Logfile - if filename == "auto" { - filename = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.log", os.Getpid())) + logfile := s.Logfile + if logfile != "" { + if logfile == "auto" { + logfile = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.log", os.Getpid())) } - f, err := os.Create(filename) + f, err := os.Create(logfile) if err != nil { return errors.Errorf("Unable to create log file: %v", err) } @@ -76,7 +76,7 @@ func (s *Serve) Run(ctx context.Context, args ...string) error { out = f } - debug.Serve(ctx, s.Debug) + debug.Serve(ctx, s.Debug, debugServe{s: s, logfile: logfile, start: time.Now()}) if s.app.Remote != "" { return s.forward() @@ -121,6 +121,20 @@ func (s *Serve) forward() error { return <-errc } +// debugServe implements the debug.Instance interface. +type debugServe struct { + s *Serve + logfile string + start time.Time +} + +func (d debugServe) Logfile() string { return d.logfile } +func (d debugServe) StartTime() time.Time { return d.start } +func (d debugServe) Port() int { return d.s.Port } +func (d debugServe) Address() string { return d.s.Address } +func (d debugServe) Debug() string { return d.s.Debug } +func (d debugServe) Workdir() string { return d.s.app.wd } + type handler struct{} type rpcStats struct { diff --git a/internal/lsp/debug/info.go b/internal/lsp/debug/info.go index 4b1477c823..3a82a1f1fc 100644 --- a/internal/lsp/debug/info.go +++ b/internal/lsp/debug/info.go @@ -23,7 +23,21 @@ const ( // Version is a manually-updated mechanism for tracking versions. var Version = "master" -// This writes the version and environment information to a writer. +// PrintServerInfo writes HTML debug info to w for the Instance s. +func PrintServerInfo(w io.Writer, s Instance) { + section(w, HTML, "Server Instance", func() { + fmt.Fprintf(w, "Start time: %v\n", s.StartTime()) + fmt.Fprintf(w, "LogFile: %s\n", s.Logfile()) + fmt.Fprintf(w, "Working directory: %s\n", s.Workdir()) + fmt.Fprintf(w, "Address: %s\n", s.Address()) + fmt.Fprintf(w, "Debug address: %s\n", s.Debug()) + }) + PrintVersionInfo(w, true, HTML) +} + +// PrintVersionInfo writes version and environment information to w, using the +// output format specified by mode. verbose controls whether additional +// information is written, including section headers. func PrintVersionInfo(w io.Writer, verbose bool, mode PrintMode) { if !verbose { printBuildInfo(w, false, mode) diff --git a/internal/lsp/debug/serve.go b/internal/lsp/debug/serve.go index 9644569385..40058ea137 100644 --- a/internal/lsp/debug/serve.go +++ b/internal/lsp/debug/serve.go @@ -19,6 +19,7 @@ import ( "strconv" "strings" "sync" + "time" "golang.org/x/tools/internal/span" "golang.org/x/tools/internal/telemetry/export" @@ -27,6 +28,14 @@ import ( "golang.org/x/tools/internal/telemetry/tag" ) +type Instance interface { + Logfile() string + StartTime() time.Time + Address() string + Debug() string + Workdir() string +} + type Cache interface { ID() string FileSet() *token.FileSet @@ -163,10 +172,12 @@ func getFile(r *http.Request) interface{} { return session.File(hash) } -func getInfo(r *http.Request) interface{} { - buf := &bytes.Buffer{} - PrintVersionInfo(buf, true, HTML) - return template.HTML(buf.String()) +func getInfo(s Instance) dataFunc { + return func(r *http.Request) interface{} { + buf := &bytes.Buffer{} + PrintServerInfo(buf, s) + return template.HTML(buf.String()) + } } func getMemory(r *http.Request) interface{} { @@ -206,7 +217,7 @@ func DropView(view View) { // 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 { +func Serve(ctx context.Context, addr string, instance Instance) error { mu.Lock() defer mu.Unlock() if addr == "" { @@ -242,7 +253,7 @@ func Serve(ctx context.Context, addr string) error { mux.HandleFunc("/session/", Render(sessionTmpl, getSession)) mux.HandleFunc("/view/", Render(viewTmpl, getView)) mux.HandleFunc("/file/", Render(fileTmpl, getFile)) - mux.HandleFunc("/info", Render(infoTmpl, getInfo)) + mux.HandleFunc("/info", Render(infoTmpl, getInfo(instance))) mux.HandleFunc("/memory", Render(memoryTmpl, getMemory)) if err := http.Serve(listener, mux); err != nil { log.Error(ctx, "Debug server failed", err) @@ -253,7 +264,9 @@ func Serve(ctx context.Context, addr string) error { return nil } -func Render(tmpl *template.Template, fun func(*http.Request) interface{}) func(http.ResponseWriter, *http.Request) { +type dataFunc func(*http.Request) interface{} + +func Render(tmpl *template.Template, fun dataFunc) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { var data interface{} if fun != nil {