aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/go-macaron/toolbox/profile.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/go-macaron/toolbox/profile.go')
-rw-r--r--vendor/github.com/go-macaron/toolbox/profile.go163
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 ""
+}