internal/lsp/debug: add a facility to track known bugs

Sometimes users report issues related to edge cases in Gopls that aren't
reproducible. In some of these cases, we end up guarding against
conditions that shouldn't be possible, which is an unfortunately fragile
solution.

Add a new debug.Bug function to both annotate such branches as known
bugs, and help find them when they reoccur.  For now this just records
them in the debug server, but in the future we could send the user a
message to the effect of "hey, a known bug has occurred" for debug
builds of gopls.

Also included are some minor cosmetic fixes.

Change-Id: I95df0caf2c81f430661cabd573ce8e338fa69934
Reviewed-on: https://go-review.googlesource.com/c/tools/+/318369
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Rob Findley 2021-05-10 10:27:35 -04:00 коммит произвёл Robert Findley
Родитель 5a667787ee
Коммит fa05545715
4 изменённых файлов: 54 добавлений и 3 удалений

Просмотреть файл

@ -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 (

Просмотреть файл

@ -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

Просмотреть файл

@ -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(`
<ul>{{range .State.Clients}}<li>{{template "clientlink" .Session.ID}}</li>{{end}}</ul>
<h2>Servers</h2>
<ul>{{range .State.Servers}}<li>{{template "serverlink" .ID}}</li>{{end}}</ul>
<h2>Known bugs encountered</h2>
<dl>{{range .State.Bugs}}<dt>{{.Description}}</dt><dd>{{.Event}}</dd>{{end}}</dl>
{{end}}
`))

Просмотреть файл

@ -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 (