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  }