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