diff options
Diffstat (limited to 'internal/debug/api.go')
-rw-r--r-- | internal/debug/api.go | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/internal/debug/api.go b/internal/debug/api.go new file mode 100644 index 000000000..83857eb2e --- /dev/null +++ b/internal/debug/api.go @@ -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) +} |