profile.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. // +build linux darwin
  2. package proc
  3. import (
  4. "fmt"
  5. "os"
  6. "os/signal"
  7. "path"
  8. "runtime"
  9. "runtime/pprof"
  10. "runtime/trace"
  11. "sync/atomic"
  12. "syscall"
  13. "time"
  14. "git.i2edu.net/i2/go-zero/core/logx"
  15. )
  16. // DefaultMemProfileRate is the default memory profiling rate.
  17. // See also http://golang.org/pkg/runtime/#pkg-variables
  18. const DefaultMemProfileRate = 4096
  19. // started is non zero if a profile is running.
  20. var started uint32
  21. // Profile represents an active profiling session.
  22. type Profile struct {
  23. // closers holds cleanup functions that run after each profile
  24. closers []func()
  25. // stopped records if a call to profile.Stop has been made
  26. stopped uint32
  27. }
  28. func (p *Profile) close() {
  29. for _, closer := range p.closers {
  30. closer()
  31. }
  32. }
  33. func (p *Profile) startBlockProfile() {
  34. fn := createDumpFile("block")
  35. f, err := os.Create(fn)
  36. if err != nil {
  37. logx.Errorf("profile: could not create block profile %q: %v", fn, err)
  38. return
  39. }
  40. runtime.SetBlockProfileRate(1)
  41. logx.Infof("profile: block profiling enabled, %s", fn)
  42. p.closers = append(p.closers, func() {
  43. pprof.Lookup("block").WriteTo(f, 0)
  44. f.Close()
  45. runtime.SetBlockProfileRate(0)
  46. logx.Infof("profile: block profiling disabled, %s", fn)
  47. })
  48. }
  49. func (p *Profile) startCpuProfile() {
  50. fn := createDumpFile("cpu")
  51. f, err := os.Create(fn)
  52. if err != nil {
  53. logx.Errorf("profile: could not create cpu profile %q: %v", fn, err)
  54. return
  55. }
  56. logx.Infof("profile: cpu profiling enabled, %s", fn)
  57. pprof.StartCPUProfile(f)
  58. p.closers = append(p.closers, func() {
  59. pprof.StopCPUProfile()
  60. f.Close()
  61. logx.Infof("profile: cpu profiling disabled, %s", fn)
  62. })
  63. }
  64. func (p *Profile) startMemProfile() {
  65. fn := createDumpFile("mem")
  66. f, err := os.Create(fn)
  67. if err != nil {
  68. logx.Errorf("profile: could not create memory profile %q: %v", fn, err)
  69. return
  70. }
  71. old := runtime.MemProfileRate
  72. runtime.MemProfileRate = DefaultMemProfileRate
  73. logx.Infof("profile: memory profiling enabled (rate %d), %s", runtime.MemProfileRate, fn)
  74. p.closers = append(p.closers, func() {
  75. pprof.Lookup("heap").WriteTo(f, 0)
  76. f.Close()
  77. runtime.MemProfileRate = old
  78. logx.Infof("profile: memory profiling disabled, %s", fn)
  79. })
  80. }
  81. func (p *Profile) startMutexProfile() {
  82. fn := createDumpFile("mutex")
  83. f, err := os.Create(fn)
  84. if err != nil {
  85. logx.Errorf("profile: could not create mutex profile %q: %v", fn, err)
  86. return
  87. }
  88. runtime.SetMutexProfileFraction(1)
  89. logx.Infof("profile: mutex profiling enabled, %s", fn)
  90. p.closers = append(p.closers, func() {
  91. if mp := pprof.Lookup("mutex"); mp != nil {
  92. mp.WriteTo(f, 0)
  93. }
  94. f.Close()
  95. runtime.SetMutexProfileFraction(0)
  96. logx.Infof("profile: mutex profiling disabled, %s", fn)
  97. })
  98. }
  99. func (p *Profile) startThreadCreateProfile() {
  100. fn := createDumpFile("threadcreate")
  101. f, err := os.Create(fn)
  102. if err != nil {
  103. logx.Errorf("profile: could not create threadcreate profile %q: %v", fn, err)
  104. return
  105. }
  106. logx.Infof("profile: threadcreate profiling enabled, %s", fn)
  107. p.closers = append(p.closers, func() {
  108. if mp := pprof.Lookup("threadcreate"); mp != nil {
  109. mp.WriteTo(f, 0)
  110. }
  111. f.Close()
  112. logx.Infof("profile: threadcreate profiling disabled, %s", fn)
  113. })
  114. }
  115. func (p *Profile) startTraceProfile() {
  116. fn := createDumpFile("trace")
  117. f, err := os.Create(fn)
  118. if err != nil {
  119. logx.Errorf("profile: could not create trace output file %q: %v", fn, err)
  120. return
  121. }
  122. if err := trace.Start(f); err != nil {
  123. logx.Errorf("profile: could not start trace: %v", err)
  124. return
  125. }
  126. logx.Infof("profile: trace enabled, %s", fn)
  127. p.closers = append(p.closers, func() {
  128. trace.Stop()
  129. logx.Infof("profile: trace disabled, %s", fn)
  130. })
  131. }
  132. // Stop stops the profile and flushes any unwritten data.
  133. func (p *Profile) Stop() {
  134. if !atomic.CompareAndSwapUint32(&p.stopped, 0, 1) {
  135. // someone has already called close
  136. return
  137. }
  138. p.close()
  139. atomic.StoreUint32(&started, 0)
  140. }
  141. // StartProfile starts a new profiling session.
  142. // The caller should call the Stop method on the value returned
  143. // to cleanly stop profiling.
  144. func StartProfile() Stopper {
  145. if !atomic.CompareAndSwapUint32(&started, 0, 1) {
  146. logx.Error("profile: Start() already called")
  147. return noopStopper
  148. }
  149. var prof Profile
  150. prof.startCpuProfile()
  151. prof.startMemProfile()
  152. prof.startMutexProfile()
  153. prof.startBlockProfile()
  154. prof.startTraceProfile()
  155. prof.startThreadCreateProfile()
  156. go func() {
  157. c := make(chan os.Signal, 1)
  158. signal.Notify(c, syscall.SIGINT)
  159. <-c
  160. logx.Info("profile: caught interrupt, stopping profiles")
  161. prof.Stop()
  162. signal.Reset()
  163. syscall.Kill(os.Getpid(), syscall.SIGINT)
  164. }()
  165. return &prof
  166. }
  167. func createDumpFile(kind string) string {
  168. command := path.Base(os.Args[0])
  169. pid := syscall.Getpid()
  170. return path.Join(os.TempDir(), fmt.Sprintf("%s-%d-%s-%s.pprof",
  171. command, pid, kind, time.Now().Format(timeFormat)))
  172. }