diff options
| author | Michael Pratt <mpratt@google.com> | 2024-03-04 13:29:39 -0500 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2024-03-27 20:20:01 +0000 |
| commit | 63deaf00ea6058d1422f0b435e475666cba5743e (patch) | |
| tree | 20ea11d36b1c627fd99993ea6eaff8835e698ffc /src/cmd/internal | |
| parent | 2860e01853174e278900ef6907b1941b16fb1645 (diff) | |
| download | go-63deaf00ea6058d1422f0b435e475666cba5743e.tar.xz | |
cmd/compile,cmd/preprofile: move logic to shared common package
The processing performed in cmd/preprofile is a simple version of the
same initial processing performed by cmd/compile/internal/pgo. Refactor
this processing into the new IR-independent cmd/internal/pgo package.
Now cmd/preprofile and cmd/compile run the same code for initial
processing of a pprof profile, guaranteeing that they always stay in
sync.
Since it is now trivial, this CL makes one change to the serialization
format: the entries are ordered by weight. This allows us to avoid
sorting ByWeight on deserialization.
Impact on PGO parsing when compiling cmd/compile with PGO:
* Without preprocessing: PGO parsing ~13.7% of CPU time
* With preprocessing (unsorted): ~2.9% of CPU time (sorting ~1.7%)
* With preprocessing (sorted): ~1.3% of CPU time
The remaining 1.3% of CPU time approximately breaks down as:
* ~0.5% parsing the preprocessed profile
* ~0.7% building weighted IR call graph
* ~0.5% walking function IR to find direct calls
* ~0.2% performing lookups for indirect calls targets
For #58102.
Change-Id: Iaba425ea30b063ca195fb2f7b29342961c8a64c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/569337
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Michael Pratt <mpratt@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Diffstat (limited to 'src/cmd/internal')
| -rw-r--r-- | src/cmd/internal/pgo/deserialize.go | 102 | ||||
| -rw-r--r-- | src/cmd/internal/pgo/pgo.go | 55 | ||||
| -rw-r--r-- | src/cmd/internal/pgo/pprof.go | 140 | ||||
| -rw-r--r-- | src/cmd/internal/pgo/serialize.go | 79 | ||||
| -rw-r--r-- | src/cmd/internal/pgo/serialize_test.go | 190 | ||||
| -rw-r--r-- | src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/12fcf136fcb7463c | 2 | ||||
| -rw-r--r-- | src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/2055d314024c8d6c | 2 | ||||
| -rw-r--r-- | src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/b615162315f7b72c | 2 | ||||
| -rw-r--r-- | src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/fdc60117b431bbae | 2 |
9 files changed, 574 insertions, 0 deletions
diff --git a/src/cmd/internal/pgo/deserialize.go b/src/cmd/internal/pgo/deserialize.go new file mode 100644 index 0000000000..4b075b8daf --- /dev/null +++ b/src/cmd/internal/pgo/deserialize.go @@ -0,0 +1,102 @@ +// Copyright 2024 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. + +package pgo + +import ( + "bufio" + "fmt" + "io" + "strings" + "strconv" +) + +// IsSerialized returns true if r is a serialized Profile. +// +// IsSerialized only peeks at r, so seeking back after calling is not +// necessary. +func IsSerialized(r *bufio.Reader) (bool, error) { + hdr, err := r.Peek(len(serializationHeader)) + if err == io.EOF { + // Empty file. + return false, nil + } else if err != nil { + return false, fmt.Errorf("error reading profile header: %w", err) + } + + return string(hdr) == serializationHeader, nil +} + +// FromSerialized parses a profile from serialization output of Profile.WriteTo. +func FromSerialized(r io.Reader) (*Profile, error) { + d := emptyProfile() + + scanner := bufio.NewScanner(r) + scanner.Split(bufio.ScanLines) + + if !scanner.Scan() { + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error reading preprocessed profile: %w", err) + } + return nil, fmt.Errorf("preprocessed profile missing header") + } + if gotHdr := scanner.Text() + "\n"; gotHdr != serializationHeader { + return nil, fmt.Errorf("preprocessed profile malformed header; got %q want %q", gotHdr, serializationHeader) + } + + for scanner.Scan() { + readStr := scanner.Text() + + callerName := readStr + + if !scanner.Scan() { + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error reading preprocessed profile: %w", err) + } + return nil, fmt.Errorf("preprocessed profile entry missing callee") + } + calleeName := scanner.Text() + + if !scanner.Scan() { + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error reading preprocessed profile: %w", err) + } + return nil, fmt.Errorf("preprocessed profile entry missing weight") + } + readStr = scanner.Text() + + split := strings.Split(readStr, " ") + + if len(split) != 2 { + return nil, fmt.Errorf("preprocessed profile entry got %v want 2 fields", split) + } + + co, err := strconv.Atoi(split[0]) + if err != nil { + return nil, fmt.Errorf("preprocessed profile error processing call line: %w", err) + } + + edge := NamedCallEdge{ + CallerName: callerName, + CalleeName: calleeName, + CallSiteOffset: co, + } + + weight, err := strconv.ParseInt(split[1], 10, 64) + if err != nil { + return nil, fmt.Errorf("preprocessed profile error processing call weight: %w", err) + } + + if _, ok := d.NamedEdgeMap.Weight[edge]; ok { + return nil, fmt.Errorf("preprocessed profile contains duplicate edge %+v", edge) + } + + d.NamedEdgeMap.ByWeight = append(d.NamedEdgeMap.ByWeight, edge) // N.B. serialization is ordered. + d.NamedEdgeMap.Weight[edge] += weight + d.TotalWeight += weight + } + + return d, nil + +} diff --git a/src/cmd/internal/pgo/pgo.go b/src/cmd/internal/pgo/pgo.go new file mode 100644 index 0000000000..1d2cb880f7 --- /dev/null +++ b/src/cmd/internal/pgo/pgo.go @@ -0,0 +1,55 @@ +// Copyright 2024 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. + +// Package pgo contains the compiler-agnostic portions of PGO profile handling. +// Notably, parsing pprof profiles and serializing/deserializing from a custom +// intermediate representation. +package pgo + +// Profile contains the processed data from the PGO profile. +type Profile struct { + // TotalWeight is the aggregated edge weights across the profile. This + // helps us determine the percentage threshold for hot/cold + // partitioning. + TotalWeight int64 + + // NamedEdgeMap contains all unique call edges in the profile and their + // edge weight. + NamedEdgeMap NamedEdgeMap +} + +// NamedCallEdge identifies a call edge by linker symbol names and call site +// offset. +type NamedCallEdge struct { + CallerName string + CalleeName string + CallSiteOffset int // Line offset from function start line. +} + +// NamedEdgeMap contains all unique call edges in the profile and their +// edge weight. +type NamedEdgeMap struct { + Weight map[NamedCallEdge]int64 + + // ByWeight lists all keys in Weight, sorted by edge weight from + // highest to lowest. + ByWeight []NamedCallEdge +} + +func emptyProfile() *Profile { + // Initialize empty maps/slices for easier use without a requiring a + // nil check. + return &Profile{ + NamedEdgeMap: NamedEdgeMap{ + ByWeight: make([]NamedCallEdge, 0), + Weight: make(map[NamedCallEdge]int64), + }, + } +} + +// WeightInPercentage converts profile weights to a percentage. +func WeightInPercentage(value int64, total int64) float64 { + return (float64(value) / float64(total)) * 100 +} + diff --git a/src/cmd/internal/pgo/pprof.go b/src/cmd/internal/pgo/pprof.go new file mode 100644 index 0000000000..5e61a11141 --- /dev/null +++ b/src/cmd/internal/pgo/pprof.go @@ -0,0 +1,140 @@ +// Copyright 2024 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. + +// Package pgo contains the compiler-agnostic portions of PGO profile handling. +// Notably, parsing pprof profiles and serializing/deserializing from a custom +// intermediate representation. +package pgo + +import ( + "errors" + "fmt" + "internal/profile" + "io" + "sort" +) + +// FromPProf parses Profile from a pprof profile. +func FromPProf(r io.Reader) (*Profile, error) { + p, err := profile.Parse(r) + if errors.Is(err, profile.ErrNoData) { + // Treat a completely empty file the same as a profile with no + // samples: nothing to do. + return emptyProfile(), nil + } else if err != nil { + return nil, fmt.Errorf("error parsing profile: %w", err) + } + + if len(p.Sample) == 0 { + // We accept empty profiles, but there is nothing to do. + return emptyProfile(), nil + } + + valueIndex := -1 + for i, s := range p.SampleType { + // Samples count is the raw data collected, and CPU nanoseconds is just + // a scaled version of it, so either one we can find is fine. + if (s.Type == "samples" && s.Unit == "count") || + (s.Type == "cpu" && s.Unit == "nanoseconds") { + valueIndex = i + break + } + } + + if valueIndex == -1 { + return nil, fmt.Errorf(`profile does not contain a sample index with value/type "samples/count" or cpu/nanoseconds"`) + } + + g := profile.NewGraph(p, &profile.Options{ + SampleValue: func(v []int64) int64 { return v[valueIndex] }, + }) + + namedEdgeMap, totalWeight, err := createNamedEdgeMap(g) + if err != nil { + return nil, err + } + + if totalWeight == 0 { + return emptyProfile(), nil // accept but ignore profile with no samples. + } + + return &Profile{ + TotalWeight: totalWeight, + NamedEdgeMap: namedEdgeMap, + }, nil +} + +// createNamedEdgeMap builds a map of callsite-callee edge weights from the +// profile-graph. +// +// Caller should ignore the profile if totalWeight == 0. +func createNamedEdgeMap(g *profile.Graph) (edgeMap NamedEdgeMap, totalWeight int64, err error) { + seenStartLine := false + + // Process graph and build various node and edge maps which will + // be consumed by AST walk. + weight := make(map[NamedCallEdge]int64) + for _, n := range g.Nodes { + seenStartLine = seenStartLine || n.Info.StartLine != 0 + + canonicalName := n.Info.Name + // Create the key to the nodeMapKey. + namedEdge := NamedCallEdge{ + CallerName: canonicalName, + CallSiteOffset: n.Info.Lineno - n.Info.StartLine, + } + + for _, e := range n.Out { + totalWeight += e.WeightValue() + namedEdge.CalleeName = e.Dest.Info.Name + // Create new entry or increment existing entry. + weight[namedEdge] += e.WeightValue() + } + } + + if !seenStartLine { + // TODO(prattmic): If Function.start_line is missing we could + // fall back to using absolute line numbers, which is better + // than nothing. + return NamedEdgeMap{}, 0, fmt.Errorf("profile missing Function.start_line data (Go version of profiled application too old? Go 1.20+ automatically adds this to profiles)") + } + return postProcessNamedEdgeMap(weight, totalWeight) +} + +func sortByWeight(edges []NamedCallEdge, weight map[NamedCallEdge]int64) { + sort.Slice(edges, func(i, j int) bool { + ei, ej := edges[i], edges[j] + if wi, wj := weight[ei], weight[ej]; wi != wj { + return wi > wj // want larger weight first + } + // same weight, order by name/line number + if ei.CallerName != ej.CallerName { + return ei.CallerName < ej.CallerName + } + if ei.CalleeName != ej.CalleeName { + return ei.CalleeName < ej.CalleeName + } + return ei.CallSiteOffset < ej.CallSiteOffset + }) +} + +func postProcessNamedEdgeMap(weight map[NamedCallEdge]int64, weightVal int64) (edgeMap NamedEdgeMap, totalWeight int64, err error) { + if weightVal == 0 { + return NamedEdgeMap{}, 0, nil // accept but ignore profile with no samples. + } + byWeight := make([]NamedCallEdge, 0, len(weight)) + for namedEdge := range weight { + byWeight = append(byWeight, namedEdge) + } + sortByWeight(byWeight, weight) + + edgeMap = NamedEdgeMap{ + Weight: weight, + ByWeight: byWeight, + } + + totalWeight = weightVal + + return edgeMap, totalWeight, nil +} diff --git a/src/cmd/internal/pgo/serialize.go b/src/cmd/internal/pgo/serialize.go new file mode 100644 index 0000000000..caf67ce485 --- /dev/null +++ b/src/cmd/internal/pgo/serialize.go @@ -0,0 +1,79 @@ +// Copyright 2024 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. + +package pgo + +import ( + "bufio" + "fmt" + "io" +) + +// Serialization of a Profile allows go tool preprofile to construct the edge +// map only once (rather than once per compile process). The compiler processes +// then parse the pre-processed data directly from the serialized format. +// +// The format of the serialized output is as follows. +// +// GO PREPROFILE V1 +// caller_name +// callee_name +// "call site offset" "call edge weight" +// ... +// caller_name +// callee_name +// "call site offset" "call edge weight" +// +// Entries are sorted by "call edge weight", from highest to lowest. + +const serializationHeader = "GO PREPROFILE V1\n" + +// WriteTo writes a serialized representation of Profile to w. +// +// FromSerialized can parse the format back to Profile. +// +// WriteTo implements io.WriterTo.Write. +func (d *Profile) WriteTo(w io.Writer) (int64, error) { + bw := bufio.NewWriter(w) + + var written int64 + + // Header + n, err := bw.WriteString(serializationHeader) + written += int64(n) + if err != nil { + return written, err + } + + for _, edge := range d.NamedEdgeMap.ByWeight { + weight := d.NamedEdgeMap.Weight[edge] + + n, err = fmt.Fprintln(bw, edge.CallerName) + written += int64(n) + if err != nil { + return written, err + } + + n, err = fmt.Fprintln(bw, edge.CalleeName) + written += int64(n) + if err != nil { + return written, err + } + + n, err = fmt.Fprintf(bw, "%d %d\n", edge.CallSiteOffset, weight) + written += int64(n) + if err != nil { + return written, err + } + } + + if err := bw.Flush(); err != nil { + return written, err + } + + // No need to serialize TotalWeight, it can be trivially recomputed + // during parsing. + + return written, nil +} diff --git a/src/cmd/internal/pgo/serialize_test.go b/src/cmd/internal/pgo/serialize_test.go new file mode 100644 index 0000000000..b24163d1e2 --- /dev/null +++ b/src/cmd/internal/pgo/serialize_test.go @@ -0,0 +1,190 @@ +// Copyright 2024 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. + +package pgo + +import ( + "bytes" + "encoding/binary" + "fmt" + "reflect" + "strings" + "testing" +) + +// equal returns an error if got and want are not equal. +func equal(got, want *Profile) error { + if got.TotalWeight != want.TotalWeight { + return fmt.Errorf("got.TotalWeight %d != want.TotalWeight %d", got.TotalWeight, want.TotalWeight) + } + if !reflect.DeepEqual(got.NamedEdgeMap.ByWeight, want.NamedEdgeMap.ByWeight) { + return fmt.Errorf("got.NamedEdgeMap.ByWeight != want.NamedEdgeMap.ByWeight\ngot = %+v\nwant = %+v", got.NamedEdgeMap.ByWeight, want.NamedEdgeMap.ByWeight) + } + if !reflect.DeepEqual(got.NamedEdgeMap.Weight, want.NamedEdgeMap.Weight) { + return fmt.Errorf("got.NamedEdgeMap.Weight != want.NamedEdgeMap.Weight\ngot = %+v\nwant = %+v", got.NamedEdgeMap.Weight, want.NamedEdgeMap.Weight) + } + + return nil +} + +func testRoundTrip(t *testing.T, d *Profile) []byte { + var buf bytes.Buffer + n, err := d.WriteTo(&buf) + if err != nil { + t.Fatalf("WriteTo got err %v want nil", err) + } + if n != int64(buf.Len()) { + t.Errorf("WriteTo got n %d want %d", n, int64(buf.Len())) + } + + b := buf.Bytes() + + got, err := FromSerialized(&buf) + if err != nil { + t.Fatalf("processSerialized got err %v want nil", err) + } + if err := equal(got, d); err != nil { + t.Errorf("processSerialized output does not match input: %v", err) + } + + return b +} + +func TestEmpty(t *testing.T) { + d := emptyProfile() + b := testRoundTrip(t, d) + + // Contents should consist of only a header. + if string(b) != serializationHeader { + t.Errorf("WriteTo got %q want %q", string(b), serializationHeader) + } +} + +func TestRoundTrip(t *testing.T) { + d := &Profile{ + TotalWeight: 3, + NamedEdgeMap: NamedEdgeMap{ + ByWeight: []NamedCallEdge{ + { + CallerName: "a", + CalleeName: "b", + CallSiteOffset: 14, + }, + { + CallerName: "c", + CalleeName: "d", + CallSiteOffset: 15, + }, + }, + Weight: map[NamedCallEdge]int64{ + { + CallerName: "a", + CalleeName: "b", + CallSiteOffset: 14, + }: 2, + { + CallerName: "c", + CalleeName: "d", + CallSiteOffset: 15, + }: 1, + }, + }, + } + + testRoundTrip(t, d) +} + +func constructFuzzProfile(t *testing.T, b []byte) *Profile { + // The fuzzer can't construct an arbitrary structure, so instead we + // consume bytes from b to act as our edge data. + r := bytes.NewReader(b) + consumeString := func() (string, bool) { + // First byte: how many bytes to read for this string? We only + // use a byte to avoid making humongous strings. + length, err := r.ReadByte() + if err != nil { + return "", false + } + if length == 0 { + return "", false + } + + b := make([]byte, length) + _, err = r.Read(b) + if err != nil { + return "", false + } + + return string(b), true + } + consumeInt64 := func() (int64, bool) { + b := make([]byte, 8) + _, err := r.Read(b) + if err != nil { + return 0, false + } + + return int64(binary.LittleEndian.Uint64(b)), true + } + + d := emptyProfile() + + for { + caller, ok := consumeString() + if !ok { + break + } + if strings.ContainsAny(caller, " \r\n") { + t.Skip("caller contains space or newline") + } + + callee, ok := consumeString() + if !ok { + break + } + if strings.ContainsAny(callee, " \r\n") { + t.Skip("callee contains space or newline") + } + + line, ok := consumeInt64() + if !ok { + break + } + weight, ok := consumeInt64() + if !ok { + break + } + + edge := NamedCallEdge{ + CallerName: caller, + CalleeName: callee, + CallSiteOffset: int(line), + } + + if _, ok := d.NamedEdgeMap.Weight[edge]; ok { + t.Skip("duplicate edge") + } + + d.NamedEdgeMap.Weight[edge] = weight + d.TotalWeight += weight + } + + byWeight := make([]NamedCallEdge, 0, len(d.NamedEdgeMap.Weight)) + for namedEdge := range d.NamedEdgeMap.Weight { + byWeight = append(byWeight, namedEdge) + } + sortByWeight(byWeight, d.NamedEdgeMap.Weight) + d.NamedEdgeMap.ByWeight = byWeight + + return d +} + +func FuzzRoundTrip(f *testing.F) { + f.Add([]byte("")) // empty profile + + f.Fuzz(func(t *testing.T, b []byte) { + d := constructFuzzProfile(t, b) + testRoundTrip(t, d) + }) +} diff --git a/src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/12fcf136fcb7463c b/src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/12fcf136fcb7463c new file mode 100644 index 0000000000..31e3552bdc --- /dev/null +++ b/src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/12fcf136fcb7463c @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd00000000\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd\xfd0") diff --git a/src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/2055d314024c8d6c b/src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/2055d314024c8d6c new file mode 100644 index 0000000000..b44370f012 --- /dev/null +++ b/src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/2055d314024c8d6c @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("\x00\x040000000000000") diff --git a/src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/b615162315f7b72c b/src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/b615162315f7b72c new file mode 100644 index 0000000000..094fc10fd6 --- /dev/null +++ b/src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/b615162315f7b72c @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("\b00000000\x01\n000000000") diff --git a/src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/fdc60117b431bbae b/src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/fdc60117b431bbae new file mode 100644 index 0000000000..4f9af7b90a --- /dev/null +++ b/src/cmd/internal/pgo/testdata/fuzz/FuzzRoundTrip/fdc60117b431bbae @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("\x010\x01\r000000000") |
