| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | // Copyright 2016 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Package debug interfaces Go runtime debugging facilities. | 
					
						
							|  |  |  | // This package is mostly glue code making these facilities available | 
					
						
							|  |  |  | // through the CLI and RPC subsystem. If you want to use them from Go code, | 
					
						
							|  |  |  | // use package runtime instead. | 
					
						
							|  |  |  | package debug | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-06-14 09:58:44 -04:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | 	"errors" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"os/user" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2021-09-20 16:29:07 +03:00
										 |  |  | 	"regexp" | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2016-02-20 15:42:22 +01:00
										 |  |  | 	"runtime/debug" | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | 	"runtime/pprof" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/log" | 
					
						
							| 
									
										
										
										
											2021-09-20 16:29:07 +03:00
										 |  |  | 	"github.com/hashicorp/go-bexpr" | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Handler is the global debugging handler. | 
					
						
							|  |  |  | var Handler = new(HandlerT) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // HandlerT implements the debugging API. | 
					
						
							|  |  |  | // Do not create values of this type, use the one | 
					
						
							|  |  |  | // in the Handler variable instead. | 
					
						
							|  |  |  | type HandlerT struct { | 
					
						
							|  |  |  | 	mu        sync.Mutex | 
					
						
							|  |  |  | 	cpuW      io.WriteCloser | 
					
						
							|  |  |  | 	cpuFile   string | 
					
						
							|  |  |  | 	traceW    io.WriteCloser | 
					
						
							|  |  |  | 	traceFile string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | // Verbosity sets the log verbosity ceiling. The verbosity of individual packages | 
					
						
							|  |  |  | // and source files can be raised using Vmodule. | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | func (*HandlerT) Verbosity(level int) { | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 	glogger.Verbosity(log.Lvl(level)) | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | // Vmodule sets the log verbosity pattern. See package log for details on the | 
					
						
							|  |  |  | // pattern syntax. | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | func (*HandlerT) Vmodule(pattern string) error { | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 	return glogger.Vmodule(pattern) | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | // BacktraceAt sets the log backtrace location. See package log for details on | 
					
						
							|  |  |  | // the pattern syntax. | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | func (*HandlerT) BacktraceAt(location string) error { | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 	return glogger.BacktraceAt(location) | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-20 15:42:22 +01:00
										 |  |  | // MemStats returns detailed runtime memory statistics. | 
					
						
							|  |  |  | func (*HandlerT) MemStats() *runtime.MemStats { | 
					
						
							|  |  |  | 	s := new(runtime.MemStats) | 
					
						
							|  |  |  | 	runtime.ReadMemStats(s) | 
					
						
							|  |  |  | 	return s | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GcStats returns GC statistics. | 
					
						
							|  |  |  | func (*HandlerT) GcStats() *debug.GCStats { | 
					
						
							|  |  |  | 	s := new(debug.GCStats) | 
					
						
							|  |  |  | 	debug.ReadGCStats(s) | 
					
						
							|  |  |  | 	return s | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | // CpuProfile turns on CPU profiling for nsec seconds and writes | 
					
						
							|  |  |  | // profile data to file. | 
					
						
							|  |  |  | func (h *HandlerT) CpuProfile(file string, nsec uint) error { | 
					
						
							|  |  |  | 	if err := h.StartCPUProfile(file); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	time.Sleep(time.Duration(nsec) * time.Second) | 
					
						
							|  |  |  | 	h.StopCPUProfile() | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // StartCPUProfile turns on CPU profiling, writing to the given file. | 
					
						
							|  |  |  | func (h *HandlerT) StartCPUProfile(file string) error { | 
					
						
							|  |  |  | 	h.mu.Lock() | 
					
						
							|  |  |  | 	defer h.mu.Unlock() | 
					
						
							|  |  |  | 	if h.cpuW != nil { | 
					
						
							|  |  |  | 		return errors.New("CPU profiling already in progress") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	f, err := os.Create(expandHome(file)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := pprof.StartCPUProfile(f); err != nil { | 
					
						
							|  |  |  | 		f.Close() | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	h.cpuW = f | 
					
						
							|  |  |  | 	h.cpuFile = file | 
					
						
							| 
									
										
										
										
											2017-03-03 11:41:52 +02:00
										 |  |  | 	log.Info("CPU profiling started", "dump", h.cpuFile) | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // StopCPUProfile stops an ongoing CPU profile. | 
					
						
							|  |  |  | func (h *HandlerT) StopCPUProfile() error { | 
					
						
							|  |  |  | 	h.mu.Lock() | 
					
						
							|  |  |  | 	defer h.mu.Unlock() | 
					
						
							|  |  |  | 	pprof.StopCPUProfile() | 
					
						
							|  |  |  | 	if h.cpuW == nil { | 
					
						
							|  |  |  | 		return errors.New("CPU profiling not in progress") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-03-03 11:41:52 +02:00
										 |  |  | 	log.Info("Done writing CPU profile", "dump", h.cpuFile) | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | 	h.cpuW.Close() | 
					
						
							|  |  |  | 	h.cpuW = nil | 
					
						
							|  |  |  | 	h.cpuFile = "" | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-06 10:24:16 +02:00
										 |  |  | // GoTrace turns on tracing for nsec seconds and writes | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | // trace data to file. | 
					
						
							| 
									
										
										
										
											2016-05-06 10:24:16 +02:00
										 |  |  | func (h *HandlerT) GoTrace(file string, nsec uint) error { | 
					
						
							| 
									
										
										
										
											2016-05-06 11:15:05 +02:00
										 |  |  | 	if err := h.StartGoTrace(file); err != nil { | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	time.Sleep(time.Duration(nsec) * time.Second) | 
					
						
							| 
									
										
										
										
											2016-05-06 11:15:05 +02:00
										 |  |  | 	h.StopGoTrace() | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 00:52:21 +01:00
										 |  |  | // BlockProfile turns on goroutine profiling for nsec seconds and writes profile data to | 
					
						
							|  |  |  | // file. It uses a profile rate of 1 for most accurate information. If a different rate is | 
					
						
							|  |  |  | // desired, set the rate and write the profile manually. | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | func (*HandlerT) BlockProfile(file string, nsec uint) error { | 
					
						
							|  |  |  | 	runtime.SetBlockProfileRate(1) | 
					
						
							|  |  |  | 	time.Sleep(time.Duration(nsec) * time.Second) | 
					
						
							|  |  |  | 	defer runtime.SetBlockProfileRate(0) | 
					
						
							|  |  |  | 	return writeProfile("block", file) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetBlockProfileRate sets the rate of goroutine block profile data collection. | 
					
						
							|  |  |  | // rate 0 disables block profiling. | 
					
						
							|  |  |  | func (*HandlerT) SetBlockProfileRate(rate int) { | 
					
						
							|  |  |  | 	runtime.SetBlockProfileRate(rate) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // WriteBlockProfile writes a goroutine blocking profile to the given file. | 
					
						
							|  |  |  | func (*HandlerT) WriteBlockProfile(file string) error { | 
					
						
							|  |  |  | 	return writeProfile("block", file) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 00:52:21 +01:00
										 |  |  | // MutexProfile turns on mutex profiling for nsec seconds and writes profile data to file. | 
					
						
							|  |  |  | // It uses a profile rate of 1 for most accurate information. If a different rate is | 
					
						
							|  |  |  | // desired, set the rate and write the profile manually. | 
					
						
							|  |  |  | func (*HandlerT) MutexProfile(file string, nsec uint) error { | 
					
						
							|  |  |  | 	runtime.SetMutexProfileFraction(1) | 
					
						
							|  |  |  | 	time.Sleep(time.Duration(nsec) * time.Second) | 
					
						
							|  |  |  | 	defer runtime.SetMutexProfileFraction(0) | 
					
						
							|  |  |  | 	return writeProfile("mutex", file) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetMutexProfileFraction sets the rate of mutex profiling. | 
					
						
							|  |  |  | func (*HandlerT) SetMutexProfileFraction(rate int) { | 
					
						
							|  |  |  | 	runtime.SetMutexProfileFraction(rate) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // WriteMutexProfile writes a goroutine blocking profile to the given file. | 
					
						
							|  |  |  | func (*HandlerT) WriteMutexProfile(file string) error { | 
					
						
							|  |  |  | 	return writeProfile("mutex", file) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | // WriteMemProfile writes an allocation profile to the given file. | 
					
						
							|  |  |  | // Note that the profiling rate cannot be set through the API, | 
					
						
							|  |  |  | // it must be set on the command line. | 
					
						
							|  |  |  | func (*HandlerT) WriteMemProfile(file string) error { | 
					
						
							|  |  |  | 	return writeProfile("heap", file) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-20 16:29:07 +03:00
										 |  |  | // Stacks returns a printed representation of the stacks of all goroutines. It | 
					
						
							|  |  |  | // also permits the following optional filters to be used: | 
					
						
							|  |  |  | //   - filter: boolean expression of packages to filter for | 
					
						
							|  |  |  | func (*HandlerT) Stacks(filter *string) string { | 
					
						
							| 
									
										
										
										
											2018-06-14 09:58:44 -04:00
										 |  |  | 	buf := new(bytes.Buffer) | 
					
						
							|  |  |  | 	pprof.Lookup("goroutine").WriteTo(buf, 2) | 
					
						
							| 
									
										
										
										
											2021-09-20 16:29:07 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// If any filtering was requested, execute them now | 
					
						
							|  |  |  | 	if filter != nil && len(*filter) > 0 { | 
					
						
							|  |  |  | 		expanded := *filter | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// The input filter is a logical expression of package names. Transform | 
					
						
							|  |  |  | 		// it into a proper boolean expression that can be fed into a parser and | 
					
						
							|  |  |  | 		// interpreter: | 
					
						
							|  |  |  | 		// | 
					
						
							|  |  |  | 		// E.g. (eth || snap) && !p2p -> (eth in Value || snap in Value) && p2p not in Value | 
					
						
							| 
									
										
										
										
											2021-09-20 18:55:35 +03:00
										 |  |  | 		expanded = regexp.MustCompile(`[:/\.A-Za-z0-9_-]+`).ReplaceAllString(expanded, "`$0` in Value") | 
					
						
							| 
									
										
										
										
											2021-09-20 16:29:07 +03:00
										 |  |  | 		expanded = regexp.MustCompile("!(`[:/\\.A-Za-z0-9_-]+`)").ReplaceAllString(expanded, "$1 not") | 
					
						
							|  |  |  | 		expanded = strings.Replace(expanded, "||", "or", -1) | 
					
						
							|  |  |  | 		expanded = strings.Replace(expanded, "&&", "and", -1) | 
					
						
							|  |  |  | 		log.Info("Expanded filter expression", "filter", *filter, "expanded", expanded) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		expr, err := bexpr.CreateEvaluator(expanded) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			log.Error("Failed to parse filter expression", "expanded", expanded, "err", err) | 
					
						
							|  |  |  | 			return "" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Split the goroutine dump into segments and filter each | 
					
						
							|  |  |  | 		dump := buf.String() | 
					
						
							|  |  |  | 		buf.Reset() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for _, trace := range strings.Split(dump, "\n\n") { | 
					
						
							|  |  |  | 			if ok, _ := expr.Evaluate(map[string]string{"Value": trace}); ok { | 
					
						
							|  |  |  | 				buf.WriteString(trace) | 
					
						
							|  |  |  | 				buf.WriteString("\n\n") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-14 09:58:44 -04:00
										 |  |  | 	return buf.String() | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-29 17:31:14 +08:00
										 |  |  | // FreeOSMemory forces a garbage collection. | 
					
						
							| 
									
										
										
										
											2017-09-11 09:33:18 +02:00
										 |  |  | func (*HandlerT) FreeOSMemory() { | 
					
						
							|  |  |  | 	debug.FreeOSMemory() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-11 12:29:47 +02:00
										 |  |  | // SetGCPercent sets the garbage collection target percentage. It returns the previous | 
					
						
							|  |  |  | // setting. A negative value disables GC. | 
					
						
							|  |  |  | func (*HandlerT) SetGCPercent(v int) int { | 
					
						
							|  |  |  | 	return debug.SetGCPercent(v) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | func writeProfile(name, file string) error { | 
					
						
							|  |  |  | 	p := pprof.Lookup(name) | 
					
						
							| 
									
										
										
										
											2017-03-03 11:41:52 +02:00
										 |  |  | 	log.Info("Writing profile records", "count", p.Count(), "type", name, "dump", file) | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | 	f, err := os.Create(expandHome(file)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer f.Close() | 
					
						
							|  |  |  | 	return p.WriteTo(f, 0) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // expands home directory in file paths. | 
					
						
							|  |  |  | // ~someuser/tmp will not be expanded. | 
					
						
							|  |  |  | func expandHome(p string) string { | 
					
						
							|  |  |  | 	if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { | 
					
						
							|  |  |  | 		home := os.Getenv("HOME") | 
					
						
							|  |  |  | 		if home == "" { | 
					
						
							|  |  |  | 			if usr, err := user.Current(); err == nil { | 
					
						
							|  |  |  | 				home = usr.HomeDir | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if home != "" { | 
					
						
							|  |  |  | 			p = home + p[1:] | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return filepath.Clean(p) | 
					
						
							|  |  |  | } |