Commit 1b5413b8 authored by Artyom Artamonov's avatar Artyom Artamonov Committed by GitHub

op-service/pprof: Support profiling to file (#6739) (#8709)

parent 4250b6e5
...@@ -12,7 +12,7 @@ import ( ...@@ -12,7 +12,7 @@ import (
"github.com/ethereum-optimism/optimism/op-batcher/flags" "github.com/ethereum-optimism/optimism/op-batcher/flags"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
) )
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-batcher/flags" "github.com/ethereum-optimism/optimism/op-batcher/flags"
"github.com/ethereum-optimism/optimism/op-service/log" "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum-optimism/optimism/op-service/metrics" "github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum-optimism/optimism/op-service/rpc" "github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -30,7 +30,7 @@ func validBatcherConfig() batcher.CLIConfig { ...@@ -30,7 +30,7 @@ func validBatcherConfig() batcher.CLIConfig {
TxMgrConfig: txmgr.NewCLIConfig("fake", txmgr.DefaultBatcherFlagValues), TxMgrConfig: txmgr.NewCLIConfig("fake", txmgr.DefaultBatcherFlagValues),
LogConfig: log.DefaultCLIConfig(), LogConfig: log.DefaultCLIConfig(),
MetricsConfig: metrics.DefaultCLIConfig(), MetricsConfig: metrics.DefaultCLIConfig(),
PprofConfig: pprof.DefaultCLIConfig(), PprofConfig: oppprof.DefaultCLIConfig(),
// The compressor config is not checked in config.Check() // The compressor config is not checked in config.Check()
RPC: rpc.DefaultCLIConfig(), RPC: rpc.DefaultCLIConfig(),
} }
......
...@@ -5,9 +5,7 @@ import ( ...@@ -5,9 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net"
_ "net/http/pprof" _ "net/http/pprof"
"strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
"time" "time"
...@@ -24,7 +22,7 @@ import ( ...@@ -24,7 +22,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/httputil" "github.com/ethereum-optimism/optimism/op-service/httputil"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
) )
...@@ -62,13 +60,12 @@ type BatcherService struct { ...@@ -62,13 +60,12 @@ type BatcherService struct {
Version string Version string
pprofSrv *httputil.HTTPServer pprofService *oppprof.Service
metricsSrv *httputil.HTTPServer metricsSrv *httputil.HTTPServer
rpcServer *oprpc.Server rpcServer *oprpc.Server
balanceMetricer io.Closer balanceMetricer io.Closer
stopped atomic.Bool
stopped atomic.Bool
NotSubmittingOnStart bool NotSubmittingOnStart bool
} }
...@@ -111,7 +108,7 @@ func (bs *BatcherService) initFromCLIConfig(ctx context.Context, version string, ...@@ -111,7 +108,7 @@ func (bs *BatcherService) initFromCLIConfig(ctx context.Context, version string,
return fmt.Errorf("failed to start metrics server: %w", err) return fmt.Errorf("failed to start metrics server: %w", err)
} }
if err := bs.initPProf(cfg); err != nil { if err := bs.initPProf(cfg); err != nil {
return fmt.Errorf("failed to start pprof server: %w", err) return fmt.Errorf("failed to init profiling: %w", err)
} }
bs.initDriver() bs.initDriver()
if err := bs.initRPCServer(cfg); err != nil { if err := bs.initRPCServer(cfg); err != nil {
...@@ -216,16 +213,19 @@ func (bs *BatcherService) initTxManager(cfg *CLIConfig) error { ...@@ -216,16 +213,19 @@ func (bs *BatcherService) initTxManager(cfg *CLIConfig) error {
} }
func (bs *BatcherService) initPProf(cfg *CLIConfig) error { func (bs *BatcherService) initPProf(cfg *CLIConfig) error {
if !cfg.PprofConfig.Enabled { bs.pprofService = oppprof.New(
return nil cfg.PprofConfig.ListenEnabled,
} cfg.PprofConfig.ListenAddr,
log.Debug("starting pprof server", "addr", net.JoinHostPort(cfg.PprofConfig.ListenAddr, strconv.Itoa(cfg.PprofConfig.ListenPort))) cfg.PprofConfig.ListenPort,
srv, err := oppprof.StartServer(cfg.PprofConfig.ListenAddr, cfg.PprofConfig.ListenPort) cfg.PprofConfig.ProfileType,
if err != nil { cfg.PprofConfig.ProfileDir,
return err cfg.PprofConfig.ProfileFilename,
)
if err := bs.pprofService.Start(); err != nil {
return fmt.Errorf("failed to start pprof service: %w", err)
} }
bs.pprofSrv = srv
log.Info("started pprof server", "addr", srv.Addr())
return nil return nil
} }
...@@ -326,8 +326,8 @@ func (bs *BatcherService) Stop(ctx context.Context) error { ...@@ -326,8 +326,8 @@ func (bs *BatcherService) Stop(ctx context.Context) error {
result = errors.Join(result, fmt.Errorf("failed to stop RPC server: %w", err)) result = errors.Join(result, fmt.Errorf("failed to stop RPC server: %w", err))
} }
} }
if bs.pprofSrv != nil { if bs.pprofService != nil {
if err := bs.pprofSrv.Stop(ctx); err != nil { if err := bs.pprofService.Stop(ctx); err != nil {
result = errors.Join(result, fmt.Errorf("failed to stop PProf server: %w", err)) result = errors.Join(result, fmt.Errorf("failed to stop PProf server: %w", err))
} }
} }
......
...@@ -10,7 +10,7 @@ import ( ...@@ -10,7 +10,7 @@ import (
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
) )
......
...@@ -11,7 +11,7 @@ import ( ...@@ -11,7 +11,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/chaincfg"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
) )
......
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
openum "github.com/ethereum-optimism/optimism/op-service/enum" openum "github.com/ethereum-optimism/optimism/op-service/enum"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
) )
......
...@@ -24,7 +24,7 @@ import ( ...@@ -24,7 +24,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/dial" "github.com/ethereum-optimism/optimism/op-service/dial"
"github.com/ethereum-optimism/optimism/op-service/httputil" "github.com/ethereum-optimism/optimism/op-service/httputil"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum-optimism/optimism/op-service/sources/batching" "github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
) )
...@@ -46,8 +46,8 @@ type Service struct { ...@@ -46,8 +46,8 @@ type Service struct {
l1Client *ethclient.Client l1Client *ethclient.Client
pollClient client.RPC pollClient client.RPC
pprofSrv *httputil.HTTPServer pprofService *oppprof.Service
metricsSrv *httputil.HTTPServer metricsSrv *httputil.HTTPServer
balanceMetricer io.Closer balanceMetricer io.Closer
...@@ -71,28 +71,28 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*Se ...@@ -71,28 +71,28 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*Se
func (s *Service) initFromConfig(ctx context.Context, cfg *config.Config) error { func (s *Service) initFromConfig(ctx context.Context, cfg *config.Config) error {
if err := s.initTxManager(cfg); err != nil { if err := s.initTxManager(cfg); err != nil {
return err return fmt.Errorf("failed to init tx manager: %w", err)
} }
if err := s.initL1Client(ctx, cfg); err != nil { if err := s.initL1Client(ctx, cfg); err != nil {
return err return fmt.Errorf("failed to init l1 client: %w", err)
} }
if err := s.initRollupClient(ctx, cfg); err != nil { if err := s.initRollupClient(ctx, cfg); err != nil {
return err return fmt.Errorf("failed to init rollup client: %w", err)
} }
if err := s.initPollClient(ctx, cfg); err != nil { if err := s.initPollClient(ctx, cfg); err != nil {
return err return fmt.Errorf("failed to init poll client: %w", err)
} }
if err := s.initPProfServer(&cfg.PprofConfig); err != nil { if err := s.initPProf(&cfg.PprofConfig); err != nil {
return err return fmt.Errorf("failed to init profiling: %w", err)
} }
if err := s.initMetricsServer(&cfg.MetricsConfig); err != nil { if err := s.initMetricsServer(&cfg.MetricsConfig); err != nil {
return err return fmt.Errorf("failed to init metrics server: %w", err)
} }
if err := s.initGameLoader(cfg); err != nil { if err := s.initGameLoader(cfg); err != nil {
return err return fmt.Errorf("failed to init game loader: %w", err)
} }
if err := s.initScheduler(ctx, cfg); err != nil { if err := s.initScheduler(ctx, cfg); err != nil {
return err return fmt.Errorf("failed to init scheduler: %w", err)
} }
s.initMonitor(cfg) s.initMonitor(cfg)
...@@ -129,17 +129,20 @@ func (s *Service) initPollClient(ctx context.Context, cfg *config.Config) error ...@@ -129,17 +129,20 @@ func (s *Service) initPollClient(ctx context.Context, cfg *config.Config) error
return nil return nil
} }
func (s *Service) initPProfServer(cfg *oppprof.CLIConfig) error { func (s *Service) initPProf(cfg *oppprof.CLIConfig) error {
if !cfg.Enabled { s.pprofService = oppprof.New(
return nil cfg.ListenEnabled,
} cfg.ListenAddr,
s.logger.Debug("starting pprof", "addr", cfg.ListenAddr, "port", cfg.ListenPort) cfg.ListenPort,
pprofSrv, err := oppprof.StartServer(cfg.ListenAddr, cfg.ListenPort) cfg.ProfileType,
if err != nil { cfg.ProfileDir,
return fmt.Errorf("failed to start pprof server: %w", err) cfg.ProfileFilename,
)
if err := s.pprofService.Start(); err != nil {
return fmt.Errorf("failed to start pprof service: %w", err)
} }
s.pprofSrv = pprofSrv
s.logger.Info("started pprof server", "addr", pprofSrv.Addr())
return nil return nil
} }
...@@ -231,8 +234,8 @@ func (s *Service) Stop(ctx context.Context) error { ...@@ -231,8 +234,8 @@ func (s *Service) Stop(ctx context.Context) error {
if s.faultGamesCloser != nil { if s.faultGamesCloser != nil {
s.faultGamesCloser() s.faultGamesCloser()
} }
if s.pprofSrv != nil { if s.pprofService != nil {
if err := s.pprofSrv.Stop(ctx); err != nil { if err := s.pprofService.Stop(ctx); err != nil {
result = errors.Join(result, fmt.Errorf("failed to close pprof server: %w", err)) result = errors.Join(result, fmt.Errorf("failed to close pprof server: %w", err))
} }
} }
......
...@@ -12,7 +12,7 @@ import ( ...@@ -12,7 +12,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
) )
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
opflags "github.com/ethereum-optimism/optimism/op-service/flags" opflags "github.com/ethereum-optimism/optimism/op-service/flags"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
) )
......
...@@ -41,7 +41,7 @@ import ( ...@@ -41,7 +41,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup/driver" "github.com/ethereum-optimism/optimism/op-node/rollup/driver"
"github.com/ethereum-optimism/optimism/op-service/client" "github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum-optimism/optimism/op-service/retry" "github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
......
...@@ -6,7 +6,7 @@ import ( ...@@ -6,7 +6,7 @@ import (
"github.com/ethereum-optimism/optimism/op-heartbeat/flags" "github.com/ethereum-optimism/optimism/op-heartbeat/flags"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
......
...@@ -23,7 +23,7 @@ import ( ...@@ -23,7 +23,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/httputil" "github.com/ethereum-optimism/optimism/op-service/httputil"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
) )
const ( const (
...@@ -60,13 +60,14 @@ func Main(version string) func(ctx *cli.Context) error { ...@@ -60,13 +60,14 @@ func Main(version string) func(ctx *cli.Context) error {
} }
type HeartbeatService struct { type HeartbeatService struct {
pprof, metrics, http *httputil.HTTPServer metrics, http *httputil.HTTPServer
pprofService *oppprof.Service
} }
func (hs *HeartbeatService) Stop(ctx context.Context) error { func (hs *HeartbeatService) Stop(ctx context.Context) error {
var result error var result error
if hs.pprof != nil { if hs.pprofService != nil {
result = errors.Join(result, hs.pprof.Stop(ctx)) result = errors.Join(result, hs.pprofService.Stop(ctx))
} }
if hs.metrics != nil { if hs.metrics != nil {
result = errors.Join(result, hs.metrics.Stop(ctx)) result = errors.Join(result, hs.metrics.Stop(ctx))
...@@ -93,14 +94,17 @@ func Start(ctx context.Context, l log.Logger, cfg Config, version string) (*Hear ...@@ -93,14 +94,17 @@ func Start(ctx context.Context, l log.Logger, cfg Config, version string) (*Hear
} }
pprofCfg := cfg.Pprof pprofCfg := cfg.Pprof
if pprofCfg.Enabled { hs.pprofService = oppprof.New(
l.Debug("starting pprof", "addr", pprofCfg.ListenAddr, "port", pprofCfg.ListenPort) pprofCfg.ListenEnabled,
pprofSrv, err := oppprof.StartServer(pprofCfg.ListenAddr, pprofCfg.ListenPort) pprofCfg.ListenAddr,
if err != nil { pprofCfg.ListenPort,
return nil, errors.Join(fmt.Errorf("failed to start pprof server: %w", err), hs.Stop(ctx)) pprofCfg.ProfileType,
} pprofCfg.ProfileDir,
l.Info("started pprof server", "addr", pprofSrv.Addr()) pprofCfg.ProfileFilename,
hs.pprof = pprofSrv )
if err := hs.pprofService.Start(); err != nil {
return nil, fmt.Errorf("failed to start pprof service: %w", err)
} }
metrics := NewMetrics(registry) metrics := NewMetrics(registry)
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
openum "github.com/ethereum-optimism/optimism/op-service/enum" openum "github.com/ethereum-optimism/optimism/op-service/enum"
opflags "github.com/ethereum-optimism/optimism/op-service/flags" opflags "github.com/ethereum-optimism/optimism/op-service/flags"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-service/sources"
) )
...@@ -176,23 +177,6 @@ var ( ...@@ -176,23 +177,6 @@ var (
Value: 7300, Value: 7300,
EnvVars: prefixEnvVars("METRICS_PORT"), EnvVars: prefixEnvVars("METRICS_PORT"),
} }
PprofEnabledFlag = &cli.BoolFlag{
Name: "pprof.enabled",
Usage: "Enable the pprof server",
EnvVars: prefixEnvVars("PPROF_ENABLED"),
}
PprofAddrFlag = &cli.StringFlag{
Name: "pprof.addr",
Usage: "pprof listening address",
Value: "0.0.0.0", // TODO(CLI-4159): Switch to 127.0.0.1
EnvVars: prefixEnvVars("PPROF_ADDR"),
}
PprofPortFlag = &cli.IntFlag{
Name: "pprof.port",
Usage: "pprof listening port",
Value: 6060,
EnvVars: prefixEnvVars("PPROF_PORT"),
}
SnapshotLog = &cli.StringFlag{ SnapshotLog = &cli.StringFlag{
Name: "snapshotlog.file", Name: "snapshotlog.file",
Usage: "Path to the snapshot log file", Usage: "Path to the snapshot log file",
...@@ -289,9 +273,6 @@ var optionalFlags = []cli.Flag{ ...@@ -289,9 +273,6 @@ var optionalFlags = []cli.Flag{
MetricsEnabledFlag, MetricsEnabledFlag,
MetricsAddrFlag, MetricsAddrFlag,
MetricsPortFlag, MetricsPortFlag,
PprofEnabledFlag,
PprofAddrFlag,
PprofPortFlag,
SnapshotLog, SnapshotLog,
HeartbeatEnabledFlag, HeartbeatEnabledFlag,
HeartbeatMonikerFlag, HeartbeatMonikerFlag,
...@@ -317,6 +298,7 @@ func init() { ...@@ -317,6 +298,7 @@ func init() {
DeprecatedFlags = append(DeprecatedFlags, deprecatedP2PFlags(EnvVarPrefix)...) DeprecatedFlags = append(DeprecatedFlags, deprecatedP2PFlags(EnvVarPrefix)...)
optionalFlags = append(optionalFlags, P2PFlags(EnvVarPrefix)...) optionalFlags = append(optionalFlags, P2PFlags(EnvVarPrefix)...)
optionalFlags = append(optionalFlags, oplog.CLIFlags(EnvVarPrefix)...) optionalFlags = append(optionalFlags, oplog.CLIFlags(EnvVarPrefix)...)
optionalFlags = append(optionalFlags, oppprof.CLIFlags(EnvVarPrefix)...)
optionalFlags = append(optionalFlags, DeprecatedFlags...) optionalFlags = append(optionalFlags, DeprecatedFlags...)
optionalFlags = append(optionalFlags, opflags.CLIFlags(EnvVarPrefix)...) optionalFlags = append(optionalFlags, opflags.CLIFlags(EnvVarPrefix)...)
Flags = append(requiredFlags, optionalFlags...) Flags = append(requiredFlags, optionalFlags...)
......
...@@ -12,7 +12,7 @@ import ( ...@@ -12,7 +12,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/driver" "github.com/ethereum-optimism/optimism/op-node/rollup/driver"
"github.com/ethereum-optimism/optimism/op-node/rollup/sync" "github.com/ethereum-optimism/optimism/op-node/rollup/sync"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
......
...@@ -4,8 +4,6 @@ import ( ...@@ -4,8 +4,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net"
"strconv"
"sync/atomic" "sync/atomic"
"time" "time"
...@@ -26,7 +24,7 @@ import ( ...@@ -26,7 +24,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/version" "github.com/ethereum-optimism/optimism/op-node/version"
"github.com/ethereum-optimism/optimism/op-service/client" "github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum-optimism/optimism/op-service/retry" "github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-service/sources"
) )
...@@ -55,8 +53,8 @@ type OpNode struct { ...@@ -55,8 +53,8 @@ type OpNode struct {
rollupHalt string // when to halt the rollup, disabled if empty rollupHalt string // when to halt the rollup, disabled if empty
pprofSrv *httputil.HTTPServer pprofService *oppprof.Service
metricsSrv *httputil.HTTPServer metricsSrv *httputil.HTTPServer
beacon *sources.L1BeaconClient beacon *sources.L1BeaconClient
...@@ -142,7 +140,7 @@ func (n *OpNode) init(ctx context.Context, cfg *Config, snapshotLog log.Logger) ...@@ -142,7 +140,7 @@ func (n *OpNode) init(ctx context.Context, cfg *Config, snapshotLog log.Logger)
n.metrics.RecordUp() n.metrics.RecordUp()
n.initHeartbeat(cfg) n.initHeartbeat(cfg)
if err := n.initPProf(cfg); err != nil { if err := n.initPProf(cfg); err != nil {
return fmt.Errorf("failed to init pprof server: %w", err) return fmt.Errorf("failed to init profiling: %w", err)
} }
return nil return nil
} }
...@@ -393,16 +391,19 @@ func (n *OpNode) initHeartbeat(cfg *Config) { ...@@ -393,16 +391,19 @@ func (n *OpNode) initHeartbeat(cfg *Config) {
} }
func (n *OpNode) initPProf(cfg *Config) error { func (n *OpNode) initPProf(cfg *Config) error {
if !cfg.Pprof.Enabled { n.pprofService = oppprof.New(
return nil cfg.Pprof.ListenEnabled,
} cfg.Pprof.ListenAddr,
log.Debug("starting pprof server", "addr", net.JoinHostPort(cfg.Pprof.ListenAddr, strconv.Itoa(cfg.Pprof.ListenPort))) cfg.Pprof.ListenPort,
srv, err := oppprof.StartServer(cfg.Pprof.ListenAddr, cfg.Pprof.ListenPort) cfg.Pprof.ProfileType,
if err != nil { cfg.Pprof.ProfileDir,
return err cfg.Pprof.ProfileFilename,
)
if err := n.pprofService.Start(); err != nil {
return fmt.Errorf("failed to start pprof service: %w", err)
} }
n.pprofSrv = srv
log.Info("started pprof server", "addr", srv.Addr())
return nil return nil
} }
...@@ -621,8 +622,8 @@ func (n *OpNode) Stop(ctx context.Context) error { ...@@ -621,8 +622,8 @@ func (n *OpNode) Stop(ctx context.Context) error {
} }
// Close metrics and pprof only after we are done idling // Close metrics and pprof only after we are done idling
if n.pprofSrv != nil { if n.pprofService != nil {
if err := n.pprofSrv.Stop(ctx); err != nil { if err := n.pprofService.Stop(ctx); err != nil {
result = multierror.Append(result, fmt.Errorf("failed to close pprof server: %w", err)) result = multierror.Append(result, fmt.Errorf("failed to close pprof server: %w", err))
} }
} }
......
...@@ -9,12 +9,14 @@ import ( ...@@ -9,12 +9,14 @@ import (
"os" "os"
"strings" "strings"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/flags" "github.com/ethereum-optimism/optimism/op-node/flags"
"github.com/ethereum-optimism/optimism/op-node/node" "github.com/ethereum-optimism/optimism/op-node/node"
p2pcli "github.com/ethereum-optimism/optimism/op-node/p2p/cli" p2pcli "github.com/ethereum-optimism/optimism/op-node/p2p/cli"
...@@ -22,8 +24,6 @@ import ( ...@@ -22,8 +24,6 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup/driver" "github.com/ethereum-optimism/optimism/op-node/rollup/driver"
"github.com/ethereum-optimism/optimism/op-node/rollup/sync" "github.com/ethereum-optimism/optimism/op-node/rollup/sync"
opflags "github.com/ethereum-optimism/optimism/op-service/flags" opflags "github.com/ethereum-optimism/optimism/op-service/flags"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/ethereum-optimism/optimism/op-service/sources"
) )
// NewConfig creates a Config from the provided flags or environment variables. // NewConfig creates a Config from the provided flags or environment variables.
...@@ -88,11 +88,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { ...@@ -88,11 +88,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
ListenAddr: ctx.String(flags.MetricsAddrFlag.Name), ListenAddr: ctx.String(flags.MetricsAddrFlag.Name),
ListenPort: ctx.Int(flags.MetricsPortFlag.Name), ListenPort: ctx.Int(flags.MetricsPortFlag.Name),
}, },
Pprof: oppprof.CLIConfig{ Pprof: oppprof.ReadCLIConfig(ctx),
Enabled: ctx.Bool(flags.PprofEnabledFlag.Name),
ListenAddr: ctx.String(flags.PprofAddrFlag.Name),
ListenPort: ctx.Int(flags.PprofPortFlag.Name),
},
P2P: p2pConfig, P2P: p2pConfig,
P2PSigner: p2pSignerSetup, P2PSigner: p2pSignerSetup,
L1EpochPollInterval: ctx.Duration(flags.L1EpochPollIntervalFlag.Name), L1EpochPollInterval: ctx.Duration(flags.L1EpochPollIntervalFlag.Name),
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
) )
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-proposer/flags" "github.com/ethereum-optimism/optimism/op-proposer/flags"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
) )
......
...@@ -5,8 +5,6 @@ import ( ...@@ -5,8 +5,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net"
"strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
"time" "time"
...@@ -18,7 +16,7 @@ import ( ...@@ -18,7 +16,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/dial" "github.com/ethereum-optimism/optimism/op-service/dial"
"github.com/ethereum-optimism/optimism/op-service/httputil" "github.com/ethereum-optimism/optimism/op-service/httputil"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -61,9 +59,9 @@ type ProposerService struct { ...@@ -61,9 +59,9 @@ type ProposerService struct {
Version string Version string
pprofSrv *httputil.HTTPServer pprofService *oppprof.Service
metricsSrv *httputil.HTTPServer metricsSrv *httputil.HTTPServer
rpcServer *oprpc.Server rpcServer *oprpc.Server
balanceMetricer io.Closer balanceMetricer io.Closer
...@@ -105,7 +103,7 @@ func (ps *ProposerService) initFromCLIConfig(ctx context.Context, version string ...@@ -105,7 +103,7 @@ func (ps *ProposerService) initFromCLIConfig(ctx context.Context, version string
return fmt.Errorf("failed to start metrics server: %w", err) return fmt.Errorf("failed to start metrics server: %w", err)
} }
if err := ps.initPProf(cfg); err != nil { if err := ps.initPProf(cfg); err != nil {
return fmt.Errorf("failed to start pprof server: %w", err) return fmt.Errorf("failed to init profiling: %w", err)
} }
if err := ps.initDriver(); err != nil { if err := ps.initDriver(); err != nil {
return fmt.Errorf("failed to init Driver: %w", err) return fmt.Errorf("failed to init Driver: %w", err)
...@@ -166,16 +164,19 @@ func (ps *ProposerService) initTxManager(cfg *CLIConfig) error { ...@@ -166,16 +164,19 @@ func (ps *ProposerService) initTxManager(cfg *CLIConfig) error {
} }
func (ps *ProposerService) initPProf(cfg *CLIConfig) error { func (ps *ProposerService) initPProf(cfg *CLIConfig) error {
if !cfg.PprofConfig.Enabled { ps.pprofService = oppprof.New(
return nil cfg.PprofConfig.ListenEnabled,
} cfg.PprofConfig.ListenAddr,
log.Debug("starting pprof server", "addr", net.JoinHostPort(cfg.PprofConfig.ListenAddr, strconv.Itoa(cfg.PprofConfig.ListenPort))) cfg.PprofConfig.ListenPort,
srv, err := oppprof.StartServer(cfg.PprofConfig.ListenAddr, cfg.PprofConfig.ListenPort) cfg.PprofConfig.ProfileType,
if err != nil { cfg.PprofConfig.ProfileDir,
return err cfg.PprofConfig.ProfileFilename,
)
if err := ps.pprofService.Start(); err != nil {
return fmt.Errorf("failed to start pprof service: %w", err)
} }
ps.pprofSrv = srv
log.Info("started pprof server", "addr", srv.Addr())
return nil return nil
} }
...@@ -294,8 +295,8 @@ func (ps *ProposerService) Stop(ctx context.Context) error { ...@@ -294,8 +295,8 @@ func (ps *ProposerService) Stop(ctx context.Context) error {
result = errors.Join(result, fmt.Errorf("failed to stop RPC server: %w", err)) result = errors.Join(result, fmt.Errorf("failed to stop RPC server: %w", err))
} }
} }
if ps.pprofSrv != nil { if ps.pprofService != nil {
if err := ps.pprofSrv.Stop(ctx); err != nil { if err := ps.pprofService.Stop(ctx); err != nil {
result = errors.Join(result, fmt.Errorf("failed to stop PProf server: %w", err)) result = errors.Join(result, fmt.Errorf("failed to stop PProf server: %w", err))
} }
} }
......
package flags
import (
"os"
"path/filepath"
)
// PathFlag accepts path to some file or directory and splits it
// into dirPath and filename (both can be empty)
type PathFlag struct {
originalPath string
dirPath string
filename string
}
func (f *PathFlag) Set(path string) error {
f.originalPath = path
fileInfo, err := os.Stat(path)
if err != nil && !os.IsNotExist(err) {
return err
}
if fileInfo != nil && fileInfo.IsDir() {
f.dirPath = path
return nil
}
f.dirPath, f.filename = filepath.Split(path)
return nil
}
func (f *PathFlag) String() string {
return f.originalPath
}
func (f *PathFlag) Clone() any {
return &PathFlag{
originalPath: f.originalPath,
dirPath: f.dirPath,
filename: f.filename,
}
}
func (f *PathFlag) Dir() string {
return f.dirPath
}
func (f *PathFlag) Filename() string {
return f.filename
}
package pprof package oppprof
import ( import (
"errors" "errors"
"fmt"
"math" "math"
"strings"
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
openum "github.com/ethereum-optimism/optimism/op-service/enum"
"github.com/ethereum-optimism/optimism/op-service/flags"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
const ( const (
EnabledFlagName = "pprof.enabled" EnabledFlagName = "pprof.enabled"
ListenAddrFlagName = "pprof.addr" ListenAddrFlagName = "pprof.addr"
PortFlagName = "pprof.port" PortFlagName = "pprof.port"
defaultListenAddr = "0.0.0.0" ProfileTypeFlagName = "pprof.type"
defaultListenPort = 6060 ProfilePathFlagName = "pprof.path"
defaultListenAddr = "0.0.0.0"
defaultListenPort = 6060
) )
var allowedProfileTypes = []profileType{"cpu", "heap", "goroutine", "threadcreate", "block", "mutex", "allocs"}
type profileType string
func (t profileType) String() string {
return string(t)
}
func (t *profileType) Set(value string) error {
if !validProfileType(profileType(value)) {
return fmt.Errorf("unknown profile type: %q", value)
}
*t = profileType(value)
return nil
}
func (t *profileType) Clone() any {
cpy := *t
return &cpy
}
func validProfileType(value profileType) bool {
for _, k := range allowedProfileTypes {
if k == value {
return true
}
}
return false
}
func DefaultCLIConfig() CLIConfig { func DefaultCLIConfig() CLIConfig {
return CLIConfig{ return CLIConfig{
Enabled: false, ListenEnabled: false,
ListenAddr: defaultListenAddr, ListenAddr: defaultListenAddr,
ListenPort: defaultListenPort, ListenPort: defaultListenPort,
} }
} }
...@@ -43,17 +79,36 @@ func CLIFlags(envPrefix string) []cli.Flag { ...@@ -43,17 +79,36 @@ func CLIFlags(envPrefix string) []cli.Flag {
Value: defaultListenPort, Value: defaultListenPort,
EnvVars: opservice.PrefixEnvVar(envPrefix, "PPROF_PORT"), EnvVars: opservice.PrefixEnvVar(envPrefix, "PPROF_PORT"),
}, },
&cli.GenericFlag{
Name: ProfilePathFlagName,
Usage: "pprof file path. If it is a directory, the path is {dir}/{profileType}.prof",
Value: new(flags.PathFlag),
EnvVars: opservice.PrefixEnvVar(envPrefix, "PPROF_PATH"),
},
&cli.GenericFlag{
Name: ProfileTypeFlagName,
Usage: "pprof profile type. One of " + openum.EnumString(allowedProfileTypes),
Value: func() *profileType {
defaultProfType := profileType("")
return &defaultProfType
}(),
EnvVars: opservice.PrefixEnvVar(envPrefix, "PPROF_TYPE"),
},
} }
} }
type CLIConfig struct { type CLIConfig struct {
Enabled bool ListenEnabled bool
ListenAddr string ListenAddr string
ListenPort int ListenPort int
ProfileType profileType
ProfileDir string
ProfileFilename string
} }
func (m CLIConfig) Check() error { func (m CLIConfig) Check() error {
if !m.Enabled { if !m.ListenEnabled {
return nil return nil
} }
...@@ -65,9 +120,13 @@ func (m CLIConfig) Check() error { ...@@ -65,9 +120,13 @@ func (m CLIConfig) Check() error {
} }
func ReadCLIConfig(ctx *cli.Context) CLIConfig { func ReadCLIConfig(ctx *cli.Context) CLIConfig {
profilePathFlag := ctx.Generic(ProfilePathFlagName).(*flags.PathFlag)
return CLIConfig{ return CLIConfig{
Enabled: ctx.Bool(EnabledFlagName), ListenEnabled: ctx.Bool(EnabledFlagName),
ListenAddr: ctx.String(ListenAddrFlagName), ListenAddr: ctx.String(ListenAddrFlagName),
ListenPort: ctx.Int(PortFlagName), ListenPort: ctx.Int(PortFlagName),
ProfileType: profileType(strings.ToLower(ctx.String(ProfileTypeFlagName))),
ProfileDir: profilePathFlag.Dir(),
ProfileFilename: profilePathFlag.Filename(),
} }
} }
package oppprof
import (
"context"
"io"
"net"
"net/http"
httpPprof "net/http/pprof"
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"strconv"
"github.com/ethereum-optimism/optimism/op-service/httputil"
"github.com/ethereum/go-ethereum/log"
)
type Service struct {
listenEnabled bool
listenAddr string
listenPort int
profileType string
profileDir string
profileFilename string
cpuFile io.Closer
httpServer *httputil.HTTPServer
}
func New(listenEnabled bool, listenAddr string, listenPort int, profType profileType, profileDir, profileFilename string) *Service {
return &Service{
listenEnabled: listenEnabled,
listenAddr: listenAddr,
listenPort: listenPort,
profileType: string(profType),
profileDir: profileDir,
profileFilename: profileFilename,
}
}
func (s *Service) Start() error {
switch s.profileType {
case "cpu":
if err := s.startCPUProfile(); err != nil {
return err
}
case "block":
runtime.SetBlockProfileRate(1)
case "mutex":
runtime.SetMutexProfileFraction(1)
}
if s.listenEnabled {
if err := s.startServer(); err != nil {
return err
}
}
if s.profileType != "" {
log.Info("start profiling to file", "profile_type", s.profileType, "profile_filepath", s.buildTargetFilePath())
}
return nil
}
func (s *Service) Stop(ctx context.Context) error {
switch s.profileType {
case "cpu":
pprof.StopCPUProfile()
if s.cpuFile != nil {
if err := s.cpuFile.Close(); err != nil {
return err
}
}
case "heap":
runtime.GC()
fallthrough
default:
profile := pprof.Lookup(s.profileType)
if profile == nil {
break
}
filepath := s.buildTargetFilePath()
log.Info("saving profile info", "profile_type", s.profileType, "profile_filepath", s.buildTargetFilePath())
f, err := os.Create(filepath)
if err != nil {
return err
}
defer f.Close()
_ = profile.WriteTo(f, 0)
}
if s.httpServer != nil {
if err := s.httpServer.Stop(ctx); err != nil {
return err
}
}
return nil
}
func (s *Service) startServer() error {
log.Debug("starting pprof server", "addr", net.JoinHostPort(s.listenAddr, strconv.Itoa(s.listenPort)))
mux := http.NewServeMux()
// have to do below to support multiple servers, since the
// pprof import only uses DefaultServeMux
mux.Handle("/debug/pprof/", http.HandlerFunc(httpPprof.Index))
mux.Handle("/debug/pprof/profile", http.HandlerFunc(httpPprof.Profile))
mux.Handle("/debug/pprof/symbol", http.HandlerFunc(httpPprof.Symbol))
mux.Handle("/debug/pprof/trace", http.HandlerFunc(httpPprof.Trace))
addr := net.JoinHostPort(s.listenAddr, strconv.Itoa(s.listenPort))
var err error
s.httpServer, err = httputil.StartHTTPServer(addr, mux)
if err != nil {
return err
}
log.Info("started pprof server", "addr", s.httpServer.Addr())
return nil
}
func (s *Service) startCPUProfile() error {
f, err := os.Create(s.buildTargetFilePath())
if err != nil {
return err
}
err = pprof.StartCPUProfile(f)
s.cpuFile = f
return err
}
func (s *Service) buildTargetFilePath() string {
filename := s.profileType + ".prof"
if s.profileFilename != "" {
filename = s.profileFilename
}
return filepath.Join(s.profileDir, filename)
}
package pprof
import (
"net"
"net/http"
"net/http/pprof"
"strconv"
"github.com/ethereum-optimism/optimism/op-service/httputil"
)
func StartServer(hostname string, port int) (*httputil.HTTPServer, error) {
mux := http.NewServeMux()
// have to do below to support multiple servers, since the
// pprof import only uses DefaultServeMux
mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
addr := net.JoinHostPort(hostname, strconv.Itoa(port))
return httputil.StartHTTPServer(addr, mux)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment