github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/lsp/debug/serve.go (about)

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package debug
     6  
     7  import (
     8  	"archive/zip"
     9  	"bytes"
    10  	"context"
    11  	"fmt"
    12  	"html/template"
    13  	"io"
    14  	stdlog "log"
    15  	"net"
    16  	"net/http"
    17  	"net/http/pprof"
    18  	"os"
    19  	"path"
    20  	"path/filepath"
    21  	"runtime"
    22  	rpprof "runtime/pprof"
    23  	"sort"
    24  	"strconv"
    25  	"strings"
    26  	"sync"
    27  	"time"
    28  
    29  	"github.com/powerman/golang-tools/internal/event"
    30  	"github.com/powerman/golang-tools/internal/event/core"
    31  	"github.com/powerman/golang-tools/internal/event/export"
    32  	"github.com/powerman/golang-tools/internal/event/export/metric"
    33  	"github.com/powerman/golang-tools/internal/event/export/ocagent"
    34  	"github.com/powerman/golang-tools/internal/event/export/prometheus"
    35  	"github.com/powerman/golang-tools/internal/event/keys"
    36  	"github.com/powerman/golang-tools/internal/event/label"
    37  	"github.com/powerman/golang-tools/internal/lsp/cache"
    38  	"github.com/powerman/golang-tools/internal/lsp/debug/log"
    39  	"github.com/powerman/golang-tools/internal/lsp/debug/tag"
    40  	"github.com/powerman/golang-tools/internal/lsp/protocol"
    41  	"github.com/powerman/golang-tools/internal/lsp/source"
    42  	errors "golang.org/x/xerrors"
    43  )
    44  
    45  type contextKeyType int
    46  
    47  const (
    48  	instanceKey contextKeyType = iota
    49  	traceKey
    50  )
    51  
    52  // An Instance holds all debug information associated with a gopls instance.
    53  type Instance struct {
    54  	Logfile       string
    55  	StartTime     time.Time
    56  	ServerAddress string
    57  	Workdir       string
    58  	OCAgentConfig string
    59  
    60  	LogWriter io.Writer
    61  
    62  	exporter event.Exporter
    63  
    64  	ocagent    *ocagent.Exporter
    65  	prometheus *prometheus.Exporter
    66  	rpcs       *Rpcs
    67  	traces     *traces
    68  	State      *State
    69  
    70  	serveMu              sync.Mutex
    71  	debugAddress         string
    72  	listenedDebugAddress string
    73  }
    74  
    75  // State holds debugging information related to the server state.
    76  type State struct {
    77  	mu      sync.Mutex
    78  	clients []*Client
    79  	servers []*Server
    80  
    81  	// bugs maps bug description -> formatted event
    82  	bugs map[string]string
    83  }
    84  
    85  func Bug(ctx context.Context, desc, format string, args ...interface{}) {
    86  	labels := []label.Label{tag.Bug.Of(desc)}
    87  	_, file, line, ok := runtime.Caller(1)
    88  	if ok {
    89  		labels = append(labels, tag.Callsite.Of(fmt.Sprintf("%s:%d", file, line)))
    90  	}
    91  	msg := fmt.Sprintf(format, args...)
    92  	event.Log(ctx, msg, labels...)
    93  }
    94  
    95  type bug struct {
    96  	Description, Event string
    97  }
    98  
    99  func (st *State) Bugs() []bug {
   100  	st.mu.Lock()
   101  	defer st.mu.Unlock()
   102  	var bugs []bug
   103  	for k, v := range st.bugs {
   104  		bugs = append(bugs, bug{k, v})
   105  	}
   106  	sort.Slice(bugs, func(i, j int) bool {
   107  		return bugs[i].Description < bugs[j].Description
   108  	})
   109  	return bugs
   110  }
   111  
   112  func (st *State) recordBug(description, event string) {
   113  	st.mu.Lock()
   114  	defer st.mu.Unlock()
   115  	if st.bugs == nil {
   116  		st.bugs = make(map[string]string)
   117  	}
   118  	st.bugs[description] = event
   119  }
   120  
   121  // Caches returns the set of Cache objects currently being served.
   122  func (st *State) Caches() []*cache.Cache {
   123  	var caches []*cache.Cache
   124  	seen := make(map[string]struct{})
   125  	for _, client := range st.Clients() {
   126  		cache, ok := client.Session.Cache().(*cache.Cache)
   127  		if !ok {
   128  			continue
   129  		}
   130  		if _, found := seen[cache.ID()]; found {
   131  			continue
   132  		}
   133  		seen[cache.ID()] = struct{}{}
   134  		caches = append(caches, cache)
   135  	}
   136  	return caches
   137  }
   138  
   139  // Cache returns the Cache that matches the supplied id.
   140  func (st *State) Cache(id string) *cache.Cache {
   141  	for _, c := range st.Caches() {
   142  		if c.ID() == id {
   143  			return c
   144  		}
   145  	}
   146  	return nil
   147  }
   148  
   149  // Sessions returns the set of Session objects currently being served.
   150  func (st *State) Sessions() []*cache.Session {
   151  	var sessions []*cache.Session
   152  	for _, client := range st.Clients() {
   153  		sessions = append(sessions, client.Session)
   154  	}
   155  	return sessions
   156  }
   157  
   158  // Session returns the Session that matches the supplied id.
   159  func (st *State) Session(id string) *cache.Session {
   160  	for _, s := range st.Sessions() {
   161  		if s.ID() == id {
   162  			return s
   163  		}
   164  	}
   165  	return nil
   166  }
   167  
   168  // Views returns the set of View objects currently being served.
   169  func (st *State) Views() []*cache.View {
   170  	var views []*cache.View
   171  	for _, s := range st.Sessions() {
   172  		for _, v := range s.Views() {
   173  			if cv, ok := v.(*cache.View); ok {
   174  				views = append(views, cv)
   175  			}
   176  		}
   177  	}
   178  	return views
   179  }
   180  
   181  // View returns the View that matches the supplied id.
   182  func (st *State) View(id string) *cache.View {
   183  	for _, v := range st.Views() {
   184  		if v.ID() == id {
   185  			return v
   186  		}
   187  	}
   188  	return nil
   189  }
   190  
   191  // Clients returns the set of Clients currently being served.
   192  func (st *State) Clients() []*Client {
   193  	st.mu.Lock()
   194  	defer st.mu.Unlock()
   195  	clients := make([]*Client, len(st.clients))
   196  	copy(clients, st.clients)
   197  	return clients
   198  }
   199  
   200  // Client returns the Client matching the supplied id.
   201  func (st *State) Client(id string) *Client {
   202  	for _, c := range st.Clients() {
   203  		if c.Session.ID() == id {
   204  			return c
   205  		}
   206  	}
   207  	return nil
   208  }
   209  
   210  // Servers returns the set of Servers the instance is currently connected to.
   211  func (st *State) Servers() []*Server {
   212  	st.mu.Lock()
   213  	defer st.mu.Unlock()
   214  	servers := make([]*Server, len(st.servers))
   215  	copy(servers, st.servers)
   216  	return servers
   217  }
   218  
   219  // A Client is an incoming connection from a remote client.
   220  type Client struct {
   221  	Session      *cache.Session
   222  	DebugAddress string
   223  	Logfile      string
   224  	GoplsPath    string
   225  	ServerID     string
   226  	Service      protocol.Server
   227  }
   228  
   229  // A Server is an outgoing connection to a remote LSP server.
   230  type Server struct {
   231  	ID           string
   232  	DebugAddress string
   233  	Logfile      string
   234  	GoplsPath    string
   235  	ClientID     string
   236  }
   237  
   238  // AddClient adds a client to the set being served.
   239  func (st *State) addClient(session *cache.Session) {
   240  	st.mu.Lock()
   241  	defer st.mu.Unlock()
   242  	st.clients = append(st.clients, &Client{Session: session})
   243  }
   244  
   245  // DropClient removes a client from the set being served.
   246  func (st *State) dropClient(session source.Session) {
   247  	st.mu.Lock()
   248  	defer st.mu.Unlock()
   249  	for i, c := range st.clients {
   250  		if c.Session == session {
   251  			copy(st.clients[i:], st.clients[i+1:])
   252  			st.clients[len(st.clients)-1] = nil
   253  			st.clients = st.clients[:len(st.clients)-1]
   254  			return
   255  		}
   256  	}
   257  }
   258  
   259  // AddServer adds a server to the set being queried. In practice, there should
   260  // be at most one remote server.
   261  func (st *State) updateServer(server *Server) {
   262  	st.mu.Lock()
   263  	defer st.mu.Unlock()
   264  	for i, existing := range st.servers {
   265  		if existing.ID == server.ID {
   266  			// Replace, rather than mutate, to avoid a race.
   267  			newServers := make([]*Server, len(st.servers))
   268  			copy(newServers, st.servers[:i])
   269  			newServers[i] = server
   270  			copy(newServers[i+1:], st.servers[i+1:])
   271  			st.servers = newServers
   272  			return
   273  		}
   274  	}
   275  	st.servers = append(st.servers, server)
   276  }
   277  
   278  // DropServer drops a server from the set being queried.
   279  func (st *State) dropServer(id string) {
   280  	st.mu.Lock()
   281  	defer st.mu.Unlock()
   282  	for i, s := range st.servers {
   283  		if s.ID == id {
   284  			copy(st.servers[i:], st.servers[i+1:])
   285  			st.servers[len(st.servers)-1] = nil
   286  			st.servers = st.servers[:len(st.servers)-1]
   287  			return
   288  		}
   289  	}
   290  }
   291  
   292  // an http.ResponseWriter that filters writes
   293  type filterResponse struct {
   294  	w    http.ResponseWriter
   295  	edit func([]byte) []byte
   296  }
   297  
   298  func (c filterResponse) Header() http.Header {
   299  	return c.w.Header()
   300  }
   301  
   302  func (c filterResponse) Write(buf []byte) (int, error) {
   303  	ans := c.edit(buf)
   304  	return c.w.Write(ans)
   305  }
   306  
   307  func (c filterResponse) WriteHeader(n int) {
   308  	c.w.WriteHeader(n)
   309  }
   310  
   311  // replace annoying nuls by spaces
   312  func cmdline(w http.ResponseWriter, r *http.Request) {
   313  	fake := filterResponse{
   314  		w: w,
   315  		edit: func(buf []byte) []byte {
   316  			return bytes.ReplaceAll(buf, []byte{0}, []byte{' '})
   317  		},
   318  	}
   319  	pprof.Cmdline(fake, r)
   320  }
   321  
   322  func (i *Instance) getCache(r *http.Request) interface{} {
   323  	return i.State.Cache(path.Base(r.URL.Path))
   324  }
   325  
   326  func (i *Instance) getSession(r *http.Request) interface{} {
   327  	return i.State.Session(path.Base(r.URL.Path))
   328  }
   329  
   330  func (i *Instance) getClient(r *http.Request) interface{} {
   331  	return i.State.Client(path.Base(r.URL.Path))
   332  }
   333  
   334  func (i *Instance) getServer(r *http.Request) interface{} {
   335  	i.State.mu.Lock()
   336  	defer i.State.mu.Unlock()
   337  	id := path.Base(r.URL.Path)
   338  	for _, s := range i.State.servers {
   339  		if s.ID == id {
   340  			return s
   341  		}
   342  	}
   343  	return nil
   344  }
   345  
   346  func (i *Instance) getView(r *http.Request) interface{} {
   347  	return i.State.View(path.Base(r.URL.Path))
   348  }
   349  
   350  func (i *Instance) getFile(r *http.Request) interface{} {
   351  	identifier := path.Base(r.URL.Path)
   352  	sid := path.Base(path.Dir(r.URL.Path))
   353  	s := i.State.Session(sid)
   354  	if s == nil {
   355  		return nil
   356  	}
   357  	for _, o := range s.Overlays() {
   358  		if o.FileIdentity().Hash == identifier {
   359  			return o
   360  		}
   361  	}
   362  	return nil
   363  }
   364  
   365  func (i *Instance) getInfo(r *http.Request) interface{} {
   366  	buf := &bytes.Buffer{}
   367  	i.PrintServerInfo(r.Context(), buf)
   368  	return template.HTML(buf.String())
   369  }
   370  
   371  func (i *Instance) AddService(s protocol.Server, session *cache.Session) {
   372  	for _, c := range i.State.clients {
   373  		if c.Session == session {
   374  			c.Service = s
   375  			return
   376  		}
   377  	}
   378  	stdlog.Printf("unable to find a Client to add the protocol.Server to")
   379  }
   380  
   381  func getMemory(_ *http.Request) interface{} {
   382  	var m runtime.MemStats
   383  	runtime.ReadMemStats(&m)
   384  	return m
   385  }
   386  
   387  func init() {
   388  	event.SetExporter(makeGlobalExporter(os.Stderr))
   389  }
   390  
   391  func GetInstance(ctx context.Context) *Instance {
   392  	if ctx == nil {
   393  		return nil
   394  	}
   395  	v := ctx.Value(instanceKey)
   396  	if v == nil {
   397  		return nil
   398  	}
   399  	return v.(*Instance)
   400  }
   401  
   402  // WithInstance creates debug instance ready for use using the supplied
   403  // configuration and stores it in the returned context.
   404  func WithInstance(ctx context.Context, workdir, agent string) context.Context {
   405  	i := &Instance{
   406  		StartTime:     time.Now(),
   407  		Workdir:       workdir,
   408  		OCAgentConfig: agent,
   409  	}
   410  	i.LogWriter = os.Stderr
   411  	ocConfig := ocagent.Discover()
   412  	//TODO: we should not need to adjust the discovered configuration
   413  	ocConfig.Address = i.OCAgentConfig
   414  	i.ocagent = ocagent.Connect(ocConfig)
   415  	i.prometheus = prometheus.New()
   416  	i.rpcs = &Rpcs{}
   417  	i.traces = &traces{}
   418  	i.State = &State{}
   419  	i.exporter = makeInstanceExporter(i)
   420  	return context.WithValue(ctx, instanceKey, i)
   421  }
   422  
   423  // SetLogFile sets the logfile for use with this instance.
   424  func (i *Instance) SetLogFile(logfile string, isDaemon bool) (func(), error) {
   425  	// TODO: probably a better solution for deferring closure to the caller would
   426  	// be for the debug instance to itself be closed, but this fixes the
   427  	// immediate bug of logs not being captured.
   428  	closeLog := func() {}
   429  	if logfile != "" {
   430  		if logfile == "auto" {
   431  			if isDaemon {
   432  				logfile = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-daemon-%d.log", os.Getpid()))
   433  			} else {
   434  				logfile = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.log", os.Getpid()))
   435  			}
   436  		}
   437  		f, err := os.Create(logfile)
   438  		if err != nil {
   439  			return nil, errors.Errorf("unable to create log file: %w", err)
   440  		}
   441  		closeLog = func() {
   442  			defer f.Close()
   443  		}
   444  		stdlog.SetOutput(io.MultiWriter(os.Stderr, f))
   445  		i.LogWriter = f
   446  	}
   447  	i.Logfile = logfile
   448  	return closeLog, nil
   449  }
   450  
   451  // Serve starts and runs a debug server in the background on the given addr.
   452  // It also logs the port the server starts on, to allow for :0 auto assigned
   453  // ports.
   454  func (i *Instance) Serve(ctx context.Context, addr string) (string, error) {
   455  	stdlog.SetFlags(stdlog.Lshortfile)
   456  	if addr == "" {
   457  		return "", nil
   458  	}
   459  	i.serveMu.Lock()
   460  	defer i.serveMu.Unlock()
   461  
   462  	if i.listenedDebugAddress != "" {
   463  		// Already serving. Return the bound address.
   464  		return i.listenedDebugAddress, nil
   465  	}
   466  
   467  	i.debugAddress = addr
   468  	listener, err := net.Listen("tcp", i.debugAddress)
   469  	if err != nil {
   470  		return "", err
   471  	}
   472  	i.listenedDebugAddress = listener.Addr().String()
   473  
   474  	port := listener.Addr().(*net.TCPAddr).Port
   475  	if strings.HasSuffix(i.debugAddress, ":0") {
   476  		stdlog.Printf("debug server listening at http://localhost:%d", port)
   477  	}
   478  	event.Log(ctx, "Debug serving", tag.Port.Of(port))
   479  	go func() {
   480  		mux := http.NewServeMux()
   481  		mux.HandleFunc("/", render(MainTmpl, func(*http.Request) interface{} { return i }))
   482  		mux.HandleFunc("/debug/", render(DebugTmpl, nil))
   483  		mux.HandleFunc("/debug/pprof/", pprof.Index)
   484  		mux.HandleFunc("/debug/pprof/cmdline", cmdline)
   485  		mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
   486  		mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
   487  		mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
   488  		if i.prometheus != nil {
   489  			mux.HandleFunc("/metrics/", i.prometheus.Serve)
   490  		}
   491  		if i.rpcs != nil {
   492  			mux.HandleFunc("/rpc/", render(RPCTmpl, i.rpcs.getData))
   493  		}
   494  		if i.traces != nil {
   495  			mux.HandleFunc("/trace/", render(TraceTmpl, i.traces.getData))
   496  		}
   497  		mux.HandleFunc("/cache/", render(CacheTmpl, i.getCache))
   498  		mux.HandleFunc("/session/", render(SessionTmpl, i.getSession))
   499  		mux.HandleFunc("/view/", render(ViewTmpl, i.getView))
   500  		mux.HandleFunc("/client/", render(ClientTmpl, i.getClient))
   501  		mux.HandleFunc("/server/", render(ServerTmpl, i.getServer))
   502  		mux.HandleFunc("/file/", render(FileTmpl, i.getFile))
   503  		mux.HandleFunc("/info", render(InfoTmpl, i.getInfo))
   504  		mux.HandleFunc("/memory", render(MemoryTmpl, getMemory))
   505  		if err := http.Serve(listener, mux); err != nil {
   506  			event.Error(ctx, "Debug server failed", err)
   507  			return
   508  		}
   509  		event.Log(ctx, "Debug server finished")
   510  	}()
   511  	return i.listenedDebugAddress, nil
   512  }
   513  
   514  func (i *Instance) DebugAddress() string {
   515  	i.serveMu.Lock()
   516  	defer i.serveMu.Unlock()
   517  	return i.debugAddress
   518  }
   519  
   520  func (i *Instance) ListenedDebugAddress() string {
   521  	i.serveMu.Lock()
   522  	defer i.serveMu.Unlock()
   523  	return i.listenedDebugAddress
   524  }
   525  
   526  // MonitorMemory starts recording memory statistics each second.
   527  func (i *Instance) MonitorMemory(ctx context.Context) {
   528  	tick := time.NewTicker(time.Second)
   529  	nextThresholdGiB := uint64(1)
   530  	go func() {
   531  		for {
   532  			<-tick.C
   533  			var mem runtime.MemStats
   534  			runtime.ReadMemStats(&mem)
   535  			if mem.HeapAlloc < nextThresholdGiB*1<<30 {
   536  				continue
   537  			}
   538  			if err := i.writeMemoryDebug(nextThresholdGiB, true); err != nil {
   539  				event.Error(ctx, "writing memory debug info", err)
   540  			}
   541  			if err := i.writeMemoryDebug(nextThresholdGiB, false); err != nil {
   542  				event.Error(ctx, "writing memory debug info", err)
   543  			}
   544  			event.Log(ctx, fmt.Sprintf("Wrote memory usage debug info to %v", os.TempDir()))
   545  			nextThresholdGiB++
   546  		}
   547  	}()
   548  }
   549  
   550  func (i *Instance) writeMemoryDebug(threshold uint64, withNames bool) error {
   551  	suffix := "withnames"
   552  	if !withNames {
   553  		suffix = "nonames"
   554  	}
   555  
   556  	filename := fmt.Sprintf("gopls.%d-%dGiB-%s.zip", os.Getpid(), threshold, suffix)
   557  	zipf, err := os.OpenFile(filepath.Join(os.TempDir(), filename), os.O_CREATE|os.O_RDWR, 0644)
   558  	if err != nil {
   559  		return err
   560  	}
   561  	zipw := zip.NewWriter(zipf)
   562  
   563  	f, err := zipw.Create("heap.pb.gz")
   564  	if err != nil {
   565  		return err
   566  	}
   567  	if err := rpprof.Lookup("heap").WriteTo(f, 0); err != nil {
   568  		return err
   569  	}
   570  
   571  	f, err = zipw.Create("goroutines.txt")
   572  	if err != nil {
   573  		return err
   574  	}
   575  	if err := rpprof.Lookup("goroutine").WriteTo(f, 1); err != nil {
   576  		return err
   577  	}
   578  
   579  	for _, cache := range i.State.Caches() {
   580  		cf, err := zipw.Create(fmt.Sprintf("cache-%v.html", cache.ID()))
   581  		if err != nil {
   582  			return err
   583  		}
   584  		if _, err := cf.Write([]byte(cache.PackageStats(withNames))); err != nil {
   585  			return err
   586  		}
   587  	}
   588  
   589  	if err := zipw.Close(); err != nil {
   590  		return err
   591  	}
   592  	return zipf.Close()
   593  }
   594  
   595  func makeGlobalExporter(stderr io.Writer) event.Exporter {
   596  	p := export.Printer{}
   597  	var pMu sync.Mutex
   598  	return func(ctx context.Context, ev core.Event, lm label.Map) context.Context {
   599  		i := GetInstance(ctx)
   600  
   601  		if event.IsLog(ev) {
   602  			// Don't log context cancellation errors.
   603  			if err := keys.Err.Get(ev); errors.Is(err, context.Canceled) {
   604  				return ctx
   605  			}
   606  			// Make sure any log messages without an instance go to stderr.
   607  			if i == nil {
   608  				pMu.Lock()
   609  				p.WriteEvent(stderr, ev, lm)
   610  				pMu.Unlock()
   611  			}
   612  			level := log.LabeledLevel(lm)
   613  			// Exclude trace logs from LSP logs.
   614  			if level < log.Trace {
   615  				ctx = protocol.LogEvent(ctx, ev, lm, messageType(level))
   616  			}
   617  		}
   618  		if i == nil {
   619  			return ctx
   620  		}
   621  		return i.exporter(ctx, ev, lm)
   622  	}
   623  }
   624  
   625  func messageType(l log.Level) protocol.MessageType {
   626  	switch l {
   627  	case log.Error:
   628  		return protocol.Error
   629  	case log.Warning:
   630  		return protocol.Warning
   631  	case log.Debug:
   632  		return protocol.Log
   633  	}
   634  	return protocol.Info
   635  }
   636  
   637  func makeInstanceExporter(i *Instance) event.Exporter {
   638  	exporter := func(ctx context.Context, ev core.Event, lm label.Map) context.Context {
   639  		if i.ocagent != nil {
   640  			ctx = i.ocagent.ProcessEvent(ctx, ev, lm)
   641  		}
   642  		if i.prometheus != nil {
   643  			ctx = i.prometheus.ProcessEvent(ctx, ev, lm)
   644  		}
   645  		if i.rpcs != nil {
   646  			ctx = i.rpcs.ProcessEvent(ctx, ev, lm)
   647  		}
   648  		if i.traces != nil {
   649  			ctx = i.traces.ProcessEvent(ctx, ev, lm)
   650  		}
   651  		if event.IsLog(ev) {
   652  			if s := cache.KeyCreateSession.Get(ev); s != nil {
   653  				i.State.addClient(s)
   654  			}
   655  			if sid := tag.NewServer.Get(ev); sid != "" {
   656  				i.State.updateServer(&Server{
   657  					ID:           sid,
   658  					Logfile:      tag.Logfile.Get(ev),
   659  					DebugAddress: tag.DebugAddress.Get(ev),
   660  					GoplsPath:    tag.GoplsPath.Get(ev),
   661  					ClientID:     tag.ClientID.Get(ev),
   662  				})
   663  			}
   664  			if s := cache.KeyShutdownSession.Get(ev); s != nil {
   665  				i.State.dropClient(s)
   666  			}
   667  			if sid := tag.EndServer.Get(ev); sid != "" {
   668  				i.State.dropServer(sid)
   669  			}
   670  			if s := cache.KeyUpdateSession.Get(ev); s != nil {
   671  				if c := i.State.Client(s.ID()); c != nil {
   672  					c.DebugAddress = tag.DebugAddress.Get(ev)
   673  					c.Logfile = tag.Logfile.Get(ev)
   674  					c.ServerID = tag.ServerID.Get(ev)
   675  					c.GoplsPath = tag.GoplsPath.Get(ev)
   676  				}
   677  			}
   678  		}
   679  		if b := tag.Bug.Get(ev); b != "" {
   680  			i.State.recordBug(b, fmt.Sprintf("%v", ev))
   681  		}
   682  		return ctx
   683  	}
   684  	// StdTrace must be above export.Spans below (by convention, export
   685  	// middleware applies its wrapped exporter last).
   686  	exporter = StdTrace(exporter)
   687  	metrics := metric.Config{}
   688  	registerMetrics(&metrics)
   689  	exporter = metrics.Exporter(exporter)
   690  	exporter = export.Spans(exporter)
   691  	exporter = export.Labels(exporter)
   692  	return exporter
   693  }
   694  
   695  type dataFunc func(*http.Request) interface{}
   696  
   697  func render(tmpl *template.Template, fun dataFunc) func(http.ResponseWriter, *http.Request) {
   698  	return func(w http.ResponseWriter, r *http.Request) {
   699  		var data interface{}
   700  		if fun != nil {
   701  			data = fun(r)
   702  		}
   703  		if err := tmpl.Execute(w, data); err != nil {
   704  			event.Error(context.Background(), "", err)
   705  			http.Error(w, err.Error(), http.StatusInternalServerError)
   706  		}
   707  	}
   708  }
   709  
   710  func commas(s string) string {
   711  	for i := len(s); i > 3; {
   712  		i -= 3
   713  		s = s[:i] + "," + s[i:]
   714  	}
   715  	return s
   716  }
   717  
   718  func fuint64(v uint64) string {
   719  	return commas(strconv.FormatUint(v, 10))
   720  }
   721  
   722  func fuint32(v uint32) string {
   723  	return commas(strconv.FormatUint(uint64(v), 10))
   724  }
   725  
   726  func fcontent(v []byte) string {
   727  	return string(v)
   728  }
   729  
   730  var BaseTemplate = template.Must(template.New("").Parse(`
   731  <html>
   732  <head>
   733  <title>{{template "title" .}}</title>
   734  <style>
   735  .profile-name{
   736  	display:inline-block;
   737  	width:6rem;
   738  }
   739  td.value {
   740    text-align: right;
   741  }
   742  ul.events {
   743  	list-style-type: none;
   744  }
   745  
   746  </style>
   747  {{block "head" .}}{{end}}
   748  </head>
   749  <body>
   750  <a href="/">Main</a>
   751  <a href="/info">Info</a>
   752  <a href="/memory">Memory</a>
   753  <a href="/metrics">Metrics</a>
   754  <a href="/rpc">RPC</a>
   755  <a href="/trace">Trace</a>
   756  <hr>
   757  <h1>{{template "title" .}}</h1>
   758  {{block "body" .}}
   759  Unknown page
   760  {{end}}
   761  </body>
   762  </html>
   763  
   764  {{define "cachelink"}}<a href="/cache/{{.}}">Cache {{.}}</a>{{end}}
   765  {{define "clientlink"}}<a href="/client/{{.}}">Client {{.}}</a>{{end}}
   766  {{define "serverlink"}}<a href="/server/{{.}}">Server {{.}}</a>{{end}}
   767  {{define "sessionlink"}}<a href="/session/{{.}}">Session {{.}}</a>{{end}}
   768  {{define "viewlink"}}<a href="/view/{{.}}">View {{.}}</a>{{end}}
   769  {{define "filelink"}}<a href="/file/{{.Session}}/{{.FileIdentity.Hash}}">{{.FileIdentity.URI}}</a>{{end}}
   770  `)).Funcs(template.FuncMap{
   771  	"fuint64":  fuint64,
   772  	"fuint32":  fuint32,
   773  	"fcontent": fcontent,
   774  	"localAddress": func(s string) string {
   775  		// Try to translate loopback addresses to localhost, both for cosmetics and
   776  		// because unspecified ipv6 addresses can break links on Windows.
   777  		//
   778  		// TODO(rfindley): In the future, it would be better not to assume the
   779  		// server is running on localhost, and instead construct this address using
   780  		// the remote host.
   781  		host, port, err := net.SplitHostPort(s)
   782  		if err != nil {
   783  			return s
   784  		}
   785  		ip := net.ParseIP(host)
   786  		if ip == nil {
   787  			return s
   788  		}
   789  		if ip.IsLoopback() || ip.IsUnspecified() {
   790  			return "localhost:" + port
   791  		}
   792  		return s
   793  	},
   794  	"options": func(s *cache.Session) []sessionOption {
   795  		return showOptions(s.Options())
   796  	},
   797  })
   798  
   799  var MainTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
   800  {{define "title"}}GoPls server information{{end}}
   801  {{define "body"}}
   802  <h2>Caches</h2>
   803  <ul>{{range .State.Caches}}<li>{{template "cachelink" .ID}}</li>{{end}}</ul>
   804  <h2>Sessions</h2>
   805  <ul>{{range .State.Sessions}}<li>{{template "sessionlink" .ID}} from {{template "cachelink" .Cache.ID}}</li>{{end}}</ul>
   806  <h2>Views</h2>
   807  <ul>{{range .State.Views}}<li>{{.Name}} is {{template "viewlink" .ID}} from {{template "sessionlink" .Session.ID}} in {{.Folder}}</li>{{end}}</ul>
   808  <h2>Clients</h2>
   809  <ul>{{range .State.Clients}}<li>{{template "clientlink" .Session.ID}}</li>{{end}}</ul>
   810  <h2>Servers</h2>
   811  <ul>{{range .State.Servers}}<li>{{template "serverlink" .ID}}</li>{{end}}</ul>
   812  <h2>Known bugs encountered</h2>
   813  <dl>{{range .State.Bugs}}<dt>{{.Description}}</dt><dd>{{.Event}}</dd>{{end}}</dl>
   814  {{end}}
   815  `))
   816  
   817  var InfoTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
   818  {{define "title"}}GoPls version information{{end}}
   819  {{define "body"}}
   820  {{.}}
   821  {{end}}
   822  `))
   823  
   824  var MemoryTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
   825  {{define "title"}}GoPls memory usage{{end}}
   826  {{define "head"}}<meta http-equiv="refresh" content="5">{{end}}
   827  {{define "body"}}
   828  <h2>Stats</h2>
   829  <table>
   830  <tr><td class="label">Allocated bytes</td><td class="value">{{fuint64 .HeapAlloc}}</td></tr>
   831  <tr><td class="label">Total allocated bytes</td><td class="value">{{fuint64 .TotalAlloc}}</td></tr>
   832  <tr><td class="label">System bytes</td><td class="value">{{fuint64 .Sys}}</td></tr>
   833  <tr><td class="label">Heap system bytes</td><td class="value">{{fuint64 .HeapSys}}</td></tr>
   834  <tr><td class="label">Malloc calls</td><td class="value">{{fuint64 .Mallocs}}</td></tr>
   835  <tr><td class="label">Frees</td><td class="value">{{fuint64 .Frees}}</td></tr>
   836  <tr><td class="label">Idle heap bytes</td><td class="value">{{fuint64 .HeapIdle}}</td></tr>
   837  <tr><td class="label">In use bytes</td><td class="value">{{fuint64 .HeapInuse}}</td></tr>
   838  <tr><td class="label">Released to system bytes</td><td class="value">{{fuint64 .HeapReleased}}</td></tr>
   839  <tr><td class="label">Heap object count</td><td class="value">{{fuint64 .HeapObjects}}</td></tr>
   840  <tr><td class="label">Stack in use bytes</td><td class="value">{{fuint64 .StackInuse}}</td></tr>
   841  <tr><td class="label">Stack from system bytes</td><td class="value">{{fuint64 .StackSys}}</td></tr>
   842  <tr><td class="label">Bucket hash bytes</td><td class="value">{{fuint64 .BuckHashSys}}</td></tr>
   843  <tr><td class="label">GC metadata bytes</td><td class="value">{{fuint64 .GCSys}}</td></tr>
   844  <tr><td class="label">Off heap bytes</td><td class="value">{{fuint64 .OtherSys}}</td></tr>
   845  </table>
   846  <h2>By size</h2>
   847  <table>
   848  <tr><th>Size</th><th>Mallocs</th><th>Frees</th></tr>
   849  {{range .BySize}}<tr><td class="value">{{fuint32 .Size}}</td><td class="value">{{fuint64 .Mallocs}}</td><td class="value">{{fuint64 .Frees}}</td></tr>{{end}}
   850  </table>
   851  {{end}}
   852  `))
   853  
   854  var DebugTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
   855  {{define "title"}}GoPls Debug pages{{end}}
   856  {{define "body"}}
   857  <a href="/debug/pprof">Profiling</a>
   858  {{end}}
   859  `))
   860  
   861  var CacheTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
   862  {{define "title"}}Cache {{.ID}}{{end}}
   863  {{define "body"}}
   864  <h2>memoize.Store entries</h2>
   865  <ul>{{range $k,$v := .MemStats}}<li>{{$k}} - {{$v}}</li>{{end}}</ul>
   866  <h2>Per-package usage - not accurate, for guidance only</h2>
   867  {{.PackageStats true}}
   868  {{end}}
   869  `))
   870  
   871  var ClientTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
   872  {{define "title"}}Client {{.Session.ID}}{{end}}
   873  {{define "body"}}
   874  Using session: <b>{{template "sessionlink" .Session.ID}}</b><br>
   875  {{if .DebugAddress}}Debug this client at: <a href="http://{{localAddress .DebugAddress}}">{{localAddress .DebugAddress}}</a><br>{{end}}
   876  Logfile: {{.Logfile}}<br>
   877  Gopls Path: {{.GoplsPath}}<br>
   878  <h2>Diagnostics</h2>
   879  {{/*Service: []protocol.Server; each server has map[uri]fileReports;
   880  	each fileReport: map[diagnosticSoure]diagnosticReport
   881  	diagnosticSource is one of 5 source
   882  	diagnosticReport: snapshotID and map[hash]*source.Diagnostic
   883  	sourceDiagnostic: struct {
   884  		Range    protocol.Range
   885  		Message  string
   886  		Source   string
   887  		Code     string
   888  		CodeHref string
   889  		Severity protocol.DiagnosticSeverity
   890  		Tags     []protocol.DiagnosticTag
   891  
   892  		Related []RelatedInformation
   893  	}
   894  	RelatedInformation: struct {
   895  		URI     span.URI
   896  		Range   protocol.Range
   897  		Message string
   898  	}
   899  	*/}}
   900  <ul>{{range $k, $v := .Service.Diagnostics}}<li>{{$k}}:<ol>{{range $v}}<li>{{.}}</li>{{end}}</ol></li>{{end}}</ul>
   901  {{end}}
   902  `))
   903  
   904  var ServerTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
   905  {{define "title"}}Server {{.ID}}{{end}}
   906  {{define "body"}}
   907  {{if .DebugAddress}}Debug this server at: <a href="http://{{localAddress .DebugAddress}}">{{localAddress .DebugAddress}}</a><br>{{end}}
   908  Logfile: {{.Logfile}}<br>
   909  Gopls Path: {{.GoplsPath}}<br>
   910  {{end}}
   911  `))
   912  
   913  var SessionTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
   914  {{define "title"}}Session {{.ID}}{{end}}
   915  {{define "body"}}
   916  From: <b>{{template "cachelink" .Cache.ID}}</b><br>
   917  <h2>Views</h2>
   918  <ul>{{range .Views}}<li>{{.Name}} is {{template "viewlink" .ID}} in {{.Folder}}</li>{{end}}</ul>
   919  <h2>Overlays</h2>
   920  <ul>{{range .Overlays}}<li>{{template "filelink" .}}</li>{{end}}</ul>
   921  <h2>Options</h2>
   922  {{range options .}}
   923  <p><b>{{.Name}}</b> {{.Type}}</p>
   924  <p><i>default:</i> {{.Default}}</p>
   925  {{if ne .Default .Current}}<p><i>current:</i> {{.Current}}</p>{{end}}
   926  {{end}}
   927  {{end}}
   928  `))
   929  
   930  var ViewTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
   931  {{define "title"}}View {{.ID}}{{end}}
   932  {{define "body"}}
   933  Name: <b>{{.Name}}</b><br>
   934  Folder: <b>{{.Folder}}</b><br>
   935  From: <b>{{template "sessionlink" .Session.ID}}</b><br>
   936  <h2>Environment</h2>
   937  <ul>{{range .Options.Env}}<li>{{.}}</li>{{end}}</ul>
   938  {{end}}
   939  `))
   940  
   941  var FileTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
   942  {{define "title"}}Overlay {{.FileIdentity.Hash}}{{end}}
   943  {{define "body"}}
   944  {{with .}}
   945  	From: <b>{{template "sessionlink" .Session}}</b><br>
   946  	URI: <b>{{.URI}}</b><br>
   947  	Identifier: <b>{{.FileIdentity.Hash}}</b><br>
   948  	Version: <b>{{.Version}}</b><br>
   949  	Kind: <b>{{.Kind}}</b><br>
   950  {{end}}
   951  <h3>Contents</h3>
   952  <pre>{{fcontent .Read}}</pre>
   953  {{end}}
   954  `))