github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/internal/debug/api.go (about)

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