github.com/MetalBlockchain/metalgo@v1.11.9/utils/profiler/profiler.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package profiler
     5  
     6  import (
     7  	"errors"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  	"runtime/pprof"
    12  
    13  	"github.com/MetalBlockchain/metalgo/utils/perms"
    14  )
    15  
    16  const (
    17  	// Name of file that CPU profile is written to when StartCPUProfiler called
    18  	cpuProfileFile = "cpu.profile"
    19  	// Name of file that memory profile is written to when MemoryProfile called
    20  	memProfileFile = "mem.profile"
    21  	// Name of file that lock profile is written to
    22  	lockProfileFile = "lock.profile"
    23  )
    24  
    25  var (
    26  	_ Profiler = (*profiler)(nil)
    27  
    28  	errCPUProfilerRunning    = errors.New("cpu profiler already running")
    29  	errCPUProfilerNotRunning = errors.New("cpu profiler doesn't exist")
    30  )
    31  
    32  // Profiler provides helper methods for measuring the current performance of
    33  // this process
    34  type Profiler interface {
    35  	// StartCPUProfiler starts measuring the cpu utilization of this process
    36  	StartCPUProfiler() error
    37  
    38  	// StopCPUProfiler stops measuring the cpu utilization of this process
    39  	StopCPUProfiler() error
    40  
    41  	// MemoryProfile dumps the current memory utilization of this process
    42  	MemoryProfile() error
    43  
    44  	// LockProfile dumps the current lock statistics of this process
    45  	LockProfile() error
    46  }
    47  
    48  type profiler struct {
    49  	dir,
    50  	cpuProfileName,
    51  	memProfileName,
    52  	lockProfileName string
    53  
    54  	cpuProfileFile *os.File
    55  }
    56  
    57  func New(dir string) Profiler {
    58  	return newProfiler(dir)
    59  }
    60  
    61  func newProfiler(dir string) *profiler {
    62  	return &profiler{
    63  		dir:             dir,
    64  		cpuProfileName:  filepath.Join(dir, cpuProfileFile),
    65  		memProfileName:  filepath.Join(dir, memProfileFile),
    66  		lockProfileName: filepath.Join(dir, lockProfileFile),
    67  	}
    68  }
    69  
    70  func (p *profiler) StartCPUProfiler() error {
    71  	if p.cpuProfileFile != nil {
    72  		return errCPUProfilerRunning
    73  	}
    74  
    75  	if err := os.MkdirAll(p.dir, perms.ReadWriteExecute); err != nil {
    76  		return err
    77  	}
    78  	file, err := perms.Create(p.cpuProfileName, perms.ReadWrite)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	if err := pprof.StartCPUProfile(file); err != nil {
    83  		_ = file.Close() // Return the original error
    84  		return err
    85  	}
    86  	runtime.SetMutexProfileFraction(1)
    87  
    88  	p.cpuProfileFile = file
    89  	return nil
    90  }
    91  
    92  func (p *profiler) StopCPUProfiler() error {
    93  	if p.cpuProfileFile == nil {
    94  		return errCPUProfilerNotRunning
    95  	}
    96  
    97  	pprof.StopCPUProfile()
    98  	err := p.cpuProfileFile.Close()
    99  	p.cpuProfileFile = nil
   100  	return err
   101  }
   102  
   103  func (p *profiler) MemoryProfile() error {
   104  	if err := os.MkdirAll(p.dir, perms.ReadWriteExecute); err != nil {
   105  		return err
   106  	}
   107  	file, err := perms.Create(p.memProfileName, perms.ReadWrite)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	runtime.GC() // get up-to-date statistics
   112  	if err := pprof.WriteHeapProfile(file); err != nil {
   113  		_ = file.Close() // Return the original error
   114  		return err
   115  	}
   116  	return file.Close()
   117  }
   118  
   119  func (p *profiler) LockProfile() error {
   120  	if err := os.MkdirAll(p.dir, perms.ReadWriteExecute); err != nil {
   121  		return err
   122  	}
   123  	file, err := perms.Create(p.lockProfileName, perms.ReadWrite)
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	profile := pprof.Lookup("mutex")
   129  	if err := profile.WriteTo(file, 1); err != nil {
   130  		_ = file.Close() // Return the original error
   131  		return err
   132  	}
   133  	return file.Close()
   134  }