From b66c3bf35e4354deb07e3e938d3e19a919c9297f Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Mon, 9 May 2016 16:18:56 -0700 Subject: [PATCH] server: Enable server profiling as needed. (#1565) --- commands.go | 8 - globals.go | 2 + main.go | 18 ++ vendor/github.com/pkg/profile/AUTHORS | 1 + vendor/github.com/pkg/profile/LICENSE | 24 +++ vendor/github.com/pkg/profile/README.md | 44 +++++ vendor/github.com/pkg/profile/profile.go | 199 ++++++++++++++++++++++ vendor/github.com/pkg/profile/wercker.yml | 1 + vendor/vendor.json | 5 + 9 files changed, 294 insertions(+), 8 deletions(-) create mode 100644 vendor/github.com/pkg/profile/AUTHORS create mode 100644 vendor/github.com/pkg/profile/LICENSE create mode 100644 vendor/github.com/pkg/profile/README.md create mode 100644 vendor/github.com/pkg/profile/profile.go create mode 100644 vendor/github.com/pkg/profile/wercker.yml diff --git a/commands.go b/commands.go index e4ea007d2..dfc51f35b 100644 --- a/commands.go +++ b/commands.go @@ -26,14 +26,6 @@ var commandsTree = newTrie() // Collection of minio flags currently supported. var globalFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "quiet, q", - Usage: "Suppress chatty console output.", - }, - cli.BoolFlag{ - Name: "debug", - Usage: "Enable debugging output.", - }, cli.StringFlag{ Name: "config-dir, C", Value: mustGetConfigPath(), diff --git a/globals.go b/globals.go index bb65c27bc..ad270b1fb 100644 --- a/globals.go +++ b/globals.go @@ -35,6 +35,8 @@ const ( globalMinioCertFile = "public.crt" globalMinioKeyFile = "private.key" globalMinioConfigFile = "config.json" + globalMinioProfilePath = "profile" + // Add new global values here. ) var ( diff --git a/main.go b/main.go index ebaa2f709..2b1fdf926 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,7 @@ package main import ( "fmt" "os" + "path/filepath" "runtime" "sort" "strconv" @@ -28,6 +29,7 @@ import ( "github.com/minio/mc/pkg/console" "github.com/minio/minio/pkg/probe" "github.com/olekukonko/ts" + "github.com/pkg/profile" ) var ( @@ -176,7 +178,23 @@ func checkMainSyntax(c *cli.Context) { } } +// mustGetProfilePath must get location that the profile will be written to. +func mustGetProfilePath() string { + return filepath.Join(mustGetConfigPath(), globalMinioProfilePath) +} + func main() { + // Enable profiling supported modes are [cpu, mem, block]. + // ``MINIO_PROFILER`` supported options are [cpu, mem, block]. + switch os.Getenv("MINIO_PROFILER") { + case "cpu": + defer profile.Start(profile.CPUProfile, profile.ProfilePath(mustGetProfilePath())).Stop() + case "mem": + defer profile.Start(profile.MemProfile, profile.ProfilePath(mustGetProfilePath())).Stop() + case "block": + defer profile.Start(profile.BlockProfile, profile.ProfilePath(mustGetProfilePath())).Stop() + } + probe.Init() // Set project's root source path. probe.SetAppInfo("Release-Tag", minioReleaseTag) probe.SetAppInfo("Commit-ID", minioShortCommitID) diff --git a/vendor/github.com/pkg/profile/AUTHORS b/vendor/github.com/pkg/profile/AUTHORS new file mode 100644 index 000000000..00441d354 --- /dev/null +++ b/vendor/github.com/pkg/profile/AUTHORS @@ -0,0 +1 @@ +Dave Cheney diff --git a/vendor/github.com/pkg/profile/LICENSE b/vendor/github.com/pkg/profile/LICENSE new file mode 100644 index 000000000..f747a8411 --- /dev/null +++ b/vendor/github.com/pkg/profile/LICENSE @@ -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. diff --git a/vendor/github.com/pkg/profile/README.md b/vendor/github.com/pkg/profile/README.md new file mode 100644 index 000000000..e856f896c --- /dev/null +++ b/vendor/github.com/pkg/profile/README.md @@ -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). diff --git a/vendor/github.com/pkg/profile/profile.go b/vendor/github.com/pkg/profile/profile.go new file mode 100644 index 000000000..38c3e4762 --- /dev/null +++ b/vendor/github.com/pkg/profile/profile.go @@ -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 +} diff --git a/vendor/github.com/pkg/profile/wercker.yml b/vendor/github.com/pkg/profile/wercker.yml new file mode 100644 index 000000000..41d2c5280 --- /dev/null +++ b/vendor/github.com/pkg/profile/wercker.yml @@ -0,0 +1 @@ +box: wercker/golang diff --git a/vendor/vendor.json b/vendor/vendor.json index f34171c9b..617014c13 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -107,6 +107,11 @@ "revision": "ecf753e7c962639ab5a1fb46f7da627d4c0a04b8", "revisionTime": "2014-04-12T15:01:45-07:00" }, + { + "path": "github.com/pkg/profile", + "revision": "19396ecfda37c1c9ddaa52019adca59d582e4832", + "revisionTime": "2016-03-15T12:21:13+11:00" + }, { "path": "github.com/rs/cors", "revision": "eb527c8097e0f19a3ff7b253a3fe70545070f420",