profile.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. package xweb
  2. import (
  3. "fmt"
  4. "io"
  5. "log"
  6. "os"
  7. "runtime"
  8. "runtime/debug"
  9. runtimePprof "runtime/pprof"
  10. "strconv"
  11. "time"
  12. )
  13. var startTime = time.Now()
  14. var pid int
  15. func init() {
  16. pid = os.Getpid()
  17. }
  18. // start cpu profile monitor
  19. func StartCPUProfile() {
  20. f, err := os.Create("cpu-" + strconv.Itoa(pid) + ".pprof")
  21. if err != nil {
  22. log.Fatal(err)
  23. }
  24. runtimePprof.StartCPUProfile(f)
  25. }
  26. // stop cpu profile monitor
  27. func StopCPUProfile() {
  28. runtimePprof.StopCPUProfile()
  29. }
  30. // print gc information to io.Writer
  31. func PrintGCSummary(w io.Writer) {
  32. memStats := &runtime.MemStats{}
  33. runtime.ReadMemStats(memStats)
  34. gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)}
  35. debug.ReadGCStats(gcstats)
  36. printGC(memStats, gcstats, w)
  37. }
  38. func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats, w io.Writer) {
  39. if gcstats.NumGC > 0 {
  40. lastPause := gcstats.Pause[0]
  41. elapsed := time.Now().Sub(startTime)
  42. overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100
  43. allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds()
  44. 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",
  45. gcstats.NumGC,
  46. FriendlyTime(lastPause),
  47. FriendlyTime(AvgTime(gcstats.Pause)),
  48. overhead,
  49. FriendlyBytes(memStats.Alloc),
  50. FriendlyBytes(memStats.Sys),
  51. FriendlyBytes(uint64(allocatedRate)),
  52. FriendlyTime(gcstats.PauseQuantiles[94]),
  53. FriendlyTime(gcstats.PauseQuantiles[98]),
  54. FriendlyTime(gcstats.PauseQuantiles[99]))
  55. } else {
  56. // while GC has disabled
  57. elapsed := time.Now().Sub(startTime)
  58. allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds()
  59. fmt.Fprintf(w, "Alloc:%s Sys:%s Alloc(Rate):%s/s\n",
  60. FriendlyBytes(memStats.Alloc),
  61. FriendlyBytes(memStats.Sys),
  62. FriendlyBytes(uint64(allocatedRate)))
  63. }
  64. }
  65. func AvgTime(items []time.Duration) time.Duration {
  66. var sum time.Duration
  67. for _, item := range items {
  68. sum += item
  69. }
  70. return time.Duration(int64(sum) / int64(len(items)))
  71. }
  72. // format bytes number friendly
  73. func FriendlyBytes(bytes uint64) string {
  74. units := [...]string{"YB", "ZB", "EB", "PB", "TB", "GB", "MB", "KB", "B"}
  75. total := len(units)
  76. for total--; total > 0 && bytes > 1024; total-- {
  77. bytes /= 1024
  78. }
  79. return fmt.Sprintf("%d%s", bytes, units[total])
  80. }
  81. // short string format
  82. func FriendlyTime(d time.Duration) string {
  83. u := uint64(d)
  84. if u < uint64(time.Second) {
  85. switch {
  86. case u == 0:
  87. return "0"
  88. case u < uint64(time.Microsecond):
  89. return fmt.Sprintf("%.2fns", float64(u))
  90. case u < uint64(time.Millisecond):
  91. return fmt.Sprintf("%.2fus", float64(u)/1000)
  92. default:
  93. return fmt.Sprintf("%.2fms", float64(u)/1000/1000)
  94. }
  95. } else {
  96. switch {
  97. case u < uint64(time.Minute):
  98. return fmt.Sprintf("%.2fs", float64(u)/1000/1000/1000)
  99. case u < uint64(time.Hour):
  100. return fmt.Sprintf("%.2fm", float64(u)/1000/1000/1000/60)
  101. default:
  102. return fmt.Sprintf("%.2fh", float64(u)/1000/1000/1000/60/60)
  103. }
  104. }
  105. }