.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 / trace.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/internal/lsp/debug/trace.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/internal/lsp/debug/trace.go
new file mode 100644 (file)
index 0000000..ca61286
--- /dev/null
@@ -0,0 +1,226 @@
+// Copyright 2019 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 debug
+
+import (
+       "bytes"
+       "context"
+       "fmt"
+       "html/template"
+       "net/http"
+       "runtime/trace"
+       "sort"
+       "strings"
+       "sync"
+       "time"
+
+       "golang.org/x/tools/internal/event"
+       "golang.org/x/tools/internal/event/core"
+       "golang.org/x/tools/internal/event/export"
+       "golang.org/x/tools/internal/event/label"
+)
+
+var TraceTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
+{{define "title"}}Trace Information{{end}}
+{{define "body"}}
+       {{range .Traces}}<a href="/trace/{{.Name}}">{{.Name}}</a> last: {{.Last.Duration}}, longest: {{.Longest.Duration}}<br>{{end}}
+       {{if .Selected}}
+               <H2>{{.Selected.Name}}</H2>
+               {{if .Selected.Last}}<H3>Last</H3><ul>{{template "details" .Selected.Last}}</ul>{{end}}
+               {{if .Selected.Longest}}<H3>Longest</H3><ul>{{template "details" .Selected.Longest}}</ul>{{end}}
+       {{end}}
+{{end}}
+{{define "details"}}
+       <li>{{.Offset}} {{.Name}} {{.Duration}} {{.Tags}}</li>
+       {{if .Events}}<ul class=events>{{range .Events}}<li>{{.Offset}} {{.Tags}}</li>{{end}}</ul>{{end}}
+       {{if .Children}}<ul>{{range .Children}}{{template "details" .}}{{end}}</ul>{{end}}
+{{end}}
+`))
+
+type traces struct {
+       mu         sync.Mutex
+       sets       map[string]*traceSet
+       unfinished map[export.SpanContext]*traceData
+}
+
+type TraceResults struct { // exported for testing
+       Traces   []*traceSet
+       Selected *traceSet
+}
+
+type traceSet struct {
+       Name    string
+       Last    *traceData
+       Longest *traceData
+}
+
+type traceData struct {
+       TraceID  export.TraceID
+       SpanID   export.SpanID
+       ParentID export.SpanID
+       Name     string
+       Start    time.Time
+       Finish   time.Time
+       Offset   time.Duration
+       Duration time.Duration
+       Tags     string
+       Events   []traceEvent
+       Children []*traceData
+}
+
+type traceEvent struct {
+       Time   time.Time
+       Offset time.Duration
+       Tags   string
+}
+
+func StdTrace(exporter event.Exporter) event.Exporter {
+       return func(ctx context.Context, ev core.Event, lm label.Map) context.Context {
+               span := export.GetSpan(ctx)
+               if span == nil {
+                       return exporter(ctx, ev, lm)
+               }
+               switch {
+               case event.IsStart(ev):
+                       if span.ParentID.IsValid() {
+                               region := trace.StartRegion(ctx, span.Name)
+                               ctx = context.WithValue(ctx, traceKey, region)
+                       } else {
+                               var task *trace.Task
+                               ctx, task = trace.NewTask(ctx, span.Name)
+                               ctx = context.WithValue(ctx, traceKey, task)
+                       }
+                       // Log the start event as it may contain useful labels.
+                       msg := formatEvent(ctx, ev, lm)
+                       trace.Log(ctx, "start", msg)
+               case event.IsLog(ev):
+                       category := ""
+                       if event.IsError(ev) {
+                               category = "error"
+                       }
+                       msg := formatEvent(ctx, ev, lm)
+                       trace.Log(ctx, category, msg)
+               case event.IsEnd(ev):
+                       if v := ctx.Value(traceKey); v != nil {
+                               v.(interface{ End() }).End()
+                       }
+               }
+               return exporter(ctx, ev, lm)
+       }
+}
+
+func formatEvent(ctx context.Context, ev core.Event, lm label.Map) string {
+       buf := &bytes.Buffer{}
+       p := export.Printer{}
+       p.WriteEvent(buf, ev, lm)
+       return buf.String()
+}
+
+func (t *traces) ProcessEvent(ctx context.Context, ev core.Event, lm label.Map) context.Context {
+       t.mu.Lock()
+       defer t.mu.Unlock()
+       span := export.GetSpan(ctx)
+       if span == nil {
+               return ctx
+       }
+
+       switch {
+       case event.IsStart(ev):
+               if t.sets == nil {
+                       t.sets = make(map[string]*traceSet)
+                       t.unfinished = make(map[export.SpanContext]*traceData)
+               }
+               // just starting, add it to the unfinished map
+               td := &traceData{
+                       TraceID:  span.ID.TraceID,
+                       SpanID:   span.ID.SpanID,
+                       ParentID: span.ParentID,
+                       Name:     span.Name,
+                       Start:    span.Start().At(),
+                       Tags:     renderLabels(span.Start()),
+               }
+               t.unfinished[span.ID] = td
+               // and wire up parents if we have them
+               if !span.ParentID.IsValid() {
+                       return ctx
+               }
+               parentID := export.SpanContext{TraceID: span.ID.TraceID, SpanID: span.ParentID}
+               parent, found := t.unfinished[parentID]
+               if !found {
+                       // trace had an invalid parent, so it cannot itself be valid
+                       return ctx
+               }
+               parent.Children = append(parent.Children, td)
+
+       case event.IsEnd(ev):
+               // finishing, must be already in the map
+               td, found := t.unfinished[span.ID]
+               if !found {
+                       return ctx // if this happens we are in a bad place
+               }
+               delete(t.unfinished, span.ID)
+
+               td.Finish = span.Finish().At()
+               td.Duration = span.Finish().At().Sub(span.Start().At())
+               events := span.Events()
+               td.Events = make([]traceEvent, len(events))
+               for i, event := range events {
+                       td.Events[i] = traceEvent{
+                               Time: event.At(),
+                               Tags: renderLabels(event),
+                       }
+               }
+
+               set, ok := t.sets[span.Name]
+               if !ok {
+                       set = &traceSet{Name: span.Name}
+                       t.sets[span.Name] = set
+               }
+               set.Last = td
+               if set.Longest == nil || set.Last.Duration > set.Longest.Duration {
+                       set.Longest = set.Last
+               }
+               if !td.ParentID.IsValid() {
+                       fillOffsets(td, td.Start)
+               }
+       }
+       return ctx
+}
+
+func (t *traces) getData(req *http.Request) interface{} {
+       if len(t.sets) == 0 {
+               return nil
+       }
+       data := TraceResults{}
+       data.Traces = make([]*traceSet, 0, len(t.sets))
+       for _, set := range t.sets {
+               data.Traces = append(data.Traces, set)
+       }
+       sort.Slice(data.Traces, func(i, j int) bool { return data.Traces[i].Name < data.Traces[j].Name })
+       if bits := strings.SplitN(req.URL.Path, "/trace/", 2); len(bits) > 1 {
+               data.Selected = t.sets[bits[1]]
+       }
+       return data
+}
+
+func fillOffsets(td *traceData, start time.Time) {
+       td.Offset = td.Start.Sub(start)
+       for i := range td.Events {
+               td.Events[i].Offset = td.Events[i].Time.Sub(start)
+       }
+       for _, child := range td.Children {
+               fillOffsets(child, start)
+       }
+}
+
+func renderLabels(labels label.List) string {
+       buf := &bytes.Buffer{}
+       for index := 0; labels.Valid(index); index++ {
+               if l := labels.Label(index); l.Valid() {
+                       fmt.Fprintf(buf, "%v ", l)
+               }
+       }
+       return buf.String()
+}