github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/internal/debug/api.go (about)

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