| // Copyright 2025 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 trace_test |
| |
| import ( |
| "bytes" |
| inttrace "internal/trace" |
| "internal/trace/testtrace" |
| "io" |
| "runtime" |
| "runtime/trace" |
| "slices" |
| "testing" |
| ) |
| |
| func TestSubscribers(t *testing.T) { |
| validate := func(t *testing.T, source string, tr io.Reader) { |
| // Prepare to read the trace snapshot. |
| r, err := inttrace.NewReader(tr) |
| if err != nil { |
| t.Fatalf("unexpected error creating trace reader for %s: %v", source, err) |
| return |
| } |
| |
| v := testtrace.NewValidator() |
| // These platforms can't guarantee a monotonically increasing clock reading in a short trace. |
| if runtime.GOOS == "windows" || runtime.GOARCH == "wasm" { |
| v.SkipClockSnapshotChecks() |
| } |
| // Make sure there are Sync events: at the start and end. |
| var syncs []int |
| evs := 0 |
| for { |
| ev, err := r.ReadEvent() |
| if err == io.EOF { |
| break |
| } |
| if err != nil { |
| t.Fatalf("unexpected error reading trace for %s: %v", source, err) |
| } |
| if err := v.Event(ev); err != nil { |
| t.Fatalf("event validation failed: %s", err) |
| } |
| if ev.Kind() == inttrace.EventSync { |
| syncs = append(syncs, evs) |
| } |
| evs++ |
| } |
| ends := []int{syncs[0], syncs[len(syncs)-1]} |
| if wantEnds := []int{0, evs - 1}; !slices.Equal(wantEnds, ends) { |
| t.Errorf("expected a sync event at each end of the trace, found sync events at %d instead of %d for %s", |
| ends, wantEnds, source) |
| } |
| } |
| |
| validateTraces := func(t *testing.T, tReader, frReader io.Reader) { |
| validate(t, "tracer", tReader) |
| validate(t, "flightRecorder", frReader) |
| } |
| startFlightRecorder := func(t *testing.T) *trace.FlightRecorder { |
| fr := trace.NewFlightRecorder(trace.FlightRecorderConfig{}) |
| if err := fr.Start(); err != nil { |
| t.Fatalf("unexpected error creating flight recorder: %v", err) |
| } |
| return fr |
| } |
| startTrace := func(t *testing.T, w io.Writer) { |
| if err := trace.Start(w); err != nil { |
| t.Fatalf("unexpected error starting flight recorder: %v", err) |
| } |
| } |
| stopFlightRecorder := func(t *testing.T, fr *trace.FlightRecorder, w io.Writer) { |
| if _, err := fr.WriteTo(w); err != nil { |
| t.Fatalf("unexpected error writing trace from flight recorder: %v", err) |
| } |
| fr.Stop() |
| } |
| stopTrace := func() { |
| trace.Stop() |
| } |
| t.Run("start(flight)_start(trace)_stop(trace)_stop(flight)", func(t *testing.T) { |
| if trace.IsEnabled() { |
| t.Skip("skipping because trace is already enabled") |
| } |
| frBuf := new(bytes.Buffer) |
| tBuf := new(bytes.Buffer) |
| fr := startFlightRecorder(t) |
| defer fr.Stop() |
| startTrace(t, tBuf) |
| defer trace.Stop() |
| stopTrace() |
| stopFlightRecorder(t, fr, frBuf) |
| validateTraces(t, tBuf, frBuf) |
| }) |
| t.Run("start(trace)_start(flight)_stop(trace)_stop(flight)", func(t *testing.T) { |
| if trace.IsEnabled() { |
| t.Skip("skipping because trace is already enabled") |
| } |
| frBuf := new(bytes.Buffer) |
| tBuf := new(bytes.Buffer) |
| startTrace(t, tBuf) |
| defer trace.Stop() |
| fr := startFlightRecorder(t) |
| defer fr.Stop() |
| stopTrace() |
| stopFlightRecorder(t, fr, frBuf) |
| validateTraces(t, tBuf, frBuf) |
| }) |
| t.Run("start(flight)_stop(flight)_start(trace)_stop(trace)", func(t *testing.T) { |
| if trace.IsEnabled() { |
| t.Skip("skipping because trace is already enabled") |
| } |
| frBuf := new(bytes.Buffer) |
| tBuf := new(bytes.Buffer) |
| fr := startFlightRecorder(t) |
| defer fr.Stop() |
| stopFlightRecorder(t, fr, frBuf) |
| startTrace(t, tBuf) |
| defer trace.Stop() |
| stopTrace() |
| validateTraces(t, tBuf, frBuf) |
| }) |
| t.Run("start(flight)_stop(flight)_start(trace)_stop(trace)", func(t *testing.T) { |
| if trace.IsEnabled() { |
| t.Skip("skipping because trace is already enabled") |
| } |
| frBuf := new(bytes.Buffer) |
| tBuf := new(bytes.Buffer) |
| fr := startFlightRecorder(t) |
| defer fr.Stop() |
| stopFlightRecorder(t, fr, frBuf) |
| startTrace(t, tBuf) |
| defer trace.Stop() |
| stopTrace() |
| validateTraces(t, tBuf, frBuf) |
| }) |
| t.Run("start(flight)_start(trace)_stop(flight)_stop(trace)", func(t *testing.T) { |
| if trace.IsEnabled() { |
| t.Skip("skipping because trace is already enabled") |
| } |
| frBuf := new(bytes.Buffer) |
| tBuf := new(bytes.Buffer) |
| fr := startFlightRecorder(t) |
| defer fr.Stop() |
| startTrace(t, tBuf) |
| defer trace.Stop() |
| stopFlightRecorder(t, fr, frBuf) |
| stopTrace() |
| validateTraces(t, tBuf, frBuf) |
| }) |
| } |