trace: regenerate experimental API from go@d892cb4

For golang/go#62627.

Change-Id: I4671289210fe99f8c728a8c556e29abd4e45de02
Reviewed-on: https://go-review.googlesource.com/c/exp/+/566076
Reviewed-by: Nicolas Hillegeer <aktau@google.com>
Auto-Submit: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/trace/base.go b/trace/base.go
index 2c4db2f..86585b1 100644
--- a/trace/base.go
+++ b/trace/base.go
@@ -13,6 +13,7 @@
 
 import (
 	"fmt"
+	"math"
 	"strings"
 
 	"golang.org/x/exp/trace/internal/event"
@@ -48,6 +49,7 @@
 	freq    frequency
 	strings dataTable[stringID, string]
 	stacks  dataTable[stackID, stack]
+	pcs     map[uint64]frame
 
 	// extraStrings are strings that get generated during
 	// parsing but haven't come directly from the trace, so
@@ -127,8 +129,12 @@
 			minID = id
 		}
 	}
+	if maxID >= math.MaxInt {
+		// We can't create a slice big enough to hold maxID elements
+		return
+	}
 	// We're willing to waste at most 2x memory.
-	if int(maxID-minID) > 2*len(d.sparse) {
+	if int(maxID-minID) > max(len(d.sparse), 2*len(d.sparse)) {
 		return
 	}
 	if int(minID) > len(d.sparse) {
@@ -150,7 +156,7 @@
 	if id == 0 {
 		return *new(E), true
 	}
-	if int(id) < len(d.dense) {
+	if uint64(id) < uint64(len(d.dense)) {
 		if d.present[id/8]&(uint8(1)<<(id%8)) != 0 {
 			return d.dense[id], true
 		}
@@ -240,12 +246,12 @@
 
 // stack represents a goroutine stack sample.
 type stack struct {
-	frames []frame
+	pcs []uint64
 }
 
 func (s stack) String() string {
 	var sb strings.Builder
-	for _, frame := range s.frames {
+	for _, frame := range s.pcs {
 		fmt.Fprintf(&sb, "\t%#v\n", frame)
 	}
 	return sb.String()
diff --git a/trace/batch.go b/trace/batch.go
index 9fafac9..0bde22d 100644
--- a/trace/batch.go
+++ b/trace/batch.go
@@ -9,7 +9,6 @@
 package trace
 
 import (
-	"bufio"
 	"bytes"
 	"encoding/binary"
 	"fmt"
@@ -47,7 +46,10 @@
 }
 
 // readBatch reads the next full batch from r.
-func readBatch(r *bufio.Reader) (batch, uint64, error) {
+func readBatch(r interface {
+	io.Reader
+	io.ByteReader
+}) (batch, uint64, error) {
 	// Read batch header byte.
 	b, err := r.ReadByte()
 	if err != nil {
diff --git a/trace/batchcursor.go b/trace/batchcursor.go
index f8fc3fd..9582468 100644
--- a/trace/batchcursor.go
+++ b/trace/batchcursor.go
@@ -72,7 +72,7 @@
 	// Get the event type.
 	typ := event.Type(b[0])
 	specs := go122.Specs()
-	if int(typ) > len(specs) {
+	if int(typ) >= len(specs) {
 		return 0, 0, fmt.Errorf("found invalid event type: %v", typ)
 	}
 	e.typ = typ
@@ -86,11 +86,17 @@
 
 	// Read timestamp diff.
 	ts, nb := binary.Uvarint(b[n:])
+	if nb <= 0 {
+		return 0, 0, fmt.Errorf("found invalid uvarint for timestamp")
+	}
 	n += nb
 
 	// Read the rest of the arguments.
 	for i := 0; i < len(spec.Args)-1; i++ {
 		arg, nb := binary.Uvarint(b[n:])
+		if nb <= 0 {
+			return 0, 0, fmt.Errorf("found invalid uvarint")
+		}
 		e.args[i] = arg
 		n += nb
 	}
diff --git a/trace/event.go b/trace/event.go
index a9d5947..a73e624 100644
--- a/trace/event.go
+++ b/trace/event.go
@@ -268,7 +268,8 @@
 		return true
 	}
 	stk := s.table.stacks.mustGet(s.id)
-	for _, f := range stk.frames {
+	for _, pc := range stk.pcs {
+		f := s.table.pcs[pc]
 		sf := StackFrame{
 			PC:   f.pc,
 			Func: s.table.strings.mustGet(f.funcID),
diff --git a/trace/generation.go b/trace/generation.go
index 9364b43..28ec773 100644
--- a/trace/generation.go
+++ b/trace/generation.go
@@ -47,7 +47,9 @@
 // batch read of the next generation, if any.
 func readGeneration(r *bufio.Reader, spill *spilledBatch) (*generation, *spilledBatch, error) {
 	g := &generation{
-		evTable: new(evTable),
+		evTable: &evTable{
+			pcs: make(map[uint64]frame),
+		},
 		batches: make(map[ThreadID][]batch),
 	}
 	// Process the spilled batch.
@@ -110,7 +112,7 @@
 	g.strings.compactify()
 
 	// Validate stacks.
-	if err := validateStackStrings(&g.stacks, &g.strings); err != nil {
+	if err := validateStackStrings(&g.stacks, &g.strings, g.pcs); err != nil {
 		return nil, nil, err
 	}
 
@@ -134,7 +136,7 @@
 			return err
 		}
 	case b.isStacksBatch():
-		if err := addStacks(&g.stacks, b); err != nil {
+		if err := addStacks(&g.stacks, g.pcs, b); err != nil {
 			return err
 		}
 	case b.isCPUSamplesBatch():
@@ -160,11 +162,20 @@
 
 // validateStackStrings makes sure all the string references in
 // the stack table are present in the string table.
-func validateStackStrings(stacks *dataTable[stackID, stack], strings *dataTable[stringID, string]) error {
+func validateStackStrings(
+	stacks *dataTable[stackID, stack],
+	strings *dataTable[stringID, string],
+	frames map[uint64]frame,
+) error {
 	var err error
 	stacks.forEach(func(id stackID, stk stack) bool {
-		for _, frame := range stk.frames {
-			_, ok := strings.get(frame.funcID)
+		for _, pc := range stk.pcs {
+			frame, ok := frames[pc]
+			if !ok {
+				err = fmt.Errorf("found unknown pc %x for stack %d", pc, id)
+				return false
+			}
+			_, ok = strings.get(frame.funcID)
 			if !ok {
 				err = fmt.Errorf("found invalid func string ID %d for stack %d", frame.funcID, id)
 				return false
@@ -241,7 +252,7 @@
 // addStacks takes a batch whose first byte is an EvStacks event
 // (indicating that the batch contains only stacks) and adds each
 // string contained therein to the provided stacks map.
-func addStacks(stackTable *dataTable[stackID, stack], b batch) error {
+func addStacks(stackTable *dataTable[stackID, stack], pcs map[uint64]frame, b batch) error {
 	if !b.isStacksBatch() {
 		return fmt.Errorf("internal error: addStacks called on non-stacks batch")
 	}
@@ -277,7 +288,7 @@
 		}
 
 		// Each frame consists of 4 fields: pc, funcID (string), fileID (string), line.
-		frames := make([]frame, 0, nFrames)
+		frames := make([]uint64, 0, nFrames)
 		for i := uint64(0); i < nFrames; i++ {
 			// Read the frame data.
 			pc, err := binary.ReadUvarint(r)
@@ -296,16 +307,20 @@
 			if err != nil {
 				return fmt.Errorf("reading frame %d's line for stack %d: %w", i+1, id, err)
 			}
-			frames = append(frames, frame{
-				pc:     pc,
-				funcID: stringID(funcID),
-				fileID: stringID(fileID),
-				line:   line,
-			})
+			frames = append(frames, pc)
+
+			if _, ok := pcs[pc]; !ok {
+				pcs[pc] = frame{
+					pc:     pc,
+					funcID: stringID(funcID),
+					fileID: stringID(fileID),
+					line:   line,
+				}
+			}
 		}
 
 		// Add the stack to the map.
-		if err := stackTable.insert(stackID(id), stack{frames: frames}); err != nil {
+		if err := stackTable.insert(stackID(id), stack{pcs: frames}); err != nil {
 			return err
 		}
 	}
diff --git a/trace/order.go b/trace/order.go
index c89e5da..e0f77c6 100644
--- a/trace/order.go
+++ b/trace/order.go
@@ -96,6 +96,9 @@
 	case go122.EvProcStatus:
 		pid := ProcID(ev.args[0])
 		status := go122.ProcStatus(ev.args[1])
+		if int(status) >= len(go122ProcStatus2ProcState) {
+			return curCtx, false, fmt.Errorf("invalid status for proc %d: %d", pid, status)
+		}
 		oldState := go122ProcStatus2ProcState[status]
 		if s, ok := o.pStates[pid]; ok {
 			if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscall {
@@ -272,6 +275,10 @@
 		gid := GoID(ev.args[0])
 		mid := ThreadID(ev.args[1])
 		status := go122.GoStatus(ev.args[2])
+
+		if int(status) >= len(go122GoStatus2GoState) {
+			return curCtx, false, fmt.Errorf("invalid status for goroutine %d: %d", gid, status)
+		}
 		oldState := go122GoStatus2GoState[status]
 		if s, ok := o.gStates[gid]; ok {
 			if s.status != status {
@@ -299,6 +306,13 @@
 			// Otherwise, we're talking about a G sitting in a syscall on an M.
 			// Validate the named M.
 			if mid == curCtx.M {
+				if gen != o.initialGen && curCtx.G != gid {
+					// If this isn't the first generation, we *must* have seen this
+					// binding occur already. Even if the G was blocked in a syscall
+					// for multiple generations since trace start, we would have seen
+					// a previous GoStatus event that bound the goroutine to an M.
+					return curCtx, false, fmt.Errorf("inconsistent thread for syscalling goroutine %d: thread has goroutine %d", gid, curCtx.G)
+				}
 				newCtx.G = gid
 				break
 			}
@@ -646,7 +660,11 @@
 		if !ok {
 			return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, typ)
 		}
-		if err := o.gStates[curCtx.G].beginRegion(userRegion{tid, name}); err != nil {
+		gState, ok := o.gStates[curCtx.G]
+		if !ok {
+			return curCtx, false, fmt.Errorf("encountered EvUserRegionBegin without known state for current goroutine %d", curCtx.G)
+		}
+		if err := gState.beginRegion(userRegion{tid, name}); err != nil {
 			return curCtx, false, err
 		}
 		return curCtx, true, nil
@@ -660,7 +678,11 @@
 		if !ok {
 			return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, typ)
 		}
-		if err := o.gStates[curCtx.G].endRegion(userRegion{tid, name}); err != nil {
+		gState, ok := o.gStates[curCtx.G]
+		if !ok {
+			return curCtx, false, fmt.Errorf("encountered EvUserRegionEnd without known state for current goroutine %d", curCtx.G)
+		}
+		if err := gState.endRegion(userRegion{tid, name}); err != nil {
 			return curCtx, false, err
 		}
 		return curCtx, true, nil
@@ -762,7 +784,11 @@
 		// ever reference curCtx.P. However, be lenient about this like we are with
 		// GCMarkAssistActive; there's no reason the runtime couldn't change to block
 		// in the middle of a sweep.
-		if err := o.pStates[pid].activeRange(makeRangeType(typ, 0), gen == o.initialGen); err != nil {
+		pState, ok := o.pStates[pid]
+		if !ok {
+			return curCtx, false, fmt.Errorf("encountered GCSweepActive for unknown proc %d", pid)
+		}
+		if err := pState.activeRange(makeRangeType(typ, 0), gen == o.initialGen); err != nil {
 			return curCtx, false, err
 		}
 		return curCtx, true, nil
@@ -785,7 +811,11 @@
 		if typ == go122.EvSTWBegin {
 			desc = stringID(ev.args[0])
 		}
-		if err := o.gStates[curCtx.G].beginRange(makeRangeType(typ, desc)); err != nil {
+		gState, ok := o.gStates[curCtx.G]
+		if !ok {
+			return curCtx, false, fmt.Errorf("encountered event of type %d without known state for current goroutine %d", typ, curCtx.G)
+		}
+		if err := gState.beginRange(makeRangeType(typ, desc)); err != nil {
 			return curCtx, false, err
 		}
 		return curCtx, true, nil
@@ -794,7 +824,11 @@
 		// N.B. Like GoStatus, this can happen at any time, because it can
 		// reference a non-running goroutine. Don't check anything about the
 		// current scheduler context.
-		if err := o.gStates[gid].activeRange(makeRangeType(typ, 0), gen == o.initialGen); err != nil {
+		gState, ok := o.gStates[gid]
+		if !ok {
+			return curCtx, false, fmt.Errorf("uninitialized goroutine %d found during %s", gid, go122.EventString(typ))
+		}
+		if err := gState.activeRange(makeRangeType(typ, 0), gen == o.initialGen); err != nil {
 			return curCtx, false, err
 		}
 		return curCtx, true, nil
@@ -802,7 +836,11 @@
 		if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
 			return curCtx, false, err
 		}
-		desc, err := o.gStates[curCtx.G].endRange(typ)
+		gState, ok := o.gStates[curCtx.G]
+		if !ok {
+			return curCtx, false, fmt.Errorf("encountered event of type %d without known state for current goroutine %d", typ, curCtx.G)
+		}
+		desc, err := gState.endRange(typ)
 		if err != nil {
 			return curCtx, false, err
 		}
@@ -921,6 +959,10 @@
 
 // endRegion ends a user region on the goroutine.
 func (s *gState) endRegion(r userRegion) error {
+	if len(s.regions) == 0 {
+		// We do not know about regions that began before tracing started.
+		return nil
+	}
 	if next := s.regions[len(s.regions)-1]; next != r {
 		return fmt.Errorf("misuse of region in goroutine %v: region end %v when the inner-most active region start event is %v", s.id, r, next)
 	}
diff --git a/trace/reader.go b/trace/reader.go
index c561d58..d7311c9 100644
--- a/trace/reader.go
+++ b/trace/reader.go
@@ -161,6 +161,9 @@
 	}
 	// Try to advance the head of the frontier, which should have the minimum timestamp.
 	// This should be by far the most common case
+	if len(r.frontier) == 0 {
+		return Event{}, fmt.Errorf("broken trace: frontier is empty:\n[gen=%d]\n\n%s\n%s\n", r.gen.gen, dumpFrontier(r.frontier), dumpOrdering(&r.order))
+	}
 	bc := r.frontier[0]
 	if ctx, ok, err := r.order.advance(&bc.ev, r.gen.evTable, bc.m, r.gen.gen); err != nil {
 		return Event{}, err
diff --git a/trace/reader_test.go b/trace/reader_test.go
index 84d04a6..401441c 100644
--- a/trace/reader_test.go
+++ b/trace/reader_test.go
@@ -50,6 +50,53 @@
 	}
 }
 
+func FuzzReader(f *testing.F) {
+	// Currently disabled because the parser doesn't do much validation and most
+	// getters can be made to panic. Turn this on once the parser is meant to
+	// reject invalid traces.
+	const testGetters = false
+
+	f.Fuzz(func(t *testing.T, b []byte) {
+		r, err := trace.NewReader(bytes.NewReader(b))
+		if err != nil {
+			return
+		}
+		for {
+			ev, err := r.ReadEvent()
+			if err != nil {
+				break
+			}
+
+			if !testGetters {
+				continue
+			}
+			// Make sure getters don't do anything that panics
+			switch ev.Kind() {
+			case trace.EventLabel:
+				ev.Label()
+			case trace.EventLog:
+				ev.Log()
+			case trace.EventMetric:
+				ev.Metric()
+			case trace.EventRangeActive, trace.EventRangeBegin:
+				ev.Range()
+			case trace.EventRangeEnd:
+				ev.Range()
+				ev.RangeAttributes()
+			case trace.EventStateTransition:
+				ev.StateTransition()
+			case trace.EventRegionBegin, trace.EventRegionEnd:
+				ev.Region()
+			case trace.EventTaskBegin, trace.EventTaskEnd:
+				ev.Task()
+			case trace.EventSync:
+			case trace.EventStackSample:
+			case trace.EventBad:
+			}
+		}
+	})
+}
+
 func testReader(t *testing.T, tr io.Reader, exp *testtrace.Expectation) {
 	r, err := trace.NewReader(tr)
 	if err != nil {
diff --git a/trace/testdata/fuzz/FuzzReader/0cb1786dee0f090b b/trace/testdata/fuzz/FuzzReader/0cb1786dee0f090b
new file mode 100644
index 0000000..326ebe1
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/0cb1786dee0f090b
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x190000\x01\x0100\x88\x00\b0000000")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/1e45307d5b2ec36d b/trace/testdata/fuzz/FuzzReader/1e45307d5b2ec36d
new file mode 100644
index 0000000..406af9c
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/1e45307d5b2ec36d
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01000\x85\x00\b0001")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/2b05796f9b2fc48d b/trace/testdata/fuzz/FuzzReader/2b05796f9b2fc48d
new file mode 100644
index 0000000..50fdccd
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/2b05796f9b2fc48d
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00-0000\x01\x0100\x88\x00\b0000000")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/2b9be9aebe08d511 b/trace/testdata/fuzz/FuzzReader/2b9be9aebe08d511
new file mode 100644
index 0000000..6bcb99a
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/2b9be9aebe08d511
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x0f00\x120\x01\x0100\x88\x00\b0000000")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/344331b314da0b08 b/trace/testdata/fuzz/FuzzReader/344331b314da0b08
new file mode 100644
index 0000000..de6e469
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/344331b314da0b08
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\b0000\x01\x01\xff00\xb8\x00\x1900\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x04\x1900\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x04\x1900\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x04\x1901\xff\xff\xff\xff\xff\xff\xff\xff0\x800")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/365d7b5b633b3f97 b/trace/testdata/fuzz/FuzzReader/365d7b5b633b3f97
new file mode 100644
index 0000000..8dc370f
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/365d7b5b633b3f97
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x0100\x8c0\x85\x00\b0000")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/4d9ddc909984e871 b/trace/testdata/fuzz/FuzzReader/4d9ddc909984e871
new file mode 100644
index 0000000..040b2a4
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/4d9ddc909984e871
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x11\r\xa700\x01\x19000\x02$000000\x01\x0100\x05\b0000\x01\x0110\x11\r\xa700\x01\x19 00\x02\x110 0000")
diff --git a/trace/testdata/fuzz/FuzzReader/56f073e57903588c b/trace/testdata/fuzz/FuzzReader/56f073e57903588c
new file mode 100644
index 0000000..d34fe3f
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/56f073e57903588c
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\x1f0000\x01\x0100\x88\x00\b0000000")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566 b/trace/testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566
new file mode 100644
index 0000000..5677261
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/9d6ee7d3ddf8d566
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x11\r\xa700\x01\x19000\x02#000000\x01\x0100\x05\b0000\x01\x0110\x11\r\xa700\x01\x19 00\x02\x110 0000")
diff --git a/trace/testdata/fuzz/FuzzReader/aeb749b6bc317b66 b/trace/testdata/fuzz/FuzzReader/aeb749b6bc317b66
new file mode 100644
index 0000000..f93b5a9
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/aeb749b6bc317b66
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01000\x85\x00\b0000")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/closing-unknown-region b/trace/testdata/fuzz/FuzzReader/closing-unknown-region
new file mode 100644
index 0000000..7433214
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/closing-unknown-region
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xf6\x9f\n\x9fÕ\xb4\x99\xb2\x06\x11\r\xa7\x02\x00\x01\x19\x05\x01\xf6\x9f\n\x02+\x04\x01\x00\x00")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/d478e18d2d6756b7 b/trace/testdata/fuzz/FuzzReader/d478e18d2d6756b7
new file mode 100644
index 0000000..3e5fda8
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/d478e18d2d6756b7
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x0100\x85\x00\"0000\x01\x0100\x88\x00\b0000000")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/d91203cd397aa0bc b/trace/testdata/fuzz/FuzzReader/d91203cd397aa0bc
new file mode 100644
index 0000000..d24b94a
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/d91203cd397aa0bc
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01001\x85\x00\b0000")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/invalid-proc-state b/trace/testdata/fuzz/FuzzReader/invalid-proc-state
new file mode 100644
index 0000000..e5d3258
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/invalid-proc-state
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x94镴\x99\xb2\x06\x05\r\xa7\x02\x00E")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/large-id b/trace/testdata/fuzz/FuzzReader/large-id
new file mode 100644
index 0000000..0fb6273
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/large-id
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x94镴\x99\xb2\x06\f\x02\x03\xff\xff\xff\xff\xff\xff\xff\x9f\x1d\x00")
\ No newline at end of file
diff --git a/trace/testdata/fuzz/FuzzReader/malformed-timestamp b/trace/testdata/fuzz/FuzzReader/malformed-timestamp
new file mode 100644
index 0000000..850ca50
--- /dev/null
+++ b/trace/testdata/fuzz/FuzzReader/malformed-timestamp
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("go 1.22 trace\x00\x00\x00\x01\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x87ߕ\xb4\x99\xb2\x06\x05\b\xa8ֹ\a\x01\x01\xfa\x9f\n\xa5ѕ\xb4\x99\xb2\x06\x0e\n\x97\x96\x96\x96\x96\x96\x96\x96\x96\x96\x01\x01\x01")
diff --git a/trace/testdata/tests/go122-annotations-stress.test b/trace/testdata/tests/go122-annotations-stress.test
index fe3c84b..8da8c0f 100644
--- a/trace/testdata/tests/go122-annotations-stress.test
+++ b/trace/testdata/tests/go122-annotations-stress.test
@@ -896,7 +896,7 @@
 String id=19
 	data="sleep"
 String id=20
-	data="runtime.GoSched"
+	data="runtime.Gosched"
 String id=21
 	data="start trace"
 String id=22
diff --git a/trace/testdata/tests/go122-annotations.test b/trace/testdata/tests/go122-annotations.test
index 4749d82..e468673 100644
--- a/trace/testdata/tests/go122-annotations.test
+++ b/trace/testdata/tests/go122-annotations.test
@@ -220,7 +220,7 @@
 String id=19
 	data="sleep"
 String id=20
-	data="runtime.GoSched"
+	data="runtime.Gosched"
 String id=21
 	data="start trace"
 String id=22
diff --git a/trace/testdata/tests/go122-gc-stress.test b/trace/testdata/tests/go122-gc-stress.test
index 8d77fe1..d5e7266 100644
--- a/trace/testdata/tests/go122-gc-stress.test
+++ b/trace/testdata/tests/go122-gc-stress.test
@@ -4086,7 +4086,7 @@
 String id=19
 	data="sleep"
 String id=20
-	data="runtime.GoSched"
+	data="runtime.Gosched"
 String id=21
 	data="GC mark termination"
 String id=22