github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/profile/profile.go (about) 1 // Package profile provides a simple way to manage runtime/pprof 2 // profiling of your Go application. 3 package profile 4 5 import ( 6 "io/ioutil" 7 "log" 8 "os" 9 "os/signal" 10 "path/filepath" 11 "runtime" 12 "runtime/pprof" 13 "runtime/trace" 14 "sync/atomic" 15 ) 16 17 const ( 18 cpuMode = iota 19 memMode 20 mutexMode 21 blockMode 22 traceMode 23 threadCreateMode 24 goroutineMode 25 ) 26 27 // Profile represents an active profiling session. 28 type Profile struct { 29 // quiet suppresses informational messages during profiling. 30 quiet bool 31 32 // noShutdownHook controls whether the profiling package should 33 // hook SIGINT to write profiles cleanly. 34 noShutdownHook bool 35 36 // mode holds the type of profiling that will be made 37 mode int 38 39 // path holds the base path where various profiling files are written. 40 // If blank, the base path will be generated by ioutil.TempDir. 41 path string 42 43 // memProfileRate holds the rate for the memory profile. 44 memProfileRate int 45 46 // memProfileType holds the profile type for memory 47 // profiles. Allowed values are `heap` and `allocs`. 48 memProfileType string 49 50 // closer holds a cleanup function that run after each profile 51 closer func() 52 53 // stopped records if a call to profile.Stop has been made 54 stopped uint32 55 } 56 57 // NoShutdownHook controls whether the profiling package should 58 // hook SIGINT to write profiles cleanly. 59 // Programs with more sophisticated signal handling should set 60 // this to true and ensure the Stop() function returned from Start() 61 // is called during shutdown. 62 func NoShutdownHook(p *Profile) { p.noShutdownHook = true } 63 64 // Quiet suppresses informational messages during profiling. 65 func Quiet(p *Profile) { p.quiet = true } 66 67 // CPUProfile enables cpu profiling. 68 // It disables any previous profiling settings. 69 func CPUProfile(p *Profile) { p.mode = cpuMode } 70 71 // DefaultMemProfileRate is the default memory profiling rate. 72 // See also http://golang.org/pkg/runtime/#pkg-variables 73 const DefaultMemProfileRate = 4096 74 75 // MemProfile enables memory profiling. 76 // It disables any previous profiling settings. 77 func MemProfile(p *Profile) { 78 p.memProfileRate = DefaultMemProfileRate 79 p.mode = memMode 80 } 81 82 // MemProfileRate enables memory profiling at the preferred rate. 83 // It disables any previous profiling settings. 84 func MemProfileRate(rate int) func(*Profile) { 85 return func(p *Profile) { 86 p.memProfileRate = rate 87 p.mode = memMode 88 } 89 } 90 91 // MemProfileHeap changes which type of memory profiling to profile 92 // the heap. 93 func MemProfileHeap(p *Profile) { 94 p.memProfileType = "heap" 95 p.mode = memMode 96 } 97 98 // MemProfileAllocs changes which type of memory to profile 99 // allocations. 100 func MemProfileAllocs(p *Profile) { 101 p.memProfileType = "allocs" 102 p.mode = memMode 103 } 104 105 // MutexProfile enables mutex profiling. 106 // It disables any previous profiling settings. 107 func MutexProfile(p *Profile) { p.mode = mutexMode } 108 109 // BlockProfile enables block (contention) profiling. 110 // It disables any previous profiling settings. 111 func BlockProfile(p *Profile) { p.mode = blockMode } 112 113 // Trace profile enables execution tracing. 114 // It disables any previous profiling settings. 115 func TraceProfile(p *Profile) { p.mode = traceMode } 116 117 // ThreadcreationProfile enables thread creation profiling.. 118 // It disables any previous profiling settings. 119 func ThreadcreationProfile(p *Profile) { p.mode = threadCreateMode } 120 121 // GoroutineProfile enables goroutine profiling. 122 // It disables any previous profiling settings. 123 func GoroutineProfile(p *Profile) { p.mode = goroutineMode } 124 125 // ProfilePath controls the base path where various profiling 126 // files are written. If blank, the base path will be generated 127 // by ioutil.TempDir. 128 func ProfilePath(path string) func(*Profile) { 129 return func(p *Profile) { 130 p.path = path 131 } 132 } 133 134 // Stop stops the profile and flushes any unwritten data. 135 func (p *Profile) Stop() { 136 if !atomic.CompareAndSwapUint32(&p.stopped, 0, 1) { 137 // someone has already called close 138 return 139 } 140 p.closer() 141 atomic.StoreUint32(&started, 0) 142 } 143 144 // started is non zero if a profile is running. 145 var started uint32 146 147 // Start starts a new profiling session. 148 // The caller should call the Stop method on the value returned 149 // to cleanly stop profiling. 150 func Start(options ...func(*Profile)) interface { 151 Stop() 152 } { 153 if !atomic.CompareAndSwapUint32(&started, 0, 1) { 154 log.Fatal("profile: Start() already called") 155 } 156 157 var prof Profile 158 for _, option := range options { 159 option(&prof) 160 } 161 162 path, err := func() (string, error) { 163 if p := prof.path; p != "" { 164 return p, os.MkdirAll(p, 0777) 165 } 166 return ioutil.TempDir("", "profile") 167 }() 168 169 if err != nil { 170 log.Fatalf("profile: could not create initial output directory: %v", err) 171 } 172 173 logf := func(format string, args ...interface{}) { 174 if !prof.quiet { 175 log.Printf(format, args...) 176 } 177 } 178 179 if prof.memProfileType == "" { 180 prof.memProfileType = "heap" 181 } 182 183 switch prof.mode { 184 case cpuMode: 185 fn := filepath.Join(path, "cpu.pprof") 186 f, err := os.Create(fn) 187 if err != nil { 188 log.Fatalf("profile: could not create cpu profile %q: %v", fn, err) 189 } 190 logf("profile: cpu profiling enabled, %s", fn) 191 pprof.StartCPUProfile(f) 192 prof.closer = func() { 193 pprof.StopCPUProfile() 194 f.Close() 195 logf("profile: cpu profiling disabled, %s", fn) 196 } 197 198 case memMode: 199 fn := filepath.Join(path, "mem.pprof") 200 f, err := os.Create(fn) 201 if err != nil { 202 log.Fatalf("profile: could not create memory profile %q: %v", fn, err) 203 } 204 old := runtime.MemProfileRate 205 runtime.MemProfileRate = prof.memProfileRate 206 logf("profile: memory profiling enabled (rate %d), %s", runtime.MemProfileRate, fn) 207 prof.closer = func() { 208 pprof.Lookup(prof.memProfileType).WriteTo(f, 0) 209 f.Close() 210 runtime.MemProfileRate = old 211 logf("profile: memory profiling disabled, %s", fn) 212 } 213 214 case mutexMode: 215 fn := filepath.Join(path, "mutex.pprof") 216 f, err := os.Create(fn) 217 if err != nil { 218 log.Fatalf("profile: could not create mutex profile %q: %v", fn, err) 219 } 220 runtime.SetMutexProfileFraction(1) 221 logf("profile: mutex profiling enabled, %s", fn) 222 prof.closer = func() { 223 if mp := pprof.Lookup("mutex"); mp != nil { 224 mp.WriteTo(f, 0) 225 } 226 f.Close() 227 runtime.SetMutexProfileFraction(0) 228 logf("profile: mutex profiling disabled, %s", fn) 229 } 230 231 case blockMode: 232 fn := filepath.Join(path, "block.pprof") 233 f, err := os.Create(fn) 234 if err != nil { 235 log.Fatalf("profile: could not create block profile %q: %v", fn, err) 236 } 237 runtime.SetBlockProfileRate(1) 238 logf("profile: block profiling enabled, %s", fn) 239 prof.closer = func() { 240 pprof.Lookup("block").WriteTo(f, 0) 241 f.Close() 242 runtime.SetBlockProfileRate(0) 243 logf("profile: block profiling disabled, %s", fn) 244 } 245 246 case threadCreateMode: 247 fn := filepath.Join(path, "threadcreation.pprof") 248 f, err := os.Create(fn) 249 if err != nil { 250 log.Fatalf("profile: could not create thread creation profile %q: %v", fn, err) 251 } 252 logf("profile: thread creation profiling enabled, %s", fn) 253 prof.closer = func() { 254 if mp := pprof.Lookup("threadcreate"); mp != nil { 255 mp.WriteTo(f, 0) 256 } 257 f.Close() 258 logf("profile: thread creation profiling disabled, %s", fn) 259 } 260 261 case traceMode: 262 fn := filepath.Join(path, "trace.out") 263 f, err := os.Create(fn) 264 if err != nil { 265 log.Fatalf("profile: could not create trace output file %q: %v", fn, err) 266 } 267 if err := trace.Start(f); err != nil { 268 log.Fatalf("profile: could not start trace: %v", err) 269 } 270 logf("profile: trace enabled, %s", fn) 271 prof.closer = func() { 272 trace.Stop() 273 logf("profile: trace disabled, %s", fn) 274 } 275 276 case goroutineMode: 277 fn := filepath.Join(path, "goroutine.pprof") 278 f, err := os.Create(fn) 279 if err != nil { 280 log.Fatalf("profile: could not create goroutine profile %q: %v", fn, err) 281 } 282 logf("profile: goroutine profiling enabled, %s", fn) 283 prof.closer = func() { 284 if mp := pprof.Lookup("goroutine"); mp != nil { 285 mp.WriteTo(f, 0) 286 } 287 f.Close() 288 logf("profile: goroutine profiling disabled, %s", fn) 289 } 290 } 291 292 if !prof.noShutdownHook { 293 go func() { 294 c := make(chan os.Signal, 1) 295 signal.Notify(c, os.Interrupt) 296 <-c 297 298 log.Println("profile: caught interrupt, stopping profiles") 299 prof.Stop() 300 301 os.Exit(0) 302 }() 303 } 304 305 return &prof 306 }