golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/trace/internal/testgen/go122/trace.go (about) 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 // Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT. 6 7 //go:build go1.21 8 9 package testkit 10 11 import ( 12 "bytes" 13 "encoding/binary" 14 "fmt" 15 "os" 16 "regexp" 17 "strings" 18 19 "golang.org/x/exp/trace" 20 "golang.org/x/exp/trace/internal/event" 21 "golang.org/x/exp/trace/internal/event/go122" 22 "golang.org/x/exp/trace/internal/raw" 23 "golang.org/x/exp/trace/internal/version" 24 "golang.org/x/tools/txtar" 25 ) 26 27 func Main(f func(*Trace)) { 28 // Create an output file. 29 out, err := os.Create(os.Args[1]) 30 if err != nil { 31 panic(err.Error()) 32 } 33 defer out.Close() 34 35 // Create a new trace. 36 trace := NewTrace() 37 38 // Call the generator. 39 f(trace) 40 41 // Write out the generator's state. 42 if _, err := out.Write(trace.Generate()); err != nil { 43 panic(err.Error()) 44 } 45 } 46 47 // Trace represents an execution trace for testing. 48 // 49 // It does a little bit of work to ensure that the produced trace is valid, 50 // just for convenience. It mainly tracks batches and batch sizes (so they're 51 // trivially correct), tracks strings and stacks, and makes sure emitted string 52 // and stack batches are valid. That last part can be controlled by a few options. 53 // 54 // Otherwise, it performs no validation on the trace at all. 55 type Trace struct { 56 // Trace data state. 57 ver version.Version 58 names map[string]event.Type 59 specs []event.Spec 60 events []raw.Event 61 gens []*Generation 62 validTimestamps bool 63 64 // Expectation state. 65 bad bool 66 badMatch *regexp.Regexp 67 } 68 69 // NewTrace creates a new trace. 70 func NewTrace() *Trace { 71 ver := version.Go122 72 return &Trace{ 73 names: event.Names(ver.Specs()), 74 specs: ver.Specs(), 75 validTimestamps: true, 76 } 77 } 78 79 // ExpectFailure writes down that the trace should be broken. The caller 80 // must provide a pattern matching the expected error produced by the parser. 81 func (t *Trace) ExpectFailure(pattern string) { 82 t.bad = true 83 t.badMatch = regexp.MustCompile(pattern) 84 } 85 86 // ExpectSuccess writes down that the trace should successfully parse. 87 func (t *Trace) ExpectSuccess() { 88 t.bad = false 89 } 90 91 // RawEvent emits an event into the trace. name must correspond to one 92 // of the names in Specs() result for the version that was passed to 93 // this trace. 94 func (t *Trace) RawEvent(typ event.Type, data []byte, args ...uint64) { 95 t.events = append(t.events, t.createEvent(typ, data, args...)) 96 } 97 98 // DisableTimestamps makes the timestamps for all events generated after 99 // this call zero. Raw events are exempted from this because the caller 100 // has to pass their own timestamp into those events anyway. 101 func (t *Trace) DisableTimestamps() { 102 t.validTimestamps = false 103 } 104 105 // Generation creates a new trace generation. 106 // 107 // This provides more structure than Event to allow for more easily 108 // creating complex traces that are mostly or completely correct. 109 func (t *Trace) Generation(gen uint64) *Generation { 110 g := &Generation{ 111 trace: t, 112 gen: gen, 113 strings: make(map[string]uint64), 114 stacks: make(map[stack]uint64), 115 } 116 t.gens = append(t.gens, g) 117 return g 118 } 119 120 // Generate creates a test file for the trace. 121 func (t *Trace) Generate() []byte { 122 // Trace file contents. 123 var buf bytes.Buffer 124 tw, err := raw.NewTextWriter(&buf, version.Go122) 125 if err != nil { 126 panic(err.Error()) 127 } 128 129 // Write raw top-level events. 130 for _, e := range t.events { 131 tw.WriteEvent(e) 132 } 133 134 // Write generations. 135 for _, g := range t.gens { 136 g.writeEventsTo(tw) 137 } 138 139 // Expectation file contents. 140 expect := []byte("SUCCESS\n") 141 if t.bad { 142 expect = []byte(fmt.Sprintf("FAILURE %q\n", t.badMatch)) 143 } 144 145 // Create the test file's contents. 146 return txtar.Format(&txtar.Archive{ 147 Files: []txtar.File{ 148 {Name: "expect", Data: expect}, 149 {Name: "trace", Data: buf.Bytes()}, 150 }, 151 }) 152 } 153 154 func (t *Trace) createEvent(ev event.Type, data []byte, args ...uint64) raw.Event { 155 spec := t.specs[ev] 156 if ev != go122.EvStack { 157 if arity := len(spec.Args); len(args) != arity { 158 panic(fmt.Sprintf("expected %d args for %s, got %d", arity, spec.Name, len(args))) 159 } 160 } 161 return raw.Event{ 162 Version: version.Go122, 163 Ev: ev, 164 Args: args, 165 Data: data, 166 } 167 } 168 169 type stack struct { 170 stk [32]trace.StackFrame 171 len int 172 } 173 174 var ( 175 NoString = "" 176 NoStack = []trace.StackFrame{} 177 ) 178 179 // Generation represents a single generation in the trace. 180 type Generation struct { 181 trace *Trace 182 gen uint64 183 batches []*Batch 184 strings map[string]uint64 185 stacks map[stack]uint64 186 187 // Options applied when Trace.Generate is called. 188 ignoreStringBatchSizeLimit bool 189 ignoreStackBatchSizeLimit bool 190 } 191 192 // Batch starts a new event batch in the trace data. 193 // 194 // This is convenience function for generating correct batches. 195 func (g *Generation) Batch(thread trace.ThreadID, time Time) *Batch { 196 if !g.trace.validTimestamps { 197 time = 0 198 } 199 b := &Batch{ 200 gen: g, 201 thread: thread, 202 timestamp: time, 203 } 204 g.batches = append(g.batches, b) 205 return b 206 } 207 208 // String registers a string with the trace. 209 // 210 // This is a convenience function for easily adding correct 211 // strings to traces. 212 func (g *Generation) String(s string) uint64 { 213 if len(s) == 0 { 214 return 0 215 } 216 if id, ok := g.strings[s]; ok { 217 return id 218 } 219 id := uint64(len(g.strings) + 1) 220 g.strings[s] = id 221 return id 222 } 223 224 // Stack registers a stack with the trace. 225 // 226 // This is a convenience function for easily adding correct 227 // stacks to traces. 228 func (g *Generation) Stack(stk []trace.StackFrame) uint64 { 229 if len(stk) == 0 { 230 return 0 231 } 232 if len(stk) > 32 { 233 panic("stack too big for test") 234 } 235 var stkc stack 236 copy(stkc.stk[:], stk) 237 stkc.len = len(stk) 238 if id, ok := g.stacks[stkc]; ok { 239 return id 240 } 241 id := uint64(len(g.stacks) + 1) 242 g.stacks[stkc] = id 243 return id 244 } 245 246 // writeEventsTo emits event batches in the generation to tw. 247 func (g *Generation) writeEventsTo(tw *raw.TextWriter) { 248 // Write event batches for the generation. 249 for _, b := range g.batches { 250 b.writeEventsTo(tw) 251 } 252 253 // Write frequency. 254 b := g.newStructuralBatch() 255 b.RawEvent(go122.EvFrequency, nil, 15625000) 256 b.writeEventsTo(tw) 257 258 // Write stacks. 259 b = g.newStructuralBatch() 260 b.RawEvent(go122.EvStacks, nil) 261 for stk, id := range g.stacks { 262 stk := stk.stk[:stk.len] 263 args := []uint64{id} 264 for _, f := range stk { 265 args = append(args, f.PC, g.String(f.Func), g.String(f.File), f.Line) 266 } 267 b.RawEvent(go122.EvStack, nil, args...) 268 269 // Flush the batch if necessary. 270 if !g.ignoreStackBatchSizeLimit && b.size > go122.MaxBatchSize/2 { 271 b.writeEventsTo(tw) 272 b = g.newStructuralBatch() 273 } 274 } 275 b.writeEventsTo(tw) 276 277 // Write strings. 278 b = g.newStructuralBatch() 279 b.RawEvent(go122.EvStrings, nil) 280 for s, id := range g.strings { 281 b.RawEvent(go122.EvString, []byte(s), id) 282 283 // Flush the batch if necessary. 284 if !g.ignoreStringBatchSizeLimit && b.size > go122.MaxBatchSize/2 { 285 b.writeEventsTo(tw) 286 b = g.newStructuralBatch() 287 } 288 } 289 b.writeEventsTo(tw) 290 } 291 292 func (g *Generation) newStructuralBatch() *Batch { 293 return &Batch{gen: g, thread: trace.NoThread} 294 } 295 296 // Batch represents an event batch. 297 type Batch struct { 298 gen *Generation 299 thread trace.ThreadID 300 timestamp Time 301 size uint64 302 events []raw.Event 303 } 304 305 // Event emits an event into a batch. name must correspond to one 306 // of the names in Specs() result for the version that was passed to 307 // this trace. Callers must omit the timestamp delta. 308 func (b *Batch) Event(name string, args ...any) { 309 ev, ok := b.gen.trace.names[name] 310 if !ok { 311 panic(fmt.Sprintf("invalid or unknown event %s", name)) 312 } 313 var uintArgs []uint64 314 argOff := 0 315 if b.gen.trace.specs[ev].IsTimedEvent { 316 if b.gen.trace.validTimestamps { 317 uintArgs = []uint64{1} 318 } else { 319 uintArgs = []uint64{0} 320 } 321 argOff = 1 322 } 323 spec := b.gen.trace.specs[ev] 324 if arity := len(spec.Args) - argOff; len(args) != arity { 325 panic(fmt.Sprintf("expected %d args for %s, got %d", arity, spec.Name, len(args))) 326 } 327 for i, arg := range args { 328 uintArgs = append(uintArgs, b.uintArgFor(arg, spec.Args[i+argOff])) 329 } 330 b.RawEvent(ev, nil, uintArgs...) 331 } 332 333 func (b *Batch) uintArgFor(arg any, argSpec string) uint64 { 334 components := strings.SplitN(argSpec, "_", 2) 335 typStr := components[0] 336 if len(components) == 2 { 337 typStr = components[1] 338 } 339 var u uint64 340 switch typStr { 341 case "value": 342 u = arg.(uint64) 343 case "stack": 344 u = b.gen.Stack(arg.([]trace.StackFrame)) 345 case "seq": 346 u = uint64(arg.(Seq)) 347 case "pstatus": 348 u = uint64(arg.(go122.ProcStatus)) 349 case "gstatus": 350 u = uint64(arg.(go122.GoStatus)) 351 case "g": 352 u = uint64(arg.(trace.GoID)) 353 case "m": 354 u = uint64(arg.(trace.ThreadID)) 355 case "p": 356 u = uint64(arg.(trace.ProcID)) 357 case "string": 358 u = b.gen.String(arg.(string)) 359 case "task": 360 u = uint64(arg.(trace.TaskID)) 361 default: 362 panic(fmt.Sprintf("unsupported arg type %q for spec %q", typStr, argSpec)) 363 } 364 return u 365 } 366 367 // RawEvent emits an event into a batch. name must correspond to one 368 // of the names in Specs() result for the version that was passed to 369 // this trace. 370 func (b *Batch) RawEvent(typ event.Type, data []byte, args ...uint64) { 371 ev := b.gen.trace.createEvent(typ, data, args...) 372 373 // Compute the size of the event and add it to the batch. 374 b.size += 1 // One byte for the event header. 375 var buf [binary.MaxVarintLen64]byte 376 for _, arg := range args { 377 b.size += uint64(binary.PutUvarint(buf[:], arg)) 378 } 379 if len(data) != 0 { 380 b.size += uint64(binary.PutUvarint(buf[:], uint64(len(data)))) 381 b.size += uint64(len(data)) 382 } 383 384 // Add the event. 385 b.events = append(b.events, ev) 386 } 387 388 // writeEventsTo emits events in the batch, including the batch header, to tw. 389 func (b *Batch) writeEventsTo(tw *raw.TextWriter) { 390 tw.WriteEvent(raw.Event{ 391 Version: version.Go122, 392 Ev: go122.EvEventBatch, 393 Args: []uint64{b.gen.gen, uint64(b.thread), uint64(b.timestamp), b.size}, 394 }) 395 for _, e := range b.events { 396 tw.WriteEvent(e) 397 } 398 } 399 400 // Seq represents a sequence counter. 401 type Seq uint64 402 403 // Time represents a low-level trace timestamp (which does not necessarily 404 // correspond to nanoseconds, like trace.Time does). 405 type Time uint64