| | |
| | |
| | |
| |
|
| | package trace |
| |
|
| | import ( |
| | "fmt" |
| | "internal/trace/tracev2" |
| | "io" |
| | "runtime" |
| | "sync" |
| | "sync/atomic" |
| | _ "unsafe" |
| | ) |
| |
|
| | var tracing traceMultiplexer |
| |
|
| | type traceMultiplexer struct { |
| | sync.Mutex |
| | enabled atomic.Bool |
| | subscribers int |
| |
|
| | subscribersMu sync.Mutex |
| | traceStartWriter io.Writer |
| | flightRecorder *recorder |
| | } |
| |
|
| | func (t *traceMultiplexer) subscribeFlightRecorder(r *recorder) error { |
| | t.Lock() |
| | defer t.Unlock() |
| |
|
| | t.subscribersMu.Lock() |
| | if t.flightRecorder != nil { |
| | t.subscribersMu.Unlock() |
| | return fmt.Errorf("flight recorder already enabled") |
| | } |
| | t.flightRecorder = r |
| | t.subscribersMu.Unlock() |
| |
|
| | if err := t.addedSubscriber(); err != nil { |
| | t.subscribersMu.Lock() |
| | t.flightRecorder = nil |
| | t.subscribersMu.Unlock() |
| | return err |
| | } |
| | return nil |
| | } |
| |
|
| | func (t *traceMultiplexer) unsubscribeFlightRecorder() error { |
| | t.Lock() |
| | defer t.Unlock() |
| |
|
| | t.removingSubscriber() |
| |
|
| | t.subscribersMu.Lock() |
| | if t.flightRecorder == nil { |
| | t.subscribersMu.Unlock() |
| | return fmt.Errorf("attempt to unsubscribe missing flight recorder") |
| | } |
| | t.flightRecorder = nil |
| | t.subscribersMu.Unlock() |
| |
|
| | t.removedSubscriber() |
| | return nil |
| | } |
| |
|
| | func (t *traceMultiplexer) subscribeTraceStartWriter(w io.Writer) error { |
| | t.Lock() |
| | defer t.Unlock() |
| |
|
| | t.subscribersMu.Lock() |
| | if t.traceStartWriter != nil { |
| | t.subscribersMu.Unlock() |
| | return fmt.Errorf("execution tracer already enabled") |
| | } |
| | t.traceStartWriter = w |
| | t.subscribersMu.Unlock() |
| |
|
| | if err := t.addedSubscriber(); err != nil { |
| | t.subscribersMu.Lock() |
| | t.traceStartWriter = nil |
| | t.subscribersMu.Unlock() |
| | return err |
| | } |
| | return nil |
| | } |
| |
|
| | func (t *traceMultiplexer) unsubscribeTraceStartWriter() { |
| | t.Lock() |
| | defer t.Unlock() |
| |
|
| | t.removingSubscriber() |
| |
|
| | t.subscribersMu.Lock() |
| | if t.traceStartWriter == nil { |
| | t.subscribersMu.Unlock() |
| | return |
| | } |
| | t.traceStartWriter = nil |
| | t.subscribersMu.Unlock() |
| |
|
| | t.removedSubscriber() |
| | return |
| | } |
| |
|
| | func (t *traceMultiplexer) addedSubscriber() error { |
| | if t.enabled.Load() { |
| | |
| | runtime_traceAdvance(false) |
| | } else { |
| | if err := t.startLocked(); err != nil { |
| | return err |
| | } |
| | } |
| | t.subscribers++ |
| | return nil |
| | } |
| |
|
| | func (t *traceMultiplexer) removingSubscriber() { |
| | if t.subscribers == 0 { |
| | return |
| | } |
| | t.subscribers-- |
| | if t.subscribers == 0 { |
| | runtime.StopTrace() |
| | t.enabled.Store(false) |
| | } else { |
| | |
| | runtime_traceAdvance(false) |
| | } |
| | } |
| |
|
| | func (t *traceMultiplexer) removedSubscriber() { |
| | if t.subscribers > 0 { |
| | |
| | runtime_traceAdvance(false) |
| | } |
| | } |
| |
|
| | func (t *traceMultiplexer) startLocked() error { |
| | if err := runtime.StartTrace(); err != nil { |
| | return err |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | t.subscribersMu.Lock() |
| | flightRecorder := t.flightRecorder |
| | traceStartWriter := t.traceStartWriter |
| | t.subscribersMu.Unlock() |
| |
|
| | go func() { |
| | header := runtime.ReadTrace() |
| | if traceStartWriter != nil { |
| | traceStartWriter.Write(header) |
| | } |
| | if flightRecorder != nil { |
| | flightRecorder.Write(header) |
| | } |
| |
|
| | for { |
| | data := runtime.ReadTrace() |
| | if data == nil { |
| | break |
| | } |
| | if traceStartWriter != nil { |
| | traceStartWriter.Write(data) |
| | } |
| | if flightRecorder != nil { |
| | flightRecorder.Write(data) |
| | } |
| | if len(data) == 1 && tracev2.EventType(data[0]) == tracev2.EvEndOfGeneration { |
| | if flightRecorder != nil { |
| | flightRecorder.endGeneration() |
| | } |
| |
|
| | |
| | t.subscribersMu.Lock() |
| | frIsNew := flightRecorder != t.flightRecorder && t.flightRecorder != nil |
| | trIsNew := traceStartWriter != t.traceStartWriter && t.traceStartWriter != nil |
| | flightRecorder = t.flightRecorder |
| | traceStartWriter = t.traceStartWriter |
| | t.subscribersMu.Unlock() |
| |
|
| | if trIsNew { |
| | traceStartWriter.Write(header) |
| | } |
| | if frIsNew { |
| | flightRecorder.Write(header) |
| | } |
| | } |
| | } |
| | }() |
| | t.enabled.Store(true) |
| | return nil |
| | } |
| |
|
| | |
| | func runtime_readTrace() (buf []byte) |
| |
|