|
|
@@ -0,0 +1,192 @@
|
|
|
+// 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)
|
|
|
+}
|