github.com/astaxie/beego@v1.12.3/toolbox/profile.go (about)

     1  // Copyright 2014 beego Author. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package toolbox
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"log"
    21  	"os"
    22  	"path"
    23  	"runtime"
    24  	"runtime/debug"
    25  	"runtime/pprof"
    26  	"strconv"
    27  	"time"
    28  )
    29  
    30  var startTime = time.Now()
    31  var pid int
    32  
    33  func init() {
    34  	pid = os.Getpid()
    35  }
    36  
    37  // ProcessInput parse input command string
    38  func ProcessInput(input string, w io.Writer) {
    39  	switch input {
    40  	case "lookup goroutine":
    41  		p := pprof.Lookup("goroutine")
    42  		p.WriteTo(w, 2)
    43  	case "lookup heap":
    44  		p := pprof.Lookup("heap")
    45  		p.WriteTo(w, 2)
    46  	case "lookup threadcreate":
    47  		p := pprof.Lookup("threadcreate")
    48  		p.WriteTo(w, 2)
    49  	case "lookup block":
    50  		p := pprof.Lookup("block")
    51  		p.WriteTo(w, 2)
    52  	case "get cpuprof":
    53  		GetCPUProfile(w)
    54  	case "get memprof":
    55  		MemProf(w)
    56  	case "gc summary":
    57  		PrintGCSummary(w)
    58  	}
    59  }
    60  
    61  // MemProf record memory profile in pprof
    62  func MemProf(w io.Writer) {
    63  	filename := "mem-" + strconv.Itoa(pid) + ".memprof"
    64  	if f, err := os.Create(filename); err != nil {
    65  		fmt.Fprintf(w, "create file %s error %s\n", filename, err.Error())
    66  		log.Fatal("record heap profile failed: ", err)
    67  	} else {
    68  		runtime.GC()
    69  		pprof.WriteHeapProfile(f)
    70  		f.Close()
    71  		fmt.Fprintf(w, "create heap profile %s \n", filename)
    72  		_, fl := path.Split(os.Args[0])
    73  		fmt.Fprintf(w, "Now you can use this to check it: go tool pprof %s %s\n", fl, filename)
    74  	}
    75  }
    76  
    77  // GetCPUProfile start cpu profile monitor
    78  func GetCPUProfile(w io.Writer) {
    79  	sec := 30
    80  	filename := "cpu-" + strconv.Itoa(pid) + ".pprof"
    81  	f, err := os.Create(filename)
    82  	if err != nil {
    83  		fmt.Fprintf(w, "Could not enable CPU profiling: %s\n", err)
    84  		log.Fatal("record cpu profile failed: ", err)
    85  	}
    86  	pprof.StartCPUProfile(f)
    87  	time.Sleep(time.Duration(sec) * time.Second)
    88  	pprof.StopCPUProfile()
    89  
    90  	fmt.Fprintf(w, "create cpu profile %s \n", filename)
    91  	_, fl := path.Split(os.Args[0])
    92  	fmt.Fprintf(w, "Now you can use this to check it: go tool pprof %s %s\n", fl, filename)
    93  }
    94  
    95  // PrintGCSummary print gc information to io.Writer
    96  func PrintGCSummary(w io.Writer) {
    97  	memStats := &runtime.MemStats{}
    98  	runtime.ReadMemStats(memStats)
    99  	gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)}
   100  	debug.ReadGCStats(gcstats)
   101  
   102  	printGC(memStats, gcstats, w)
   103  }
   104  
   105  func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats, w io.Writer) {
   106  
   107  	if gcstats.NumGC > 0 {
   108  		lastPause := gcstats.Pause[0]
   109  		elapsed := time.Now().Sub(startTime)
   110  		overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100
   111  		allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds()
   112  
   113  		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",
   114  			gcstats.NumGC,
   115  			toS(lastPause),
   116  			toS(avg(gcstats.Pause)),
   117  			overhead,
   118  			toH(memStats.Alloc),
   119  			toH(memStats.Sys),
   120  			toH(uint64(allocatedRate)),
   121  			toS(gcstats.PauseQuantiles[94]),
   122  			toS(gcstats.PauseQuantiles[98]),
   123  			toS(gcstats.PauseQuantiles[99]))
   124  	} else {
   125  		// while GC has disabled
   126  		elapsed := time.Now().Sub(startTime)
   127  		allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds()
   128  
   129  		fmt.Fprintf(w, "Alloc:%s Sys:%s Alloc(Rate):%s/s\n",
   130  			toH(memStats.Alloc),
   131  			toH(memStats.Sys),
   132  			toH(uint64(allocatedRate)))
   133  	}
   134  }
   135  
   136  func avg(items []time.Duration) time.Duration {
   137  	var sum time.Duration
   138  	for _, item := range items {
   139  		sum += item
   140  	}
   141  	return time.Duration(int64(sum) / int64(len(items)))
   142  }
   143  
   144  // format bytes number friendly
   145  func toH(bytes uint64) string {
   146  	switch {
   147  	case bytes < 1024:
   148  		return fmt.Sprintf("%dB", bytes)
   149  	case bytes < 1024*1024:
   150  		return fmt.Sprintf("%.2fK", float64(bytes)/1024)
   151  	case bytes < 1024*1024*1024:
   152  		return fmt.Sprintf("%.2fM", float64(bytes)/1024/1024)
   153  	default:
   154  		return fmt.Sprintf("%.2fG", float64(bytes)/1024/1024/1024)
   155  	}
   156  }
   157  
   158  // short string format
   159  func toS(d time.Duration) string {
   160  
   161  	u := uint64(d)
   162  	if u < uint64(time.Second) {
   163  		switch {
   164  		case u == 0:
   165  			return "0"
   166  		case u < uint64(time.Microsecond):
   167  			return fmt.Sprintf("%.2fns", float64(u))
   168  		case u < uint64(time.Millisecond):
   169  			return fmt.Sprintf("%.2fus", float64(u)/1000)
   170  		default:
   171  			return fmt.Sprintf("%.2fms", float64(u)/1000/1000)
   172  		}
   173  	} else {
   174  		switch {
   175  		case u < uint64(time.Minute):
   176  			return fmt.Sprintf("%.2fs", float64(u)/1000/1000/1000)
   177  		case u < uint64(time.Hour):
   178  			return fmt.Sprintf("%.2fm", float64(u)/1000/1000/1000/60)
   179  		default:
   180  			return fmt.Sprintf("%.2fh", float64(u)/1000/1000/1000/60/60)
   181  		}
   182  	}
   183  
   184  }