trace: regenerate trace from go@b89f946

[git-generate]
cd trace
./gen.bash $HOME/work/go

Change-Id: I8cd13461f1ecec0790f682343a7a9f7e8766be0f
Reviewed-on: https://go-review.googlesource.com/c/exp/+/588140
Reviewed-by: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Michael Knyszek <mknyszek@google.com>
diff --git a/trace/base.go b/trace/base.go
index fb874db..d3d5db4 100644
--- a/trace/base.go
+++ b/trace/base.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
@@ -65,6 +65,10 @@
 	extraStrings   []string
 	extraStringIDs map[string]extraStringID
 	nextExtra      extraStringID
+
+	// expData contains extra unparsed data that is accessible
+	// only to ExperimentEvent via an EventExperimental event.
+	expData map[event.Experiment]*ExperimentalData
 }
 
 // addExtraString adds an extra string to the evTable and returns
diff --git a/trace/batch.go b/trace/batch.go
index 0bde22d..4d6c530 100644
--- a/trace/batch.go
+++ b/trace/batch.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
@@ -27,22 +27,23 @@
 	m    ThreadID
 	time timestamp
 	data []byte
+	exp  event.Experiment
 }
 
 func (b *batch) isStringsBatch() bool {
-	return len(b.data) > 0 && event.Type(b.data[0]) == go122.EvStrings
+	return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvStrings
 }
 
 func (b *batch) isStacksBatch() bool {
-	return len(b.data) > 0 && event.Type(b.data[0]) == go122.EvStacks
+	return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvStacks
 }
 
 func (b *batch) isCPUSamplesBatch() bool {
-	return len(b.data) > 0 && event.Type(b.data[0]) == go122.EvCPUSamples
+	return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvCPUSamples
 }
 
 func (b *batch) isFreqBatch() bool {
-	return len(b.data) > 0 && event.Type(b.data[0]) == go122.EvFrequency
+	return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvFrequency
 }
 
 // readBatch reads the next full batch from r.
@@ -55,8 +56,18 @@
 	if err != nil {
 		return batch{}, 0, err
 	}
-	if typ := event.Type(b); typ != go122.EvEventBatch {
-		return batch{}, 0, fmt.Errorf("expected batch event (%s), got %s", go122.EventString(go122.EvEventBatch), go122.EventString(typ))
+	if typ := event.Type(b); typ != go122.EvEventBatch && typ != go122.EvExperimentalBatch {
+		return batch{}, 0, fmt.Errorf("expected batch event, got %s", go122.EventString(typ))
+	}
+
+	// Read the experiment of we have one.
+	exp := event.NoExperiment
+	if event.Type(b) == go122.EvExperimentalBatch {
+		e, err := r.ReadByte()
+		if err != nil {
+			return batch{}, 0, err
+		}
+		exp = event.Experiment(e)
 	}
 
 	// Read the batch header: gen (generation), thread (M) ID, base timestamp
@@ -99,5 +110,6 @@
 		m:    ThreadID(m),
 		time: timestamp(ts),
 		data: data.Bytes(),
+		exp:  exp,
 	}, gen, nil
 }
