github.com/april1989/origin-go-tools@v0.0.32/internal/lsp/debug/trace.go (about) 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 "bytes" 9 "context" 10 "fmt" 11 "html/template" 12 "net/http" 13 "sort" 14 "strings" 15 "sync" 16 "time" 17 18 "github.com/april1989/origin-go-tools/internal/event" 19 "github.com/april1989/origin-go-tools/internal/event/core" 20 "github.com/april1989/origin-go-tools/internal/event/export" 21 "github.com/april1989/origin-go-tools/internal/event/label" 22 ) 23 24 var traceTmpl = template.Must(template.Must(baseTemplate.Clone()).Parse(` 25 {{define "title"}}Trace Information{{end}} 26 {{define "body"}} 27 {{range .Traces}}<a href="/trace/{{.Name}}">{{.Name}}</a> last: {{.Last.Duration}}, longest: {{.Longest.Duration}}<br>{{end}} 28 {{if .Selected}} 29 <H2>{{.Selected.Name}}</H2> 30 {{if .Selected.Last}}<H3>Last</H3><ul>{{template "details" .Selected.Last}}</ul>{{end}} 31 {{if .Selected.Longest}}<H3>Longest</H3><ul>{{template "details" .Selected.Longest}}</ul>{{end}} 32 {{end}} 33 {{end}} 34 {{define "details"}} 35 <li>{{.Offset}} {{.Name}} {{.Duration}} {{.Tags}}</li> 36 {{if .Events}}<ul class=events>{{range .Events}}<li>{{.Offset}} {{.Tags}}</li>{{end}}</ul>{{end}} 37 {{if .Children}}<ul>{{range .Children}}{{template "details" .}}{{end}}</ul>{{end}} 38 {{end}} 39 `)) 40 41 type traces struct { 42 mu sync.Mutex 43 sets map[string]*traceSet 44 unfinished map[export.SpanContext]*traceData 45 } 46 47 type traceResults struct { 48 Traces []*traceSet 49 Selected *traceSet 50 } 51 52 type traceSet struct { 53 Name string 54 Last *traceData 55 Longest *traceData 56 } 57 58 type traceData struct { 59 TraceID export.TraceID 60 SpanID export.SpanID 61 ParentID export.SpanID 62 Name string 63 Start time.Time 64 Finish time.Time 65 Offset time.Duration 66 Duration time.Duration 67 Tags string 68 Events []traceEvent 69 Children []*traceData 70 } 71 72 type traceEvent struct { 73 Time time.Time 74 Offset time.Duration 75 Tags string 76 } 77 78 func (t *traces) ProcessEvent(ctx context.Context, ev core.Event, lm label.Map) context.Context { 79 t.mu.Lock() 80 defer t.mu.Unlock() 81 span := export.GetSpan(ctx) 82 if span == nil { 83 return ctx 84 } 85 86 switch { 87 case event.IsStart(ev): 88 if t.sets == nil { 89 t.sets = make(map[string]*traceSet) 90 t.unfinished = make(map[export.SpanContext]*traceData) 91 } 92 // just starting, add it to the unfinished map 93 td := &traceData{ 94 TraceID: span.ID.TraceID, 95 SpanID: span.ID.SpanID, 96 ParentID: span.ParentID, 97 Name: span.Name, 98 Start: span.Start().At(), 99 Tags: renderLabels(span.Start()), 100 } 101 t.unfinished[span.ID] = td 102 // and wire up parents if we have them 103 if !span.ParentID.IsValid() { 104 return ctx 105 } 106 parentID := export.SpanContext{TraceID: span.ID.TraceID, SpanID: span.ParentID} 107 parent, found := t.unfinished[parentID] 108 if !found { 109 // trace had an invalid parent, so it cannot itself be valid 110 return ctx 111 } 112 parent.Children = append(parent.Children, td) 113 114 case event.IsEnd(ev): 115 // finishing, must be already in the map 116 td, found := t.unfinished[span.ID] 117 if !found { 118 return ctx // if this happens we are in a bad place 119 } 120 delete(t.unfinished, span.ID) 121 122 td.Finish = span.Finish().At() 123 td.Duration = span.Finish().At().Sub(span.Start().At()) 124 events := span.Events() 125 td.Events = make([]traceEvent, len(events)) 126 for i, event := range events { 127 td.Events[i] = traceEvent{ 128 Time: event.At(), 129 Tags: renderLabels(event), 130 } 131 } 132 133 set, ok := t.sets[span.Name] 134 if !ok { 135 set = &traceSet{Name: span.Name} 136 t.sets[span.Name] = set 137 } 138 set.Last = td 139 if set.Longest == nil || set.Last.Duration > set.Longest.Duration { 140 set.Longest = set.Last 141 } 142 if !td.ParentID.IsValid() { 143 fillOffsets(td, td.Start) 144 } 145 } 146 return ctx 147 } 148 149 func (t *traces) getData(req *http.Request) interface{} { 150 if len(t.sets) == 0 { 151 return nil 152 } 153 data := traceResults{} 154 data.Traces = make([]*traceSet, 0, len(t.sets)) 155 for _, set := range t.sets { 156 data.Traces = append(data.Traces, set) 157 } 158 sort.Slice(data.Traces, func(i, j int) bool { return data.Traces[i].Name < data.Traces[j].Name }) 159 if bits := strings.SplitN(req.URL.Path, "/trace/", 2); len(bits) > 1 { 160 data.Selected = t.sets[bits[1]] 161 } 162 return data 163 } 164 165 func fillOffsets(td *traceData, start time.Time) { 166 td.Offset = td.Start.Sub(start) 167 for i := range td.Events { 168 td.Events[i].Offset = td.Events[i].Time.Sub(start) 169 } 170 for _, child := range td.Children { 171 fillOffsets(child, start) 172 } 173 } 174 175 func renderLabels(labels label.List) string { 176 buf := &bytes.Buffer{} 177 for index := 0; labels.Valid(index); index++ { 178 if l := labels.Label(index); l.Valid() { 179 fmt.Fprintf(buf, "%v ", l) 180 } 181 } 182 return buf.String() 183 }