Skip to content

Commit

Permalink
sweet: add support for execution traces and refactor profile plumbing
Browse files Browse the repository at this point in the history
This change does a lot at once, but it's mostly refactoring. First, it
moves most of the profile abstraction out of benchmarks/internal/driver
and into a new shared package called diagnostics. It also renames
profiles to diagnostics to better capture the breadth of what this
mechanism collects. Then, it adds support for turning on diagnostics
from configuration files. Next, it adds support for generating
additional configurations to capture the overhead of collecting
diagnostics, starting with CPU profiling. Lastly, it adds support for
the new Trace diagnostic.

(This change also fixes a bug in go-build where Linux perf flags weren't
being propagated.)

In the future, core dumps could easily be folded into this new
diagnostics abstraction.

For golang/go#57175.

Change-Id: I999773e8be28c46fb5d4f6a79a94d542491e3754
Reviewed-on: https://go-review.googlesource.com/c/benchmarks/+/459095
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
  • Loading branch information
mknyszek committed Feb 14, 2023
1 parent 8520151 commit 2874b5b
Show file tree
Hide file tree
Showing 11 changed files with 496 additions and 191 deletions.
71 changes: 49 additions & 22 deletions sweet/benchmarks/go-build/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"golang.org/x/benchmarks/sweet/benchmarks/internal/cgroups"
"golang.org/x/benchmarks/sweet/benchmarks/internal/driver"
"golang.org/x/benchmarks/sweet/common"
"golang.org/x/benchmarks/sweet/common/diagnostics"
sprofile "golang.org/x/benchmarks/sweet/common/profile"
)