diff --git a/trace/batchcursor.go b/trace/batchcursor.go
index 9582468..c3b0f51 100644
--- a/trace/batchcursor.go
+++ b/trace/batchcursor.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/batchcursor_test.go b/trace/batchcursor_test.go
index b13dbc1..542b32e 100644
--- a/trace/batchcursor_test.go
+++ b/trace/batchcursor_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/cmd/gotraceeventstats/main.go b/trace/cmd/gotraceeventstats/main.go
new file mode 100644
index 0000000..e89f476
--- /dev/null
+++ b/trace/cmd/gotraceeventstats/main.go
@@ -0,0 +1,140 @@
+// Copyright 2023 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.
+
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
+
+//go:build go1.21
+
+package main
+
+import (
+	"cmp"
+	"encoding/binary"
+	"flag"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"slices"
+	"text/tabwriter"
+
+	"golang.org/x/exp/trace/internal/event"
+	"golang.org/x/exp/trace/internal/raw"
+)
+
+func init() {
+	flag.Usage = func() {
+		fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [mode]\n", os.Args[0])
+		fmt.Fprintf(flag.CommandLine.Output(), "\n")
+		fmt.Fprintf(flag.CommandLine.Output(), "Accepts a trace at stdin.\n")
+		fmt.Fprintf(flag.CommandLine.Output(), "\n")
+		fmt.Fprintf(flag.CommandLine.Output(), "Supported modes:")
+		fmt.Fprintf(flag.CommandLine.Output(), "\n")
+		fmt.Fprintf(flag.CommandLine.Output(), "* size  - dumps size stats\n")
+		fmt.Fprintf(flag.CommandLine.Output(), "\n")
+		flag.PrintDefaults()
+	}
+	log.SetFlags(0)
+}
+
+func main() {
+	log.SetPrefix("")
+	flag.Parse()
+
+	if flag.NArg() != 1 {
+		log.Print("missing mode argument")
+		flag.Usage()
+		os.Exit(1)
+	}
+	var err error
+	switch mode := flag.Arg(0); mode {
+	case "size":
+		err = printSizeStats(os.Stdin)
+	default:
+		log.Printf("unknown mode %q", mode)
+		flag.Usage()
+		os.Exit(1)
+	}
+	if err != nil {
+		log.Fatalf("error: %v", err)
+		os.Exit(1)
+	}
+}
+
+func printSizeStats(r io.Reader) error {
+	cr := countingReader{Reader: r}
+	tr, err := raw.NewReader(&cr)
+	if err != nil {
+		return err
+	}
+	type eventStats struct {
+		typ   event.Type
+		count int
+		bytes int
+	}
+	var stats [256]eventStats
+	for i := range stats {
+		stats[i].typ = event.Type(i)
+	}
+	eventsRead := 0
+	for {
+		e, err := tr.ReadEvent()
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			return err
+		}
+		s := &stats[e.Ev]
+		s.count++
+		s.bytes += encodedSize(&e)
+		eventsRead++
+	}
+	slices.SortFunc(stats[:], func(a, b eventStats) int {
+		return cmp.Compare(b.bytes, a.bytes)
+	})
+	specs := tr.Version().Specs()
+	w := tabwriter.NewWriter(os.Stdout, 3, 8, 2, ' ', 0)
+	fmt.Fprintf(w, "Event\tBytes\t%%\tCount\t%%\n")
+	fmt.Fprintf(w, "-\t-\t-\t-\t-\n")
+	for i := range stats {
+		stat := &stats[i]
+		name := ""
+		if int(stat.typ) >= len(specs) {
+			name = fmt.Sprintf("<unknown (%d)>", stat.typ)
+		} else {
+			name = specs[stat.typ].Name
+		}
+		bytesPct := float64(stat.bytes) / float64(cr.bytesRead) * 100
+		countPct := float64(stat.count) / float64(eventsRead) * 100
+		fmt.Fprintf(w, "%s\t%d\t%.2f%%\t%d\t%.2f%%\n", name, stat.bytes, bytesPct, stat.count, countPct)
+	}
+	w.Flush()
+	return nil
+}
+
+func encodedSize(e *raw.Event) int {
+	size := 1
+	var buf [binary.MaxVarintLen64]byte
+	for _, arg := range e.Args {
+		size += binary.PutUvarint(buf[:], arg)
+	}
+	spec := e.Version.Specs()[e.Ev]
+	if spec.HasData {
+		size += binary.PutUvarint(buf[:], uint64(len(e.Data)))
+		size += len(e.Data)
+	}
+	return size
+}
+
+type countingReader struct {
+	io.Reader
+	bytesRead int
+}
+
+func (r *countingReader) Read(b []byte) (int, error) {
+	n, err := r.Reader.Read(b)
+	r.bytesRead += n
+	return n, err
+}
diff --git a/trace/cmd/gotraceraw/main.go b/trace/cmd/gotraceraw/main.go
index c544141..71e8e5b 100644
--- a/trace/cmd/gotraceraw/main.go
+++ b/trace/cmd/gotraceraw/main.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/cmd/gotracevalidate/main.go b/trace/cmd/gotracevalidate/main.go
index cb7166c..41d2ffe 100644
--- a/trace/cmd/gotracevalidate/main.go
+++ b/trace/cmd/gotracevalidate/main.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/event.go b/trace/event.go
index 2a76eb8..6fb6f60 100644
--- a/trace/event.go
+++ b/trace/event.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
@@ -80,8 +80,13 @@
 	// EventLog represents a runtime/trace.Log call.
 	EventLog
 
-	// Transitions in state for some resource.
+	// EventStateTransition represents a state change for some resource.
 	EventStateTransition
+
+	// EventExperimental is an experimental event that is unvalidated and exposed in a raw form.
+	// Users are expected to understand the format and perform their own validation. These events
+	// may always be safely ignored.
+	EventExperimental
 )
 
 // String returns a string form of the EventKind.
@@ -107,6 +112,7 @@
 	EventRegionEnd:       "RegionEnd",
 	EventLog:             "Log",
 	EventStateTransition: "StateTransition",
+	EventExperimental:    "Experimental",
 }
 
 const maxTime = Time(math.MaxInt64)
@@ -304,6 +310,42 @@
 	Line uint64
 }
 
+// ExperimentalEvent presents a raw view of an experimental event's arguments and thier names.
+type ExperimentalEvent struct {
+	// Name is the name of the event.
+	Name string
+
+	// ArgNames is the names of the event's arguments in order.
+	// This may refer to a globally shared slice. Copy before mutating.
+	ArgNames []string
+
+	// Args contains the event's arguments.
+	Args []uint64
+
+	// Data is additional unparsed data that is associated with the experimental event.
+	// Data is likely to be shared across many ExperimentalEvents, so callers that parse
+	// Data are encouraged to cache the parse result and look it up by the value of Data.
+	Data *ExperimentalData
+}
+
+// ExperimentalData represents some raw and unparsed sidecar data present in the trace that is
+// associated with certain kinds of experimental events. For example, this data may contain
+// tables needed to interpret ExperimentalEvent arguments, or the ExperimentEvent could just be
+// a placeholder for a differently encoded event that's actually present in the experimental data.
+type ExperimentalData struct {
+	// Batches contain the actual experimental data, along with metadata about each batch.
+	Batches []ExperimentalBatch
+}
+
+// ExperimentalBatch represents a packet of unparsed data along with metadata about that packet.
+type ExperimentalBatch struct {
+	// Thread is the ID of the thread that produced a packet of data.
+	Thread ThreadID
+
+	// Data is a packet of unparsed data all produced by one thread.
+	Data []byte
+}
+
 // Event represents a single event in the trace.
 type Event struct {
 	table *evTable
@@ -617,6 +659,23 @@
 	return s
 }
 
