diff options
Diffstat (limited to 'vendor/github.com/go-macaron/toolbox/profile.go')
-rw-r--r-- | vendor/github.com/go-macaron/toolbox/profile.go | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/vendor/github.com/go-macaron/toolbox/profile.go b/vendor/github.com/go-macaron/toolbox/profile.go new file mode 100644 index 00000000..1eb2cdfe --- /dev/null +++ b/vendor/github.com/go-macaron/toolbox/profile.go @@ -0,0 +1,163 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package toolbox + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "path" + "runtime" + "runtime/debug" + "runtime/pprof" + "time" + + "github.com/Unknwon/com" + "gopkg.in/macaron.v1" +) + +var ( + profilePath string + pid int + startTime = time.Now() + inCPUProfile bool +) + +// StartCPUProfile starts CPU profile monitor. +func StartCPUProfile() error { + if inCPUProfile { + return errors.New("CPU profile has alreday been started!") + } + inCPUProfile = true + + os.MkdirAll(profilePath, os.ModePerm) + f, err := os.Create(path.Join(profilePath, "cpu-"+com.ToStr(pid)+".pprof")) + if err != nil { + panic("fail to record CPU profile: " + err.Error()) + } + pprof.StartCPUProfile(f) + return nil +} + +// StopCPUProfile stops CPU profile monitor. +func StopCPUProfile() error { + if !inCPUProfile { + return errors.New("CPU profile hasn't been started!") + } + pprof.StopCPUProfile() + inCPUProfile = false + return nil +} + +func init() { + pid = os.Getpid() +} + +// DumpMemProf dumps memory profile in pprof. +func DumpMemProf(w io.Writer) { + pprof.WriteHeapProfile(w) +} + +func dumpMemProf() { + os.MkdirAll(profilePath, os.ModePerm) + f, err := os.Create(path.Join(profilePath, "mem-"+com.ToStr(pid)+".memprof")) + if err != nil { + panic("fail to record memory profile: " + err.Error()) + } + runtime.GC() + DumpMemProf(f) + f.Close() +} + +func avg(items []time.Duration) time.Duration { + var sum time.Duration + for _, item := range items { + sum += item + } + return time.Duration(int64(sum) / int64(len(items))) +} + +func dumpGC(memStats *runtime.MemStats, gcstats *debug.GCStats, w io.Writer) { + + if gcstats.NumGC > 0 { + lastPause := gcstats.Pause[0] + elapsed := time.Now().Sub(startTime) + overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100 + allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() + + fmt.Fprintf(w, "NumGC:%d Pause:%s Pause(Avg):%s Overhead:%3.2f%% Alloc:%s Sys:%s Alloc(Rate):%s/s Histogram:%s %s %s \n", + gcstats.NumGC, + com.ToStr(lastPause), + com.ToStr(avg(gcstats.Pause)), + overhead, + com.HumaneFileSize(memStats.Alloc), + com.HumaneFileSize(memStats.Sys), + com.HumaneFileSize(uint64(allocatedRate)), + com.ToStr(gcstats.PauseQuantiles[94]), + com.ToStr(gcstats.PauseQuantiles[98]), + com.ToStr(gcstats.PauseQuantiles[99])) + } else { + // while GC has disabled + elapsed := time.Now().Sub(startTime) + allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() + + fmt.Fprintf(w, "Alloc:%s Sys:%s Alloc(Rate):%s/s\n", + com.HumaneFileSize(memStats.Alloc), + com.HumaneFileSize(memStats.Sys), + com.HumaneFileSize(uint64(allocatedRate))) + } +} + +// DumpGCSummary dumps GC information to io.Writer +func DumpGCSummary(w io.Writer) { + memStats := &runtime.MemStats{} + runtime.ReadMemStats(memStats) + gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)} + debug.ReadGCStats(gcstats) + + dumpGC(memStats, gcstats, w) +} + +func handleProfile(ctx *macaron.Context) string { + switch ctx.Query("op") { + case "startcpu": + if err := StartCPUProfile(); err != nil { + return err.Error() + } + case "stopcpu": + if err := StopCPUProfile(); err != nil { + return err.Error() + } + case "mem": + dumpMemProf() + case "gc": + var buf bytes.Buffer + DumpGCSummary(&buf) + return string(buf.Bytes()) + default: + return fmt.Sprintf(`<p>Available operations:</p> +<ol> + <li><a href="%[1]s?op=startcpu">Start CPU profile</a></li> + <li><a href="%[1]s?op=stopcpu">Stop CPU profile</a></li> + <li><a href="%[1]s?op=mem">Dump memory profile</a></li> + <li><a href="%[1]s?op=gc">Dump GC summary</a></li> +</ol>`, opt.ProfileURLPrefix) + } + ctx.Redirect(opt.ProfileURLPrefix) + return "" +} |