blob: 0e6c57cbc6d3520bbd70ec37288e2693b67585d9 [file] [log] [blame]
// 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)
})
}