github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/internal/debug/api.go (about)

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