github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/internal/debug/api.go (about) 1 package debug 2 3 import ( 4 "errors" 5 "io" 6 "os" 7 "os/user" 8 "path/filepath" 9 "runtime" 10 "runtime/debug" 11 "runtime/pprof" 12 "strings" 13 "sync" 14 "time" 15 16 "github.com/neatlab/neatio/chain/log" 17 ) 18 19 var Handler = new(HandlerT) 20 21 type HandlerT struct { 22 mu sync.Mutex 23 cpuW io.WriteCloser 24 cpuFile string 25 traceW io.WriteCloser 26 traceFile string 27 } 28 29 func (*HandlerT) Verbosity(level int) { 30 log.Root().GetHandler().(*log.GlogHandler).Verbosity(log.Lvl(level)) 31 log.RangeLogger(func(key, value interface{}) bool { 32 if logger, ok := value.(log.Logger); ok { 33 logger.GetHandler().(*log.GlogHandler).Verbosity(log.Lvl(level)) 34 } 35 return true 36 }) 37 } 38 39 func (*HandlerT) Vmodule(pattern string) error { 40 return glogger.Vmodule(pattern) 41 } 42 43 func (*HandlerT) BacktraceAt(location string) error { 44 return glogger.BacktraceAt(location) 45 } 46 47 func (*HandlerT) MemStats() *runtime.MemStats { 48 s := new(runtime.MemStats) 49 runtime.ReadMemStats(s) 50 return s 51 } 52 53 func (*HandlerT) GcStats() *debug.GCStats { 54 s := new(debug.GCStats) 55 debug.ReadGCStats(s) 56 return s 57 } 58 59 func (h *HandlerT) CpuProfile(file string, nsec uint) error { 60 if err := h.StartCPUProfile(file); err != nil { 61 return err 62 } 63 time.Sleep(time.Duration(nsec) * time.Second) 64 h.StopCPUProfile() 65 return nil 66 } 67 68 func (h *HandlerT) StartCPUProfile(file string) error { 69 h.mu.Lock() 70 defer h.mu.Unlock() 71 if h.cpuW != nil { 72 return errors.New("CPU profiling already in progress") 73 } 74 f, err := os.Create(expandHome(file)) 75 if err != nil { 76 return err 77 } 78 if err := pprof.StartCPUProfile(f); err != nil { 79 f.Close() 80 return err 81 } 82 h.cpuW = f 83 h.cpuFile = file 84 log.Info("CPU profiling started", "dump", h.cpuFile) 85 return nil 86 } 87 88 func (h *HandlerT) StopCPUProfile() error { 89 h.mu.Lock() 90 defer h.mu.Unlock() 91 pprof.StopCPUProfile() 92 if h.cpuW == nil { 93 return errors.New("CPU profiling not in progress") 94 } 95 log.Info("Done writing CPU profile", "dump", h.cpuFile) 96 h.cpuW.Close() 97 h.cpuW = nil 98 h.cpuFile = "" 99 return nil 100 } 101 102 func (h *HandlerT) GoTrace(file string, nsec uint) error { 103 if err := h.StartGoTrace(file); err != nil { 104 return err 105 } 106 time.Sleep(time.Duration(nsec) * time.Second) 107 h.StopGoTrace() 108 return nil 109 } 110 111 func (*HandlerT) BlockProfile(file string, nsec uint) error { 112 runtime.SetBlockProfileRate(1) 113 time.Sleep(time.Duration(nsec) * time.Second) 114 defer runtime.SetBlockProfileRate(0) 115 return writeProfile("block", file) 116 } 117 118 func (*HandlerT) SetBlockProfileRate(rate int) { 119 runtime.SetBlockProfileRate(rate) 120 } 121 122 func (*HandlerT) WriteBlockProfile(file string) error { 123 return writeProfile("block", file) 124 } 125 126 func (*HandlerT) MutexProfile(file string, nsec uint) error { 127 runtime.SetMutexProfileFraction(1) 128 time.Sleep(time.Duration(nsec) * time.Second) 129 defer runtime.SetMutexProfileFraction(0) 130 return writeProfile("mutex", file) 131 } 132 133 func (*HandlerT) SetMutexProfileFraction(rate int) { 134 runtime.SetMutexProfileFraction(rate) 135 } 136 137 func (*HandlerT) WriteMutexProfile(file string) error { 138 return writeProfile("mutex", file) 139 } 140 141 func (*HandlerT) WriteMemProfile(file string) error { 142 return writeProfile("heap", file) 143 } 144 145 func (*HandlerT) Stacks() string { 146 buf := make([]byte, 1024*1024) 147 buf = buf[:runtime.Stack(buf, true)] 148 return string(buf) 149 } 150 151 func (*HandlerT) FreeOSMemory() { 152 debug.FreeOSMemory() 153 } 154 155 func (*HandlerT) SetGCPercent(v int) int { 156 return debug.SetGCPercent(v) 157 } 158 159 func writeProfile(name, file string) error { 160 p := pprof.Lookup(name) 161 log.Info("Writing profile records", "count", p.Count(), "type", name, "dump", file) 162 f, err := os.Create(expandHome(file)) 163 if err != nil { 164 return err 165 } 166 defer f.Close() 167 return p.WriteTo(f, 0) 168 } 169 170 func expandHome(p string) string { 171 if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { 172 home := os.Getenv("HOME") 173 if home == "" { 174 if usr, err := user.Current(); err == nil { 175 home = usr.HomeDir 176 } 177 } 178 if home != "" { 179 p = home + p[1:] 180 } 181 } 182 return filepath.Clean(p) 183 }