github.com/dominant-strategies/go-quai@v0.28.2/internal/debug/api.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package debug interfaces Go runtime debugging facilities.
    18  // This package is mostly glue code making these facilities available
    19  // through the CLI and RPC subsystem. If you want to use them from Go code,
    20  // use package runtime instead.
    21  package debug
    22  
    23  import (
    24  	"bytes"
    25  	"errors"
    26  	"io"
    27  	"os"
    28  	"os/user"
    29  	"path/filepath"
    30  	"runtime"
    31  	"runtime/debug"
    32  	"runtime/pprof"
    33  	"strings"
    34  	"sync"
    35  	"time"
    36  
    37  	"github.com/dominant-strategies/go-quai/log"
    38  )
    39  
    40  // Handler is the global debugging handler.
    41  var Handler = new(HandlerT)
    42  
    43  // HandlerT implements the debugging API.
    44  // Do not create values of this type, use the one
    45  // in the Handler variable instead.
    46  type HandlerT struct {
    47  	mu        sync.Mutex
    48  	cpuW      io.WriteCloser
    49  	cpuFile   string
    50  	traceW    io.WriteCloser
    51  	traceFile string
    52  }
    53  
    54  // Verbosity sets the log verbosity ceiling. The verbosity of individual packages
    55  // and source files can be raised using Vmodule.
    56  func (*HandlerT) Verbosity(level int) {
    57  	log.SetLevelInt(level)
    58  }
    59  
    60  // MemStats returns detailed runtime memory statistics.
    61  func (*HandlerT) MemStats() *runtime.MemStats {
    62  	s := new(runtime.MemStats)
    63  	runtime.ReadMemStats(s)
    64  	return s
    65  }
    66  
    67  // GcStats returns GC statistics.
    68  func (*HandlerT) GcStats() *debug.GCStats {
    69  	s := new(debug.GCStats)
    70  	debug.ReadGCStats(s)
    71  	return s
    72  }
    73  
    74  // CpuProfile turns on CPU profiling for nsec seconds and writes
    75  // profile data to file.
    76  func (h *HandlerT) CpuProfile(file string, nsec uint) error {
    77  	if err := h.StartCPUProfile(file); err != nil {
    78  		return err
    79  	}
    80  	time.Sleep(time.Duration(nsec) * time.Second)
    81  	h.StopCPUProfile()
    82  	return nil
    83  }
    84  
    85  // StartCPUProfile turns on CPU profiling, writing to the given file.
    86  func (h *HandlerT) StartCPUProfile(file string) error {
    87  	h.mu.Lock()
    88  	defer h.mu.Unlock()
    89  	if h.cpuW != nil {
    90  		return errors.New("CPU profiling already in progress")
    91  	}
    92  	f, err := os.Create(expandHome(file))
    93  	if err != nil {
    94  		return err
    95  	}
    96  	if err := pprof.StartCPUProfile(f); err != nil {
    97  		f.Close()
    98  		return err
    99  	}
   100  	h.cpuW = f
   101  	h.cpuFile = file
   102  	log.Info("CPU profiling started", "dump", h.cpuFile)
   103  	return nil
   104  }
   105  
   106  // StopCPUProfile stops an ongoing CPU profile.
   107  func (h *HandlerT) StopCPUProfile() error {
   108  	h.mu.Lock()
   109  	defer h.mu.Unlock()
   110  	pprof.StopCPUProfile()
   111  	if h.cpuW == nil {
   112  		return errors.New("CPU profiling not in progress")
   113  	}
   114  	log.Info("Done writing CPU profile", "dump", h.cpuFile)
   115  	h.cpuW.Close()
   116  	h.cpuW = nil
   117  	h.cpuFile = ""
   118  	return nil
   119  }
   120  
   121  // GoTrace turns on tracing for nsec seconds and writes
   122  // trace data to file.
   123  func (h *HandlerT) GoTrace(file string, nsec uint) error {
   124  	if err := h.StartGoTrace(file); err != nil {
   125  		return err
   126  	}
   127  	time.Sleep(time.Duration(nsec) * time.Second)
   128  	h.StopGoTrace()
   129  	return nil
   130  }
   131  
   132  // BlockProfile turns on goroutine profiling for nsec seconds and writes profile data to
   133  // file. It uses a profile rate of 1 for most accurate information. If a different rate is
   134  // desired, set the rate and write the profile manually.
   135  func (*HandlerT) BlockProfile(file string, nsec uint) error {
   136  	runtime.SetBlockProfileRate(1)
   137  	time.Sleep(time.Duration(nsec) * time.Second)
   138  	defer runtime.SetBlockProfileRate(0)
   139  	return writeProfile("block", file)
   140  }
   141  
   142  // SetBlockProfileRate sets the rate of goroutine block profile data collection.
   143  // rate 0 disables block profiling.
   144  func (*HandlerT) SetBlockProfileRate(rate int) {
   145  	runtime.SetBlockProfileRate(rate)
   146  }
   147  
   148  // WriteBlockProfile writes a goroutine blocking profile to the given file.
   149  func (*HandlerT) WriteBlockProfile(file string) error {
   150  	return writeProfile("block", file)
   151  }
   152  
   153  // MutexProfile turns on mutex profiling for nsec seconds and writes profile data to file.
   154  // It uses a profile rate of 1 for most accurate information. If a different rate is
   155  // desired, set the rate and write the profile manually.
   156  func (*HandlerT) MutexProfile(file string, nsec uint) error {
   157  	runtime.SetMutexProfileFraction(1)
   158  	time.Sleep(time.Duration(nsec) * time.Second)
   159  	defer runtime.SetMutexProfileFraction(0)
   160  	return writeProfile("mutex", file)
   161  }
   162  
   163  // SetMutexProfileFraction sets the rate of mutex profiling.
   164  func (*HandlerT) SetMutexProfileFraction(rate int) {
   165  	runtime.SetMutexProfileFraction(rate)
   166  }
   167  
   168  // WriteMutexProfile writes a goroutine blocking profile to the given file.
   169  func (*HandlerT) WriteMutexProfile(file string) error {
   170  	return writeProfile("mutex", file)
   171  }
   172  
   173  // WriteMemProfile writes an allocation profile to the given file.
   174  // Note that the profiling rate cannot be set through the API,
   175  // it must be set on the command line.
   176  func (*HandlerT) WriteMemProfile(file string) error {
   177  	return writeProfile("heap", file)
   178  }
   179  
   180  // Stacks returns a printed representation of the stacks of all goroutines.
   181  func (*HandlerT) Stacks() string {
   182  	buf := new(bytes.Buffer)
   183  	pprof.Lookup("goroutine").WriteTo(buf, 2)
   184  	return buf.String()
   185  }
   186  
   187  // FreeOSMemory forces a garbage collection.
   188  func (*HandlerT) FreeOSMemory() {
   189  	debug.FreeOSMemory()
   190  }
   191  
   192  // SetGCPercent sets the garbage collection target percentage. It returns the previous
   193  // setting. A negative value disables GC.
   194  func (*HandlerT) SetGCPercent(v int) int {
   195  	return debug.SetGCPercent(v)
   196  }
   197  
   198  func writeProfile(name, file string) error {
   199  	p := pprof.Lookup(name)
   200  	log.Info("Writing profile records", "count", p.Count(), "type", name, "dump", file)
   201  	f, err := os.Create(expandHome(file))
   202  	if err != nil {
   203  		return err
   204  	}
   205  	defer f.Close()
   206  	return p.WriteTo(f, 0)
   207  }
   208  
   209  // expands home directory in file paths.
   210  // ~someuser/tmp will not be expanded.
   211  func expandHome(p string) string {
   212  	if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
   213  		home := os.Getenv("HOME")
   214  		if home == "" {
   215  			if usr, err := user.Current(); err == nil {
   216  				home = usr.HomeDir
   217  			}
   218  		}
   219  		if home != "" {
   220  			p = home + p[1:]
   221  		}
   222  	}
   223  	return filepath.Clean(p)
   224  }