// 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) }) }