github.com/aquanetwork/aquachain@v1.7.8/internal/debug/api.go (about)

     1  // Copyright 2016 The aquachain Authors
     2  // This file is part of the aquachain library.
     3  //
     4  // The aquachain 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 aquachain 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 aquachain 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  	"gitlab.com/aquachain/aquachain/common/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  const (
    54  	VmoduleGood  = "p2p/discover=3,aqua/*=4,consensus/*=9,core/*=9,rpc/*=9,node/*=9,opt/*=9"
    55  	VmoduleGreat = "p2p/discover=3,aqua/*=9,consensus/*=9,core/*=9,rpc/*=9,node/*=9,opt/*=9"
    56  )
    57  
    58  // Verbosity sets the log verbosity ceiling. The verbosity of individual packages
    59  // and source files can be raised using Vmodule.
    60  func (*HandlerT) Verbosity(level int) {
    61  	glogger.Verbosity(log.Lvl(level))
    62  }
    63  
    64  func wrapVmodule(pattern string) string {
    65  	if pattern == "good" {
    66  		pattern = VmoduleGood
    67  	} else if pattern == "great" {
    68  		pattern = VmoduleGreat
    69  	}
    70  	return pattern
    71  }
    72  
    73  // Vmodule sets the log verbosity pattern. See package log for details on the
    74  // pattern syntax.
    75  func (*HandlerT) Vmodule(pattern string) error {
    76  	return glogger.Vmodule(pattern)
    77  }
    78  
    79  // BacktraceAt sets the log backtrace location. See package log for details on
    80  // the pattern syntax.
    81  func (*HandlerT) BacktraceAt(location string) error {
    82  	return glogger.BacktraceAt(location)
    83  }
    84  
    85  // MemStats returns detailed runtime memory statistics.
    86  func (*HandlerT) MemStats() *runtime.MemStats {
    87  	s := new(runtime.MemStats)
    88  	runtime.ReadMemStats(s)
    89  	return s
    90  }
    91  
    92  // GcStats returns GC statistics.
    93  func (*HandlerT) GcStats() *debug.GCStats {
    94  	s := new(debug.GCStats)
    95  	debug.ReadGCStats(s)
    96  	return s
    97  }
    98  
    99  // CpuProfile turns on CPU profiling for nsec seconds and writes
   100  // profile data to file.
   101  func (h *HandlerT) CpuProfile(file string, nsec uint) error {
   102  	if err := h.StartCPUProfile(file); err != nil {
   103  		return err
   104  	}
   105  	time.Sleep(time.Duration(nsec) * time.Second)
   106  	h.StopCPUProfile()
   107  	return nil
   108  }
   109  
   110  // StartCPUProfile turns on CPU profiling, writing to the given file.
   111  func (h *HandlerT) StartCPUProfile(file string) error {
   112  	h.mu.Lock()
   113  	defer h.mu.Unlock()
   114  	if h.cpuW != nil {
   115  		return errors.New("CPU profiling already in progress")
   116  	}
   117  	f, err := os.Create(expandHome(file))
   118  	if err != nil {
   119  		return err
   120  	}
   121  	if err := pprof.StartCPUProfile(f); err != nil {
   122  		f.Close()
   123  		return err
   124  	}
   125  	h.cpuW = f
   126  	h.cpuFile = file
   127  	log.Info("CPU profiling started", "dump", h.cpuFile)
   128  	return nil
   129  }
   130  
   131  // StopCPUProfile stops an ongoing CPU profile.
   132  func (h *HandlerT) StopCPUProfile() error {
   133  	h.mu.Lock()
   134  	defer h.mu.Unlock()
   135  	pprof.StopCPUProfile()
   136  	if h.cpuW == nil {
   137  		return errors.New("CPU profiling not in progress")
   138  	}
   139  	log.Info("Done writing CPU profile", "dump", h.cpuFile)
   140  	h.cpuW.Close()
   141  	h.cpuW = nil
   142  	h.cpuFile = ""
   143  	return nil
   144  }
   145  
   146  // GoTrace turns on tracing for nsec seconds and writes
   147  // trace data to file.
   148  func (h *HandlerT) GoTrace(file string, nsec uint) error {
   149  	if err := h.StartGoTrace(file); err != nil {
   150  		return err
   151  	}
   152  	time.Sleep(time.Duration(nsec) * time.Second)
   153  	h.StopGoTrace()
   154  	return nil
   155  }
   156  
   157  // BlockProfile turns on CPU profiling for nsec seconds and writes
   158  // profile data to file. It uses a profile rate of 1 for most accurate
   159  // information. If a different rate is desired, set the rate
   160  // and write the profile manually.
   161  func (*HandlerT) BlockProfile(file string, nsec uint) error {
   162  	runtime.SetBlockProfileRate(1)
   163  	time.Sleep(time.Duration(nsec) * time.Second)
   164  	defer runtime.SetBlockProfileRate(0)
   165  	return writeProfile("block", file)
   166  }
   167  
   168  // SetBlockProfileRate sets the rate of goroutine block profile data collection.
   169  // rate 0 disables block profiling.
   170  func (*HandlerT) SetBlockProfileRate(rate int) {
   171  	runtime.SetBlockProfileRate(rate)
   172  }
   173  
   174  // WriteBlockProfile writes a goroutine blocking profile to the given file.
   175  func (*HandlerT) WriteBlockProfile(file string) error {
   176  	return writeProfile("block", file)
   177  }
   178  
   179  // WriteMemProfile writes an allocation profile to the given file.
   180  // Note that the profiling rate cannot be set through the API,
   181  // it must be set on the command line.
   182  func (*HandlerT) WriteMemProfile(file string) error {
   183  	return writeProfile("heap", file)
   184  }
   185  
   186  // Stacks returns a printed representation of the stacks of all goroutines.
   187  func (*HandlerT) Stacks() string {
   188  	buf := make([]byte, 1024*1024)
   189  	buf = buf[:runtime.Stack(buf, true)]
   190  	return string(buf)
   191  }
   192  
   193  // FreeOSMemory returns unused memory to the OS.
   194  func (*HandlerT) FreeOSMemory() {
   195  	debug.FreeOSMemory()
   196  }
   197  
   198  // SetGCPercent sets the garbage collection target percentage. It returns the previous
   199  // setting. A negative value disables GC.
   200  func (*HandlerT) SetGCPercent(v int) int {
   201  	return debug.SetGCPercent(v)
   202  }
   203  
   204  func writeProfile(name, file string) error {
   205  	p := pprof.Lookup(name)
   206  	log.Info("Writing profile records", "count", p.Count(), "type", name, "dump", file)
   207  	f, err := os.Create(expandHome(file))
   208  	if err != nil {
   209  		return err
   210  	}
   211  	defer f.Close()
   212  	return p.WriteTo(f, 0)
   213  }
   214  
   215  // expands home directory in file paths.
   216  // ~someuser/tmp will not be expanded.
   217  func expandHome(p string) string {
   218  	if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
   219  		home := os.Getenv("HOME")
   220  		if home == "" {
   221  			if usr, err := user.Current(); err == nil {
   222  				home = usr.HomeDir
   223  			}
   224  		}
   225  		if home != "" {
   226  			p = home + p[1:]
   227  		}
   228  	}
   229  	return filepath.Clean(p)
   230  }