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