github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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 "regexp" 31 "runtime" 32 "runtime/debug" 33 "runtime/pprof" 34 "strings" 35 "sync" 36 "time" 37 38 "github.com/ethereum/go-ethereum/log" 39 "github.com/hashicorp/go-bexpr" 40 ) 41 42 // Handler is the global debugging handler. 43 var Handler = new(HandlerT) 44 45 // HandlerT implements the debugging API. 46 // Do not create values of this type, use the one 47 // in the Handler variable instead. 48 type HandlerT struct { 49 mu sync.Mutex 50 cpuW io.WriteCloser 51 cpuFile string 52 traceW io.WriteCloser 53 traceFile string 54 } 55 56 // Verbosity sets the log verbosity ceiling. The verbosity of individual packages 57 // and source files can be raised using Vmodule. 58 func (*HandlerT) Verbosity(level int) { 59 glogger.Verbosity(log.FromLegacyLevel(level)) 60 } 61 62 // Vmodule sets the log verbosity pattern. See package log for details on the 63 // pattern syntax. 64 func (*HandlerT) Vmodule(pattern string) error { 65 return glogger.Vmodule(pattern) 66 } 67 68 // MemStats returns detailed runtime memory statistics. 69 func (*HandlerT) MemStats() *runtime.MemStats { 70 s := new(runtime.MemStats) 71 runtime.ReadMemStats(s) 72 return s 73 } 74 75 // GcStats returns GC statistics. 76 func (*HandlerT) GcStats() *debug.GCStats { 77 s := new(debug.GCStats) 78 debug.ReadGCStats(s) 79 return s 80 } 81 82 // CpuProfile turns on CPU profiling for nsec seconds and writes 83 // profile data to file. 84 func (h *HandlerT) CpuProfile(file string, nsec uint) error { 85 if err := h.StartCPUProfile(file); err != nil { 86 return err 87 } 88 time.Sleep(time.Duration(nsec) * time.Second) 89 h.StopCPUProfile() 90 return nil 91 } 92 93 // StartCPUProfile turns on CPU profiling, writing to the given file. 94 func (h *HandlerT) StartCPUProfile(file string) error { 95 h.mu.Lock() 96 defer h.mu.Unlock() 97 if h.cpuW != nil { 98 return errors.New("CPU profiling already in progress") 99 } 100 f, err := os.Create(expandHome(file)) 101 if err != nil { 102 return err 103 } 104 if err := pprof.StartCPUProfile(f); err != nil { 105 f.Close() 106 return err 107 } 108 h.cpuW = f 109 h.cpuFile = file 110 log.Info("CPU profiling started", "dump", h.cpuFile) 111 return nil 112 } 113 114 // StopCPUProfile stops an ongoing CPU profile. 115 func (h *HandlerT) StopCPUProfile() error { 116 h.mu.Lock() 117 defer h.mu.Unlock() 118 pprof.StopCPUProfile() 119 if h.cpuW == nil { 120 return errors.New("CPU profiling not in progress") 121 } 122 log.Info("Done writing CPU profile", "dump", h.cpuFile) 123 h.cpuW.Close() 124 h.cpuW = nil 125 h.cpuFile = "" 126 return nil 127 } 128 129 // GoTrace turns on tracing for nsec seconds and writes 130 // trace data to file. 131 func (h *HandlerT) GoTrace(file string, nsec uint) error { 132 if err := h.StartGoTrace(file); err != nil { 133 return err 134 } 135 time.Sleep(time.Duration(nsec) * time.Second) 136 h.StopGoTrace() 137 return nil 138 } 139 140 // BlockProfile turns on goroutine profiling for nsec seconds and writes profile data to 141 // file. It uses a profile rate of 1 for most accurate information. If a different rate is 142 // desired, set the rate and write the profile manually. 143 func (*HandlerT) BlockProfile(file string, nsec uint) error { 144 runtime.SetBlockProfileRate(1) 145 time.Sleep(time.Duration(nsec) * time.Second) 146 defer runtime.SetBlockProfileRate(0) 147 return writeProfile("block", file) 148 } 149 150 // SetBlockProfileRate sets the rate of goroutine block profile data collection. 151 // rate 0 disables block profiling. 152 func (*HandlerT) SetBlockProfileRate(rate int) { 153 runtime.SetBlockProfileRate(rate) 154 } 155 156 // WriteBlockProfile writes a goroutine blocking profile to the given file. 157 func (*HandlerT) WriteBlockProfile(file string) error { 158 return writeProfile("block", file) 159 } 160 161 // MutexProfile turns on mutex profiling for nsec seconds and writes profile data to file. 162 // It uses a profile rate of 1 for most accurate information. If a different rate is 163 // desired, set the rate and write the profile manually. 164 func (*HandlerT) MutexProfile(file string, nsec uint) error { 165 runtime.SetMutexProfileFraction(1) 166 time.Sleep(time.Duration(nsec) * time.Second) 167 defer runtime.SetMutexProfileFraction(0) 168 return writeProfile("mutex", file) 169 } 170 171 // SetMutexProfileFraction sets the rate of mutex profiling. 172 func (*HandlerT) SetMutexProfileFraction(rate int) { 173 runtime.SetMutexProfileFraction(rate) 174 } 175 176 // WriteMutexProfile writes a goroutine blocking profile to the given file. 177 func (*HandlerT) WriteMutexProfile(file string) error { 178 return writeProfile("mutex", file) 179 } 180 181 // WriteMemProfile writes an allocation profile to the given file. 182 // Note that the profiling rate cannot be set through the API, 183 // it must be set on the command line. 184 func (*HandlerT) WriteMemProfile(file string) error { 185 return writeProfile("heap", file) 186 } 187 188 // Stacks returns a printed representation of the stacks of all goroutines. It 189 // also permits the following optional filters to be used: 190 // - filter: boolean expression of packages to filter for 191 func (*HandlerT) Stacks(filter *string) string { 192 buf := new(bytes.Buffer) 193 pprof.Lookup("goroutine").WriteTo(buf, 2) 194 195 // If any filtering was requested, execute them now 196 if filter != nil && len(*filter) > 0 { 197 expanded := *filter 198 199 // The input filter is a logical expression of package names. Transform 200 // it into a proper boolean expression that can be fed into a parser and 201 // interpreter: 202 // 203 // E.g. (eth || snap) && !p2p -> (eth in Value || snap in Value) && p2p not in Value 204 expanded = regexp.MustCompile(`[:/\.A-Za-z0-9_-]+`).ReplaceAllString(expanded, "`$0` in Value") 205 expanded = regexp.MustCompile("!(`[:/\\.A-Za-z0-9_-]+`)").ReplaceAllString(expanded, "$1 not") 206 expanded = strings.ReplaceAll(expanded, "||", "or") 207 expanded = strings.ReplaceAll(expanded, "&&", "and") 208 log.Info("Expanded filter expression", "filter", *filter, "expanded", expanded) 209 210 expr, err := bexpr.CreateEvaluator(expanded) 211 if err != nil { 212 log.Error("Failed to parse filter expression", "expanded", expanded, "err", err) 213 return "" 214 } 215 // Split the goroutine dump into segments and filter each 216 dump := buf.String() 217 buf.Reset() 218 219 for _, trace := range strings.Split(dump, "\n\n") { 220 if ok, _ := expr.Evaluate(map[string]string{"Value": trace}); ok { 221 buf.WriteString(trace) 222 buf.WriteString("\n\n") 223 } 224 } 225 } 226 return buf.String() 227 } 228 229 // FreeOSMemory forces a garbage collection. 230 func (*HandlerT) FreeOSMemory() { 231 debug.FreeOSMemory() 232 } 233 234 // SetGCPercent sets the garbage collection target percentage. It returns the previous 235 // setting. A negative value disables GC. 236 func (*HandlerT) SetGCPercent(v int) int { 237 return debug.SetGCPercent(v) 238 } 239 240 func writeProfile(name, file string) error { 241 p := pprof.Lookup(name) 242 log.Info("Writing profile records", "count", p.Count(), "type", name, "dump", file) 243 f, err := os.Create(expandHome(file)) 244 if err != nil { 245 return err 246 } 247 defer f.Close() 248 return p.WriteTo(f, 0) 249 } 250 251 // expands home directory in file paths. 252 // ~someuser/tmp will not be expanded. 253 func expandHome(p string) string { 254 if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { 255 home := os.Getenv("HOME") 256 if home == "" { 257 if usr, err := user.Current(); err == nil { 258 home = usr.HomeDir 259 } 260 } 261 if home != "" { 262 p = home + p[1:] 263 } 264 } 265 return filepath.Clean(p) 266 }