]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: add -pgo build flag
authorCherry Mui <cherryyz@google.com>
Wed, 28 Sep 2022 22:50:01 +0000 (18:50 -0400)
committerCherry Mui <cherryyz@google.com>
Tue, 8 Nov 2022 18:50:59 +0000 (18:50 +0000)
Add a -pgo flag for "go build" (and other build actions), to
specify the file path of a profile used for PGO. Special name
"off" turns off PGO.

The given profile path is passed to the compiler.

The build cache is sensitive to the content of the given PGO
profile.

TODO: auto mode.

For #55022.

Change-Id: Ieee1b131b4c041f9502fd0a1acf112f3e44246be
Reviewed-on: https://go-review.googlesource.com/c/go/+/438736
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Cherry Mui <cherryyz@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
src/cmd/go/alldocs.go
src/cmd/go/internal/cfg/cfg.go
src/cmd/go/internal/load/pkg.go
src/cmd/go/internal/work/build.go
src/cmd/go/internal/work/exec.go
src/cmd/go/internal/work/gc.go
src/cmd/go/internal/work/init.go
src/cmd/go/testdata/script/build_pgo.txt [new file with mode: 0644]

index 051cf25996aacdfb47d2cc997e56c91d89d9ef20..821ebef3ac987bc8a1e498426ed2e6c4efc882d9 100644 (file)
 //             include path must be in the same directory as the Go package they are
 //             included from, and overlays will not appear when binaries and tests are
 //             run through go run and go test respectively.
+//     -pgo file
+//             specify the file path of a profile for profile-guided optimization (PGO).
+//             Special name "off" turns off PGO.
 //     -pkgdir dir
 //             install and load all packages from dir instead of the usual locations.
 //             For example, when building with a non-standard configuration,
index ba0294459e69f933ae20935531c2bfd02d9fb56a..30acde0a5a500b5b091802051339bcbb86478a5e 100644 (file)
@@ -79,6 +79,8 @@ var (
        BuildN                 bool                    // -n flag
        BuildO                 string                  // -o flag
        BuildP                 = runtime.GOMAXPROCS(0) // -p flag
+       BuildPGO               string                  // -pgo flag
+       BuildPGOFile           string                  // profile selected by -pgo flag, an absolute path (if not empty)
        BuildPkgdir            string                  // -pkgdir flag
        BuildRace              bool                    // -race flag
        BuildToolexec          []string                // -toolexec flag
index 0e1a632d7a7a2557237eae43e20d0aa2de6085a1..da5b305dbbf87dfe6c1431ce97362231966955da 100644 (file)
@@ -2387,6 +2387,13 @@ func (p *Package) setBuildInfo(autoVCS bool) {
                        appendSetting("-ldflags", ldflags)
                }
        }
+       if cfg.BuildPGOFile != "" {
+               if cfg.BuildTrimpath {
+                       appendSetting("-pgo", filepath.Base(cfg.BuildPGOFile))
+               } else {
+                       appendSetting("-pgo", cfg.BuildPGOFile)
+               }
+       }
        if cfg.BuildMSan {
                appendSetting("-msan", "true")
        }
index 553cd66ef3198ebb77d861f8fc6e2326fd6fd59d..f93fb0bb6af56c3c75111dcf5a405fe12fe69eeb 100644 (file)
@@ -158,6 +158,9 @@ and test commands:
                include path must be in the same directory as the Go package they are
                included from, and overlays will not appear when binaries and tests are
                run through go run and go test respectively.
+       -pgo file
+               specify the file path of a profile for profile-guided optimization (PGO).
+               Special name "off" turns off PGO.
        -pkgdir dir
                install and load all packages from dir instead of the usual locations.
                For example, when building with a non-standard configuration,
@@ -312,6 +315,7 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
        cmd.Flag.StringVar(&cfg.BuildContext.InstallSuffix, "installsuffix", "", "")
        cmd.Flag.Var(&load.BuildLdflags, "ldflags", "")
        cmd.Flag.BoolVar(&cfg.BuildLinkshared, "linkshared", false, "")
+       cmd.Flag.StringVar(&cfg.BuildPGO, "pgo", "", "")
        cmd.Flag.StringVar(&cfg.BuildPkgdir, "pkgdir", "", "")
        cmd.Flag.BoolVar(&cfg.BuildRace, "race", false, "")
        cmd.Flag.BoolVar(&cfg.BuildMSan, "msan", false, "")
