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