Skip to content

Commit

Permalink
internal/lsp: add server instance to debug info
Browse files Browse the repository at this point in the history
When debugging multiple instances of gopls simultaneously, it is useful
to be able to inspect stateful debugging information for each server
instance, such as the location of logfiles and server startup
information.

This CL adds an additional section to the /info http handler, that
formats additional information related to the gopls instance handling
the request.

Updates golang/go#34111

Change-Id: I6cb8073800ce52b0645f1898461a19e1ac980d2b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/214803
Reviewed-by: Rebecca Stambler <[email protected]>
Run-TryBot: Robert Findley <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
  • Loading branch information
findleyr committed Jan 15, 2020
1 parent 8503576 commit 98c82cf
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 14 deletions.
26 changes: 20 additions & 6 deletions internal/lsp/cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down
16 changes: 15 additions & 1 deletion internal/lsp/debug/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
27 changes: 20 additions & 7 deletions internal/lsp/debug/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"strconv"
"strings"
"sync"
"time"

"golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/telemetry/export"
Expand All @@ -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
Expand Down Expand Up @@ -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{} {
Expand Down Expand Up @@ -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 == "" {
Expand Down Expand Up @@ -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)
Expand All @@ -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 {
Expand Down

0 comments on commit 98c82cf

Please sign in to comment.