193 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			193 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|   | // 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 ( | ||
|  | 	"errors" | ||
|  | 	"io" | ||
|  | 	"os" | ||
|  | 	"os/user" | ||
|  | 	"path/filepath" | ||
|  | 	"runtime" | ||
|  | 	"runtime/pprof" | ||
|  | 	"strings" | ||
|  | 	"sync" | ||
|  | 	"time" | ||
|  | 
 | ||
|  | 	"github.com/ethereum/go-ethereum/logger" | ||
|  | 	"github.com/ethereum/go-ethereum/logger/glog" | ||
|  | ) | ||
|  | 
 | ||
|  | // 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 | ||
|  | } | ||
|  | 
 | ||
|  | // Verbosity sets the glog verbosity floor. | ||
|  | // The verbosity of individual packages and source files | ||
|  | // can be raised using Vmodule. | ||
|  | func (*HandlerT) Verbosity(level int) { | ||
|  | 	glog.SetV(level) | ||
|  | } | ||
|  | 
 | ||
|  | // Vmodule sets the glog verbosity pattern. See package | ||
|  | // glog for details on pattern syntax. | ||
|  | func (*HandlerT) Vmodule(pattern string) error { | ||
|  | 	return glog.GetVModule().Set(pattern) | ||
|  | } | ||
|  | 
 | ||
|  | // BacktraceAt sets the glog backtrace location. | ||
|  | // See package glog for details on pattern syntax. | ||
|  | func (*HandlerT) BacktraceAt(location string) error { | ||
|  | 	return glog.GetTraceLocation().Set(location) | ||
|  | } | ||
|  | 
 | ||
|  | // 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 | ||
|  | 	glog.V(logger.Info).Infoln("CPU profiling started, writing to", h.cpuFile) | ||
|  | 	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") | ||
|  | 	} | ||
|  | 	glog.V(logger.Info).Infoln("done writing CPU profile to", h.cpuFile) | ||
|  | 	h.cpuW.Close() | ||
|  | 	h.cpuW = nil | ||
|  | 	h.cpuFile = "" | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | // Trace turns on tracing for nsec seconds and writes | ||
|  | // trace data to file. | ||
|  | func (h *HandlerT) Trace(file string, nsec uint) error { | ||
|  | 	if err := h.StartTrace(file); err != nil { | ||
|  | 		return err | ||
|  | 	} | ||
|  | 	time.Sleep(time.Duration(nsec) * time.Second) | ||
|  | 	h.StopTrace() | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | // BlockProfile turns on CPU 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) 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) | ||
|  | } | ||
|  | 
 | ||
|  | // 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) | ||
|  | } | ||
|  | 
 | ||
|  | // Stacks returns a printed representation of the stacks of all goroutines. | ||
|  | func (*HandlerT) Stacks() string { | ||
|  | 	buf := make([]byte, 1024*1024) | ||
|  | 	buf = buf[:runtime.Stack(buf, true)] | ||
|  | 	return string(buf) | ||
|  | } | ||
|  | 
 | ||
|  | func writeProfile(name, file string) error { | ||
|  | 	p := pprof.Lookup(name) | ||
|  | 	glog.V(logger.Info).Infof("writing %d %s profile records to %s", p.Count(), name, file) | ||
|  | 	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) | ||
|  | } |