gitlab.com/flarenetwork/coreth@v0.1.1/internal/debug/api.go (about)

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