1 // Copyright 2023 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.
16 "internal/trace/v2/event"
17 "internal/trace/v2/event/go122"
18 "internal/trace/v2/raw"
19 "internal/trace/v2/version"
23 func Main(f func(*Trace)) {
24 // Create an output file.
25 out, err := os.Create(os.Args[1])
31 // Create a new trace.
34 // Call the generator.
37 // Write out the generator's state.
38 if _, err := out.Write(trace.Generate()); err != nil {
43 // Trace represents an execution trace for testing.
45 // It does a little bit of work to ensure that the produced trace is valid,
46 // just for convenience. It mainly tracks batches and batch sizes (so they're
47 // trivially correct), tracks strings and stacks, and makes sure emitted string
48 // and stack batches are valid. That last part can be controlled by a few options.
50 // Otherwise, it performs no validation on the trace at all.
54 names map[string]event.Type
62 badMatch *regexp.Regexp
65 // NewTrace creates a new trace.
66 func NewTrace() *Trace {
69 names: event.Names(ver.Specs()),
71 validTimestamps: true,
75 // ExpectFailure writes down that the trace should be broken. The caller
76 // must provide a pattern matching the expected error produced by the parser.
77 func (t *Trace) ExpectFailure(pattern string) {
79 t.badMatch = regexp.MustCompile(pattern)
82 // ExpectSuccess writes down that the trace should successfully parse.
83 func (t *Trace) ExpectSuccess() {
87 // RawEvent emits an event into the trace. name must correspond to one
88 // of the names in Specs() result for the version that was passed to
90 func (t *Trace) RawEvent(typ event.Type, data []byte, args ...uint64) {
91 t.events = append(t.events, t.createEvent(typ, data, args...))
94 // DisableTimestamps makes the timestamps for all events generated after
95 // this call zero. Raw events are exempted from this because the caller
96 // has to pass their own timestamp into those events anyway.
97 func (t *Trace) DisableTimestamps() {
98 t.validTimestamps = false
101 // Generation creates a new trace generation.
103 // This provides more structure than Event to allow for more easily
104 // creating complex traces that are mostly or completely correct.
105 func (t *Trace) Generation(gen uint64) *Generation {
109 strings: make(map[string]uint64),
110 stacks: make(map[stack]uint64),
112 t.gens = append(t.gens, g)
116 // Generate creates a test file for the trace.
117 func (t *Trace) Generate() []byte {
118 // Trace file contents.
120 tw, err := raw.NewTextWriter(&buf, version.Go122)
125 // Write raw top-level events.
126 for _, e := range t.events {
130 // Write generations.
131 for _, g := range t.gens {
135 // Expectation file contents.
136 expect := []byte("SUCCESS\n")
138 expect = []byte(fmt.Sprintf("FAILURE %q\n", t.badMatch))
141 // Create the test file's contents.
142 return txtar.Format(&txtar.Archive{
144 {Name: "expect", Data: expect},
145 {Name: "trace", Data: buf.Bytes()},
150 func (t *Trace) createEvent(ev event.Type, data []byte, args ...uint64) raw.Event {
152 if ev != go122.EvStack {
153 if arity := len(spec.Args); len(args) != arity {
154 panic(fmt.Sprintf("expected %d args for %s, got %d", arity, spec.Name, len(args)))
158 Version: version.Go122,
166 stk [32]trace.StackFrame
172 NoStack = []trace.StackFrame{}
175 // Generation represents a single generation in the trace.
176 type Generation struct {
180 strings map[string]uint64
181 stacks map[stack]uint64
183 // Options applied when Trace.Generate is called.
184 ignoreStringBatchSizeLimit bool
185 ignoreStackBatchSizeLimit bool
188 // Batch starts a new event batch in the trace data.
190 // This is convenience function for generating correct batches.
191 func (g *Generation) Batch(thread trace.ThreadID, time Time) *Batch {
192 if !g.trace.validTimestamps {
200 g.batches = append(g.batches, b)
204 // String registers a string with the trace.
206 // This is a convenience function for easily adding correct
207 // strings to traces.
208 func (g *Generation) String(s string) uint64 {
212 if id, ok := g.strings[s]; ok {
215 id := uint64(len(g.strings) + 1)
220 // Stack registers a stack with the trace.
222 // This is a convenience function for easily adding correct
224 func (g *Generation) Stack(stk []trace.StackFrame) uint64 {
229 panic("stack too big for test")
232 copy(stkc.stk[:], stk)
234 if id, ok := g.stacks[stkc]; ok {
237 id := uint64(len(g.stacks) + 1)
242 // writeEventsTo emits event batches in the generation to tw.
243 func (g *Generation) writeEventsTo(tw *raw.TextWriter) {
244 // Write event batches for the generation.
245 for _, b := range g.batches {
250 b := g.newStructuralBatch()
251 b.RawEvent(go122.EvFrequency, nil, 15625000)
255 b = g.newStructuralBatch()
256 b.RawEvent(go122.EvStacks, nil)
257 for stk, id := range g.stacks {
258 stk := stk.stk[:stk.len]
260 for _, f := range stk {
261 args = append(args, f.PC, g.String(f.Func), g.String(f.File), f.Line)
263 b.RawEvent(go122.EvStack, nil, args...)
265 // Flush the batch if necessary.
266 if !g.ignoreStackBatchSizeLimit && b.size > go122.MaxBatchSize/2 {
268 b = g.newStructuralBatch()
274 b = g.newStructuralBatch()
275 b.RawEvent(go122.EvStrings, nil)
276 for s, id := range g.strings {
277 b.RawEvent(go122.EvString, []byte(s), id)
279 // Flush the batch if necessary.
280 if !g.ignoreStringBatchSizeLimit && b.size > go122.MaxBatchSize/2 {
282 b = g.newStructuralBatch()
288 func (g *Generation) newStructuralBatch() *Batch {
289 return &Batch{gen: g, thread: trace.NoThread}
292 // Batch represents an event batch.
295 thread trace.ThreadID
301 // Event emits an event into a batch. name must correspond to one
302 // of the names in Specs() result for the version that was passed to
303 // this trace. Callers must omit the timestamp delta.
304 func (b *Batch) Event(name string, args ...any) {
305 ev, ok := b.gen.trace.names[name]
307 panic(fmt.Sprintf("invalid or unknown event %s", name))
309 var uintArgs []uint64
311 if b.gen.trace.specs[ev].IsTimedEvent {
312 if b.gen.trace.validTimestamps {
313 uintArgs = []uint64{1}
315 uintArgs = []uint64{0}
319 spec := b.gen.trace.specs[ev]
320 if arity := len(spec.Args) - argOff; len(args) != arity {
321 panic(fmt.Sprintf("expected %d args for %s, got %d", arity, spec.Name, len(args)))
323 for i, arg := range args {
324 uintArgs = append(uintArgs, b.uintArgFor(arg, spec.Args[i+argOff]))
326 b.RawEvent(ev, nil, uintArgs...)
329 func (b *Batch) uintArgFor(arg any, argSpec string) uint64 {
330 components := strings.SplitN(argSpec, "_", 2)
331 typStr := components[0]
332 if len(components) == 2 {
333 typStr = components[1]
340 u = b.gen.Stack(arg.([]trace.StackFrame))
342 u = uint64(arg.(Seq))
344 u = uint64(arg.(go122.ProcStatus))
346 u = uint64(arg.(go122.GoStatus))
348 u = uint64(arg.(trace.GoID))
350 u = uint64(arg.(trace.ThreadID))
352 u = uint64(arg.(trace.ProcID))
354 u = b.gen.String(arg.(string))
356 u = uint64(arg.(trace.TaskID))
358 panic(fmt.Sprintf("unsupported arg type %q for spec %q", typStr, argSpec))
363 // RawEvent emits an event into a batch. name must correspond to one
364 // of the names in Specs() result for the version that was passed to
366 func (b *Batch) RawEvent(typ event.Type, data []byte, args ...uint64) {
367 ev := b.gen.trace.createEvent(typ, data, args...)
369 // Compute the size of the event and add it to the batch.
370 b.size += 1 // One byte for the event header.
371 var buf [binary.MaxVarintLen64]byte
372 for _, arg := range args {
373 b.size += uint64(binary.PutUvarint(buf[:], arg))
376 b.size += uint64(binary.PutUvarint(buf[:], uint64(len(data))))
377 b.size += uint64(len(data))
381 b.events = append(b.events, ev)
384 // writeEventsTo emits events in the batch, including the batch header, to tw.
385 func (b *Batch) writeEventsTo(tw *raw.TextWriter) {
386 tw.WriteEvent(raw.Event{
387 Version: version.Go122,
388 Ev: go122.EvEventBatch,
389 Args: []uint64{b.gen.gen, uint64(b.thread), uint64(b.timestamp), b.size},
391 for _, e := range b.events {
396 // Seq represents a sequence counter.
399 // Time represents a low-level trace timestamp (which does not necessarily
400 // correspond to nanoseconds, like trace.Time does).