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