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