github.com/bigzoro/my_simplechain@v0.0.0-20240315012955-8ad0a2a29bb9/internal/debug/api.go (about)

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