From ad886faec521f1edcb90f6f8eb4555608d085312 Mon Sep 17 00:00:00 2001 From: Determinant Date: Tue, 13 Aug 2019 14:05:49 -0400 Subject: add an option to call geth entry; add vendor --- internal/debug/api.go | 236 +++++++++++++++++++++++++++++++++++ internal/debug/flags.go | 174 ++++++++++++++++++++++++++ internal/debug/loudpanic.go | 27 ++++ internal/debug/loudpanic_fallback.go | 24 ++++ internal/debug/trace.go | 63 ++++++++++ internal/debug/trace_fallback.go | 31 +++++ 6 files changed, 555 insertions(+) create mode 100644 internal/debug/api.go create mode 100644 internal/debug/flags.go create mode 100644 internal/debug/loudpanic.go create mode 100644 internal/debug/loudpanic_fallback.go create mode 100644 internal/debug/trace.go create mode 100644 internal/debug/trace_fallback.go (limited to 'internal') diff --git a/internal/debug/api.go b/internal/debug/api.go new file mode 100644 index 0000000..86a4218 --- /dev/null +++ b/internal/debug/api.go @@ -0,0 +1,236 @@ +// 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 . + +// 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 ( + "bytes" + "errors" + "io" + "os" + "os/user" + "path/filepath" + "runtime" + "runtime/debug" + "runtime/pprof" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/log" +) + +// 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 log verbosity ceiling. The verbosity of individual packages +// and source files can be raised using Vmodule. +func (*HandlerT) Verbosity(level int) { + glogger.Verbosity(log.Lvl(level)) +} + +// Vmodule sets the log verbosity pattern. See package log for details on the +// pattern syntax. +func (*HandlerT) Vmodule(pattern string) error { + return glogger.Vmodule(pattern) +} + +// BacktraceAt sets the log backtrace location. See package log for details on +// the pattern syntax. +func (*HandlerT) BacktraceAt(location string) error { + return glogger.BacktraceAt(location) +} + +// 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 +} + +// 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 + log.Info("CPU profiling started", "dump", 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") + } + log.Info("Done writing CPU profile", "dump", h.cpuFile) + h.cpuW.Close() + h.cpuW = nil + h.cpuFile = "" + return nil +} + +// GoTrace turns on tracing for nsec seconds and writes +// trace data to file. +func (h *HandlerT) GoTrace(file string, nsec uint) error { + if err := h.StartGoTrace(file); err != nil { + return err + } + time.Sleep(time.Duration(nsec) * time.Second) + h.StopGoTrace() + return nil +} + +// 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. +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) +} + +// 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) +} + +// 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 := new(bytes.Buffer) + pprof.Lookup("goroutine").WriteTo(buf, 2) + return buf.String() +} + +// FreeOSMemory returns unused memory to the OS. +func (*HandlerT) FreeOSMemory() { + debug.FreeOSMemory() +} + +// 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) +} + +func writeProfile(name, file string) error { + p := pprof.Lookup(name) + log.Info("Writing profile records", "count", p.Count(), "type", name, "dump", 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) +} diff --git a/internal/debug/flags.go b/internal/debug/flags.go new file mode 100644 index 0000000..46c8fe9 --- /dev/null +++ b/internal/debug/flags.go @@ -0,0 +1,174 @@ +// 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 . + +package debug + +import ( + "fmt" + "io" + "net/http" + _ "net/http/pprof" + "os" + "runtime" + + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/metrics/exp" + "github.com/fjl/memsize/memsizeui" + colorable "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" + "gopkg.in/urfave/cli.v1" +) + +var Memsize memsizeui.Handler + +var ( + verbosityFlag = cli.IntFlag{ + Name: "verbosity", + Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail", + Value: 3, + } + vmoduleFlag = cli.StringFlag{ + Name: "vmodule", + Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4)", + Value: "", + } + backtraceAtFlag = cli.StringFlag{ + Name: "backtrace", + Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")", + Value: "", + } + debugFlag = cli.BoolFlag{ + Name: "debug", + Usage: "Prepends log messages with call-site location (file and line number)", + } + pprofFlag = cli.BoolFlag{ + Name: "pprof", + Usage: "Enable the pprof HTTP server", + } + pprofPortFlag = cli.IntFlag{ + Name: "pprofport", + Usage: "pprof HTTP server listening port", + Value: 6060, + } + pprofAddrFlag = cli.StringFlag{ + Name: "pprofaddr", + Usage: "pprof HTTP server listening interface", + Value: "127.0.0.1", + } + memprofilerateFlag = cli.IntFlag{ + Name: "memprofilerate", + Usage: "Turn on memory profiling with the given rate", + Value: runtime.MemProfileRate, + } + blockprofilerateFlag = cli.IntFlag{ + Name: "blockprofilerate", + Usage: "Turn on block profiling with the given rate", + } + cpuprofileFlag = cli.StringFlag{ + Name: "cpuprofile", + Usage: "Write CPU profile to the given file", + } + traceFlag = cli.StringFlag{ + Name: "trace", + Usage: "Write execution trace to the given file", + } +) + +// Flags holds all command-line flags required for debugging. +var Flags = []cli.Flag{ + verbosityFlag, vmoduleFlag, backtraceAtFlag, debugFlag, + pprofFlag, pprofAddrFlag, pprofPortFlag, + memprofilerateFlag, blockprofilerateFlag, cpuprofileFlag, traceFlag, +} + +var ( + ostream log.Handler + glogger *log.GlogHandler +) + +func init() { + usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" + output := io.Writer(os.Stderr) + if usecolor { + output = colorable.NewColorableStderr() + } + ostream = log.StreamHandler(output, log.TerminalFormat(usecolor)) + glogger = log.NewGlogHandler(ostream) +} + +// Setup initializes profiling and logging based on the CLI flags. +// It should be called as early as possible in the program. +func Setup(ctx *cli.Context, logdir string) error { + // logging + log.PrintOrigins(ctx.GlobalBool(debugFlag.Name)) + if logdir != "" { + rfh, err := log.RotatingFileHandler( + logdir, + 262144, + log.JSONFormatOrderedEx(false, true), + ) + if err != nil { + return err + } + glogger.SetHandler(log.MultiHandler(ostream, rfh)) + } + glogger.Verbosity(log.Lvl(ctx.GlobalInt(verbosityFlag.Name))) + glogger.Vmodule(ctx.GlobalString(vmoduleFlag.Name)) + glogger.BacktraceAt(ctx.GlobalString(backtraceAtFlag.Name)) + log.Root().SetHandler(glogger) + + // profiling, tracing + runtime.MemProfileRate = ctx.GlobalInt(memprofilerateFlag.Name) + Handler.SetBlockProfileRate(ctx.GlobalInt(blockprofilerateFlag.Name)) + if traceFile := ctx.GlobalString(traceFlag.Name); traceFile != "" { + if err := Handler.StartGoTrace(traceFile); err != nil { + return err + } + } + if cpuFile := ctx.GlobalString(cpuprofileFlag.Name); cpuFile != "" { + if err := Handler.StartCPUProfile(cpuFile); err != nil { + return err + } + } + + // pprof server + if ctx.GlobalBool(pprofFlag.Name) { + address := fmt.Sprintf("%s:%d", ctx.GlobalString(pprofAddrFlag.Name), ctx.GlobalInt(pprofPortFlag.Name)) + StartPProf(address) + } + return nil +} + +func StartPProf(address string) { + // Hook go-metrics into expvar on any /debug/metrics request, load all vars + // from the registry into expvar, and execute regular expvar handler. + exp.Exp(metrics.DefaultRegistry) + http.Handle("/memsize/", http.StripPrefix("/memsize", &Memsize)) + log.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", address)) + go func() { + if err := http.ListenAndServe(address, nil); err != nil { + log.Error("Failure in running pprof server", "err", err) + } + }() +} + +// Exit stops all running profiles, flushing their output to the +// respective file. +func Exit() { + Handler.StopCPUProfile() + Handler.StopGoTrace() +} diff --git a/internal/debug/loudpanic.go b/internal/debug/loudpanic.go new file mode 100644 index 0000000..572ebce --- /dev/null +++ b/internal/debug/loudpanic.go @@ -0,0 +1,27 @@ +// 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 . + +// +build go1.6 + +package debug + +import "runtime/debug" + +// LoudPanic panics in a way that gets all goroutine stacks printed on stderr. +func LoudPanic(x interface{}) { + debug.SetTraceback("all") + panic(x) +} diff --git a/internal/debug/loudpanic_fallback.go b/internal/debug/loudpanic_fallback.go new file mode 100644 index 0000000..4ce4985 --- /dev/null +++ b/internal/debug/loudpanic_fallback.go @@ -0,0 +1,24 @@ +// 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 . + +// +build !go1.6 + +package debug + +// LoudPanic panics in a way that gets all goroutine stacks printed on stderr. +func LoudPanic(x interface{}) { + panic(x) +} diff --git a/internal/debug/trace.go b/internal/debug/trace.go new file mode 100644 index 0000000..cab5dea --- /dev/null +++ b/internal/debug/trace.go @@ -0,0 +1,63 @@ +// 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 . + +//+build go1.5 + +package debug + +import ( + "errors" + "os" + "runtime/trace" + + "github.com/ethereum/go-ethereum/log" +) + +// StartGoTrace turns on tracing, writing to the given file. +func (h *HandlerT) StartGoTrace(file string) error { + h.mu.Lock() + defer h.mu.Unlock() + if h.traceW != nil { + return errors.New("trace already in progress") + } + f, err := os.Create(expandHome(file)) + if err != nil { + return err + } + if err := trace.Start(f); err != nil { + f.Close() + return err + } + h.traceW = f + h.traceFile = file + log.Info("Go tracing started", "dump", h.traceFile) + return nil +} + +// StopTrace stops an ongoing trace. +func (h *HandlerT) StopGoTrace() error { + h.mu.Lock() + defer h.mu.Unlock() + trace.Stop() + if h.traceW == nil { + return errors.New("trace not in progress") + } + log.Info("Done writing Go trace", "dump", h.traceFile) + h.traceW.Close() + h.traceW = nil + h.traceFile = "" + return nil +} diff --git a/internal/debug/trace_fallback.go b/internal/debug/trace_fallback.go new file mode 100644 index 0000000..4118ff4 --- /dev/null +++ b/internal/debug/trace_fallback.go @@ -0,0 +1,31 @@ +// 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 . + +//+build !go1.5 + +// no-op implementation of tracing methods for Go < 1.5. + +package debug + +import "errors" + +func (*HandlerT) StartGoTrace(string) error { + return errors.New("tracing is not supported on Go < 1.5") +} + +func (*HandlerT) StopGoTrace() error { + return errors.New("tracing is not supported on Go < 1.5") +} -- cgit v1.2.3