| 
									
										
										
										
											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 ( | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"os/user" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2016-02-20 15:42:22 +01:00
										 |  |  | 	"runtime/debug" | 
					
						
							| 
									
										
										
										
											2016-01-26 14:39:21 +01:00
										 |  |  | 	"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) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	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) | 
					
						
							|  |  |  | } |