]> Cypherpunks.ru repositories - gostls13.git/blob - src/internal/trace/v2/internal/testgen/go122/trace.go
internal/trace/v2: resolve syscall parsing ambiguity
[gostls13.git] / src / internal / trace / v2 / internal / testgen / go122 / trace.go
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.
4
5 package testkit
6
7 import (
8         "bytes"
9         "encoding/binary"
10         "fmt"
11         "os"
12         "regexp"
13         "strings"
14
15         "internal/trace/v2"
16         "internal/trace/v2/event"
17         "internal/trace/v2/event/go122"
18         "internal/trace/v2/raw"
19         "internal/trace/v2/version"
20         "internal/txtar"
21 )
22
23 func Main(f func(*Trace)) {
24         // Create an output file.
25         out, err := os.Create(os.Args[1])
26         if err != nil {
27                 panic(err.Error())
28         }
29         defer out.Close()
30
31         // Create a new trace.
32         trace := NewTrace()
33
34         // Call the generator.
35         f(trace)
36
37         // Write out the generator's state.
38         if _, err := out.Write(trace.Generate()); err != nil {
39                 panic(err.Error())
40         }
41 }
42
43 // Trace represents an execution trace for testing.
44 //
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.
49 //
50 // Otherwise, it performs no validation on the trace at all.
51 type Trace struct {
52         // Trace data state.
53         ver             version.Version
54         names           map[string]event.Type
55         specs           []event.Spec
56         events          []raw.Event
57         gens            []*Generation
58         validTimestamps bool
59
60         // Expectation state.
61         bad      bool
62         badMatch *regexp.Regexp
63 }
64
65 // NewTrace creates a new trace.
66 func NewTrace() *Trace {
67         ver := version.Go122
68         return &Trace{
69                 names:           event.Names(ver.Specs()),
70                 specs:           ver.Specs(),
71                 validTimestamps: true,
72         }
73 }
74
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) {
78         t.bad = true
79         t.badMatch = regexp.MustCompile(pattern)
80 }
81
82 // ExpectSuccess writes down that the trace should successfully parse.
83 func (t *Trace) ExpectSuccess() {
84         t.bad = false
85 }
86
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
89 // this trace.
90 func (t *Trace) RawEvent(typ event.Type, data []byte, args ...uint64) {
91         t.events = append(t.events, t.createEvent(typ, data, args...))
92 }
93
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
99 }
100
101 // Generation creates a new trace generation.
102 //
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 {
106         g := &Generation{
107                 trace:   t,
108                 gen:     gen,
109                 strings: make(map[string]uint64),
110                 stacks:  make(map[stack]uint64),
111         }
112         t.gens = append(t.gens, g)
113         return g
114 }
115
116 // Generate creates a test file for the trace.
117 func (t *Trace) Generate() []byte {
118         // Trace file contents.
119         var buf bytes.Buffer
120         tw, err := raw.NewTextWriter(&buf, version.Go122)
121         if err != nil {
122                 panic(err.Error())
123         }
124
125         // Write raw top-level events.
126         for _, e := range t.events {
127                 tw.WriteEvent(e)
128         }
129
130         // Write generations.
131         for _, g := range t.gens {
132                 g.writeEventsTo(tw)
133         }
134
135         // Expectation file contents.
136         expect := []byte("SUCCESS\n")
137         if t.bad {
138                 expect = []byte(fmt.Sprintf("FAILURE %q\n", t.badMatch))
139         }
140
141         // Create the test file's contents.
142         return txtar.Format(&txtar.Archive{
143                 Files: []txtar.File{
144                         {Name: "expect", Data: expect},
145                         {Name: "trace", Data: buf.Bytes()},
146                 },
147         })
148 }
149
150 func (t *Trace) createEvent(ev event.Type, data []byte, args ...uint64) raw.Event {
151         spec := t.specs[ev]
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)))
155                 }
156         }
157         return raw.Event{
158                 Version: version.Go122,
159                 Ev:      ev,
160                 Args:    args,
161                 Data:    data,
162         }
163 }
164
165 type stack struct {
166         stk [32]trace.StackFrame
167         len int
168 }
169
170 var (
171         NoString = ""
172         NoStack  = []trace.StackFrame{}
173 )
174
175 // Generation represents a single generation in the trace.
176 type Generation struct {
177         trace   *Trace
178         gen     uint64
179         batches []*Batch
180         strings map[string]uint64
181         stacks  map[stack]uint64
182
183         // Options applied when Trace.Generate is called.
184         ignoreStringBatchSizeLimit bool
185         ignoreStackBatchSizeLimit  bool
186 }
187
188 // Batch starts a new event batch in the trace data.
189 //
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 {
193                 time = 0
194         }
195         b := &Batch{
196                 gen:       g,
197                 thread:    thread,
198                 timestamp: time,
199         }
200         g.batches = append(g.batches, b)
201         return b
202 }
203
204 // String registers a string with the trace.
205 //
206 // This is a convenience function for easily adding correct
207 // strings to traces.
208 func (g *Generation) String(s string) uint64 {
209         if len(s) == 0 {
210                 return 0
211         }
212         if id, ok := g.strings[s]; ok {
213                 return id
214         }
215         id := uint64(len(g.strings) + 1)
216         g.strings[s] = id
217         return id
218 }
219
220 // Stack registers a stack with the trace.
221 //
222 // This is a convenience function for easily adding correct
223 // stacks to traces.
224 func (g *Generation) Stack(stk []trace.StackFrame) uint64 {
225         if len(stk) == 0 {
226                 return 0
227         }
228         if len(stk) > 32 {
229                 panic("stack too big for test")
230         }
231         var stkc stack
232         copy(stkc.stk[:], stk)
233         stkc.len = len(stk)
234         if id, ok := g.stacks[stkc]; ok {
235                 return id
236         }
237         id := uint64(len(g.stacks) + 1)
238         g.stacks[stkc] = id
239         return id
240 }
241
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 {
246                 b.writeEventsTo(tw)
247         }
248
249         // Write frequency.
250         b := g.newStructuralBatch()
251         b.RawEvent(go122.EvFrequency, nil, 15625000)
252         b.writeEventsTo(tw)
253
254         // Write stacks.
255         b = g.newStructuralBatch()
256         b.RawEvent(go122.EvStacks, nil)
257         for stk, id := range g.stacks {
258                 stk := stk.stk[:stk.len]
259                 args := []uint64{id}
260                 for _, f := range stk {
261                         args = append(args, f.PC, g.String(f.Func), g.String(f.File), f.Line)
262                 }
263                 b.RawEvent(go122.EvStack, nil, args...)
264
265                 // Flush the batch if necessary.
266                 if !g.ignoreStackBatchSizeLimit && b.size > go122.MaxBatchSize/2 {
267                         b.writeEventsTo(tw)
268                         b = g.newStructuralBatch()
269                 }
270         }
271         b.writeEventsTo(tw)
272
273         // Write strings.
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)
278
279                 // Flush the batch if necessary.
280                 if !g.ignoreStringBatchSizeLimit && b.size > go122.MaxBatchSize/2 {
281                         b.writeEventsTo(tw)
282                         b = g.newStructuralBatch()
283                 }
284         }
285         b.writeEventsTo(tw)
286 }
287
288 func (g *Generation) newStructuralBatch() *Batch {
289         return &Batch{gen: g, thread: trace.NoThread}
290 }
291
292 // Batch represents an event batch.
293 type Batch struct {
294         gen       *Generation
295         thread    trace.ThreadID
296         timestamp Time
297         size      uint64
298         events    []raw.Event
299 }
300
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]
306         if !ok {
307                 panic(fmt.Sprintf("invalid or unknown event %s", name))
308         }
309         var uintArgs []uint64
310         argOff := 0
311         if b.gen.trace.specs[ev].IsTimedEvent {
312                 if b.gen.trace.validTimestamps {
313                         uintArgs = []uint64{1}
314                 } else {
315                         uintArgs = []uint64{0}
316                 }
317                 argOff = 1
318         }
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)))
322         }
323         for i, arg := range args {
324                 uintArgs = append(uintArgs, b.uintArgFor(arg, spec.Args[i+argOff]))
325         }
326         b.RawEvent(ev, nil, uintArgs...)
327 }
328
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]
334         }
335         var u uint64
336         switch typStr {
337         case "value":
338                 u = arg.(uint64)
339         case "stack":
340                 u = b.gen.Stack(arg.([]trace.StackFrame))
341         case "seq":
342                 u = uint64(arg.(Seq))
343         case "pstatus":
344                 u = uint64(arg.(go122.ProcStatus))
345         case "gstatus":
346                 u = uint64(arg.(go122.GoStatus))
347         case "g":
348                 u = uint64(arg.(trace.GoID))
349         case "m":
350                 u = uint64(arg.(trace.ThreadID))
351         case "p":
352                 u = uint64(arg.(trace.ProcID))
353         case "string":
354                 u = b.gen.String(arg.(string))
355         case "task":
356                 u = uint64(arg.(trace.TaskID))
357         default:
358                 panic(fmt.Sprintf("unsupported arg type %q for spec %q", typStr, argSpec))
359         }
360         return u
361 }
362
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
365 // this trace.
366 func (b *Batch) RawEvent(typ event.Type, data []byte, args ...uint64) {
367         ev := b.gen.trace.createEvent(typ, data, args...)
368
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))
374         }
375         if len(data) != 0 {
376                 b.size += uint64(binary.PutUvarint(buf[:], uint64(len(data))))
377                 b.size += uint64(len(data))
378         }
379
380         // Add the event.
381         b.events = append(b.events, ev)
382 }
383
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},
390         })
391         for _, e := range b.events {
392                 tw.WriteEvent(e)
393         }
394 }
395
396 // Seq represents a sequence counter.
397 type Seq uint64
398
399 // Time represents a low-level trace timestamp (which does not necessarily
400 // correspond to nanoseconds, like trace.Time does).
401 type Time uint64