Skip to content

Commit

Permalink
multi: separate profiler config
Browse files Browse the repository at this point in the history
  • Loading branch information
ziggie1984 committed Sep 25, 2024
1 parent 956b0f1 commit 483a06b
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 77 deletions.
54 changes: 7 additions & 47 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,12 +352,7 @@ type Config struct {

DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify <global-level>,<subsystem>=<level>,<subsystem2>=<level>,... to set the log level for individual subsystems -- Use show to list available subsystems"`

CPUProfile string `long:"cpuprofile" description:"Write CPU profile to the specified file"`

Profile string `long:"profile" description:"Enable HTTP profiling on either a port or host:port"`

BlockingProfile int `long:"blockingprofile" description:"Used to enable a blocking profile to be served on the profiling port. This takes a value from 0 to 1, with 1 including every blocking event, and 0 including no events."`
MutexProfile int `long:"mutexprofile" description:"Used to Enable a mutex profile to be served on the profiling port. This takes a value from 0 to 1, with 1 including every mutex event, and 0 including no events."`
Pprof *lncfg.Pprof `group:"Pprof" namespace:"pprof"`

UnsafeDisconnect bool `long:"unsafe-disconnect" description:"DEPRECATED: Allows the rpcserver to intentionally disconnect from peers with open channels. THIS FLAG WILL BE REMOVED IN 0.10.0" hidden:"true"`
UnsafeReplay bool `long:"unsafe-replay" description:"Causes a link to replay the adds on its commitment txn after starting up, this enables testing of the sphinx replay logic."`
Expand Down Expand Up @@ -821,7 +816,8 @@ func LoadConfig(interceptor signal.Interceptor) (*Config, error) {
cleanCfg, err := ValidateConfig(
cfg, interceptor, fileParser, flagParser,
)
if usageErr, ok := err.(*usageError); ok {
var usageErr *lncfg.UsageError
if errors.As(err, &usageErr) {
// The logging system might not yet be initialized, so we also
// write to stderr to make sure the error appears somewhere.
_, _ = fmt.Fprintln(os.Stderr, usageMessage)
Expand All @@ -830,9 +826,9 @@ func LoadConfig(interceptor signal.Interceptor) (*Config, error) {
// The log subsystem might not yet be initialized. But we still
// try to log the error there since some packaging solutions
// might only look at the log and not stdout/stderr.
ltndLog.Warnf("Error validating config: %v", usageErr.err)
ltndLog.Warnf("Error validating config: %v", err)

return nil, usageErr.err
return nil, err
}
if err != nil {
// The log subsystem might not yet be initialized. But we still
Expand All @@ -856,18 +852,6 @@ func LoadConfig(interceptor signal.Interceptor) (*Config, error) {
return cleanCfg, nil
}

// usageError is an error type that signals a problem with the supplied flags.
type usageError struct {
err error
}

// Error returns the error string.
//
// NOTE: This is part of the error interface.
func (u *usageError) Error() string {
return u.err.Error()
}

// ValidateConfig check the given configuration to be sane. This makes sure no
// illegal values or combination of values are set. All file system paths are
// normalized. The cleaned up config is returned on success.
Expand Down Expand Up @@ -1347,31 +1331,6 @@ func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
cfg.Autopilot.MaxChannelSize = int64(MaxFundingAmount)
}

// Validate profile port or host:port.
if cfg.Profile != "" {
str := "%s: The profile port must be between 1024 and 65535"

// Try to parse Profile as a host:port.
_, hostPort, err := net.SplitHostPort(cfg.Profile)
if err == nil {
// Determine if the port is valid.
profilePort, err := strconv.Atoi(hostPort)
if err != nil || profilePort < 1024 || profilePort > 65535 {
return nil, &usageError{mkErr(str)}
}
} else {
// Try to parse Profile as a port.
profilePort, err := strconv.Atoi(cfg.Profile)
if err != nil || profilePort < 1024 || profilePort > 65535 {
return nil, &usageError{mkErr(str)}
}

// Since the user just set a port, we will serve debugging
// information over localhost.
cfg.Profile = net.JoinHostPort("127.0.0.1", cfg.Profile)
}
}

// We'll now construct the network directory which will be where we
// store all the data specific to this chain/network.
cfg.networkDir = filepath.Join(
Expand Down Expand Up @@ -1469,7 +1428,7 @@ func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
err = build.ParseAndSetDebugLevels(cfg.DebugLevel, cfg.LogWriter)
if err != nil {
str := "error parsing debug level: %v"
return nil, &usageError{mkErr(str, err)}
return nil, &lncfg.UsageError{Err: mkErr(str, err)}
}

// At least one RPCListener is required. So listen on localhost per
Expand Down Expand Up @@ -1714,6 +1673,7 @@ func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
cfg.Htlcswitch,
cfg.Invoices,
cfg.Routing,
cfg.Pprof,
)
if err != nil {
return nil, err
Expand Down
25 changes: 25 additions & 0 deletions lncfg/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package lncfg

import "fmt"

// UsageError is an error type that signals a problem with the supplied flags.
type UsageError struct {
Err error
}

// Error returns the error string.
//
// NOTE: This is part of the error interface.
func (u *UsageError) Error() string {
return u.Err.Error()
}

// Unwrap returns the underlying error.
func (u *UsageError) Unwrap() error {
return u.Err
}

// mkErr creates a new error from a string.
func mkErr(format string, args ...interface{}) error {
return fmt.Errorf(format, args...)
}
67 changes: 67 additions & 0 deletions lncfg/pprof.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package lncfg

import (
"net"
"strconv"
)

// Pprof holds the configuration options for LND's built-in pprof server.
//
//nolint:lll
type Pprof struct {
CPUProfile string `long:"cpuprofile" description:"Write CPU profile to the specified file"`

Profile string `long:"profile" description:"Enable HTTP profiling on either a port or host:port"`

BlockingProfile int `long:"blockingprofile" description:"Used to enable a blocking profile to be served on the profiling port. This takes a value from 0 to 1, with 1 including every blocking event, and 0 including no events."`
MutexProfile int `long:"mutexprofile" description:"Used to Enable a mutex profile to be served on the profiling port. This takes a value from 0 to 1, with 1 including every mutex event, and 0 including no events."`
}

// Validate checks the values configured for the profiler.
func (p *Pprof) Validate() error {
if p.BlockingProfile > 0 {
log.Warn("Blocking profile enabled only useful for " +
"debugging because of significant performance impact")
}

if p.MutexProfile > 0 {
log.Warn("Mutex profile enabled only useful for " +
"debugging because of significant performance impact")
}

if p.CPUProfile != "" {
log.Warn("CPU profile enabled only useful for " +
"debugging because of significant performance impact")
}

if p.Profile != "" {
str := "%v: The profile port must be between 1024 and 65535"

// Try to parse Profile as a host:port.
_, hostPort, err := net.SplitHostPort(p.Profile)
if err == nil {
// Determine if the port is valid.
profilePort, err := strconv.Atoi(hostPort)

if err != nil || profilePort < 1024 ||
profilePort > 65535 {

return &UsageError{Err: mkErr(str, hostPort)}
}
} else {
// Try to parse Profile as a port.
profilePort, err := strconv.Atoi(p.Profile)
if err != nil || profilePort < 1024 ||
profilePort > 65535 {

return &UsageError{Err: mkErr(str, p.Profile)}
}

// Since the user just set a port, we will serve
// debugging information over localhost.
p.Profile = net.JoinHostPort("127.0.0.1", p.Profile)
}
}

return nil
}
19 changes: 10 additions & 9 deletions lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg,
defer cancel()

// Enable http profiling server if requested.
if cfg.Profile != "" {
if cfg.Pprof.Profile != "" {
// Create the http handler.
pprofMux := http.NewServeMux()
pprofMux.HandleFunc("/debug/pprof/", pprof.Index)
Expand All @@ -202,11 +202,11 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg,
pprofMux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
pprofMux.HandleFunc("/debug/pprof/trace", pprof.Trace)

if cfg.BlockingProfile != 0 {
runtime.SetBlockProfileRate(cfg.BlockingProfile)
if cfg.Pprof.BlockingProfile != 0 {
runtime.SetBlockProfileRate(cfg.Pprof.BlockingProfile)
}
if cfg.MutexProfile != 0 {
runtime.SetMutexProfileFraction(cfg.MutexProfile)
if cfg.Pprof.MutexProfile != 0 {
runtime.SetMutexProfileFraction(cfg.Pprof.MutexProfile)
}

// Redirect all requests to the pprof handler, thus visiting
Expand All @@ -216,11 +216,11 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg,
"/debug/pprof/", http.StatusSeeOther,
))

ltndLog.Infof("Pprof listening on %v", cfg.Profile)
ltndLog.Infof("Pprof listening on %v", cfg.Pprof.Profile)

// Create the pprof server.
pprofServer := &http.Server{
Addr: cfg.Profile,
Addr: cfg.Pprof.Profile,
Handler: pprofMux,
ReadHeaderTimeout: cfg.HTTPHeaderTimeout,
}
Expand All @@ -245,8 +245,8 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg,
}

// Write cpu profile if requested.
if cfg.CPUProfile != "" {
f, err := os.Create(cfg.CPUProfile)
if cfg.Pprof.CPUProfile != "" {
f, err := os.Create(cfg.Pprof.CPUProfile)
if err != nil {
return mkErr("unable to create CPU profile: %v", err)
}
Expand Down Expand Up @@ -876,6 +876,7 @@ func startGrpcListen(cfg *Config, grpcServer *grpc.Server,

// If Prometheus monitoring is enabled, start the Prometheus exporter.
if cfg.Prometheus.Enabled() {

err := monitoring.ExportPrometheusMetrics(
grpcServer, cfg.Prometheus,
)
Expand Down
46 changes: 25 additions & 21 deletions sample-lnd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -266,27 +266,6 @@
; Example:
; debuglevel=debug,PEER=info

; Write CPU profile to the specified file.
; cpuprofile=

; Enable HTTP profiling on given port -- NOTE port must be between 1024 and
; 65536. The profile can be access at: http://localhost:<PORT>/debug/pprof/.
; You can also provide it as host:port to enable profiling for remote debugging.
; For example 0.0.0.0:<PORT> to enable profiling for all interfaces on the given
; port.
; profile=

; Enable a blocking profile to be obtained from the profiling port. A blocking
; profile can show where goroutines are blocking (stuck on mutexes, I/O, etc).
; This takes a value from 0 to 1, with 0 turning off the setting, and 1 sampling
; every blocking event (it's a rate value).
; blockingprofile=0

; Enable a mutex profile to be obtained from the profiling port. A mutex
; profile can show where goroutines are blocked on mutexes, and which mutexes
; have high contention. This takes a value from 0 to 1, with 0 turning off the
; setting, and 1 sampling every mutex event (it's a rate value).
; mutexprofile=0

; DEPRECATED: Allows the rpcserver to intentionally disconnect from peers with
; open channels. THIS FLAG WILL BE REMOVED IN 0.10.0.
Expand Down Expand Up @@ -1811,3 +1790,28 @@
; no active gRPC streams. This might be useful to keep the underlying HTTP/2
; connection open for future requests.
; grpc.client-allow-ping-without-stream=false


[pprof]

; Enable HTTP profiling on given port -- NOTE port must be between 1024 and
; 65536. The profile can be access at: http://localhost:<PORT>/debug/pprof/.
; You can also provide it as host:port to enable profiling for remote debugging.
; For example 0.0.0.0:<PORT> to enable profiling for all interfaces on the given
; port.
; pprof.profile=

; Write CPU profile to the specified file.
; pprof.cpuprofile=

; Enable a blocking profile to be obtained from the profiling port. A blocking
; profile can show where goroutines are blocking (stuck on mutexes, I/O, etc).
; This takes a value from 0 to 1, with 0 turning off the setting, and 1 sampling
; every blocking event (it's a rate value).
; pprof.blockingprofile=0

; Enable a mutex profile to be obtained from the profiling port. A mutex
; profile can show where goroutines are blocked on mutexes, and which mutexes
; have high contention. This takes a value from 0 to 1, with 0 turning off the
; setting, and 1 sampling every mutex event (it's a rate value).
; pprof.mutexprofile=0

0 comments on commit 483a06b

Please sign in to comment.