index c060ebd06dfca0e09b1fe2ec042ff077446d87f1..90d96400b84b84cca25d3663a0357c9ca4434088 100644 (file)
@@ -383,6 +383,9 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
        for _, file := range inputFiles {
                fmt.Fprintf(h, "file %s %s\n", file, b.fileHash(filepath.Join(p.Dir, file)))
        }
+       if cfg.BuildPGOFile != "" {
+               fmt.Fprintf(h, "pgofile %s\n", b.fileHash(cfg.BuildPGOFile))
+       }
        for _, a1 := range a.Deps {
                p1 := a1.Package
                if p1 != nil {
index cff477331508d1958cb9ee01c681c069ec1adefa..036a1880275f57d9413bc603fffdb9734b8f07d2 100644 (file)
@@ -145,6 +145,9 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg
        if p.Internal.CoverageCfg != "" {
                defaultGcFlags = append(defaultGcFlags, "-coveragecfg="+p.Internal.CoverageCfg)
        }
+       if cfg.BuildPGOFile != "" {
+               defaultGcFlags = append(defaultGcFlags, "-pgoprofile="+cfg.BuildPGOFile)
+       }
        if symabis != "" {
                defaultGcFlags = append(defaultGcFlags, "-symabis", symabis)
        }
index cfd5a505d3706e93cc637a73caafdad02b33fa18..c8fee098e05f4bc2d2d4d0b62af271b19630bbe9 100644 (file)
@@ -84,6 +84,8 @@ func BuildInit() {
        if cfg.BuildRace && cfg.BuildCoverMode != "atomic" {
                base.Fatalf(`-covermode must be "atomic", not %q, when -race is enabled`, cfg.BuildCoverMode)
        }
+
+       setPGOProfilePath()
 }
 
 // fuzzInstrumentFlags returns compiler flags that enable fuzzing instrumation
@@ -438,3 +440,21 @@ func compilerRequiredAsanVersion() error {
        }
        return nil
 }
+
+func setPGOProfilePath() {
+       switch cfg.BuildPGO {
+       case "":
+               fallthrough // default to "auto"
+       case "off":
+               // Nothing to do.
+       case "auto":
+               base.Fatalf("-pgo=auto is not implemented")
+       default:
+               // make it absolute path, as the compiler runs on various directories.
+               if p, err := filepath.Abs(cfg.BuildPGO); err != nil {
+                       base.Fatalf("fail to get absolute path of PGO file %s: %v", cfg.BuildPGO, err)
+               } else {
+                       cfg.BuildPGOFile = p
+               }
+       }
+}
diff --git a/src/cmd/go/testdata/script/build_pgo.txt b/src/cmd/go/testdata/script/build_pgo.txt
new file mode 100644 (file)
index 0000000..65ecd57
--- /dev/null
@@ -0,0 +1,62 @@
+# Test go build -pgo flag.
+# Specifically, the build cache handles profile content correctly.
+
+# this test rebuild runtime with different flags, skip in short mode
+[short] skip
+
+# build without PGO
+go build triv.go
+
+# build with PGO, should trigger rebuild
+# starting with an empty profile (the compiler accepts it)
+go build -x -pgo=prof triv.go
+stderr 'compile.*-pgoprofile=.*prof.*triv.go'
+
+# store the build ID
+go list -export -json=BuildID -pgo=prof triv.go
+stdout '"BuildID":' # check that output actually contains a build ID
+cp stdout list.out
+
+# build again with the same profile, should be cached
+go build -x -pgo=prof triv.go
+! stderr 'compile.*triv.go'
+
+# check that the build ID is the same
+go list -export -json=BuildID -pgo=prof triv.go
+cmp stdout list.out
+
+# overwrite the prof
+go run overwrite.go
+
+# build again, profile content changed, should trigger rebuild
+go build -n -pgo=prof triv.go
+stderr 'compile.*-pgoprofile=.*prof.*p.go'
+
+# check that the build ID is different
+go list -export -json=BuildID -pgo=prof triv.go
+! cmp stdout list.out
+
+-- prof --
+-- triv.go --
+package main
+func main() {}
+-- overwrite.go --
+package main
+
+import (
+       "os"
+       "runtime/pprof"
+)
+
+func main() {
+       f, err := os.Create("prof")
+       if err != nil {
+               panic(err)
+       }
+       err = pprof.StartCPUProfile(f)
+       if err != nil {
+               panic(err)
+       }
+       pprof.StopCPUProfile()
+       f.Close()
+}