+// Experimental returns a view of the raw event for an experimental event.
+//
+// Panics if Kind != EventExperimental.
+func (e Event) Experimental() ExperimentalEvent {
+	if e.Kind() != EventExperimental {
+		panic("Experimental called on non-Experimental event")
+	}
+	spec := go122.Specs()[e.base.typ]
+	argNames := spec.Args[1:]
+	return ExperimentalEvent{
+		Name:     spec.Name,
+		ArgNames: argNames, // Skip timestamp; already handled.
+		Args:     e.base.args[1 : 1+len(argNames)],
+		Data:     e.table.expData[spec.Experiment],
+	}
+}
+
 const evSync = ^event.Type(0)
 
 var go122Type2Kind = [...]EventKind{
@@ -661,6 +720,15 @@
 	go122.EvGoSwitchDestroy:     EventStateTransition,
 	go122.EvGoCreateBlocked:     EventStateTransition,
 	go122.EvGoStatusStack:       EventStateTransition,
+	go122.EvSpan:                EventExperimental,
+	go122.EvSpanAlloc:           EventExperimental,
+	go122.EvSpanFree:            EventExperimental,
+	go122.EvHeapObject:          EventExperimental,
+	go122.EvHeapObjectAlloc:     EventExperimental,
+	go122.EvHeapObjectFree:      EventExperimental,
+	go122.EvGoroutineStack:      EventExperimental,
+	go122.EvGoroutineStackAlloc: EventExperimental,
+	go122.EvGoroutineStackFree:  EventExperimental,
 	evSync:                      EventSync,
 }
 
@@ -737,6 +805,9 @@
 				return true
 			})
 		}
+	case EventExperimental:
+		r := e.Experimental()
+		fmt.Fprintf(&sb, " Name=%s ArgNames=%v Args=%v", r.Name, r.ArgNames, r.Args)
 	}
 	if stk := e.Stack(); stk != NoStack {
 		fmt.Fprintln(&sb)
diff --git a/trace/event_test.go b/trace/event_test.go
index 453c485..7906b2d 100644
--- a/trace/event_test.go
+++ b/trace/event_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/generation.go b/trace/generation.go
index 4ff535a..6b8c8b1 100644
--- a/trace/generation.go
+++ b/trace/generation.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
@@ -45,6 +45,9 @@
 // out of r. spill is the first batch of the new generation (already buffered and
 // parsed from reading the last generation). Returns the generation and the first
 // batch read of the next generation, if any.
+//
+// If gen is non-nil, it is valid and must be processed before handling the returned
+// error.
 func readGeneration(r *bufio.Reader, spill *spilledBatch) (*generation, *spilledBatch, error) {
 	g := &generation{
 		evTable: &evTable{
@@ -62,12 +65,20 @@
 	}
 	// Read batches one at a time until we either hit EOF or
 	// the next generation.
+	var spillErr error
 	for {
 		b, gen, err := readBatch(r)
 		if err == io.EOF {
 			break
 		}
 		if err != nil {
+			if g.gen != 0 {
+				// This is an error reading the first batch of the next generation.
+				// This is fine. Let's forge ahead assuming that what we've got so
+				// far is fine.
+				spillErr = err
+				break
+			}
 			return nil, nil, err
 		}
 		if gen == 0 {
@@ -125,7 +136,7 @@
 	slices.SortFunc(g.cpuSamples, func(a, b cpuSample) int {
 		return cmp.Compare(a.time, b.time)
 	})
-	return g, spill, nil
+	return g, spill, spillErr
 }
 
 // processBatch adds the batch to the generation.
@@ -154,6 +165,10 @@
 			return fmt.Errorf("found multiple frequency events")
 		}
 		g.freq = freq
+	case b.exp != event.NoExperiment:
+		if err := addExperimentalData(g.expData, b); err != nil {
+			return err
+		}
 	default:
 		g.batches[b.m] = append(g.batches[b.m], b)
 	}
@@ -416,3 +431,20 @@
 	// Convert to nanoseconds per timestamp unit.
 	return frequency(1.0 / (float64(f) / 1e9)), nil
 }
+
+// addExperimentalData takes an experimental batch and adds it to the ExperimentalData
+// for the experiment its a part of.
+func addExperimentalData(expData map[event.Experiment]*ExperimentalData, b batch) error {
+	if b.exp == event.NoExperiment {
+		return fmt.Errorf("internal error: addExperimentalData called on non-experimental batch")
+	}
+	ed, ok := expData[b.exp]
+	if !ok {
+		ed = new(ExperimentalData)
+	}
+	ed.Batches = append(ed.Batches, ExperimentalBatch{
+		Thread: b.m,
+		Data:   b.data,
+	})
+	return nil
+}
diff --git a/trace/internal/event/event.go b/trace/internal/event/event.go
index 4b5da1f..cfabfa6 100644
--- a/trace/internal/event/event.go
+++ b/trace/internal/event/event.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
@@ -32,6 +32,15 @@
 	// in the ArgTypes variable.
 	Args []string
 
+	// StringIDs indicates which of the arguments are string IDs.
+	StringIDs []int
+
+	// StackIDs indicates which of the arguments are stack IDs.
+	//
+	// The list is not sorted. The first index always refers to
+	// the main stack for the current execution context of the event.
+	StackIDs []int
+
 	// StartEv indicates the event type of the corresponding "start"
 	// event, if this event is an "end," for a pair of events that
 	// represent a time range.
@@ -48,16 +57,9 @@
 
 	// HasData is true if the event has trailer consisting of a
 	// varint length followed by unencoded bytes of some data.
-	HasData bool
-
-	// StringIDs indicates which of the arguments are string IDs.
-	StringIDs []int
-
-	// StackIDs indicates which of the arguments are stack IDs.
 	//
-	// The list is not sorted. The first index always refers to
-	// the main stack for the current execution context of the event.
-	StackIDs []int
+	// An event may not be both a timed event and have data.
+	HasData bool
 
 	// IsStack indicates that the event represents a complete
 	// stack trace. Specifically, it means that after the arguments
@@ -65,6 +67,11 @@
 	// group of 4 represents the PC, file ID, func ID, and line number
 	// in that order.
 	IsStack bool
+
+	// Experiment indicates the ID of an experiment this event is associated
+	// with. If Experiment is not NoExperiment, then the event is experimental
+	// and will be exposed as an EventExperiment.
+	Experiment Experiment
 }
 
 // ArgTypes is a list of valid argument types for use in Args.
