Skip to content

Commit

Permalink
sweet: add support for execution traces and measuring trace overhead
Browse files Browse the repository at this point in the history
For golang/go#57175.

Change-Id: I999773e8be28c46fb5d4f6a79a94d542491e3754
  • Loading branch information
mknyszek committed Dec 21, 2022
1 parent 555ee2b commit 715fba1
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 48 deletions.
42 changes: 30 additions & 12 deletions sweet/benchmarks/go-build/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,27 +99,27 @@ 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))
compileProfile, err := mergePprofProfiles(tmpDir, profilePrefix("compile", driver.ProfileCPU))
if err != nil {
return err
}
if err := driver.WriteProfile(compileProfile, driver.ProfileCPU, name+"Compile"); err != nil {
if err := driver.WritePprofProfile(compileProfile, driver.ProfileCPU, name+"Compile"); err != nil {
return err
}

linkProfile, err := mergeProfiles(tmpDir, profilePrefix("link", driver.ProfileCPU))
linkProfile, err := mergePprofProfiles(tmpDir, profilePrefix("link", driver.ProfileCPU))
if err != nil {
return err
}
if err := driver.WriteProfile(linkProfile, driver.ProfileCPU, name+"Link"); err != nil {
if err := driver.WritePprofProfile(linkProfile, driver.ProfileCPU, name+"Link"); err != nil {
return err
}
}
if driver.ProfilingEnabled(driver.ProfileMem) {
if err := copyProfiles(tmpDir, "compile", driver.ProfileMem, name+"Compile"); err != nil {
if err := copyPprofProfiles(tmpDir, "compile", driver.ProfileMem, name+"Compile"); err != nil {
return err
}
if err := copyProfiles(tmpDir, "link", driver.ProfileMem, name+"Link"); err != nil {
if err := copyPprofProfiles(tmpDir, "link", driver.ProfileMem, name+"Link"); err != nil {
return err
}
}
Expand All @@ -128,11 +128,25 @@ func run(pkgPath string) error {
return err
}
}
if driver.ProfilingEnabled(driver.ProfileTrace) {
entries, err := os.ReadDir(tmpDir)
if err != nil {
return err
}
for _, entry := range entries {
if !strings.HasPrefix(entry.Name(), profilePrefix("compile", driver.ProfileTrace)) {
continue
}
if err := driver.CopyProfile(filepath.Join(tmpDir, entry.Name()), driver.ProfileTrace, 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,16 +155,16 @@ 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 driver.ProfileType, 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
}
}
Expand Down Expand Up @@ -200,8 +214,12 @@ func runToolexec() error {
return cmd.Run()
}
var extraFlags []string
for _, typ := range []driver.ProfileType{driver.ProfileCPU, driver.ProfileMem} {
for _, typ := range []driver.ProfileType{driver.ProfileCPU, driver.ProfileMem, driver.ProfileTrace} {
if driver.ProfilingEnabled(typ) {
if bin == "link" && typ == driver.ProfileTrace {
// 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 {
Expand Down
5 changes: 4 additions & 1 deletion sweet/benchmarks/gvisor/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (c *config) profilePath(typ driver.ProfileType) string {
func (cfg *config) runscCmd(arg ...string) *exec.Cmd {
var cmd *exec.Cmd
goProfiling := false
for _, typ := range []driver.ProfileType{driver.ProfileCPU, driver.ProfileMem} {
for _, typ := range []driver.ProfileType{driver.ProfileCPU, driver.ProfileMem, driver.ProfileTrace} {
if driver.ProfilingEnabled(typ) {
goProfiling = true
break
Expand All @@ -45,6 +45,9 @@ func (cfg *config) runscCmd(arg ...string) *exec.Cmd {
if driver.ProfilingEnabled(driver.ProfileMem) {
arg = append([]string{"-profile-heap", cfg.profilePath(driver.ProfileMem)}, arg...)
}
if driver.ProfilingEnabled(driver.ProfileTrace) {
arg = append([]string{"-trace", cfg.profilePath(driver.ProfileTrace)}, arg...)
}
if driver.ProfilingEnabled(driver.ProfilePerf) {
cmd = exec.Command("perf", append([]string{"record", "-o", cfg.profilePath(driver.ProfilePerf), cfg.runscPath}, arg...)...)
} else {
Expand Down
39 changes: 35 additions & 4 deletions sweet/benchmarks/internal/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os/exec"
"path/filepath"
"runtime/pprof"
"runtime/trace"
"sort"
"strconv"
"strings"
Expand All @@ -28,6 +29,7 @@ var (
memProfileDir string
perfDir string
perfFlags string
traceDir string
short bool
)

Expand All @@ -37,6 +39,7 @@ func SetFlags(f *flag.FlagSet) {
f.StringVar(&memProfileDir, "memprofile", "", "write a memory profile to the given directory after every benchmark run")
f.StringVar(&perfDir, "perf", "", "write a Linux perf data file to the given directory after every benchmark run")
f.StringVar(&perfFlags, "perf-flags", "", "pass the following additional flags to Linux perf")
f.StringVar(&traceDir, "trace", "", "write an execution trace to the given directory after every benchmark run")
}

const (
Expand Down Expand Up @@ -104,13 +107,20 @@ func DoPerf(v bool) RunOption {
}
}

func DoTrace(v bool) RunOption {
return func(b *B) {
b.doProfile[ProfileTrace] = v
}
}

func BenchmarkPID(pid int) RunOption {
return func(b *B) {
b.pid = pid
if pid != os.Getpid() {
b.doProfile[ProfileCPU] = false
b.doProfile[ProfileMem] = false
b.doProfile[ProfilePerf] = false
b.doProfile[ProfileTrace] = false
}
}
}
Expand All @@ -136,6 +146,7 @@ var InProcessMeasurementOptions = []RunOption{
DoCPUProfile(true),
DoMemProfile(true),
DoPerf(true),
DoTrace(true),
}

type B struct {
Expand Down Expand Up @@ -184,6 +195,10 @@ func (b *B) shouldProfile(typ ProfileType) bool {
return b.doProfile[typ] && ProfilingEnabled(typ)
}

func (b *B) Name() string {
return b.name
}

func (b *B) StartTimer() {
if b.shouldProfile(ProfileCPU) {
pprof.StartCPUProfile(b.profiles[ProfileCPU])
Expand Down Expand Up @@ -449,6 +464,12 @@ func RunBenchmark(name string, f func(*B) error, opts ...RunOption) error {
}
}

if b.shouldProfile(ProfileTrace) {
if err := trace.Start(b.profiles[ProfileTrace]); err != nil {
return err
}
}

b.StartTimer()

// Run the benchmark itself.
Expand Down Expand Up @@ -513,6 +534,9 @@ func RunBenchmark(name string, f func(*B) error, opts ...RunOption) error {
return err
}
}
if typ == ProfileTrace {
trace.Stop()
}
f.Close()
}

Expand All @@ -524,15 +548,17 @@ func RunBenchmark(name string, f func(*B) error, opts ...RunOption) error {
type ProfileType string

const (
ProfileCPU ProfileType = "cpu"
ProfileMem ProfileType = "mem"
ProfilePerf ProfileType = "perf"
ProfileCPU ProfileType = "cpu"
ProfileMem ProfileType = "mem"
ProfilePerf ProfileType = "perf"
ProfileTrace ProfileType = "trace"
)

var ProfileTypes = []ProfileType{
ProfileCPU,
ProfileMem,
ProfilePerf,
ProfileTrace, // TODO(mknyszek): Consider renaming ProfileType.
}

func ProfilingEnabled(typ ProfileType) bool {
Expand All @@ -543,11 +569,13 @@ func ProfilingEnabled(typ ProfileType) bool {
return memProfileDir != ""
case ProfilePerf:
return perfDir != ""
case ProfileTrace:
return traceDir != ""
}
panic("bad profile type")
}

func WriteProfile(prof *profile.Profile, typ ProfileType, pattern string) error {
func WritePprofProfile(prof *profile.Profile, typ ProfileType, pattern string) error {
if !ProfilingEnabled(typ) {
return fmt.Errorf("this type of profile is not currently enabled")
}
Expand Down Expand Up @@ -589,6 +617,9 @@ func newProfileFile(typ ProfileType, pattern string) (*os.File, error) {
case ProfilePerf:
outDir = perfDir
patternSuffix = ".perf"
case ProfileTrace:
outDir = traceDir
patternSuffix = ".trace"
}
return os.CreateTemp(outDir, pattern+patternSuffix)
}
69 changes: 64 additions & 5 deletions sweet/benchmarks/tile38/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import (
"fmt"
"io"
"math/rand"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"sort"
"strconv"
"sync"
"sync/atomic"
"time"

Expand Down Expand Up @@ -48,6 +50,8 @@ func (c *config) profilePath(typ driver.ProfileType) string {
fname = "mem.prof"
case driver.ProfilePerf:
fname = "perf.data"
case driver.ProfileTrace:
fname = "runtime.trace"
default:
panic("unsupported profile type " + string(typ))
}
Expand All @@ -58,7 +62,7 @@ var cliCfg config

func init() {
driver.SetFlags(flag.CommandLine)
flag.StringVar(&cliCfg.host, "host", "", "hostname of tile38 server")
flag.StringVar(&cliCfg.host, "host", "127.0.0.1", "hostname of tile38 server")
flag.IntVar(&cliCfg.port, "port", 9851, "port for tile38 server")
flag.Int64Var(&cliCfg.seed, "seed", 0, "seed for PRNG")
flag.StringVar(&cliCfg.serverBin, "server", "", "path to tile38 server binary")
Expand Down Expand Up @@ -219,9 +223,10 @@ func launchServer(cfg *config, out io.Writer) (*exec.Cmd, error) {
// Set up arguments.
srvArgs := []string{
"-d", cfg.dataPath,
"-h", "127.0.0.1",
"-p", "9851",
"-h", cfg.host,
"-p", strconv.Itoa(cfg.port),
"-threads", strconv.Itoa(cfg.serverProcs),
"-pprofport", strconv.Itoa(pprofPort),
}
for _, typ := range []driver.ProfileType{driver.ProfileCPU, driver.ProfileMem} {
if driver.ProfilingEnabled(typ) {
Expand Down Expand Up @@ -271,6 +276,26 @@ func launchServer(cfg *config, out io.Writer) (*exec.Cmd, error) {
return nil, fmt.Errorf("timeout trying to connect to server: %v", err)
}

const pprofPort = 12345

func (cfg *config) readTrace(benchName string) (int64, error) {
f, err := os.Create(cfg.profilePath(driver.ProfileTrace))
if err != nil {
return 0, err
}
defer f.Close()
resp, err := http.Get(fmt.Sprintf("http://%s:%d/debug/pprof/trace", cfg.host, pprofPort))
if err != nil {
return 0, err
}
defer resp.Body.Close()
n, err := io.Copy(f, resp.Body)
if err != nil {
return 0, err
}
return n, driver.CopyProfile(cfg.profilePath(driver.ProfileTrace), driver.ProfileTrace, benchName)
}

func runOne(bench benchmark, cfg *config) (err error) {
var buf bytes.Buffer

Expand Down Expand Up @@ -308,12 +333,12 @@ func runOne(bench benchmark, cfg *config) (err error) {
// Copy it over.
for _, typ := range []driver.ProfileType{driver.ProfileCPU, driver.ProfileMem} {
if driver.ProfilingEnabled(typ) {
p, r := profile.Read(cfg.profilePath(typ))
p, r := profile.ReadPprof(cfg.profilePath(typ))
if r != nil {
err = r
return
}
if r := driver.WriteProfile(p, typ, bench.name()); r != nil {
if r := driver.WritePprofProfile(p, typ, bench.name()); r != nil {
err = r
return
}
Expand All @@ -329,12 +354,46 @@ func runOne(bench benchmark, cfg *config) (err error) {
driver.DoCoreDump(true),
driver.BenchmarkPID(srvCmd.Process.Pid),
driver.DoPerf(true),
driver.DoTrace(true),
}
iters := 20 * 50000
if cfg.short {
iters = 1000
}
return driver.RunBenchmark(bench.name(), func(d *driver.B) error {
if driver.ProfilingEnabled(driver.ProfileTrace) {
// Handle execution tracing.
//
// TODO(mknyszek): This is kind of a hack. We really should find a way to just
// enable tracing at a lower level for the entire server run.
var traceStop chan struct{}
var traceWg sync.WaitGroup
var traceBytes uint64
traceWg.Add(1)
traceStop = make(chan struct{})
go func() {
defer traceWg.Done()
for {
select {
case <-traceStop:
return
default:
}
n, err := cfg.readTrace(bench.name())
if err != nil {
fmt.Fprintf(os.Stderr, "failed to read trace: %v", err)
return
}
traceBytes += uint64(n)
}
}()
defer func() {
// Stop the trace loop.
close(traceStop)
traceWg.Wait()
d.Report("trace-bytes", traceBytes)
}()
}
return bench.run(d, cfg.host, cfg.port, cfg.serverProcs, iters)
}, opts...)
}
Expand Down
Loading

0 comments on commit 715fba1

Please sign in to comment.