github.com/bir3/gocompiler@v0.9.2202/src/cmd/gocmd/internal/trace/trace.go (about) 1 // Copyright 2020 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 trace 6 7 import ( 8 "context" 9 "encoding/json" 10 "errors" 11 "github.com/bir3/gocompiler/src/internal/trace/traceviewer/format" 12 "os" 13 "strings" 14 "sync/atomic" 15 "time" 16 ) 17 18 // Constants used in event fields. 19 // See https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU 20 // for more details. 21 const ( 22 phaseDurationBegin = "B" 23 phaseDurationEnd = "E" 24 phaseFlowStart = "s" 25 phaseFlowEnd = "f" 26 27 bindEnclosingSlice = "e" 28 ) 29 30 var traceStarted atomic.Bool 31 32 func getTraceContext(ctx context.Context) (traceContext, bool) { 33 if !traceStarted.Load() { 34 return traceContext{}, false 35 } 36 v := ctx.Value(traceKey{}) 37 if v == nil { 38 return traceContext{}, false 39 } 40 return v.(traceContext), true 41 } 42 43 // StartSpan starts a trace event with the given name. The Span ends when its Done method is called. 44 func StartSpan(ctx context.Context, name string) (context.Context, *Span) { 45 tc, ok := getTraceContext(ctx) 46 if !ok { 47 return ctx, nil 48 } 49 childSpan := &Span{t: tc.t, name: name, tid: tc.tid, start: time.Now()} 50 tc.t.writeEvent(&format.Event{ 51 Name: childSpan.name, 52 Time: float64(childSpan.start.UnixNano()) / float64(time.Microsecond), 53 TID: childSpan.tid, 54 Phase: phaseDurationBegin, 55 }) 56 ctx = context.WithValue(ctx, traceKey{}, traceContext{tc.t, tc.tid}) 57 return ctx, childSpan 58 } 59 60 // StartGoroutine associates the context with a new Thread ID. The Chrome trace viewer associates each 61 // trace event with a thread, and doesn't expect events with the same thread id to happen at the 62 // same time. 63 func StartGoroutine(ctx context.Context) context.Context { 64 tc, ok := getTraceContext(ctx) 65 if !ok { 66 return ctx 67 } 68 return context.WithValue(ctx, traceKey{}, traceContext{tc.t, tc.t.getNextTID()}) 69 } 70 71 // Flow marks a flow indicating that the 'to' span depends on the 'from' span. 72 // Flow should be called while the 'to' span is in progress. 73 func Flow(ctx context.Context, from *Span, to *Span) { 74 tc, ok := getTraceContext(ctx) 75 if !ok || from == nil || to == nil { 76 return 77 } 78 79 id := tc.t.getNextFlowID() 80 tc.t.writeEvent(&format.Event{ 81 Name: from.name + " -> " + to.name, 82 Category: "flow", 83 ID: id, 84 Time: float64(from.end.UnixNano()) / float64(time.Microsecond), 85 Phase: phaseFlowStart, 86 TID: from.tid, 87 }) 88 tc.t.writeEvent(&format.Event{ 89 Name: from.name + " -> " + to.name, 90 Category: "flow", // TODO(matloob): Add Category to Flow? 91 ID: id, 92 Time: float64(to.start.UnixNano()) / float64(time.Microsecond), 93 Phase: phaseFlowEnd, 94 TID: to.tid, 95 BindPoint: bindEnclosingSlice, 96 }) 97 } 98 99 type Span struct { 100 t *tracer 101 102 name string 103 tid uint64 104 start time.Time 105 end time.Time 106 } 107 108 func (s *Span) Done() { 109 if s == nil { 110 return 111 } 112 s.end = time.Now() 113 s.t.writeEvent(&format.Event{ 114 Name: s.name, 115 Time: float64(s.end.UnixNano()) / float64(time.Microsecond), 116 TID: s.tid, 117 Phase: phaseDurationEnd, 118 }) 119 } 120 121 type tracer struct { 122 file chan traceFile // 1-buffered 123 124 nextTID atomic.Uint64 125 nextFlowID atomic.Uint64 126 } 127 128 func (t *tracer) writeEvent(ev *format.Event) error { 129 f := <-t.file 130 defer func() { t.file <- f }() 131 var err error 132 if f.entries == 0 { 133 _, err = f.sb.WriteString("[\n") 134 } else { 135 _, err = f.sb.WriteString(",") 136 } 137 f.entries++ 138 if err != nil { 139 return nil 140 } 141 142 if err := f.enc.Encode(ev); err != nil { 143 return err 144 } 145 146 // Write event string to output file. 147 _, err = f.f.WriteString(f.sb.String()) 148 f.sb.Reset() 149 return err 150 } 151 152 func (t *tracer) Close() error { 153 f := <-t.file 154 defer func() { t.file <- f }() 155 156 _, firstErr := f.f.WriteString("]") 157 if err := f.f.Close(); firstErr == nil { 158 firstErr = err 159 } 160 return firstErr 161 } 162 163 func (t *tracer) getNextTID() uint64 { 164 return t.nextTID.Add(1) 165 } 166 167 func (t *tracer) getNextFlowID() uint64 { 168 return t.nextFlowID.Add(1) 169 } 170 171 // traceKey is the context key for tracing information. It is unexported to prevent collisions with context keys defined in 172 // other packages. 173 type traceKey struct{} 174 175 type traceContext struct { 176 t *tracer 177 tid uint64 178 } 179 180 // Start starts a trace which writes to the given file. 181 func Start(ctx context.Context, file string) (context.Context, func() error, error) { 182 traceStarted.Store(true) 183 if file == "" { 184 return nil, nil, errors.New("no trace file supplied") 185 } 186 f, err := os.Create(file) 187 if err != nil { 188 return nil, nil, err 189 } 190 t := &tracer{file: make(chan traceFile, 1)} 191 sb := new(strings.Builder) 192 t.file <- traceFile{ 193 f: f, 194 sb: sb, 195 enc: json.NewEncoder(sb), 196 } 197 ctx = context.WithValue(ctx, traceKey{}, traceContext{t: t}) 198 return ctx, t.Close, nil 199 } 200 201 type traceFile struct { 202 f *os.File 203 sb *strings.Builder 204 enc *json.Encoder 205 entries int64 206 }