@@ -91,3 +98,9 @@
 	}
 	return nameToType
 }
+
+// Experiment is an experiment ID that events may be associated with.
+type Experiment uint
+
+// NoExperiment is the reserved ID 0 indicating no experiment.
+const NoExperiment Experiment = 0
diff --git a/trace/internal/event/go122/event.go b/trace/internal/event/go122/event.go
index 3220771..5a8761f 100644
--- a/trace/internal/event/go122/event.go
+++ b/trace/internal/event/go122/event.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
@@ -77,8 +77,39 @@
 	EvGoSwitchDestroy // goroutine switch and destroy [timestamp, goroutine ID, goroutine seq]
 	EvGoCreateBlocked // goroutine creation (starts blocked) [timestamp, new goroutine ID, new stack ID, stack ID]
 
-	// GoStatus with stack.
+	// GoStatus with stack. Added in Go 1.23.
 	EvGoStatusStack // goroutine status at the start of a generation, with a stack [timestamp, goroutine ID, M ID, status, stack ID]
+
+	// Batch event for an experimental batch with a custom format. Added in Go 1.23.
+	EvExperimentalBatch // start of extra data [experiment ID, generation, M ID, timestamp, batch length, batch data...]
+)
+
+// Experiments.
+const (
+	// AllocFree is the alloc-free events experiment.
+	AllocFree event.Experiment = 1 + iota
+)
+
+// Experimental events.
+const (
+	_ event.Type = 127 + iota
+
+	// Experimental events for AllocFree.
+
+	// Experimental heap span events. Added in Go 1.23.
+	EvSpan      // heap span exists [timestamp, id, npages, type/class]
+	EvSpanAlloc // heap span alloc [timestamp, id, npages, type/class]
+	EvSpanFree  // heap span free [timestamp, id]
+
+	// Experimental heap object events. Added in Go 1.23.
+	EvHeapObject      // heap object exists [timestamp, id, type]
+	EvHeapObjectAlloc // heap object alloc [timestamp, id, type]
+	EvHeapObjectFree  // heap object free [timestamp, id]
+
+	// Experimental goroutine stack events. Added in Go 1.23.
+	EvGoroutineStack      // stack exists [timestamp, id, order]
+	EvGoroutineStackAlloc // stack alloc [timestamp, id, order]
+	EvGoroutineStackFree  // stack free [timestamp, id]
 )
 
 // EventString returns the name of a Go 1.22 event.
@@ -129,6 +160,11 @@
 		Name: "Frequency",
 		Args: []string{"freq"},
 	},
+	EvExperimentalBatch: {
+		Name:    "ExperimentalBatch",
+		Args:    []string{"exp", "gen", "m", "time"},
+		HasData: true, // Easier to represent for raw readers.
+	},
 
 	// "Timed" Events.
 	EvProcsChange: {
@@ -366,6 +402,63 @@
 		IsTimedEvent: true,
 		StackIDs:     []int{4},
 	},
+
+	// Experimental events.
+
+	EvSpan: {
+		Name:         "Span",
+		Args:         []string{"dt", "id", "npages_value", "kindclass"},
+		IsTimedEvent: true,
+		Experiment:   AllocFree,
+	},
+	EvSpanAlloc: {
+		Name:         "SpanAlloc",
+		Args:         []string{"dt", "id", "npages_value", "kindclass"},
+		IsTimedEvent: true,
+		Experiment:   AllocFree,
+	},
+	EvSpanFree: {
+		Name:         "SpanFree",
+		Args:         []string{"dt", "id"},
+		IsTimedEvent: true,
+		Experiment:   AllocFree,
+	},
+	EvHeapObject: {
+		Name:         "HeapObject",
+		Args:         []string{"dt", "id", "type"},
+		IsTimedEvent: true,
+		Experiment:   AllocFree,
+	},
+	EvHeapObjectAlloc: {
+		Name:         "HeapObjectAlloc",
+		Args:         []string{"dt", "id", "type"},
+		IsTimedEvent: true,
+		Experiment:   AllocFree,
+	},
+	EvHeapObjectFree: {
+		Name:         "HeapObjectFree",
+		Args:         []string{"dt", "id"},
+		IsTimedEvent: true,
+		Experiment:   AllocFree,
+	},
+	EvGoroutineStack: {
+		Name:         "GoroutineStack",
+		Args:         []string{"dt", "id", "order"},
+		IsTimedEvent: true,
+		Experiment:   AllocFree,
+	},
+	EvGoroutineStackAlloc: {
+		Name:         "GoroutineStackAlloc",
+		Args:         []string{"dt", "id", "order"},
+		IsTimedEvent: true,
+		Experiment:   AllocFree,
+	},
+	EvGoroutineStackFree: {
+		Name:         "GoroutineStackFree",
+		Args:         []string{"dt", "id"},
+		IsTimedEvent: true,
+		Experiment:   AllocFree,
+	},
 }
 
 type GoStatus uint8
