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