Expand Down Expand Up @@ -67,7 +68,7 @@ func run(pkgPath string) error {
"-bench-name", name,
}
flag.CommandLine.Visit(func(f *flag.Flag) {
if f.Name == "go" || f.Name == "bench-name" {
if f.Name == "go" || f.Name == "bench-name" || strings.HasPrefix(f.Name, "perf") {
// No need to pass this along.
return
}
Expand All @@ -76,8 +77,12 @@ func run(pkgPath string) error {

cmdArgs = append(cmdArgs, "-toolexec", strings.Join(selfCmd, " "))
var baseCmd *exec.Cmd
if driver.ProfilingEnabled(driver.ProfilePerf) {
baseCmd = exec.Command("perf", append([]string{"record", "-o", filepath.Join(tmpDir, "perf.data"), goTool}, cmdArgs...)...)
if driver.DiagnosticEnabled(diagnostics.Perf) {
perfArgs := []string{"record", "-o", filepath.Join(tmpDir, "perf.data")}
perfArgs = append(perfArgs, driver.PerfFlags()...)
perfArgs = append(perfArgs, goTool)
perfArgs = append(perfArgs, cmdArgs...)
baseCmd = exec.Command("perf", perfArgs...)
} else {
baseCmd = exec.Command(goTool, cmdArgs...)
}
Expand All @@ -98,41 +103,55 @@ func run(pkgPath string) error {

// Handle any CPU profiles produced, and merge them.
// Then, write them out to the canonical profiles above.
if driver.ProfilingEnabled(driver.ProfileCPU) {
compileProfile, err := mergeProfiles(tmpDir, profilePrefix("compile", driver.ProfileCPU))
if driver.DiagnosticEnabled(diagnostics.CPUProfile) {
compileProfile, err := mergePprofProfiles(tmpDir, profilePrefix("compile", diagnostics.CPUProfile))
if err != nil {
return err
}
if err := driver.WriteProfile(compileProfile, driver.ProfileCPU, name+"Compile"); err != nil {
if err := driver.WritePprofProfile(compileProfile, diagnostics.CPUProfile, name+"Compile"); err != nil {
return err
}

linkProfile, err := mergeProfiles(tmpDir, profilePrefix("link", driver.ProfileCPU))
linkProfile, err := mergePprofProfiles(tmpDir, profilePrefix("link", diagnostics.CPUProfile))
if err != nil {
return err
}
if err := driver.WriteProfile(linkProfile, driver.ProfileCPU, name+"Link"); err != nil {
if err := driver.WritePprofProfile(linkProfile, diagnostics.CPUProfile, name+"Link"); err != nil {
return err
}
}
if driver.ProfilingEnabled(driver.ProfileMem) {
if err := copyProfiles(tmpDir, "compile", driver.ProfileMem, name+"Compile"); err != nil {
if driver.DiagnosticEnabled(diagnostics.MemProfile) {
if err := copyPprofProfiles(tmpDir, "compile", diagnostics.MemProfile, name+"Compile"); err != nil {
return err
}
if err := copyProfiles(tmpDir, "link", driver.ProfileMem, name+"Link"); err != nil {
if err := copyPprofProfiles(tmpDir, "link", diagnostics.MemProfile, name+"Link"); err != nil {
return err
}
}
if driver.ProfilingEnabled(driver.ProfilePerf) {
if err := driver.CopyProfile(filepath.Join(tmpDir, "perf.data"), driver.ProfilePerf, name); err != nil {
if driver.DiagnosticEnabled(diagnostics.Perf) {
if err := driver.CopyDiagnosticData(filepath.Join(tmpDir, "perf.data"), diagnostics.Perf, name); err != nil {
return err
}
}
if driver.DiagnosticEnabled(diagnostics.Trace) {
entries, err := os.ReadDir(tmpDir)
if err != nil {
return err
}
for _, entry := range entries {
if !strings.HasPrefix(entry.Name(), profilePrefix("compile", diagnostics.Trace)) {
continue
}
if err := driver.CopyDiagnosticData(filepath.Join(tmpDir, entry.Name()), diagnostics.Trace, name+"Compile"); err != nil {
return err
}
}
}
return printOtherResults(tmpResultsDir())
}

func mergeProfiles(dir, prefix string) (*profile.Profile, error) {
profiles, err := sprofile.ReadDir(dir, func(name string) bool {
func mergePprofProfiles(dir, prefix string) (*profile.Profile, error) {
profiles, err := sprofile.ReadDirPprof(dir, func(name string) bool {
return strings.HasPrefix(name, prefix)
})
if err != nil {
Expand All @@ -141,23 +160,23 @@ func mergeProfiles(dir, prefix string) (*profile.Profile, error) {
return profile.Merge(profiles)
}

func copyProfiles(dir, bin string, typ driver.ProfileType, finalPrefix string) error {
func copyPprofProfiles(dir, bin string, typ diagnostics.Type, finalPrefix string) error {
prefix := profilePrefix(bin, typ)
profiles, err := sprofile.ReadDir(dir, func(name string) bool {
profiles, err := sprofile.ReadDirPprof(dir, func(name string) bool {
return strings.HasPrefix(name, prefix)
})
if err != nil {
return err
}
for _, profile := range profiles {
if err := driver.WriteProfile(profile, typ, finalPrefix); err != nil {
if err := driver.WritePprofProfile(profile, typ, finalPrefix); err != nil {
return err
}
}
return nil
}

func profilePrefix(bin string, typ driver.ProfileType) string {
func profilePrefix(bin string, typ diagnostics.Type) string {
return bin + "-prof." + string(typ)
}

Expand Down Expand Up @@ -200,15 +219,23 @@ func runToolexec() error {
return cmd.Run()
}
var extraFlags []string
for _, typ := range []driver.ProfileType{driver.ProfileCPU, driver.ProfileMem} {
if driver.ProfilingEnabled(typ) {
for _, typ := range []diagnostics.Type{diagnostics.CPUProfile, diagnostics.MemProfile, diagnostics.Trace} {
if driver.DiagnosticEnabled(typ) {
if bin == "link" && typ == diagnostics.Trace {
// TODO(mknyszek): Traces are not supported for the linker.
continue
}
// Stake a claim for a filename.
f, err := os.CreateTemp(tmpDir, profilePrefix(bin, typ))
if err != nil {
return err
}
f.Close()
extraFlags = append(extraFlags, "-"+string(typ)+"profile", f.Name())
flag := "-" + string(typ)
if typ == diagnostics.Trace {
flag += "profile" // The compiler flag is -traceprofile.
}
extraFlags = append(extraFlags, flag, f.Name())
}
}
cmd := exec.Command(flag.Args()[0], append(extraFlags, flag.Args()[1:]...)...)
Expand Down
26 changes: 17 additions & 9 deletions sweet/benchmarks/gvisor/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"golang.org/x/benchmarks/sweet/benchmarks/internal/driver"
"golang.org/x/benchmarks/sweet/common"
"golang.org/x/benchmarks/sweet/common/diagnostics"
)

func workloadsPath(assetsDir, subBenchmark string) string {
Expand All @@ -23,30 +24,37 @@ func workloadsPath(assetsDir, subBenchmark string) string {
return filepath.Join(assetsDir, subBenchmark, "bin", platformDir, "workload")
}

func (c *config) profilePath(typ driver.ProfileType) string {
func (c *config) profilePath(typ diagnostics.Type) string {
return filepath.Join(c.tmpDir, string(typ)+".prof")
}

func (cfg *config) runscCmd(arg ...string) *exec.Cmd {
var cmd *exec.Cmd
goProfiling := false
for _, typ := range []driver.ProfileType{driver.ProfileCPU, driver.ProfileMem} {
if driver.ProfilingEnabled(typ) {
for _, typ := range []diagnostics.Type{diagnostics.CPUProfile, diagnostics.MemProfile, diagnostics.Trace} {
if driver.DiagnosticEnabled(typ) {
goProfiling = true
break
}
}
if goProfiling {
arg = append([]string{"-profile"}, arg...)
}
if driver.ProfilingEnabled(driver.ProfileCPU) {
arg = append([]string{"-profile-cpu", cfg.profilePath(driver.ProfileCPU)}, arg...)
if driver.DiagnosticEnabled(diagnostics.CPUProfile) {
arg = append([]string{"-profile-cpu", cfg.profilePath(diagnostics.CPUProfile)}, arg...)
}
if driver.ProfilingEnabled(driver.ProfileMem) {
arg = append([]string{"-profile-heap", cfg.profilePath(driver.ProfileMem)}, arg...)
if driver.DiagnosticEnabled(diagnostics.MemProfile) {
arg = append([]string{"-profile-heap", cfg.profilePath(diagnostics.MemProfile)}, arg...)
}
if driver.ProfilingEnabled(driver.ProfilePerf) {
cmd = exec.Command("perf", append([]string{"record", "-o", cfg.profilePath(driver.ProfilePerf), cfg.runscPath}, arg...)...)
if driver.DiagnosticEnabled(diagnostics.Trace) {
arg = append([]string{"-trace", cfg.profilePath(diagnostics.Trace)}, arg...)
}
if driver.DiagnosticEnabled(diagnostics.Perf) {
perfArgs := []string{"record", "-o", cfg.profilePath(diagnostics.Perf)}
perfArgs = append(perfArgs, driver.PerfFlags()...)
perfArgs = append(perfArgs, cfg.runscPath)
perfArgs = append(perfArgs, arg...)
cmd = exec.Command("perf", perfArgs...)
} else {
cmd = exec.Command(cfg.runscPath, arg...)
}
Expand Down
7 changes: 4 additions & 3 deletions sweet/benchmarks/gvisor/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"time"

"golang.org/x/benchmarks/sweet/benchmarks/internal/driver"
"golang.org/x/benchmarks/sweet/common/diagnostics"
)

type config struct {
Expand Down Expand Up @@ -66,12 +67,12 @@ func main1() error {
}
return err
}
for _, typ := range driver.ProfileTypes {
if !driver.ProfilingEnabled(typ) {
for _, typ := range diagnostics.Types() {
if !driver.DiagnosticEnabled(typ) {
continue
}
// runscCmd ensures these are created if necessary.
if err := driver.CopyProfile(cliCfg.profilePath(typ), typ, bench.name()); err != nil {
if err := driver.CopyDiagnosticData(cliCfg.profilePath(typ), typ, bench.name()); err != nil {
return err
}
}
Expand Down
Loading

0 comments on commit 2874b5b

Please sign in to comment.