diff --git a/trace/internal/event/requirements.go b/trace/internal/event/requirements.go
index 4f41171..3482d4e 100644
--- a/trace/internal/event/requirements.go
+++ b/trace/internal/event/requirements.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/internal/oldtrace/order.go b/trace/internal/oldtrace/order.go
index 78f0818..d294546 100644
--- a/trace/internal/oldtrace/order.go
+++ b/trace/internal/oldtrace/order.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
@@ -130,20 +130,6 @@
 	return (*l)[i].ev.Ts < (*l)[j].ev.Ts
 }
 
-type eventList []Event
-
-func (l *eventList) Len() int {
-	return len(*l)
-}
-
-func (l *eventList) Less(i, j int) bool {
-	return (*l)[i].Ts < (*l)[j].Ts
-}
-
-func (l *eventList) Swap(i, j int) {
-	(*l)[i], (*l)[j] = (*l)[j], (*l)[i]
-}
-
 func (h *orderEventList) Push(x orderEvent) {
 	*h = append(*h, x)
 	heapUp(h, len(*h)-1)
diff --git a/trace/internal/oldtrace/parser.go b/trace/internal/oldtrace/parser.go
index 8ab3773..4e673bd 100644
--- a/trace/internal/oldtrace/parser.go
+++ b/trace/internal/oldtrace/parser.go
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
 // Package oldtrace implements a parser for Go execution traces from versions
 // 1.11–1.21.
 //
-// The package started as a copy of Go 1.19's internal/trace, but has been
+// The package started as a copy of Go 1.19's golang.org/x/exp/trace, but has been
 // optimized to be faster while using less memory and fewer allocations. It has
 // been further modified for the specific purpose of converting traces to the
 // new 1.22+ format.
@@ -17,6 +17,7 @@
 
 import (
 	"bytes"
+	"cmp"
 	"encoding/binary"
 	"errors"
 	"fmt"
@@ -24,6 +25,7 @@
 	"golang.org/x/exp/trace/internal/version"
 	"io"
 	"math"
+	"slices"
 	"sort"
 )
 
@@ -372,7 +374,9 @@
 	// with original timestamps corresponding to when ReadTrace pulled the data
 	// off of the profBuf queue. Re-sort them by the timestamp we captured
 	// inside the signal handler.
-	sort.Sort((*eventList)(&p.cpuSamples))
+	slices.SortFunc(p.cpuSamples, func(a, b Event) int {
+		return cmp.Compare(a.Ts, b.Ts)
+	})
 
 	allProcs := make([]proc, 0, len(p.batchOffsets))
 	for pid := range p.batchOffsets {
diff --git a/trace/internal/raw/doc.go b/trace/internal/raw/doc.go
index 149c606..a2586b3 100644
--- a/trace/internal/raw/doc.go
+++ b/trace/internal/raw/doc.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/internal/raw/event.go b/trace/internal/raw/event.go
index b7db017..8b77faa 100644
--- a/trace/internal/raw/event.go
+++ b/trace/internal/raw/event.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/internal/raw/reader.go b/trace/internal/raw/reader.go
index bf3ef18..235fc11 100644
--- a/trace/internal/raw/reader.go
+++ b/trace/internal/raw/reader.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/internal/raw/textreader.go b/trace/internal/raw/textreader.go
index 05de34c..a544449 100644
--- a/trace/internal/raw/textreader.go
+++ b/trace/internal/raw/textreader.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/internal/raw/textwriter.go b/trace/internal/raw/textwriter.go
index 8eaf359..c0f35b5 100644
--- a/trace/internal/raw/textwriter.go
+++ b/trace/internal/raw/textwriter.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/internal/raw/writer.go b/trace/internal/raw/writer.go
index f82b559..2384da8 100644
--- a/trace/internal/raw/writer.go
+++ b/trace/internal/raw/writer.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/internal/testgen/go122/trace.go b/trace/internal/testgen/go122/trace.go
index d371e0c..511414a 100644
--- a/trace/internal/testgen/go122/trace.go
+++ b/trace/internal/testgen/go122/trace.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/internal/testtrace/expectation.go b/trace/internal/testtrace/expectation.go
index 8caaeb9..e8084e5 100644
--- a/trace/internal/testtrace/expectation.go
+++ b/trace/internal/testtrace/expectation.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/internal/testtrace/format.go b/trace/internal/testtrace/format.go
index d7f61a1..474a4e7 100644
--- a/trace/internal/testtrace/format.go
+++ b/trace/internal/testtrace/format.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/internal/testtrace/validation.go b/trace/internal/testtrace/validation.go
index ddbf0b6..717cc22 100644
--- a/trace/internal/testtrace/validation.go
+++ b/trace/internal/testtrace/validation.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/internal/version/version.go b/trace/internal/version/version.go
index 1dc0dcd..c5e638a 100644
--- a/trace/internal/version/version.go
+++ b/trace/internal/version/version.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/oldtrace.go b/trace/oldtrace.go
index a051792..4777a68 100644
--- a/trace/oldtrace.go
+++ b/trace/oldtrace.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/order.go b/trace/order.go
index fec4087..91c7e25 100644
--- a/trace/order.go
+++ b/trace/order.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
@@ -147,6 +147,23 @@
 
 	// GoStatus event with a stack. Added in Go 1.23.
 	go122.EvGoStatusStack: (*ordering).advanceGoStatus,
+
+	// Experimental events.
+
+	// Experimental heap span events. Added in Go 1.23.
+	go122.EvSpan:      (*ordering).advanceAllocFree,
+	go122.EvSpanAlloc: (*ordering).advanceAllocFree,
+	go122.EvSpanFree:  (*ordering).advanceAllocFree,
+
+	// Experimental heap object events. Added in Go 1.23.
+	go122.EvHeapObject:      (*ordering).advanceAllocFree,
+	go122.EvHeapObjectAlloc: (*ordering).advanceAllocFree,
+	go122.EvHeapObjectFree:  (*ordering).advanceAllocFree,
+
+	// Experimental goroutine stack events. Added in Go 1.23.
+	go122.EvGoroutineStack:      (*ordering).advanceAllocFree,
+	go122.EvGoroutineStackAlloc: (*ordering).advanceAllocFree,
+	go122.EvGoroutineStackFree:  (*ordering).advanceAllocFree,
 }
 
 func (o *ordering) advanceProcStatus(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
@@ -1062,6 +1079,15 @@
 	return curCtx, true, nil
 }
 
+func (o *ordering) advanceAllocFree(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
+	// Handle simple instantaneous events that may or may not have a P.
+	if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MayHave}); err != nil {
+		return curCtx, false, err
+	}
+	o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
+	return curCtx, true, nil
+}
+
 // Next returns the next event in the ordering.
 func (o *ordering) Next() (Event, bool) {
 	return o.queue.pop()
@@ -1240,7 +1266,7 @@
 	return false
 }
 
