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