Skip to content

Commit 07b94b2

Browse files
felixgegopherbot
authored andcommitted
internal/trace: add generator tests for sync events
Add generator tests that verify the timestamps for the sync events emitted in the go1.25 trace format and earlier versions. Add the ability to configure the properties of the per-generation sync batches in testgen. Also refactor testgen to produce more realistic timestamps by keeping track of lastTs and using it for structural batches that don't have their own timestamps. Otherwise they default to zero which means the minTs of the generation can't be controlled. For #69869 Change-Id: I92a49b8281bc4169b63e13c030c1de7720cd6f26 Reviewed-on: https://go-review.googlesource.com/c/go/+/653876 Auto-Submit: Michael Knyszek <mknyszek@google.com> Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Knyszek <mknyszek@google.com>
1 parent b22da3f commit 07b94b2

File tree

6 files changed

+257
-13
lines changed

6 files changed

+257
-13
lines changed

src/internal/trace/internal/testgen/trace.go

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os"
1212
"regexp"
1313
"strings"
14+
"time"
1415

1516
"internal/trace"
1617
"internal/trace/raw"
@@ -55,6 +56,7 @@ type Trace struct {
5556
events []raw.Event
5657
gens []*Generation
5758
validTimestamps bool
59+
lastTs Time
5860

5961
// Expectation state.
6062
bad bool
@@ -107,6 +109,7 @@ func (t *Trace) Generation(gen uint64) *Generation {
107109
gen: gen,
108110
strings: make(map[string]uint64),
109111
stacks: make(map[stack]uint64),
112+
sync: sync{freq: 15625000},
110113
}
111114
t.gens = append(t.gens, g)
112115
return g
@@ -178,6 +181,7 @@ type Generation struct {
178181
batches []*Batch
179182
strings map[string]uint64
180183
stacks map[stack]uint64
184+
sync sync
181185

182186
// Options applied when Trace.Generate is called.
183187
ignoreStringBatchSizeLimit bool
@@ -188,14 +192,11 @@ type Generation struct {
188192
//
189193
// This is convenience function for generating correct batches.
190194
func (g *Generation) Batch(thread trace.ThreadID, time Time) *Batch {
191-
if !g.trace.validTimestamps {
192-
time = 0
193-
}
194195
b := &Batch{
195-
gen: g,
196-
thread: thread,
197-
timestamp: time,
196+
gen: g,
197+
thread: thread,
198198
}
199+
b.setTimestamp(time)
199200
g.batches = append(g.batches, b)
200201
return b
201202
}
@@ -238,20 +239,59 @@ func (g *Generation) Stack(stk []trace.StackFrame) uint64 {
238239
return id
239240
}
240241

242+
// Sync configures the sync batch for the generation. For go1.25 and later,
243+
// the time value is the timestamp of the EvClockSnapshot event. For earlier
244+
// version, the time value is the timestamp of the batch containing a lone
245+
// EvFrequency event.
246+
func (g *Generation) Sync(freq uint64, time Time, mono uint64, wall time.Time) {
247+
if g.trace.ver < version.Go125 && (mono != 0 || !wall.IsZero()) {
248+
panic(fmt.Sprintf("mono and wall args are not supported in go1.%d traces", g.trace.ver))
249+
}
250+
g.sync = sync{
251+
freq: freq,
252+
time: time,
253+
mono: mono,
254+
walltime: wall,
255+
}
256+
}
257+
258+
type sync struct {
259+
freq uint64
260+
time Time
261+
mono uint64
262+
walltime time.Time
263+
}
264+
241265
// writeEventsTo emits event batches in the generation to tw.
242266
func (g *Generation) writeEventsTo(tw *raw.TextWriter) {
267+
// go1.25+ sync batches are emitted at the start of the generation.
268+
if g.trace.ver >= version.Go125 {
269+
b := g.newStructuralBatch()
270+
// Arrange for EvClockSnapshot's ts to be exactly g.sync.time.
271+
b.setTimestamp(g.sync.time - 1)
272+
b.RawEvent(tracev2.EvSync, nil)
273+
b.RawEvent(tracev2.EvFrequency, nil, g.sync.freq)
274+
sec := uint64(g.sync.walltime.Unix())
275+
nsec := uint64(g.sync.walltime.Nanosecond())
276+
b.Event("ClockSnapshot", g.sync.mono, sec, nsec)
277+
b.writeEventsTo(tw)
278+
}
279+
243280
// Write event batches for the generation.
244281
for _, b := range g.batches {
245282
b.writeEventsTo(tw)
246283
}
247284

248-
// Write frequency.
249-
b := g.newStructuralBatch()
250-
b.RawEvent(tracev2.EvFrequency, nil, 15625000)
251-
b.writeEventsTo(tw)
285+
// Write lone EvFrequency sync batch for older traces.
286+
if g.trace.ver < version.Go125 {
287+
b := g.newStructuralBatch()
288+
b.setTimestamp(g.sync.time)
289+
b.RawEvent(tracev2.EvFrequency, nil, g.sync.freq)
290+
b.writeEventsTo(tw)
291+
}
252292

253293
// Write stacks.
254-
b = g.newStructuralBatch()
294+
b := g.newStructuralBatch()
255295
b.RawEvent(tracev2.EvStacks, nil)
256296
for stk, id := range g.stacks {
257297
stk := stk.stk[:stk.len]
@@ -285,7 +325,9 @@ func (g *Generation) writeEventsTo(tw *raw.TextWriter) {
285325
}
286326

287327
func (g *Generation) newStructuralBatch() *Batch {
288-
return &Batch{gen: g, thread: trace.NoThread}
328+
b := &Batch{gen: g, thread: trace.NoThread}
329+
b.setTimestamp(g.trace.lastTs + 1)
330+
return b
289331
}
290332

291333
// Batch represents an event batch.
@@ -310,6 +352,7 @@ func (b *Batch) Event(name string, args ...any) {
310352
if b.gen.trace.specs[ev].IsTimedEvent {
311353
if b.gen.trace.validTimestamps {
312354
uintArgs = []uint64{1}
355+
b.gen.trace.lastTs += 1
313356
} else {
314357
uintArgs = []uint64{0}
315358
}
@@ -333,7 +376,7 @@ func (b *Batch) uintArgFor(arg any, argSpec string) uint64 {
333376
}
334377
var u uint64
335378
switch typStr {
336-
case "value":
379+
case "value", "mono", "sec", "nsec":
337380
u = arg.(uint64)
338381
case "stack":
339382
u = b.gen.Stack(arg.([]trace.StackFrame))
@@ -392,6 +435,14 @@ func (b *Batch) writeEventsTo(tw *raw.TextWriter) {
392435
}
393436
}
394437

438+
// setTimestamp sets the timestamp for the batch.
439+
func (b *Batch) setTimestamp(t Time) {
440+
if b.gen.trace.validTimestamps {
441+
b.timestamp = t
442+
b.gen.trace.lastTs = t
443+
}
444+
}
445+
395446
// Seq represents a sequence counter.
396447
type Seq uint64
397448

src/internal/trace/reader_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"path/filepath"
1414
"strings"
1515
"testing"
16+
"time"
1617

1718
"internal/trace"
1819
"internal/trace/raw"
@@ -171,3 +172,76 @@ func dumpTraceToFile(t *testing.T, testName string, stress bool, b []byte) strin
171172
}
172173
return f.Name()
173174
}
175+
176+
func TestTraceGenSync(t *testing.T) {
177+
type sync struct {
178+
Time trace.Time
179+
ClockSnapshot *trace.ClockSnapshot
180+
}
181+
runTest := func(testName string, wantSyncs []sync) {
182+
t.Run(testName, func(t *testing.T) {
183+
testPath := "testdata/tests/" + testName
184+
r, _, _, err := testtrace.ParseFile(testPath)
185+
if err != nil {
186+
t.Fatalf("malformed test %s: bad trace file: %v", testPath, err)
187+
}
188+
tr, err := trace.NewReader(r)
189+
if err != nil {
190+
t.Fatalf("malformed test %s: bad trace file: %v", testPath, err)
191+
}
192+
var syncEvents []trace.Event
193+
for {
194+
ev, err := tr.ReadEvent()
195+
if err == io.EOF {
196+
break
197+
}
198+
if err != nil {
199+
t.Fatalf("malformed test %s: bad trace file: %v", testPath, err)
200+
}
201+
if ev.Kind() == trace.EventSync {
202+
syncEvents = append(syncEvents, ev)
203+
}
204+
}
205+
206+
if got, want := len(syncEvents), len(wantSyncs); got != want {
207+
t.Errorf("got %d sync events, want %d", got, want)
208+
}
209+
210+
for i, want := range wantSyncs {
211+
got := syncEvents[i]
212+
gotSync := syncEvents[i].Sync()
213+
if got.Time() != want.Time {
214+
t.Errorf("sync=%d got time %d, want %d", i+1, got.Time(), want.Time)
215+
}
216+
if gotSync.ClockSnapshot == nil && want.ClockSnapshot == nil {
217+
continue
218+
}
219+
if gotSync.ClockSnapshot.Trace != want.ClockSnapshot.Trace {
220+
t.Errorf("sync=%d got trace time %d, want %d", i+1, gotSync.ClockSnapshot.Trace, want.ClockSnapshot.Trace)
221+
}
222+
if !gotSync.ClockSnapshot.Wall.Equal(want.ClockSnapshot.Wall) {
223+
t.Errorf("sync=%d got wall time %s, want %s", i+1, gotSync.ClockSnapshot.Wall, want.ClockSnapshot.Wall)
224+
}
225+
if gotSync.ClockSnapshot.Mono != want.ClockSnapshot.Mono {
226+
t.Errorf("sync=%d got mono time %d, want %d", i+1, gotSync.ClockSnapshot.Mono, want.ClockSnapshot.Mono)
227+
}
228+
}
229+
})
230+
}
231+
232+
runTest("go123-sync.test", []sync{
233+
{10, nil},
234+
{40, nil},
235+
// The EvFrequency batch for generation 3 is emitted at trace.Time(80),
236+
// but 60 is the minTs of the generation, see b30 in the go generator.
237+
{60, nil},
238+
{63, nil},
239+
})
240+
241+
runTest("go125-sync.test", []sync{
242+
{9, &trace.ClockSnapshot{Trace: 10, Mono: 99, Wall: time.Date(2025, 2, 28, 15, 4, 9, 123, time.UTC)}},
243+
{38, &trace.ClockSnapshot{Trace: 40, Mono: 199, Wall: time.Date(2025, 2, 28, 15, 4, 10, 123, time.UTC)}},
244+
{58, &trace.ClockSnapshot{Trace: 60, Mono: 299, Wall: time.Date(2025, 2, 28, 15, 4, 11, 123, time.UTC)}},
245+
{83, nil},
246+
})
247+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"internal/trace"
9+
"internal/trace/internal/testgen"
10+
"internal/trace/tracev2"
11+
"internal/trace/version"
12+
"time"
13+
)
14+
15+
func main() {
16+
testgen.Main(version.Go123, gen)
17+
}
18+
19+
func gen(t *testgen.Trace) {
20+
g1 := t.Generation(1)
21+
g1.Sync(1000000000, 10, 0, time.Time{})
22+
b10 := g1.Batch(1, 15)
23+
b10.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning)
24+
g2 := t.Generation(2)
25+
g2.Sync(500000000, 20, 0, time.Time{})
26+
g3 := t.Generation(3)
27+
b30 := g3.Batch(1, 30)
28+
b30.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning)
29+
g3.Sync(500000000, 40, 0, time.Time{})
30+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"internal/trace"
9+
"internal/trace/internal/testgen"
10+
"internal/trace/tracev2"
11+
"internal/trace/version"
12+
"time"
13+
)
14+
15+
func main() {
16+
testgen.Main(version.Go125, gen)
17+
}
18+
19+
func gen(t *testgen.Trace) {
20+
start := time.Date(2025, 2, 28, 15, 4, 9, 123, time.UTC)
21+
g1 := t.Generation(1)
22+
g1.Sync(1000000000, 10, 99, start)
23+
b10 := g1.Batch(1, 15)
24+
b10.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning)
25+
g2 := t.Generation(2)
26+
g2.Sync(500000000, 20, 199, start.Add(1*time.Second))
27+
g3 := t.Generation(3)
28+
g3.Sync(500000000, 30, 299, start.Add(2*time.Second))
29+
b30 := g3.Batch(1, 40)
30+
b30.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning)
31+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
-- expect --
2+
SUCCESS
3+
-- trace --
4+
Trace Go1.23
5+
EventBatch gen=1 m=1 time=15 size=4
6+
ProcStatus dt=1 p=0 pstatus=1
7+
EventBatch gen=1 m=18446744073709551615 time=10 size=6
8+
Frequency freq=1000000000
9+
EventBatch gen=1 m=18446744073709551615 time=11 size=1
10+
Stacks
11+
EventBatch gen=1 m=18446744073709551615 time=12 size=1
12+
Strings
13+
EventBatch gen=2 m=18446744073709551615 time=20 size=6
14+
Frequency freq=500000000
15+
EventBatch gen=2 m=18446744073709551615 time=21 size=1
16+
Stacks
17+
EventBatch gen=2 m=18446744073709551615 time=22 size=1
18+
Strings
19+
EventBatch gen=3 m=1 time=30 size=4
20+
ProcStatus dt=1 p=0 pstatus=1
21+
EventBatch gen=3 m=18446744073709551615 time=40 size=6
22+
Frequency freq=500000000
23+
EventBatch gen=3 m=18446744073709551615 time=41 size=1
24+
Stacks
25+
EventBatch gen=3 m=18446744073709551615 time=42 size=1
26+
Strings
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
-- expect --
2+
SUCCESS
3+
-- trace --
4+
Trace Go1.25
5+
EventBatch gen=1 m=18446744073709551615 time=9 size=16
6+
Sync
7+
Frequency freq=1000000000
8+
ClockSnapshot dt=1 mono=99 sec=1740755049 nsec=123
9+
EventBatch gen=1 m=1 time=15 size=4
10+
ProcStatus dt=1 p=0 pstatus=1
11+
EventBatch gen=1 m=18446744073709551615 time=11 size=1
12+
Stacks
13+
EventBatch gen=1 m=18446744073709551615 time=12 size=1
14+
Strings
15+
EventBatch gen=2 m=18446744073709551615 time=19 size=17
16+
Sync
17+
Frequency freq=500000000
18+
ClockSnapshot dt=1 mono=199 sec=1740755050 nsec=123
19+
EventBatch gen=2 m=18446744073709551615 time=21 size=1
20+
Stacks
21+
EventBatch gen=2 m=18446744073709551615 time=22 size=1
22+
Strings
23+
EventBatch gen=3 m=18446744073709551615 time=29 size=17
24+
Sync
25+
Frequency freq=500000000
26+
ClockSnapshot dt=1 mono=299 sec=1740755051 nsec=123
27+
EventBatch gen=3 m=1 time=40 size=4
28+
ProcStatus dt=1 p=0 pstatus=1
29+
EventBatch gen=3 m=18446744073709551615 time=31 size=1
30+
Stacks
31+
EventBatch gen=3 m=18446744073709551615 time=32 size=1
32+
Strings

0 commit comments

Comments
 (0)