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