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