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