github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/debug/profile/pprof/pprof.go (about)

     1  // Licensed under the Apache License, Version 2.0 (the "License");
     2  // you may not use this file except in compliance with the License.
     3  // You may obtain a copy of the License at
     4  //
     5  //     https://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS,
     9  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  // See the License for the specific language governing permissions and
    11  // limitations under the License.
    12  //
    13  // Original source: github.com/micro/go-micro/v3/debug/profile/pprof/pprof.go
    14  
    15  // Package pprof provides a pprof profiler which writes output to /tmp/[name].{cpu,mem}.pprof
    16  package pprof
    17  
    18  import (
    19  	"os"
    20  	"path/filepath"
    21  	"runtime"
    22  	"runtime/pprof"
    23  	"sync"
    24  	"time"
    25  
    26  	"github.com/tickoalcantara12/micro/v3/service/debug/profile"
    27  )
    28  
    29  type profiler struct {
    30  	opts profile.Options
    31  
    32  	sync.Mutex
    33  	running bool
    34  	exit    chan bool
    35  
    36  	// where the cpu profile is written
    37  	cpuFile *os.File
    38  	// where the mem profile is written
    39  	memFile *os.File
    40  }
    41  
    42  func (p *profiler) writeHeap(f *os.File) {
    43  	defer f.Close()
    44  
    45  	t := time.NewTicker(time.Second * 30)
    46  	defer t.Stop()
    47  
    48  	for {
    49  		select {
    50  		case <-t.C:
    51  			runtime.GC()
    52  			pprof.WriteHeapProfile(f)
    53  		case <-p.exit:
    54  			return
    55  		}
    56  	}
    57  }
    58  
    59  func (p *profiler) Start() error {
    60  	p.Lock()
    61  	defer p.Unlock()
    62  
    63  	if p.running {
    64  		return nil
    65  	}
    66  
    67  	// create exit channel
    68  	p.exit = make(chan bool)
    69  
    70  	cpuFile := filepath.Join("/tmp", "cpu.pprof")
    71  	memFile := filepath.Join("/tmp", "mem.pprof")
    72  
    73  	if len(p.opts.Name) > 0 {
    74  		cpuFile = filepath.Join("/tmp", p.opts.Name+".cpu.pprof")
    75  		memFile = filepath.Join("/tmp", p.opts.Name+".mem.pprof")
    76  	}
    77  
    78  	f1, err := os.Create(cpuFile)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	f2, err := os.Create(memFile)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	// start cpu profiling
    89  	if err := pprof.StartCPUProfile(f1); err != nil {
    90  		return err
    91  	}
    92  
    93  	// write the heap periodically
    94  	go p.writeHeap(f2)
    95  
    96  	// set cpu file
    97  	p.cpuFile = f1
    98  	// set mem file
    99  	p.memFile = f2
   100  
   101  	p.running = true
   102  
   103  	return nil
   104  }
   105  
   106  func (p *profiler) Stop() error {
   107  	p.Lock()
   108  	defer p.Unlock()
   109  
   110  	select {
   111  	case <-p.exit:
   112  		return nil
   113  	default:
   114  		close(p.exit)
   115  		pprof.StopCPUProfile()
   116  		p.cpuFile.Close()
   117  		p.running = false
   118  		p.cpuFile = nil
   119  		p.memFile = nil
   120  		return nil
   121  	}
   122  }
   123  
   124  func (p *profiler) String() string {
   125  	return "pprof"
   126  }
   127  
   128  func NewProfile(opts ...profile.Option) profile.Profile {
   129  	var options profile.Options
   130  	for _, o := range opts {
   131  		o(&options)
   132  	}
   133  	p := new(profiler)
   134  	p.opts = options
   135  	return p
   136  }