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