-// endRange ends a special range in time on the goroutine.
+// endsRange ends a special range in time on the goroutine.
 //
 // This must line up with the start event type  of the range the goroutine is currently in.
 func (s *rangeState) endRange(typ event.Type) (stringID, error) {
diff --git a/trace/order_test.go b/trace/order_test.go
index ee9e3c6..5c0d94a 100644
--- a/trace/order_test.go
+++ b/trace/order_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/parser.go b/trace/parser.go
new file mode 100644
index 0000000..b3bdd13
--- /dev/null
+++ b/trace/parser.go
@@ -0,0 +1,83 @@
+// Copyright 2014 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.
+
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
+
+//go:build go1.21
+
+package trace
+
+// Frame is a frame in stack traces.
+type Frame struct {
+	PC   uint64
+	Fn   string
+	File string
+	Line int
+}
+
+const (
+	// Special P identifiers:
+	FakeP    = 1000000 + iota
+	TimerP   // depicts timer unblocks
+	NetpollP // depicts network unblocks
+	SyscallP // depicts returns from syscalls
+	GCP      // depicts GC state
+	ProfileP // depicts recording of CPU profile samples
+)
+
+// Event types in the trace.
+// Verbatim copy from src/runtime/trace.go with the "trace" prefix removed.
+const (
+	EvNone              = 0  // unused
+	EvBatch             = 1  // start of per-P batch of events [pid, timestamp]
+	EvFrequency         = 2  // contains tracer timer frequency [frequency (ticks per second)]
+	EvStack             = 3  // stack [stack id, number of PCs, array of {PC, func string ID, file string ID, line}]
+	EvGomaxprocs        = 4  // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack id]
+	EvProcStart         = 5  // start of P [timestamp, thread id]
+	EvProcStop          = 6  // stop of P [timestamp]
+	EvGCStart           = 7  // GC start [timestamp, seq, stack id]
+	EvGCDone            = 8  // GC done [timestamp]
+	EvSTWStart          = 9  // GC mark termination start [timestamp, kind]
+	EvSTWDone           = 10 // GC mark termination done [timestamp]
+	EvGCSweepStart      = 11 // GC sweep start [timestamp, stack id]
+	EvGCSweepDone       = 12 // GC sweep done [timestamp, swept, reclaimed]
+	EvGoCreate          = 13 // goroutine creation [timestamp, new goroutine id, new stack id, stack id]
+	EvGoStart           = 14 // goroutine starts running [timestamp, goroutine id, seq]
+	EvGoEnd             = 15 // goroutine ends [timestamp]
+	EvGoStop            = 16 // goroutine stops (like in select{}) [timestamp, stack]
+	EvGoSched           = 17 // goroutine calls Gosched [timestamp, stack]
+	EvGoPreempt         = 18 // goroutine is preempted [timestamp, stack]
+	EvGoSleep           = 19 // goroutine calls Sleep [timestamp, stack]
+	EvGoBlock           = 20 // goroutine blocks [timestamp, stack]
+	EvGoUnblock         = 21 // goroutine is unblocked [timestamp, goroutine id, seq, stack]
+	EvGoBlockSend       = 22 // goroutine blocks on chan send [timestamp, stack]
+	EvGoBlockRecv       = 23 // goroutine blocks on chan recv [timestamp, stack]
+	EvGoBlockSelect     = 24 // goroutine blocks on select [timestamp, stack]
+	EvGoBlockSync       = 25 // goroutine blocks on Mutex/RWMutex [timestamp, stack]
+	EvGoBlockCond       = 26 // goroutine blocks on Cond [timestamp, stack]
+	EvGoBlockNet        = 27 // goroutine blocks on network [timestamp, stack]
+	EvGoSysCall         = 28 // syscall enter [timestamp, stack]
+	EvGoSysExit         = 29 // syscall exit [timestamp, goroutine id, seq, real timestamp]
+	EvGoSysBlock        = 30 // syscall blocks [timestamp]
+	EvGoWaiting         = 31 // denotes that goroutine is blocked when tracing starts [timestamp, goroutine id]
+	EvGoInSyscall       = 32 // denotes that goroutine is in syscall when tracing starts [timestamp, goroutine id]
+	EvHeapAlloc         = 33 // gcController.heapLive change [timestamp, heap live bytes]
+	EvHeapGoal          = 34 // gcController.heapGoal change [timestamp, heap goal bytes]
+	EvTimerGoroutine    = 35 // denotes timer goroutine [timer goroutine id]
+	EvFutileWakeup      = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp]
+	EvString            = 37 // string dictionary entry [ID, length, string]
+	EvGoStartLocal      = 38 // goroutine starts running on the same P as the last event [timestamp, goroutine id]
+	EvGoUnblockLocal    = 39 // goroutine is unblocked on the same P as the last event [timestamp, goroutine id, stack]
+	EvGoSysExitLocal    = 40 // syscall exit on the same P as the last event [timestamp, goroutine id, real timestamp]
+	EvGoStartLabel      = 41 // goroutine starts running with label [timestamp, goroutine id, seq, label string id]
+	EvGoBlockGC         = 42 // goroutine blocks on GC assist [timestamp, stack]
+	EvGCMarkAssistStart = 43 // GC mark assist start [timestamp, stack]
+	EvGCMarkAssistDone  = 44 // GC mark assist done [timestamp]
+	EvUserTaskCreate    = 45 // trace.NewTask [timestamp, internal task id, internal parent id, name string, stack]
+	EvUserTaskEnd       = 46 // end of task [timestamp, internal task id, stack]
+	EvUserRegion        = 47 // trace.WithRegion [timestamp, internal task id, mode(0:start, 1:end), name string, stack]
+	EvUserLog           = 48 // trace.Log [timestamp, internal id, key string id, stack, value string]
+	EvCPUSample         = 49 // CPU profiling sample [timestamp, real timestamp, real P id (-1 when absent), goroutine id, stack]
+	EvCount             = 50
+)
diff --git a/trace/reader.go b/trace/reader.go
index 315c5b2..eb83539 100644
--- a/trace/reader.go
+++ b/trace/reader.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
@@ -26,6 +26,7 @@
 	lastTs      Time
 	gen         *generation
 	spill       *spilledBatch
