server: Enable server profiling as needed. (#1565)
parent
f733120d3d
commit
b66c3bf35e
@ -0,0 +1 @@ |
|||||||
|
Dave Cheney <dave@cheney.net> |
@ -0,0 +1,24 @@ |
|||||||
|
Copyright (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. |
@ -0,0 +1,44 @@ |
|||||||
|
profile |
||||||
|
======= |
||||||
|
|
||||||
|
Simple profiling support package for Go |
||||||
|
|
||||||
|
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). |
@ -0,0 +1,199 @@ |
|||||||
|
// 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" |
||||||
|
) |
||||||
|
|
||||||
|
// started counts the number of times Start has been called
|
||||||
|
var started uint32 |
||||||
|
|
||||||
|
const ( |
||||||
|
cpuMode = iota |
||||||
|
memMode |
||||||
|
blockMode |
||||||
|
) |
||||||
|
|
||||||
|
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 |
||||||
|
|
||||||
|
// closers holds the cleanup functions that run after each profile
|
||||||
|
closers []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 controls if cpu profiling will be enabled. 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 controls if memory profiling will be enabled. It disables any previous profiling settings.
|
||||||
|
func MemProfile(p *profile) { |
||||||
|
p.memProfileRate = DefaultMemProfileRate |
||||||
|
p.mode = memMode |
||||||
|
} |
||||||
|
|
||||||
|
// MemProfileRate controls if memory profiling will be enabled. Additionally, it takes a parameter which
|
||||||
|
// allows the setting of the memory profile rate.
|
||||||
|
func MemProfileRate(rate int) func(*profile) { |
||||||
|
return func(p *profile) { |
||||||
|
p.memProfileRate = rate |
||||||
|
p.mode = memMode |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// BlockProfile controls if block (contention) profiling will be enabled. It disables any previous profiling settings.
|
||||||
|
func BlockProfile(p *profile) { p.mode = blockMode } |
||||||
|
|
||||||
|
// 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 |
||||||
|
} |
||||||
|
for _, c := range p.closers { |
||||||
|
c() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 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) |
||||||
|
} |
||||||
|
|
||||||
|
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) |
||||||
|
} |
||||||
|
if !prof.quiet { |
||||||
|
log.Printf("profile: cpu profiling enabled, %s", fn) |
||||||
|
} |
||||||
|
pprof.StartCPUProfile(f) |
||||||
|
prof.closers = append(prof.closers, func() { |
||||||
|
pprof.StopCPUProfile() |
||||||
|
f.Close() |
||||||
|
}) |
||||||
|
|
||||||
|
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 |
||||||
|
if !prof.quiet { |
||||||
|
log.Printf("profile: memory profiling enabled (rate %d), %s", runtime.MemProfileRate, fn) |
||||||
|
} |
||||||
|
prof.closers = append(prof.closers, func() { |
||||||
|
pprof.Lookup("heap").WriteTo(f, 0) |
||||||
|
f.Close() |
||||||
|
runtime.MemProfileRate = old |
||||||
|
}) |
||||||
|
|
||||||
|
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) |
||||||
|
if !prof.quiet { |
||||||
|
log.Printf("profile: block profiling enabled, %s", fn) |
||||||
|
} |
||||||
|
prof.closers = append(prof.closers, func() { |
||||||
|
pprof.Lookup("block").WriteTo(f, 0) |
||||||
|
f.Close() |
||||||
|
runtime.SetBlockProfileRate(0) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
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) |
||||||
|
}() |
||||||
|
} |
||||||
|
|
||||||
|
prof.closers = append(prof.closers, func() { |
||||||
|
atomic.SwapUint32(&started, 0) |
||||||
|
}) |
||||||
|
|
||||||
|
return &prof |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
box: wercker/golang |
Loading…
Reference in new issue