github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/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  	"errors"
    25  	"io"
    26  	"os"
    27  	"os/user"
    28  	"path/filepath"
    29  	"runtime"
    30  	"runtime/debug"
    31  	"runtime/pprof"
    32  	"strings"
    33  	"sync"
    34  	"time"
    35  
    36  	"github.com/intfoundation/intchain/log"
    37  )
    38  
    39  // Handler is the global debugging handler.
    40  var Handler = new(HandlerT)
    41  
    42  // HandlerT implements the debugging API.
    43  // Do not create values of this type, use the one
    44  // in the Handler variable instead.
    45  type HandlerT struct {
    46  	mu        sync.Mutex
    47  	cpuW      io.WriteCloser
    48  	cpuFile   string
    49  	traceW    io.WriteCloser
    50  	traceFile string
    51  }
    52  
    53  // Verbosity sets the log verbosity ceiling. The verbosity of individual packages
    54  // and source files can be raised using Vmodule.
    55  func (*HandlerT) Verbosity(level int) {
    56  	log.Root().GetHandler().(*log.GlogHandler).Verbosity(log.Lvl(level))
    57  	log.RangeLogger(func(key, value interface{}) bool {
    58  		if logger, ok := value.(log.Logger); ok {
    59  			logger.GetHandler().(*log.GlogHandler).Verbosity(log.Lvl(level))
    60  		}
    61  		return true
    62  	})
    63  }
    64  
    65  // Vmodule sets the log verbosity pattern. See package log for details on the
    66  // pattern syntax.
    67  func (*HandlerT) Vmodule(pattern string) error {
    68  	return glogger.Vmodule(pattern)
    69  }
    70  
    71  // BacktraceAt sets the log backtrace location. See package log for details on
    72  // the pattern syntax.
    73  func (*HandlerT) BacktraceAt(location string) error {
    74  	return glogger.BacktraceAt(location)
    75  }
    76  
    77  // MemStats returns detailed runtime memory statistics.
    78  func (*HandlerT) MemStats() *runtime.MemStats {
    79  	s := new(runtime.MemStats)
    80  	runtime.ReadMemStats(s)
    81  	return s
    82  }
    83  
    84  // GcStats returns GC statistics.
    85  func (*HandlerT) GcStats() *debug.GCStats {
    86  	s := new(debug.GCStats)
    87  	debug.ReadGCStats(s)
    88  	return s
    89  }
    90  
    91  // CpuProfile turns on CPU profiling for nsec seconds and writes
    92  // profile data to file.
    93  func (h *HandlerT) CpuProfile(file string, nsec uint) error {
    94  	if err := h.StartCPUProfile(file); err != nil {
    95  		return err
    96  	}
    97  	time.Sleep(time.Duration(nsec) * time.Second)
    98  	h.StopCPUProfile()
    99  	return nil
   100  }
   101  
   102  // StartCPUProfile turns on CPU profiling, writing to the given file.
   103  func (h *HandlerT) StartCPUProfile(file string) error {
   104  	h.mu.Lock()
   105  	defer h.mu.Unlock()
   106  	if h.cpuW != nil {
   107  		return errors.New("CPU profiling already in progress")
   108  	}
   109  	f, err := os.Create(expandHome(file))
   110  	if err != nil {
   111  		return err
   112  	}
   113  	if err := pprof.StartCPUProfile(f); err != nil {
   114  		f.Close()
   115  		return err
   116  	}
   117  	h.cpuW = f
   118  	h.cpuFile = file
   119  	log.Info("CPU profiling started", "dump", h.cpuFile)
   120  	return nil
   121  }
   122  
   123  // StopCPUProfile stops an ongoing CPU profile.
   124  func (h *HandlerT) StopCPUProfile() error {
   125  	h.mu.Lock()
   126  	defer h.mu.Unlock()
   127  	pprof.StopCPUProfile()
   128  	if h.cpuW == nil {
   129  		return errors.New("CPU profiling not in progress")
   130  	}
   131  	log.Info("Done writing CPU profile", "dump", h.cpuFile)
   132  	h.cpuW.Close()
   133  	h.cpuW = nil
   134  	h.cpuFile = ""
   135  	return nil
   136  }
   137  
   138  // GoTrace turns on tracing for nsec seconds and writes
   139  // trace data to file.
   140  func (h *HandlerT) GoTrace(file string, nsec uint) error {
   141  	if err := h.StartGoTrace(file); err != nil {
   142  		return err
   143  	}
   144  	time.Sleep(time.Duration(nsec) * time.Second)
   145  	h.StopGoTrace()
   146  	return nil
   147  }
   148  
   149  // BlockProfile turns on goroutine profiling for nsec seconds and writes profile data to
   150  // file. It uses a profile rate of 1 for most accurate information. If a different rate is
   151  // desired, set the rate and write the profile manually.
   152  func (*HandlerT) BlockProfile(file string, nsec uint) error {
   153  	runtime.SetBlockProfileRate(1)
   154  	time.Sleep(time.Duration(nsec) * time.Second)
   155  	defer runtime.SetBlockProfileRate(0)
   156  	return writeProfile("block", file)
   157  }
   158  
   159  // SetBlockProfileRate sets the rate of goroutine block profile data collection.
   160  // rate 0 disables block profiling.
   161  func (*HandlerT) SetBlockProfileRate(rate int) {
   162  	runtime.SetBlockProfileRate(rate)
   163  }
   164  
   165  // WriteBlockProfile writes a goroutine blocking profile to the given file.
   166  func (*HandlerT) WriteBlockProfile(file string) error {
   167  	return writeProfile("block", file)
   168  }
   169  
   170  // MutexProfile turns on mutex profiling for nsec seconds and writes profile data to file.
   171  // It uses a profile rate of 1 for most accurate information. If a different rate is
   172  // desired, set the rate and write the profile manually.
   173  func (*HandlerT) MutexProfile(file string, nsec uint) error {
   174  	runtime.SetMutexProfileFraction(1)
   175  	time.Sleep(time.Duration(nsec) * time.Second)
   176  	defer runtime.SetMutexProfileFraction(0)
   177  	return writeProfile("mutex", file)
   178  }
   179  
   180  // SetMutexProfileFraction sets the rate of mutex profiling.
   181  func (*HandlerT) SetMutexProfileFraction(rate int) {
   182  	runtime.SetMutexProfileFraction(rate)
   183  }
   184  
   185  // WriteMutexProfile writes a goroutine blocking profile to the given file.
   186  func (*HandlerT) WriteMutexProfile(file string) error {
   187  	return writeProfile("mutex", file)
   188  }
   189  
   190  // WriteMemProfile writes an allocation profile to the given file.
   191  // Note that the profiling rate cannot be set through the API,
   192  // it must be set on the command line.
   193  func (*HandlerT) WriteMemProfile(file string) error {
   194  	return writeProfile("heap", file)
   195  }
   196  
   197  // Stacks returns a printed representation of the stacks of all goroutines.
   198  func (*HandlerT) Stacks() string {
   199  	buf := make([]byte, 1024*1024)
   200  	buf = buf[:runtime.Stack(buf, true)]
   201  	return string(buf)
   202  }
   203  
   204  // FreeOSMemory returns unused memory to the OS.
   205  func (*HandlerT) FreeOSMemory() {
   206  	debug.FreeOSMemory()
   207  }
   208  
   209  // SetGCPercent sets the garbage collection target percentage. It returns the previous
   210  // setting. A negative value disables GC.
   211  func (*HandlerT) SetGCPercent(v int) int {
   212  	return debug.SetGCPercent(v)
   213  }
   214  
   215  func writeProfile(name, file string) error {
   216  	p := pprof.Lookup(name)
   217  	log.Info("Writing profile records", "count", p.Count(), "type", name, "dump", file)
   218  	f, err := os.Create(expandHome(file))
   219  	if err != nil {
   220  		return err
   221  	}
   222  	defer f.Close()
   223  	return p.WriteTo(f, 0)
   224  }
   225  
   226  // expands home directory in file paths.
   227  // ~someuser/tmp will not be expanded.
   228  func expandHome(p string) string {
   229  	if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
   230  		home := os.Getenv("HOME")
   231  		if home == "" {
   232  			if usr, err := user.Current(); err == nil {
   233  				home = usr.HomeDir
   234  			}
   235  		}
   236  		if home != "" {
   237  			p = home + p[1:]
   238  		}
   239  	}
   240  	return filepath.Clean(p)
   241  }