From: Ryan Diep Date: Fri, 28 Nov 2025 08:27:09 +0000 (-0500) Subject: cmd/compile/internal/pgo: error parsing profile (for pgo) after scaling X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=a1150b5017b789c6dc22d2c3939f7df10baf3dff;p=gostls13.git cmd/compile/internal/pgo: error parsing profile (for pgo) after scaling If a valid profile is scaled such that the samples/counts become 0, an empty graph in which the compiler complain that we never saw a start line since the graph has no nodes which is a misleading error message. src/cmd/internal/pgo/pprof.go was modified to check if the graph is empty return an empty profile. GitHub-Pull-Request: golang/go#73640 Change-Id: If3f7ab8af6fcf77b2e29ae1df154f87bee377ab0 Reviewed-on: https://go-review.googlesource.com/c/go/+/725120 Reviewed-by: Michael Pratt LUCI-TryBot-Result: Go LUCI Reviewed-by: Carlos Amedee Auto-Submit: Michael Pratt --- diff --git a/src/cmd/compile/internal/test/pgo_inl_test.go b/src/cmd/compile/internal/test/pgo_inl_test.go index 0859431b02..d9d133c8d9 100644 --- a/src/cmd/compile/internal/test/pgo_inl_test.go +++ b/src/cmd/compile/internal/test/pgo_inl_test.go @@ -365,3 +365,53 @@ func TestPGOHash(t *testing.T) { t.Errorf("output contains unexpected source line, out:\n%s", out) } } + +// TestPGOZeroValues tests that a profile with all sample values scaled to zero +// is treated as empty, ensuring the build succeeds without error. +func TestPGOZeroValues(t *testing.T) { + wd, err := os.Getwd() + if err != nil { + t.Fatalf("error getting wd: %v", err) + } + srcDir := filepath.Join(wd, "testdata/pgo/inline") + + // Copy the module to a scratch location so we can add a go.mod. + dir := t.TempDir() + + originalPprofFile, err := os.Open(filepath.Join(srcDir, profFile)) + if err != nil { + t.Fatalf("error opening %v: %v", profFile, err) + } + defer originalPprofFile.Close() + + p, err := profile.Parse(originalPprofFile) + if err != nil { + t.Fatalf("error parsing %v: %v", profFile, err) + } + + // Zero out all samples to simulate a profile with all samples scaled away + for _, s := range p.Sample { + for i := range s.Value { + s.Value[i] = 0 + } + } + + emptyProf, err := os.Create(filepath.Join(dir, profFile)) + if err != nil { + t.Fatalf("error creating %v: %v", profFile, err) + } + defer emptyProf.Close() + + if err := p.Write(emptyProf); err != nil { + t.Fatalf("error writing %v: %v", profFile, err) + } + + for _, file := range []string{"inline_hot.go", "inline_hot_test.go"} { + if err := copyFile(filepath.Join(dir, file), filepath.Join(srcDir, file)); err != nil { + t.Fatalf("error copying %s: %v", file, err) + } + } + + gcflag := fmt.Sprintf("-pgoprofile=%s", profFile) + buildPGOInliningTest(t, dir, gcflag) +} diff --git a/src/cmd/internal/pgo/pprof.go b/src/cmd/internal/pgo/pprof.go index 5e61a11141..5bcd8c588d 100644 --- a/src/cmd/internal/pgo/pprof.go +++ b/src/cmd/internal/pgo/pprof.go @@ -50,6 +50,12 @@ func FromPProf(r io.Reader) (*Profile, error) { SampleValue: func(v []int64) int64 { return v[valueIndex] }, }) + if len(g.Nodes) == 0 { + // If all sample values are 0, the graph will have no nodes. + // In this case, treat it as an empty profile. + return emptyProfile(), nil + } + namedEdgeMap, totalWeight, err := createNamedEdgeMap(g) if err != nil { return nil, err