mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
- Update imports from luxdefi/node to luxfi/node - Replace libevm interfaces with direct go-ethereum imports - Add placeholder interface files for missing dependencies - Update go.mod to use local luxfi/node path - Fix merge conflicts in multiple files - Create stub implementations for missing packages This allows the project structure to align with v0.7.5 requirements but the build still needs additional work to be fully functional.
278 lines
8.3 KiB
Go
278 lines
8.3 KiB
Go
// (c) 2019-2020, Ava Labs, Inc.
|
|
//
|
|
// This file is a derived work, based on the go-ethereum library whose original
|
|
// notices appear below.
|
|
//
|
|
// It is distributed under a license compatible with the licensing terms of the
|
|
// original code from which it is derived.
|
|
//
|
|
// Much love to the original authors for their work.
|
|
// **********
|
|
// 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 (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
"regexp"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"runtime/pprof"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
"github.com/hashicorp/go-bexpr"
|
|
"log/slog"
|
|
)
|
|
|
|
// 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(slog.Level(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)
|
|
}
|
|
|
|
// 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. It
|
|
// also permits the following optional filters to be used:
|
|
// - filter: boolean expression of packages to filter for
|
|
func (*HandlerT) Stacks(filter *string) string {
|
|
buf := new(bytes.Buffer)
|
|
pprof.Lookup("goroutine").WriteTo(buf, 2)
|
|
|
|
// If any filtering was requested, execute them now
|
|
if filter != nil && len(*filter) > 0 {
|
|
expanded := *filter
|
|
|
|
// The input filter is a logical expression of package names. Transform
|
|
// it into a proper boolean expression that can be fed into a parser and
|
|
// interpreter:
|
|
//
|
|
// E.g. (eth || snap) && !p2p -> (eth in Value || snap in Value) && p2p not in Value
|
|
expanded = regexp.MustCompile(`[:/\.A-Za-z0-9_-]+`).ReplaceAllString(expanded, "`$0` in Value")
|
|
expanded = regexp.MustCompile("!(`[:/\\.A-Za-z0-9_-]+`)").ReplaceAllString(expanded, "$1 not")
|
|
expanded = strings.ReplaceAll(expanded, "||", "or")
|
|
expanded = strings.ReplaceAll(expanded, "&&", "and")
|
|
log.Info("Expanded filter expression", "filter", *filter, "expanded", expanded)
|
|
|
|
expr, err := bexpr.CreateEvaluator(expanded)
|
|
if err != nil {
|
|
log.Error("Failed to parse filter expression", "expanded", expanded, "err", err)
|
|
return ""
|
|
}
|
|
// Split the goroutine dump into segments and filter each
|
|
dump := buf.String()
|
|
buf.Reset()
|
|
|
|
for _, trace := range strings.Split(dump, "\n\n") {
|
|
if ok, _ := expr.Evaluate(map[string]string{"Value": trace}); ok {
|
|
buf.WriteString(trace)
|
|
buf.WriteString("\n\n")
|
|
}
|
|
}
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
// FreeOSMemory forces a garbage collection.
|
|
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)
|
|
}
|