--- /dev/null
+// 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)
+ })
+}