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