pax_global_header00006660000000000000000000000064131043054050014505gustar00rootroot0000000000000052 comment=5b67d428864e92711fcbd2f8629456121a56d91f profile-1.2.1/000077500000000000000000000000001310430540500131465ustar00rootroot00000000000000profile-1.2.1/.travis.yml000066400000000000000000000002631310430540500152600ustar00rootroot00000000000000language: go go_import_path: github.com/pkg/profile go: - 1.4.3 - 1.5.2 - 1.6.3 - tip script: - go test github.com/pkg/profile - go test -race github.com/pkg/profile profile-1.2.1/AUTHORS000066400000000000000000000000361310430540500142150ustar00rootroot00000000000000Dave Cheney profile-1.2.1/LICENSE000066400000000000000000000024141310430540500141540ustar00rootroot00000000000000Copyright (c) 2013 Dave Cheney. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. profile-1.2.1/README.md000066400000000000000000000024311310430540500144250ustar00rootroot00000000000000profile ======= Simple profiling support package for Go [![Build Status](https://travis-ci.org/pkg/profile.svg?branch=master)](https://travis-ci.org/pkg/profile) [![GoDoc](http://godoc.org/github.com/pkg/profile?status.svg)](http://godoc.org/github.com/pkg/profile) installation ------------ go get github.com/pkg/profile usage ----- Enabling profiling in your application is as simple as one line at the top of your main function ```go import "github.com/pkg/profile" func main() { defer profile.Start().Stop() ... } ``` options ------- What to profile is controlled by config value passed to profile.Start. By default CPU profiling is enabled. ```go import "github.com/pkg/profile" func main() { // p.Stop() must be called before the program exits to // ensure profiling information is written to disk. p := profile.Start(profile.MemProfile, profile.ProfilePath("."), profile.NoShutdownHook) ... } ``` Several convenience package level values are provided for cpu, memory, and block (contention) profiling. For more complex options, consult the [documentation](http://godoc.org/github.com/pkg/profile). contributing ------------ We welcome pull requests, bug fixes and issue reports. Before proposing a change, please discuss it first by raising an issue. profile-1.2.1/example_test.go000066400000000000000000000027011310430540500161670ustar00rootroot00000000000000package profile_test import ( "flag" "os" "github.com/pkg/profile" ) func ExampleStart() { // start a simple CPU profile and register // a defer to Stop (flush) the profiling data. defer profile.Start().Stop() } func ExampleCPUProfile() { // CPU profiling is the default profiling mode, but you can specify it // explicitly for completeness. defer profile.Start(profile.CPUProfile).Stop() } func ExampleMemProfile() { // use memory profiling, rather than the default cpu profiling. defer profile.Start(profile.MemProfile).Stop() } func ExampleMemProfileRate() { // use memory profiling with custom rate. defer profile.Start(profile.MemProfileRate(2048)).Stop() } func ExampleProfilePath() { // set the location that the profile will be written to defer profile.Start(profile.ProfilePath(os.Getenv("HOME"))).Stop() } func ExampleNoShutdownHook() { // disable the automatic shutdown hook. defer profile.Start(profile.NoShutdownHook).Stop() } func ExampleStart_withFlags() { // use the flags package to selectively enable profiling. mode := flag.String("profile.mode", "", "enable profiling mode, one of [cpu, mem, mutex, block]") flag.Parse() switch *mode { case "cpu": defer profile.Start(profile.CPUProfile).Stop() case "mem": defer profile.Start(profile.MemProfile).Stop() case "mutex": defer profile.Start(profile.MutexProfile).Stop() case "block": defer profile.Start(profile.BlockProfile).Stop() default: // do nothing } } profile-1.2.1/mutex.go000066400000000000000000000002721310430540500146400ustar00rootroot00000000000000// +build go1.8 package profile import "runtime" func enableMutexProfile() { runtime.SetMutexProfileFraction(1) } func disableMutexProfile() { runtime.SetMutexProfileFraction(0) } profile-1.2.1/mutex17.go000066400000000000000000000002161310430540500150060ustar00rootroot00000000000000// +build !go1.8 package profile // mock mutex support for Go 1.7 and earlier. func enableMutexProfile() {} func disableMutexProfile() {} profile-1.2.1/profile.go000066400000000000000000000143771310430540500151510ustar00rootroot00000000000000// Package profile provides a simple way to manage runtime/pprof // profiling of your Go application. package profile import ( "io/ioutil" "log" "os" "os/signal" "path/filepath" "runtime" "runtime/pprof" "sync/atomic" ) const ( cpuMode = iota memMode mutexMode blockMode traceMode ) // Profile represents an active profiling session. type Profile struct { // quiet suppresses informational messages during profiling. quiet bool // noShutdownHook controls whether the profiling package should // hook SIGINT to write profiles cleanly. noShutdownHook bool // mode holds the type of profiling that will be made mode int // path holds the base path where various profiling files are written. // If blank, the base path will be generated by ioutil.TempDir. path string // memProfileRate holds the rate for the memory profile. memProfileRate int // closer holds a cleanup function that run after each profile closer func() // stopped records if a call to profile.Stop has been made stopped uint32 } // NoShutdownHook controls whether the profiling package should // hook SIGINT to write profiles cleanly. // Programs with more sophisticated signal handling should set // this to true and ensure the Stop() function returned from Start() // is called during shutdown. func NoShutdownHook(p *Profile) { p.noShutdownHook = true } // Quiet suppresses informational messages during profiling. func Quiet(p *Profile) { p.quiet = true } // CPUProfile enables cpu profiling. // It disables any previous profiling settings. func CPUProfile(p *Profile) { p.mode = cpuMode } // DefaultMemProfileRate is the default memory profiling rate. // See also http://golang.org/pkg/runtime/#pkg-variables const DefaultMemProfileRate = 4096 // MemProfile enables memory profiling. // It disables any previous profiling settings. func MemProfile(p *Profile) { p.memProfileRate = DefaultMemProfileRate p.mode = memMode } // MemProfileRate enables memory profiling at the preferred rate. // It disables any previous profiling settings. func MemProfileRate(rate int) func(*Profile) { return func(p *Profile) { p.memProfileRate = rate p.mode = memMode } } // MutexProfile enables mutex profiling. // It disables any previous profiling settings. // // Mutex profiling is a no-op before go1.8. func MutexProfile(p *Profile) { p.mode = mutexMode } // BlockProfile enables block (contention) profiling. // It disables any previous profiling settings. func BlockProfile(p *Profile) { p.mode = blockMode } // Trace profile controls if execution tracing will be enabled. It disables any previous profiling settings. func TraceProfile(p *Profile) { p.mode = traceMode } // ProfilePath controls the base path where various profiling // files are written. If blank, the base path will be generated // by ioutil.TempDir. func ProfilePath(path string) func(*Profile) { return func(p *Profile) { p.path = path } } // Stop stops the profile and flushes any unwritten data. func (p *Profile) Stop() { if !atomic.CompareAndSwapUint32(&p.stopped, 0, 1) { // someone has already called close return } p.closer() atomic.StoreUint32(&started, 0) } // started is non zero if a profile is running. var started uint32 // Start starts a new profiling session. // The caller should call the Stop method on the value returned // to cleanly stop profiling. func Start(options ...func(*Profile)) interface { Stop() } { if !atomic.CompareAndSwapUint32(&started, 0, 1) { log.Fatal("profile: Start() already called") } var prof Profile for _, option := range options { option(&prof) } path, err := func() (string, error) { if p := prof.path; p != "" { return p, os.MkdirAll(p, 0777) } return ioutil.TempDir("", "profile") }() if err != nil { log.Fatalf("profile: could not create initial output directory: %v", err) } logf := func(format string, args ...interface{}) { if !prof.quiet { log.Printf(format, args...) } } switch prof.mode { case cpuMode: fn := filepath.Join(path, "cpu.pprof") f, err := os.Create(fn) if err != nil { log.Fatalf("profile: could not create cpu profile %q: %v", fn, err) } logf("profile: cpu profiling enabled, %s", fn) pprof.StartCPUProfile(f) prof.closer = func() { pprof.StopCPUProfile() f.Close() logf("profile: cpu profiling disabled, %s", fn) } case memMode: fn := filepath.Join(path, "mem.pprof") f, err := os.Create(fn) if err != nil { log.Fatalf("profile: could not create memory profile %q: %v", fn, err) } old := runtime.MemProfileRate runtime.MemProfileRate = prof.memProfileRate logf("profile: memory profiling enabled (rate %d), %s", runtime.MemProfileRate, fn) prof.closer = func() { pprof.Lookup("heap").WriteTo(f, 0) f.Close() runtime.MemProfileRate = old logf("profile: memory profiling disabled, %s", fn) } case mutexMode: fn := filepath.Join(path, "mutex.pprof") f, err := os.Create(fn) if err != nil { log.Fatalf("profile: could not create mutex profile %q: %v", fn, err) } enableMutexProfile() logf("profile: mutex profiling enabled, %s", fn) prof.closer = func() { if mp := pprof.Lookup("mutex"); mp != nil { mp.WriteTo(f, 0) } f.Close() disableMutexProfile() logf("profile: mutex profiling disabled, %s", fn) } case blockMode: fn := filepath.Join(path, "block.pprof") f, err := os.Create(fn) if err != nil { log.Fatalf("profile: could not create block profile %q: %v", fn, err) } runtime.SetBlockProfileRate(1) logf("profile: block profiling enabled, %s", fn) prof.closer = func() { pprof.Lookup("block").WriteTo(f, 0) f.Close() runtime.SetBlockProfileRate(0) logf("profile: block profiling disabled, %s", fn) } case traceMode: fn := filepath.Join(path, "trace.out") f, err := os.Create(fn) if err != nil { log.Fatalf("profile: could not create trace output file %q: %v", fn, err) } if err := startTrace(f); err != nil { log.Fatalf("profile: could not start trace: %v", err) } logf("profile: trace enabled, %s", fn) prof.closer = func() { stopTrace() logf("profile: trace disabled, %s", fn) } } if !prof.noShutdownHook { go func() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) <-c log.Println("profile: caught interrupt, stopping profiles") prof.Stop() os.Exit(0) }() } return &prof } profile-1.2.1/profile_test.go000066400000000000000000000145471310430540500162070ustar00rootroot00000000000000package profile import ( "bufio" "bytes" "io" "io/ioutil" "os" "os/exec" "path/filepath" "strings" "testing" ) type checkFn func(t *testing.T, stdout, stderr []byte, err error) func TestProfile(t *testing.T) { f, err := ioutil.TempFile("", "profile_test") if err != nil { t.Fatal(err) } defer os.Remove(f.Name()) var profileTests = []struct { name string code string checks []checkFn }{{ name: "default profile (cpu)", code: ` package main import "github.com/pkg/profile" func main() { defer profile.Start().Stop() } `, checks: []checkFn{ NoStdout, Stderr("profile: cpu profiling enabled"), NoErr, }, }, { name: "memory profile", code: ` package main import "github.com/pkg/profile" func main() { defer profile.Start(profile.MemProfile).Stop() } `, checks: []checkFn{ NoStdout, Stderr("profile: memory profiling enabled"), NoErr, }, }, { name: "memory profile (rate 2048)", code: ` package main import "github.com/pkg/profile" func main() { defer profile.Start(profile.MemProfileRate(2048)).Stop() } `, checks: []checkFn{ NoStdout, Stderr("profile: memory profiling enabled (rate 2048)"), NoErr, }, }, { name: "double start", code: ` package main import "github.com/pkg/profile" func main() { profile.Start() profile.Start() } `, checks: []checkFn{ NoStdout, Stderr("cpu profiling enabled", "profile: Start() already called"), Err, }, }, { name: "block profile", code: ` package main import "github.com/pkg/profile" func main() { defer profile.Start(profile.BlockProfile).Stop() } `, checks: []checkFn{ NoStdout, Stderr("profile: block profiling enabled"), NoErr, }, }, { name: "mutex profile", code: ` package main import "github.com/pkg/profile" func main() { defer profile.Start(profile.MutexProfile).Stop() } `, checks: []checkFn{ NoStdout, Stderr("profile: mutex profiling enabled"), NoErr, }, }, { name: "profile path", code: ` package main import "github.com/pkg/profile" func main() { defer profile.Start(profile.ProfilePath(".")).Stop() } `, checks: []checkFn{ NoStdout, Stderr("profile: cpu profiling enabled, cpu.pprof"), NoErr, }, }, { name: "profile path error", code: ` package main import "github.com/pkg/profile" func main() { defer profile.Start(profile.ProfilePath("` + f.Name() + `")).Stop() } `, checks: []checkFn{ NoStdout, Stderr("could not create initial output"), Err, }, }, { name: "multiple profile sessions", code: ` package main import "github.com/pkg/profile" func main() { profile.Start(profile.CPUProfile).Stop() profile.Start(profile.MemProfile).Stop() profile.Start(profile.BlockProfile).Stop() profile.Start(profile.CPUProfile).Stop() profile.Start(profile.MutexProfile).Stop() } `, checks: []checkFn{ NoStdout, Stderr("profile: cpu profiling enabled", "profile: cpu profiling disabled", "profile: memory profiling enabled", "profile: memory profiling disabled", "profile: block profiling enabled", "profile: block profiling disabled", "profile: cpu profiling enabled", "profile: cpu profiling disabled", "profile: mutex profiling enabled", "profile: mutex profiling disabled"), NoErr, }, }, { name: "profile quiet", code: ` package main import "github.com/pkg/profile" func main() { defer profile.Start(profile.Quiet).Stop() } `, checks: []checkFn{NoStdout, NoStderr, NoErr}, }} for _, tt := range profileTests { t.Log(tt.name) stdout, stderr, err := runTest(t, tt.code) for _, f := range tt.checks { f(t, stdout, stderr, err) } } } // NoStdout checks that stdout was blank. func NoStdout(t *testing.T, stdout, _ []byte, _ error) { if len := len(stdout); len > 0 { t.Errorf("stdout: wanted 0 bytes, got %d", len) } } // Stderr verifies that the given lines match the output from stderr func Stderr(lines ...string) checkFn { return func(t *testing.T, _, stderr []byte, _ error) { r := bytes.NewReader(stderr) if !validateOutput(r, lines) { t.Errorf("stderr: wanted '%s', got '%s'", lines, stderr) } } } // NoStderr checks that stderr was blank. func NoStderr(t *testing.T, _, stderr []byte, _ error) { if len := len(stderr); len > 0 { t.Errorf("stderr: wanted 0 bytes, got %d", len) } } // Err checks that there was an error returned func Err(t *testing.T, _, _ []byte, err error) { if err == nil { t.Errorf("expected error") } } // NoErr checks that err was nil func NoErr(t *testing.T, _, _ []byte, err error) { if err != nil { t.Errorf("error: expected nil, got %v", err) } } // validatedOutput validates the given slice of lines against data from the given reader. func validateOutput(r io.Reader, want []string) bool { s := bufio.NewScanner(r) for _, line := range want { if !s.Scan() || !strings.Contains(s.Text(), line) { return false } } return true } var validateOutputTests = []struct { input string lines []string want bool }{{ input: "", want: true, }, { input: `profile: yes `, want: true, }, { input: `profile: yes `, lines: []string{"profile: yes"}, want: true, }, { input: `profile: yes profile: no `, lines: []string{"profile: yes"}, want: true, }, { input: `profile: yes profile: no `, lines: []string{"profile: yes", "profile: no"}, want: true, }, { input: `profile: yes profile: no `, lines: []string{"profile: no"}, want: false, }} func TestValidateOutput(t *testing.T) { for _, tt := range validateOutputTests { r := strings.NewReader(tt.input) got := validateOutput(r, tt.lines) if tt.want != got { t.Errorf("validateOutput(%q, %q), want %v, got %v", tt.input, tt.lines, tt.want, got) } } } // runTest executes the go program supplied and returns the contents of stdout, // stderr, and an error which may contain status information about the result // of the program. func runTest(t *testing.T, code string) ([]byte, []byte, error) { chk := func(err error) { if err != nil { t.Fatal(err) } } gopath, err := ioutil.TempDir("", "profile-gopath") chk(err) defer os.RemoveAll(gopath) srcdir := filepath.Join(gopath, "src") err = os.Mkdir(srcdir, 0755) chk(err) src := filepath.Join(srcdir, "main.go") err = ioutil.WriteFile(src, []byte(code), 0644) chk(err) cmd := exec.Command("go", "run", src) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr err = cmd.Run() return stdout.Bytes(), stderr.Bytes(), err } profile-1.2.1/trace.go000066400000000000000000000001621310430540500145720ustar00rootroot00000000000000// +build go1.7 package profile import "runtime/trace" var startTrace = trace.Start var stopTrace = trace.Stop profile-1.2.1/trace16.go000066400000000000000000000002671310430540500147470ustar00rootroot00000000000000// +build !go1.7 package profile import "io" // mock trace support for Go 1.6 and earlier. func startTrace(w io.Writer) error { return nil } func stopTrace() {} profile-1.2.1/trace_test.go000066400000000000000000000003121310430540500156260ustar00rootroot00000000000000package profile_test import "github.com/pkg/profile" func ExampleTraceProfile() { // use execution tracing, rather than the default cpu profiling. defer profile.Start(profile.TraceProfile).Stop() }