github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/internal/debug/api.go (about)

     1  package debug
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"os"
     7  	"os/user"
     8  	"path/filepath"
     9  	"runtime"
    10  	"runtime/debug"
    11  	"runtime/pprof"
    12  	"strings"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/neatlab/neatio/chain/log"
    17  )
    18  
    19  var Handler = new(HandlerT)
    20  
    21  type HandlerT struct {
    22  	mu        sync.Mutex
    23  	cpuW      io.WriteCloser
    24  	cpuFile   string
    25  	traceW    io.WriteCloser
    26  	traceFile string
    27  }
    28  
    29  func (*HandlerT) Verbosity(level int) {
    30  	log.Root().GetHandler().(*log.GlogHandler).Verbosity(log.Lvl(level))
    31  	log.RangeLogger(func(key, value interface{}) bool {
    32  		if logger, ok := value.(log.Logger); ok {
    33  			logger.GetHandler().(*log.GlogHandler).Verbosity(log.Lvl(level))
    34  		}
    35  		return true
    36  	})
    37  }
    38  
    39  func (*HandlerT) Vmodule(pattern string) error {
    40  	return glogger.Vmodule(pattern)
    41  }
    42  
    43  func (*HandlerT) BacktraceAt(location string) error {
    44  	return glogger.BacktraceAt(location)
    45  }
    46  
    47  func (*HandlerT) MemStats() *runtime.MemStats {
    48  	s := new(runtime.MemStats)
    49  	runtime.ReadMemStats(s)
    50  	return s
    51  }
    52  
    53  func (*HandlerT) GcStats() *debug.GCStats {
    54  	s := new(debug.GCStats)
    55  	debug.ReadGCStats(s)
    56  	return s
    57  }
    58  
    59  func (h *HandlerT) CpuProfile(file string, nsec uint) error {
    60  	if err := h.StartCPUProfile(file); err != nil {
    61  		return err
    62  	}
    63  	time.Sleep(time.Duration(nsec) * time.Second)
    64  	h.StopCPUProfile()
    65  	return nil
    66  }
    67  
    68  func (h *HandlerT) StartCPUProfile(file string) error {
    69  	h.mu.Lock()
    70  	defer h.mu.Unlock()
    71  	if h.cpuW != nil {
    72  		return errors.New("CPU profiling already in progress")
    73  	}
    74  	f, err := os.Create(expandHome(file))
    75  	if err != nil {
    76  		return err
    77  	}
    78  	if err := pprof.StartCPUProfile(f); err != nil {
    79  		f.Close()
    80  		return err
    81  	}
    82  	h.cpuW = f
    83  	h.cpuFile = file
    84  	log.Info("CPU profiling started", "dump", h.cpuFile)
    85  	return nil
    86  }
    87  
    88  func (h *HandlerT) StopCPUProfile() error {
    89  	h.mu.Lock()
    90  	defer h.mu.Unlock()
    91  	pprof.StopCPUProfile()
    92  	if h.cpuW == nil {
    93  		return errors.New("CPU profiling not in progress")
    94  	}
    95  	log.Info("Done writing CPU profile", "dump", h.cpuFile)
    96  	h.cpuW.Close()
    97  	h.cpuW = nil
    98  	h.cpuFile = ""
    99  	return nil
   100  }
   101  
   102  func (h *HandlerT) GoTrace(file string, nsec uint) error {
   103  	if err := h.StartGoTrace(file); err != nil {
   104  		return err
   105  	}
   106  	time.Sleep(time.Duration(nsec) * time.Second)
   107  	h.StopGoTrace()
   108  	return nil
   109  }
   110  
   111  func (*HandlerT) BlockProfile(file string, nsec uint) error {
   112  	runtime.SetBlockProfileRate(1)
   113  	time.Sleep(time.Duration(nsec) * time.Second)
   114  	defer runtime.SetBlockProfileRate(0)
   115  	return writeProfile("block", file)
   116  }
   117  
   118  func (*HandlerT) SetBlockProfileRate(rate int) {
   119  	runtime.SetBlockProfileRate(rate)
   120  }
   121  
   122  func (*HandlerT) WriteBlockProfile(file string) error {
   123  	return writeProfile("block", file)
   124  }
   125  
   126  func (*HandlerT) MutexProfile(file string, nsec uint) error {
   127  	runtime.SetMutexProfileFraction(1)
   128  	time.Sleep(time.Duration(nsec) * time.Second)
   129  	defer runtime.SetMutexProfileFraction(0)
   130  	return writeProfile("mutex", file)
   131  }
   132  
   133  func (*HandlerT) SetMutexProfileFraction(rate int) {
   134  	runtime.SetMutexProfileFraction(rate)
   135  }
   136  
   137  func (*HandlerT) WriteMutexProfile(file string) error {
   138  	return writeProfile("mutex", file)
   139  }
   140  
   141  func (*HandlerT) WriteMemProfile(file string) error {
   142  	return writeProfile("heap", file)
   143  }
   144  
   145  func (*HandlerT) Stacks() string {
   146  	buf := make([]byte, 1024*1024)
   147  	buf = buf[:runtime.Stack(buf, true)]
   148  	return string(buf)
   149  }
   150  
   151  func (*HandlerT) FreeOSMemory() {
   152  	debug.FreeOSMemory()
   153  }
   154  
   155  func (*HandlerT) SetGCPercent(v int) int {
   156  	return debug.SetGCPercent(v)
   157  }
   158  
   159  func writeProfile(name, file string) error {
   160  	p := pprof.Lookup(name)
   161  	log.Info("Writing profile records", "count", p.Count(), "type", name, "dump", file)
   162  	f, err := os.Create(expandHome(file))
   163  	if err != nil {
   164  		return err
   165  	}
   166  	defer f.Close()
   167  	return p.WriteTo(f, 0)
   168  }
   169  
   170  func expandHome(p string) string {
   171  	if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
   172  		home := os.Getenv("HOME")
   173  		if home == "" {
   174  			if usr, err := user.Current(); err == nil {
   175  				home = usr.HomeDir
   176  			}
   177  		}
   178  		if home != "" {
   179  			p = home + p[1:]
   180  		}
   181  	}
   182  	return filepath.Clean(p)
   183  }