diff --git a/gopls/internal/regtest/misc/debugserver_test.go b/gopls/internal/regtest/misc/debugserver_test.go index 88cfded908..c0df87070c 100644 --- a/gopls/internal/regtest/misc/debugserver_test.go +++ b/gopls/internal/regtest/misc/debugserver_test.go @@ -1,6 +1,7 @@ // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. + package misc import ( diff --git a/internal/event/export/prometheus/prometheus.go b/internal/event/export/prometheus/prometheus.go index 847babcb89..0281f60a35 100644 --- a/internal/event/export/prometheus/prometheus.go +++ b/internal/event/export/prometheus/prometheus.go @@ -27,13 +27,13 @@ type Exporter struct { metrics []metric.Data } -func (e *Exporter) ProcessEvent(ctx context.Context, ev core.Event, ln label.Map) context.Context { +func (e *Exporter) ProcessEvent(ctx context.Context, ev core.Event, lm label.Map) context.Context { if !event.IsMetric(ev) { return ctx } e.mu.Lock() defer e.mu.Unlock() - metrics := metric.Entries.Get(ln).([]metric.Data) + metrics := metric.Entries.Get(lm).([]metric.Data) for _, data := range metrics { name := data.Handle() // We keep the metrics in name sorted order so the page is stable and easy diff --git a/internal/lsp/debug/serve.go b/internal/lsp/debug/serve.go index 58e59b5f2a..d7dd5445d2 100644 --- a/internal/lsp/debug/serve.go +++ b/internal/lsp/debug/serve.go @@ -20,6 +20,7 @@ import ( "path/filepath" "runtime" rpprof "runtime/pprof" + "sort" "strconv" "strings" "sync" @@ -76,6 +77,46 @@ type State struct { mu sync.Mutex clients []*Client servers []*Server + + // bugs maps bug description -> formatted event + bugs map[string]string +} + +func Bug(ctx context.Context, desc string) { + labels := [3]label.Label{ + tag.Bug.Of(desc), + } + _, file, line, ok := runtime.Caller(1) + if ok { + labels[1] = tag.Callsite.Of(fmt.Sprintf("%s:%d", file, line)) + } + core.Export(ctx, core.MakeEvent(labels, nil)) +} + +type bug struct { + Description, Event string +} + +func (st *State) Bugs() []bug { + st.mu.Lock() + defer st.mu.Unlock() + var bugs []bug + for k, v := range st.bugs { + bugs = append(bugs, bug{k, v}) + } + sort.Slice(bugs, func(i, j int) bool { + return bugs[i].Description < bugs[j].Description + }) + return bugs +} + +func (st *State) recordBug(description, event string) { + st.mu.Lock() + defer st.mu.Unlock() + if st.bugs == nil { + st.bugs = make(map[string]string) + } + st.bugs[description] = event } // Caches returns the set of Cache objects currently being served. @@ -338,7 +379,7 @@ func (i *Instance) AddService(s protocol.Server, session *cache.Session) { stdlog.Printf("unable to find a Client to add the protocol.Server to") } -func getMemory(r *http.Request) interface{} { +func getMemory(_ *http.Request) interface{} { var m runtime.MemStats runtime.ReadMemStats(&m) return m @@ -636,6 +677,9 @@ func makeInstanceExporter(i *Instance) event.Exporter { } } } + if b := tag.Bug.Get(ev); b != "" { + i.State.recordBug(b, fmt.Sprintf("%v", ev)) + } return ctx } // StdTrace must be above export.Spans below (by convention, export @@ -766,6 +810,8 @@ var MainTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`

Servers

+

Known bugs encountered

+
{{range .State.Bugs}}
{{.Description}}
{{.Event}}
{{end}}
{{end}} `)) diff --git a/internal/lsp/debug/tag/tag.go b/internal/lsp/debug/tag/tag.go index ff2f2ecd38..1d00038f0a 100644 --- a/internal/lsp/debug/tag/tag.go +++ b/internal/lsp/debug/tag/tag.go @@ -43,6 +43,10 @@ var ( ClientID = keys.NewString("client_id", "") Level = keys.NewInt("level", "The logging level") + + // Bug tracks occurrences of known bugs in the server. + Bug = keys.NewString("bug", "A bug has occurred") + Callsite = keys.NewString("callsite", "gopls function call site") ) var (