+	spillErr    error // error from reading spill
 	frontier    []*batchCursor
 	cpuSamples  []cpuSample
 	order       ordering
@@ -122,6 +123,9 @@
 			r.emittedSync = true
 			return syncEvent(r.gen.evTable, r.lastTs), nil
 		}
+		if r.spillErr != nil {
+			return Event{}, r.spillErr
+		}
 		if r.gen != nil && r.spill == nil {
 			// If we have a generation from the last read,
 			// and there's nothing left in the frontier, and
@@ -131,10 +135,12 @@
 			return Event{}, io.EOF
 		}
 		// Read the next generation.
+		var err error
 		r.gen, r.spill, err = readGeneration(r.r, r.spill)
-		if err != nil {
+		if r.gen == nil {
 			return Event{}, err
 		}
+		r.spillErr = err
 
 		// Reset CPU samples cursor.
 		r.cpuSamples = r.gen.cpuSamples
diff --git a/trace/reader_test.go b/trace/reader_test.go
index 421e044..a33e76a 100644
--- a/trace/reader_test.go
+++ b/trace/reader_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/resources.go b/trace/resources.go
index 2b152eb..f5ebc8e 100644
--- a/trace/resources.go
+++ b/trace/resources.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generate.go b/trace/testdata/generate.go
index 81b64d2..722639a 100644
--- a/trace/testdata/generate.go
+++ b/trace/testdata/generate.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-confuse-seq-across-generations.go b/trace/testdata/generators/go122-confuse-seq-across-generations.go
index 83bb8e6..487fc2d 100644
--- a/trace/testdata/generators/go122-confuse-seq-across-generations.go
+++ b/trace/testdata/generators/go122-confuse-seq-across-generations.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-create-syscall-reuse-thread-id.go b/trace/testdata/generators/go122-create-syscall-reuse-thread-id.go
index 01bee41..fbe7106 100644
--- a/trace/testdata/generators/go122-create-syscall-reuse-thread-id.go
+++ b/trace/testdata/generators/go122-create-syscall-reuse-thread-id.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-create-syscall-with-p.go b/trace/testdata/generators/go122-create-syscall-with-p.go
index 4d4d208..5af3bae 100644
--- a/trace/testdata/generators/go122-create-syscall-with-p.go
+++ b/trace/testdata/generators/go122-create-syscall-with-p.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-fail-first-gen-first.go b/trace/testdata/generators/go122-fail-first-gen-first.go
new file mode 100644
index 0000000..3c56596
--- /dev/null
+++ b/trace/testdata/generators/go122-fail-first-gen-first.go
@@ -0,0 +1,48 @@
+// Copyright 2023 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.
+
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
+
+//go:build go1.21
+
+// Regression test for #55160.
+//
+// The issue is that the parser reads ahead to the first batch of the
+// next generation to find generation boundaries, but if it finds an
+// error, it needs to delay handling that error until later. Previously
+// it would handle that error immediately and a totally valid generation
+// would be skipped for parsing and rejected because of an error in a
+// batch in the following generation.
+//
+// This test captures this behavior by making both the first generation
+// and second generation bad. It requires that the issue in the first
+// generation, which is caught when actually ordering events, be reported
+// instead of the second one.
+
+package main
+
+import (
+	"golang.org/x/exp/trace/internal/event/go122"
+	testgen "golang.org/x/exp/trace/internal/testgen/go122"
+)
+
+func main() {
+	testgen.Main(gen)
+}
+
+func gen(t *testgen.Trace) {
+	// A running goroutine emits a task begin.
+	t.RawEvent(go122.EvEventBatch, nil, 1 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 5 /*batch length*/)
+	t.RawEvent(go122.EvFrequency, nil, 15625000)
+
+	// A running goroutine emits a task begin.
+	t.RawEvent(go122.EvEventBatch, nil, 1 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 5 /*batch length*/)
+	t.RawEvent(go122.EvGoCreate, nil, 0 /*timestamp delta*/, 1 /*go ID*/, 0, 0)
+
+	// Write an invalid batch event for the next generation.
+	t.RawEvent(go122.EvEventBatch, nil, 2 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 50 /*batch length (invalid)*/)
+
+	// We should fail at the first issue, not the second one.
+	t.ExpectFailure("expected a proc but didn't have one")
+}
diff --git a/trace/testdata/generators/go122-go-create-without-running-g.go b/trace/testdata/generators/go122-go-create-without-running-g.go
index 10d0020..8d89f81 100644
--- a/trace/testdata/generators/go122-go-create-without-running-g.go
+++ b/trace/testdata/generators/go122-go-create-without-running-g.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-syscall-steal-proc-ambiguous.go b/trace/testdata/generators/go122-syscall-steal-proc-ambiguous.go
index 9b98749..41978b4 100644
--- a/trace/testdata/generators/go122-syscall-steal-proc-ambiguous.go
+++ b/trace/testdata/generators/go122-syscall-steal-proc-ambiguous.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go b/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go
index 1db4cd9..1795dd4 100644
--- a/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go
+++ b/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go b/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go
index ab0574d..75bb033 100644
--- a/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go
+++ b/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go b/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go
index 74c6c89..9215ee4 100644
--- a/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go
+++ b/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary.go b/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary.go
index deb1170..8134ca6 100644
--- a/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary.go
+++ b/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go b/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go
index 43b6767..1b2e25d 100644
--- a/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go
+++ b/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go b/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go
index 187829e..1ee3c66 100644
--- a/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go
+++ b/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-syscall-steal-proc-self.go b/trace/testdata/generators/go122-syscall-steal-proc-self.go
index 5abbcde..bae0929 100644
--- a/trace/testdata/generators/go122-syscall-steal-proc-self.go
+++ b/trace/testdata/generators/go122-syscall-steal-proc-self.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go b/trace/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go
index 8e4a00b..847c961 100644
--- a/trace/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go
+++ b/trace/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-syscall-steal-proc-simple.go b/trace/testdata/generators/go122-syscall-steal-proc-simple.go
index e2f7a68..78f936f 100644
--- a/trace/testdata/generators/go122-syscall-steal-proc-simple.go
+++ b/trace/testdata/generators/go122-syscall-steal-proc-simple.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go b/trace/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go
index f78120d..61dda38 100644
--- a/trace/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go
+++ b/trace/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/generators/go122-task-across-generations.go b/trace/testdata/generators/go122-task-across-generations.go
index 1be0161..27ce4e4 100644
--- a/trace/testdata/generators/go122-task-across-generations.go
+++ b/trace/testdata/generators/go122-task-across-generations.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21
 
diff --git a/trace/testdata/tests/go122-fail-first-gen-first.test b/trace/testdata/tests/go122-fail-first-gen-first.test
new file mode 100644
index 0000000..cc4240d
--- /dev/null
+++ b/trace/testdata/tests/go122-fail-first-gen-first.test
@@ -0,0 +1,9 @@
+-- expect --
+FAILURE "expected a proc but didn't have one"
+-- trace --
+Trace Go1.22
+EventBatch gen=1 m=0 time=0 size=5
+Frequency freq=15625000
+EventBatch gen=1 m=0 time=0 size=5
+GoCreate dt=0 new_g=1 new_stack=0 stack=0
+EventBatch gen=2 m=0 time=0 size=50
diff --git a/trace/value.go b/trace/value.go
index 629269a..577105d 100644
--- a/trace/value.go
+++ b/trace/value.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT.
+// Code generated by "gen.bash" from internal/trace; DO NOT EDIT.
 
 //go:build go1.21