[NOD-806] Exit early after panic (#650)

* [NOD-806] After panic, gracefully stop logs, and then exit immediately

* [NOD-806] Convert non-kaspad applications to use the new spawn

* [NOD-806] Fix disabled log at rpcclient

* [NOD-806] Refactor HandlePanic

* [NOD-806] Cancel Logger interface

* [NOD-806] Remove redundant spawn checks from waitgroup_test.go

* [NOD-806] Use caller subsystem when logging panics

* [NOD-806] Fix go vet errors
This commit is contained in:
Ori Newman 2020-03-08 11:24:37 +02:00 committed by GitHub
parent 606cd668ff
commit 5a99e4d2f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 84 additions and 261 deletions

View File

@ -717,11 +717,7 @@ func (a *AddrManager) Start() {
// Start the address ticker to save addresses periodically. // Start the address ticker to save addresses periodically.
a.wg.Add(1) a.wg.Add(1)
spawn(a.addressHandler, a.handlePanic) spawn(a.addressHandler)
}
func (a *AddrManager) handlePanic() {
atomic.AddInt32(&a.shutdown, 1)
} }
// Stop gracefully shuts down the address manager by stopping the main handler. // Stop gracefully shuts down the address manager by stopping the main handler.

View File

@ -10,4 +10,4 @@ import (
) )
var log, _ = logger.Get(logger.SubsystemTags.ADXR) var log, _ = logger.Get(logger.SubsystemTags.ADXR)
var spawn = panics.GoroutineWrapperFuncWithPanicHandler(log) var spawn = panics.GoroutineWrapperFunc(log)

View File

@ -23,7 +23,7 @@ const (
var ( var (
cfg *ConfigFlags cfg *ConfigFlags
log logs.Logger log *logs.Logger
spawn func(func()) spawn func(func())
) )

View File

@ -28,7 +28,5 @@ func initLog(logFile, errLogFile string) {
} }
func enableRPCLogging() { func enableRPCLogging() {
rpclog := backendLog.Logger("RPCC") rpcclient.UseLogger(backendLog, logs.LevelTrace)
rpclog.SetLevel(logs.LevelTrace)
rpcclient.UseLogger(rpclog)
} }

View File

@ -12,7 +12,7 @@ import (
) )
func main() { func main() {
defer panics.HandlePanic(log, nil, nil) defer panics.HandlePanic(log, nil)
interrupt := signal.InterruptListener() interrupt := signal.InterruptListener()
cfg, err := parseConfig() cfg, err := parseConfig()

View File

@ -234,7 +234,7 @@ func (cm *ConnManager) handleFailedConn(c *ConnReq, err error) {
} }
spawnAfter(d, func() { spawnAfter(d, func() {
cm.Connect(c) cm.Connect(c)
}, nil) })
} else if cm.cfg.GetNewAddress != nil { } else if cm.cfg.GetNewAddress != nil {
cm.failedAttempts++ cm.failedAttempts++
if cm.failedAttempts >= maxFailedAttempts { if cm.failedAttempts >= maxFailedAttempts {
@ -243,9 +243,9 @@ func (cm *ConnManager) handleFailedConn(c *ConnReq, err error) {
"-- retrying further connections every %s", maxFailedAttempts, "-- retrying further connections every %s", maxFailedAttempts,
cm.cfg.RetryDuration) cm.cfg.RetryDuration)
} }
spawnAfter(cm.cfg.RetryDuration, cm.NewConnReq, cm.handlePanic) spawnAfter(cm.cfg.RetryDuration, cm.NewConnReq)
} else { } else {
spawn(cm.NewConnReq, cm.handlePanic) spawn(cm.NewConnReq)
} }
} }
} }
@ -377,7 +377,7 @@ out:
if cm.cfg.OnDisconnection != nil { if cm.cfg.OnDisconnection != nil {
spawn(func() { spawn(func() {
cm.cfg.OnDisconnection(connReq) cm.cfg.OnDisconnection(connReq)
}, cm.handlePanic) })
} }
// All internal state has been cleaned up, if // All internal state has been cleaned up, if
@ -576,7 +576,7 @@ func (cm *ConnManager) listenHandler(listener net.Listener) {
} }
spawn(func() { spawn(func() {
cm.cfg.OnAccept(conn) cm.cfg.OnAccept(conn)
}, cm.handlePanic) })
} }
cm.wg.Done() cm.wg.Done()
@ -592,7 +592,7 @@ func (cm *ConnManager) Start() {
log.Trace("Connection manager started") log.Trace("Connection manager started")
cm.wg.Add(1) cm.wg.Add(1)
spawn(cm.connHandler, cm.handlePanic) spawn(cm.connHandler)
// Start all the listeners so long as the caller requested them and // Start all the listeners so long as the caller requested them and
// provided a callback to be invoked when connections are accepted. // provided a callback to be invoked when connections are accepted.
@ -604,12 +604,12 @@ func (cm *ConnManager) Start() {
cm.wg.Add(1) cm.wg.Add(1)
spawn(func() { spawn(func() {
cm.listenHandler(listenerCopy) cm.listenHandler(listenerCopy)
}, cm.handlePanic) })
} }
} }
for i := atomic.LoadUint64(&cm.connReqCount); i < uint64(cm.cfg.TargetOutbound); i++ { for i := atomic.LoadUint64(&cm.connReqCount); i < uint64(cm.cfg.TargetOutbound); i++ {
spawn(cm.NewConnReq, cm.handlePanic) spawn(cm.NewConnReq)
} }
} }
@ -618,10 +618,6 @@ func (cm *ConnManager) Wait() {
cm.wg.Wait() cm.wg.Wait()
} }
func (cm *ConnManager) handlePanic() {
atomic.AddInt32(&cm.stop, 1)
}
// Stop gracefully shuts down the connection manager. // Stop gracefully shuts down the connection manager.
func (cm *ConnManager) Stop() { func (cm *ConnManager) Stop() {
if atomic.AddInt32(&cm.stop, 1) != 1 { if atomic.AddInt32(&cm.stop, 1) != 1 {

View File

@ -10,5 +10,5 @@ import (
) )
var log, _ = logger.Get(logger.SubsystemTags.CMGR) var log, _ = logger.Get(logger.SubsystemTags.CMGR)
var spawn = panics.GoroutineWrapperFuncWithPanicHandler(log) var spawn = panics.GoroutineWrapperFunc(log)
var spawnAfter = panics.AfterFuncWrapperFuncWithPanicHandler(log) var spawnAfter = panics.AfterFuncWrapperFunc(log)

View File

@ -94,6 +94,6 @@ func SeedFromDNS(dagParams *dagconfig.Params, reqServices wire.ServiceFlag, incl
} }
seedFn(addresses) seedFn(addresses)
}, nil) })
} }
} }

View File

@ -24,7 +24,7 @@ const (
) )
var ( var (
log logs.Logger log *logs.Logger
spawn func(func()) spawn func(func())
shutdownChannel = make(chan error) shutdownChannel = make(chan error)
) )

View File

@ -59,7 +59,7 @@ func kaspadMain(serverChan chan<- *server.Server) error {
return err return err
} }
cfg = config.ActiveConfig() cfg = config.ActiveConfig()
defer panics.HandlePanic(kasdLog, nil, nil) defer panics.HandlePanic(kasdLog, nil)
// Get a channel that will be closed when a shutdown signal has been // Get a channel that will be closed when a shutdown signal has been
// triggered either from an OS signal such as SIGINT (Ctrl+C) or from // triggered either from an OS signal such as SIGINT (Ctrl+C) or from

View File

@ -87,7 +87,7 @@ var SubsystemTags = struct {
} }
// subsystemLoggers maps each subsystem identifier to its associated logger. // subsystemLoggers maps each subsystem identifier to its associated logger.
var subsystemLoggers = map[string]logs.Logger{ var subsystemLoggers = map[string]*logs.Logger{
SubsystemTags.ADXR: adxrLog, SubsystemTags.ADXR: adxrLog,
SubsystemTags.AMGR: amgrLog, SubsystemTags.AMGR: amgrLog,
SubsystemTags.CMGR: cmgrLog, SubsystemTags.CMGR: cmgrLog,
@ -180,7 +180,7 @@ func SupportedSubsystems() []string {
} }
// Get returns a logger of a specific sub system // Get returns a logger of a specific sub system
func Get(tag string) (logger logs.Logger, ok bool) { func Get(tag string) (logger *logs.Logger, ok bool) {
logger, ok = subsystemLoggers[tag] logger, ok = subsystemLoggers[tag]
return return
} }

View File

@ -1,64 +0,0 @@
// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package logs
// Logger is an interface which describes a level-based logger. A default
// implementation of Logger is implemented by this package and can be created
// by calling (*Backend).Logger.
type Logger interface {
// Tracef formats message according to format specifier and writes to
// to log with LevelTrace.
Tracef(format string, params ...interface{})
// Debugf formats message according to format specifier and writes to
// log with LevelDebug.
Debugf(format string, params ...interface{})
// Infof formats message according to format specifier and writes to
// log with LevelInfo.
Infof(format string, params ...interface{})
// Warnf formats message according to format specifier and writes to
// to log with LevelWarn.
Warnf(format string, params ...interface{})
// Errorf formats message according to format specifier and writes to
// to log with LevelError.
Errorf(format string, params ...interface{})
// Criticalf formats message according to format specifier and writes to
// log with LevelCritical.
Criticalf(format string, params ...interface{})
// Trace formats message using the default formats for its operands
// and writes to log with LevelTrace.
Trace(v ...interface{})
// Debug formats message using the default formats for its operands
// and writes to log with LevelDebug.
Debug(v ...interface{})
// Info formats message using the default formats for its operands
// and writes to log with LevelInfo.
Info(v ...interface{})
// Warn formats message using the default formats for its operands
// and writes to log with LevelWarn.
Warn(v ...interface{})
// Error formats message using the default formats for its operands
// and writes to log with LevelError.
Error(v ...interface{})
// Critical formats message using the default formats for its operands
// and writes to log with LevelCritical.
Critical(v ...interface{})
// Level returns the current logging level.
Level() Level
// SetLevel changes the logging level to the passed level.
SetLevel(level Level)
}

View File

@ -354,12 +354,12 @@ func (b *Backend) Close() {
// Logger returns a new logger for a particular subsystem that writes to the // Logger returns a new logger for a particular subsystem that writes to the
// Backend b. A tag describes the subsystem and is included in all log // Backend b. A tag describes the subsystem and is included in all log
// messages. The logger uses the info verbosity level by default. // messages. The logger uses the info verbosity level by default.
func (b *Backend) Logger(subsystemTag string) Logger { func (b *Backend) Logger(subsystemTag string) *Logger {
return &slog{LevelInfo, subsystemTag, b} return &Logger{LevelInfo, subsystemTag, b}
} }
// slog is a subsystem logger for a Backend. Implements the Logger interface. // Logger is a subsystem logger for a Backend.
type slog struct { type Logger struct {
lvl Level // atomic lvl Level // atomic
tag string tag string
b *Backend b *Backend
@ -367,9 +367,7 @@ type slog struct {
// Trace formats message using the default formats for its operands, prepends // Trace formats message using the default formats for its operands, prepends
// the prefix as necessary, and writes to log with LevelTrace. // the prefix as necessary, and writes to log with LevelTrace.
// func (l *Logger) Trace(args ...interface{}) {
// This is part of the Logger interface implementation.
func (l *slog) Trace(args ...interface{}) {
lvl := l.Level() lvl := l.Level()
if lvl <= LevelTrace { if lvl <= LevelTrace {
l.b.print(LevelTrace, l.tag, args...) l.b.print(LevelTrace, l.tag, args...)
@ -378,9 +376,7 @@ func (l *slog) Trace(args ...interface{}) {
// Tracef formats message according to format specifier, prepends the prefix as // Tracef formats message according to format specifier, prepends the prefix as
// necessary, and writes to log with LevelTrace. // necessary, and writes to log with LevelTrace.
// func (l *Logger) Tracef(format string, args ...interface{}) {
// This is part of the Logger interface implementation.
func (l *slog) Tracef(format string, args ...interface{}) {
lvl := l.Level() lvl := l.Level()
if lvl <= LevelTrace { if lvl <= LevelTrace {
l.b.printf(LevelTrace, l.tag, format, args...) l.b.printf(LevelTrace, l.tag, format, args...)
@ -389,9 +385,7 @@ func (l *slog) Tracef(format string, args ...interface{}) {
// Debug formats message using the default formats for its operands, prepends // Debug formats message using the default formats for its operands, prepends
// the prefix as necessary, and writes to log with LevelDebug. // the prefix as necessary, and writes to log with LevelDebug.
// func (l *Logger) Debug(args ...interface{}) {
// This is part of the Logger interface implementation.
func (l *slog) Debug(args ...interface{}) {
lvl := l.Level() lvl := l.Level()
if lvl <= LevelDebug { if lvl <= LevelDebug {
l.b.print(LevelDebug, l.tag, args...) l.b.print(LevelDebug, l.tag, args...)
@ -400,9 +394,7 @@ func (l *slog) Debug(args ...interface{}) {
// Debugf formats message according to format specifier, prepends the prefix as // Debugf formats message according to format specifier, prepends the prefix as
// necessary, and writes to log with LevelDebug. // necessary, and writes to log with LevelDebug.
// func (l *Logger) Debugf(format string, args ...interface{}) {
// This is part of the Logger interface implementation.
func (l *slog) Debugf(format string, args ...interface{}) {
lvl := l.Level() lvl := l.Level()
if lvl <= LevelDebug { if lvl <= LevelDebug {
l.b.printf(LevelDebug, l.tag, format, args...) l.b.printf(LevelDebug, l.tag, format, args...)
@ -411,9 +403,7 @@ func (l *slog) Debugf(format string, args ...interface{}) {
// Info formats message using the default formats for its operands, prepends // Info formats message using the default formats for its operands, prepends
// the prefix as necessary, and writes to log with LevelInfo. // the prefix as necessary, and writes to log with LevelInfo.
// func (l *Logger) Info(args ...interface{}) {
// This is part of the Logger interface implementation.
func (l *slog) Info(args ...interface{}) {
lvl := l.Level() lvl := l.Level()
if lvl <= LevelInfo { if lvl <= LevelInfo {
l.b.print(LevelInfo, l.tag, args...) l.b.print(LevelInfo, l.tag, args...)
@ -422,9 +412,7 @@ func (l *slog) Info(args ...interface{}) {
// Infof formats message according to format specifier, prepends the prefix as // Infof formats message according to format specifier, prepends the prefix as
// necessary, and writes to log with LevelInfo. // necessary, and writes to log with LevelInfo.
// func (l *Logger) Infof(format string, args ...interface{}) {
// This is part of the Logger interface implementation.
func (l *slog) Infof(format string, args ...interface{}) {
lvl := l.Level() lvl := l.Level()
if lvl <= LevelInfo { if lvl <= LevelInfo {
l.b.printf(LevelInfo, l.tag, format, args...) l.b.printf(LevelInfo, l.tag, format, args...)
@ -433,9 +421,7 @@ func (l *slog) Infof(format string, args ...interface{}) {
// Warn formats message using the default formats for its operands, prepends // Warn formats message using the default formats for its operands, prepends
// the prefix as necessary, and writes to log with LevelWarn. // the prefix as necessary, and writes to log with LevelWarn.
// func (l *Logger) Warn(args ...interface{}) {
// This is part of the Logger interface implementation.
func (l *slog) Warn(args ...interface{}) {
lvl := l.Level() lvl := l.Level()
if lvl <= LevelWarn { if lvl <= LevelWarn {
l.b.print(LevelWarn, l.tag, args...) l.b.print(LevelWarn, l.tag, args...)
@ -444,9 +430,7 @@ func (l *slog) Warn(args ...interface{}) {
// Warnf formats message according to format specifier, prepends the prefix as // Warnf formats message according to format specifier, prepends the prefix as
// necessary, and writes to log with LevelWarn. // necessary, and writes to log with LevelWarn.
// func (l *Logger) Warnf(format string, args ...interface{}) {
// This is part of the Logger interface implementation.
func (l *slog) Warnf(format string, args ...interface{}) {
lvl := l.Level() lvl := l.Level()
if lvl <= LevelWarn { if lvl <= LevelWarn {
l.b.printf(LevelWarn, l.tag, format, args...) l.b.printf(LevelWarn, l.tag, format, args...)
@ -455,9 +439,7 @@ func (l *slog) Warnf(format string, args ...interface{}) {
// Error formats message using the default formats for its operands, prepends // Error formats message using the default formats for its operands, prepends
// the prefix as necessary, and writes to log with LevelError. // the prefix as necessary, and writes to log with LevelError.
// func (l *Logger) Error(args ...interface{}) {
// This is part of the Logger interface implementation.
func (l *slog) Error(args ...interface{}) {
lvl := l.Level() lvl := l.Level()
if lvl <= LevelError { if lvl <= LevelError {
l.b.print(LevelError, l.tag, args...) l.b.print(LevelError, l.tag, args...)
@ -466,9 +448,7 @@ func (l *slog) Error(args ...interface{}) {
// Errorf formats message according to format specifier, prepends the prefix as // Errorf formats message according to format specifier, prepends the prefix as
// necessary, and writes to log with LevelError. // necessary, and writes to log with LevelError.
// func (l *Logger) Errorf(format string, args ...interface{}) {
// This is part of the Logger interface implementation.
func (l *slog) Errorf(format string, args ...interface{}) {
lvl := l.Level() lvl := l.Level()
if lvl <= LevelError { if lvl <= LevelError {
l.b.printf(LevelError, l.tag, format, args...) l.b.printf(LevelError, l.tag, format, args...)
@ -477,9 +457,7 @@ func (l *slog) Errorf(format string, args ...interface{}) {
// Critical formats message using the default formats for its operands, prepends // Critical formats message using the default formats for its operands, prepends
// the prefix as necessary, and writes to log with LevelCritical. // the prefix as necessary, and writes to log with LevelCritical.
// func (l *Logger) Critical(args ...interface{}) {
// This is part of the Logger interface implementation.
func (l *slog) Critical(args ...interface{}) {
lvl := l.Level() lvl := l.Level()
if lvl <= LevelCritical { if lvl <= LevelCritical {
l.b.print(LevelCritical, l.tag, args...) l.b.print(LevelCritical, l.tag, args...)
@ -488,9 +466,7 @@ func (l *slog) Critical(args ...interface{}) {
// Criticalf formats message according to format specifier, prepends the prefix // Criticalf formats message according to format specifier, prepends the prefix
// as necessary, and writes to log with LevelCritical. // as necessary, and writes to log with LevelCritical.
// func (l *Logger) Criticalf(format string, args ...interface{}) {
// This is part of the Logger interface implementation.
func (l *slog) Criticalf(format string, args ...interface{}) {
lvl := l.Level() lvl := l.Level()
if lvl <= LevelCritical { if lvl <= LevelCritical {
l.b.printf(LevelCritical, l.tag, format, args...) l.b.printf(LevelCritical, l.tag, format, args...)
@ -498,22 +474,16 @@ func (l *slog) Criticalf(format string, args ...interface{}) {
} }
// Level returns the current logging level // Level returns the current logging level
// func (l *Logger) Level() Level {
// This is part of the Logger interface implementation.
func (l *slog) Level() Level {
return Level(atomic.LoadUint32((*uint32)(&l.lvl))) return Level(atomic.LoadUint32((*uint32)(&l.lvl)))
} }
// SetLevel changes the logging level to the passed level. // SetLevel changes the logging level to the passed level.
// func (l *Logger) SetLevel(level Level) {
// This is part of the Logger interface implementation.
func (l *slog) SetLevel(level Level) {
atomic.StoreUint32((*uint32)(&l.lvl), uint32(level)) atomic.StoreUint32((*uint32)(&l.lvl), uint32(level))
} }
// Disabled is a Logger that will never output anything. // Backend returns the log backend
var Disabled Logger func (l *Logger) Backend() *Backend {
return l.b
func init() {
Disabled = &slog{lvl: LevelOff, b: NewBackend()}
} }

View File

@ -306,7 +306,7 @@ func (g *BlkTmplGenerator) populateTemplateFromCandidates(candidateTxs []*candid
txsForBlockTemplate.totalMass += selectedTx.txMass txsForBlockTemplate.totalMass += selectedTx.txMass
txsForBlockTemplate.totalFees += selectedTx.txDesc.Fee txsForBlockTemplate.totalFees += selectedTx.txDesc.Fee
log.Tracef("Adding tx %s (feePerKB %.2f)", log.Tracef("Adding tx %s (feePerKB %d)",
tx.ID(), selectedTx.txDesc.FeePerKB) tx.ID(), selectedTx.txDesc.FeePerKB)
markCandidateTxForDeletion(selectedTx) markCandidateTxForDeletion(selectedTx)

View File

@ -20,7 +20,7 @@ type blockProgressLogger struct {
receivedLogTx int64 receivedLogTx int64
lastBlockLogTime time.Time lastBlockLogTime time.Time
subsystemLogger logs.Logger subsystemLogger *logs.Logger
progressAction string progressAction string
sync.Mutex sync.Mutex
} }
@ -29,7 +29,7 @@ type blockProgressLogger struct {
// The progress message is templated as follows: // The progress message is templated as follows:
// {progressAction} {numProcessed} {blocks|block} in the last {timePeriod} // {progressAction} {numProcessed} {blocks|block} in the last {timePeriod}
// ({numTxs}, height {lastBlockHeight}, {lastBlockTimeStamp}) // ({numTxs}, height {lastBlockHeight}, {lastBlockTimeStamp})
func newBlockProgressLogger(progressMessage string, logger logs.Logger) *blockProgressLogger { func newBlockProgressLogger(progressMessage string, logger *logs.Logger) *blockProgressLogger {
return &blockProgressLogger{ return &blockProgressLogger{
lastBlockLogTime: time.Now(), lastBlockLogTime: time.Now(),
progressAction: progressMessage, progressAction: progressMessage,

View File

@ -10,4 +10,4 @@ import (
) )
var log, _ = logger.Get(logger.SubsystemTags.SYNC) var log, _ = logger.Get(logger.SubsystemTags.SYNC)
var spawn = panics.GoroutineWrapperFuncWithPanicHandler(log) var spawn = panics.GoroutineWrapperFunc(log)

View File

@ -995,7 +995,7 @@ func (sm *SyncManager) handleBlockDAGNotification(notification *blockdag.Notific
if err != nil { if err != nil {
panic(fmt.Sprintf("HandleNewBlock failed to handle block %s", block.Hash())) panic(fmt.Sprintf("HandleNewBlock failed to handle block %s", block.Hash()))
} }
}, sm.handlePanic) })
// Relay if we are current and the block was not just now unorphaned. // Relay if we are current and the block was not just now unorphaned.
// Otherwise peers that are current should already know about it // Otherwise peers that are current should already know about it
@ -1098,11 +1098,7 @@ func (sm *SyncManager) Start() {
log.Trace("Starting sync manager") log.Trace("Starting sync manager")
sm.wg.Add(1) sm.wg.Add(1)
spawn(sm.messageHandler, sm.handlePanic) spawn(sm.messageHandler)
}
func (sm *SyncManager) handlePanic() {
atomic.AddInt32(&sm.shutdown, 1)
} }
// Stop gracefully shuts down the sync manager by stopping all asynchronous // Stop gracefully shuts down the sync manager by stopping all asynchronous

View File

@ -1199,7 +1199,7 @@ out:
handlerActive = false handlerActive = false
default: default:
log.Warnf("Unsupported message command %s", log.Warnf("Unsupported message command %d",
msg.command) msg.command)
} }

View File

@ -12,9 +12,11 @@ import (
// log is a logger that is initialized with no output filters. This // log is a logger that is initialized with no output filters. This
// means the package will not perform any logging by default until the caller // means the package will not perform any logging by default until the caller
// requests it. // requests it.
var log logs.Logger var log *logs.Logger
var spawn func(func()) var spawn func(func())
const logSubsytem = "RPCC"
// The default amount of logging is none. // The default amount of logging is none.
func init() { func init() {
DisableLog() DisableLog()
@ -23,12 +25,15 @@ func init() {
// DisableLog disables all library log output. Logging output is disabled // DisableLog disables all library log output. Logging output is disabled
// by default until UseLogger is called. // by default until UseLogger is called.
func DisableLog() { func DisableLog() {
log = logs.Disabled backend := logs.NewBackend()
log = backend.Logger(logSubsytem)
log.SetLevel(logs.LevelOff)
spawn = panics.GoroutineWrapperFunc(log) spawn = panics.GoroutineWrapperFunc(log)
} }
// UseLogger uses a specified Logger to output package logging info. // UseLogger uses a specified Logger to output package logging info.
func UseLogger(logger logs.Logger) { func UseLogger(backend *logs.Backend, level logs.Level) {
log = logger log = backend.Logger(logSubsytem)
log.SetLevel(level)
spawn = panics.GoroutineWrapperFunc(log) spawn = panics.GoroutineWrapperFunc(log)
} }

View File

@ -21,16 +21,14 @@ func (sp *Peer) OnGetAddr(_ *peer.Peer, msg *wire.MsgGetAddr) {
// Do not accept getaddr requests from outbound peers. This reduces // Do not accept getaddr requests from outbound peers. This reduces
// fingerprinting attacks. // fingerprinting attacks.
if !sp.Inbound() { if !sp.Inbound() {
peerLog.Debugf("Ignoring getaddr request from outbound peer ", peerLog.Debugf("Ignoring getaddr request from outbound peer %s", sp)
"%s", sp)
return return
} }
// Only allow one getaddr request per connection to discourage // Only allow one getaddr request per connection to discourage
// address stamping of inv announcements. // address stamping of inv announcements.
if sp.sentAddrs { if sp.sentAddrs {
peerLog.Debugf("Ignoring repeated getaddr request from peer ", peerLog.Debugf("Ignoring repeated getaddr request from peer %s", sp)
"%s", sp)
return return
} }
sp.sentAddrs = true sp.sentAddrs = true

View File

@ -13,10 +13,6 @@ import (
// subsystems using the same code paths as when an interrupt signal is received. // subsystems using the same code paths as when an interrupt signal is received.
var ShutdownRequestChannel = make(chan struct{}) var ShutdownRequestChannel = make(chan struct{})
// PanicShutdownChannel is used to initiate shutdown when any thread
// panics using the same code paths as when an interrupt signal is received.
var PanicShutdownChannel = make(chan struct{})
// interruptSignals defines the default signals to catch in order to do a proper // interruptSignals defines the default signals to catch in order to do a proper
// shutdown. This may be modified during init depending on the platform. // shutdown. This may be modified during init depending on the platform.
var interruptSignals = []os.Signal{os.Interrupt} var interruptSignals = []os.Signal{os.Interrupt}
@ -39,9 +35,6 @@ func InterruptListener() <-chan struct{} {
case <-ShutdownRequestChannel: case <-ShutdownRequestChannel:
kasdLog.Info("Shutdown requested. Shutting down...") kasdLog.Info("Shutdown requested. Shutting down...")
case <-PanicShutdownChannel:
kasdLog.Info("Panic occurred. Shutting down...")
} }
close(c) close(c)
@ -57,11 +50,6 @@ func InterruptListener() <-chan struct{} {
case <-ShutdownRequestChannel: case <-ShutdownRequestChannel:
kasdLog.Info("Shutdown requested. Already " + kasdLog.Info("Shutdown requested. Already " +
"shutting down...") "shutting down...")
case <-PanicShutdownChannel:
kasdLog.Info("Panic occurred while shutting down. " +
"Forcing shut down...")
os.Exit(1)
} }
} }
}() }()

View File

@ -1,15 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package locks
import (
"github.com/kaspanet/kaspad/logger"
"github.com/kaspanet/kaspad/util/panics"
)
var (
log, _ = logger.Get(logger.SubsystemTags.UTIL)
spawn = panics.GoroutineWrapperFunc(log)
)

View File

@ -7,7 +7,6 @@ package locks
import ( import (
"sync/atomic" "sync/atomic"
"testing" "testing"
"time"
) )
// All of the tests, except TestAddAfterWait and // All of the tests, except TestAddAfterWait and
@ -17,29 +16,6 @@ import (
// behaves the same, except enabling the use of add() // behaves the same, except enabling the use of add()
// concurrently with wait() // concurrently with wait()
func spawnPatch(t *testing.T) (checkIfRunningSpawnsAreLeft func()) {
realSpawn := spawn
runningSpawns := int32(0)
spawn = func(f func()) {
atomic.AddInt32(&runningSpawns, 1)
realSpawn(func() {
f()
atomic.AddInt32(&runningSpawns, -1)
})
}
return func() {
defer func() {
spawn = realSpawn
}()
if runningSpawns != 0 {
time.Sleep(10 * time.Millisecond)
if runningSpawns != 0 {
t.Fatalf("%d running spawns left", runningSpawns)
}
}
}
}
func testWaitGroup(t *testing.T, wg1 *waitGroup, wg2 *waitGroup) { func testWaitGroup(t *testing.T, wg1 *waitGroup, wg2 *waitGroup) {
n := int64(16) n := int64(16)
wg1.add(n) wg1.add(n)
@ -67,8 +43,6 @@ func testWaitGroup(t *testing.T, wg1 *waitGroup, wg2 *waitGroup) {
} }
func TestWaitGroup(t *testing.T) { func TestWaitGroup(t *testing.T) {
checkIfRunningSpawnsAreLeft := spawnPatch(t)
defer checkIfRunningSpawnsAreLeft()
wg1 := newWaitGroup() wg1 := newWaitGroup()
wg2 := newWaitGroup() wg2 := newWaitGroup()
@ -94,8 +68,6 @@ func TestWaitGroupMisuse(t *testing.T) {
} }
func TestAddAfterWait(t *testing.T) { func TestAddAfterWait(t *testing.T) {
checkIfRunningSpawnsAreLeft := spawnPatch(t)
defer checkIfRunningSpawnsAreLeft()
wg := newWaitGroup() wg := newWaitGroup()
wg.add(1) wg.add(1)
syncChan := make(chan struct{}) syncChan := make(chan struct{})
@ -113,8 +85,6 @@ func TestAddAfterWait(t *testing.T) {
} }
func TestWaitGroupRace(t *testing.T) { func TestWaitGroupRace(t *testing.T) {
checkIfRunningSpawnsAreLeft := spawnPatch(t)
defer checkIfRunningSpawnsAreLeft()
// Run this test for about 1ms. // Run this test for about 1ms.
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
wg := newWaitGroup() wg := newWaitGroup()
@ -141,8 +111,6 @@ func TestWaitGroupRace(t *testing.T) {
} }
func TestWaitGroupAlign(t *testing.T) { func TestWaitGroupAlign(t *testing.T) {
checkIfRunningSpawnsAreLeft := spawnPatch(t)
defer checkIfRunningSpawnsAreLeft()
type X struct { type X struct {
x byte x byte
wg *waitGroup wg *waitGroup
@ -157,8 +125,6 @@ func TestWaitGroupAlign(t *testing.T) {
} }
func TestWaitAfterAddDoneCounterHasReset(t *testing.T) { func TestWaitAfterAddDoneCounterHasReset(t *testing.T) {
checkIfRunningSpawnsAreLeft := spawnPatch(t)
defer checkIfRunningSpawnsAreLeft()
wg := newWaitGroup() wg := newWaitGroup()
wg.add(1) wg.add(1)
wg.done() wg.done()

View File

@ -1,69 +1,58 @@
package panics package panics
import ( import (
"fmt"
"github.com/kaspanet/kaspad/logs" "github.com/kaspanet/kaspad/logs"
"github.com/kaspanet/kaspad/signal" "os"
"runtime/debug" "runtime/debug"
"time" "time"
) )
// HandlePanic recovers panics, log them, runs an optional panicHandler, // HandlePanic recovers panics, log them, runs an optional panicHandler,
// and then initiates a clean shutdown. // and then initiates a clean shutdown.
func HandlePanic(log logs.Logger, goroutineStackTrace []byte, panicHandler func()) { func HandlePanic(log *logs.Logger, goroutineStackTrace []byte) {
if err := recover(); err != nil { err := recover()
if err == nil {
return
}
panicHandlerDone := make(chan struct{})
go func() {
log.Criticalf("Fatal error: %+v", err) log.Criticalf("Fatal error: %+v", err)
if goroutineStackTrace != nil { if goroutineStackTrace != nil {
log.Criticalf("goroutine stack trace: %s", goroutineStackTrace) log.Criticalf("Goroutine stack trace: %s", goroutineStackTrace)
} }
log.Criticalf("Stack trace: %s", debug.Stack()) log.Criticalf("Stack trace: %s", debug.Stack())
if panicHandler != nil { log.Backend().Close()
panicHandler() panicHandlerDone <- struct{}{}
} }()
signal.PanicShutdownChannel <- struct{}{}
const panicHandlerTimeout = 5 * time.Second
select {
case <-time.Tick(panicHandlerTimeout):
fmt.Fprintln(os.Stderr, "Couldn't handle a fatal error. Exiting...")
case <-panicHandlerDone:
} }
os.Exit(1)
} }
// GoroutineWrapperFunc returns a goroutine wrapper function that handles panics and writes them to the log. // GoroutineWrapperFunc returns a goroutine wrapper function that handles panics and writes them to the log.
func GoroutineWrapperFunc(log logs.Logger) func(func()) { func GoroutineWrapperFunc(log *logs.Logger) func(func()) {
return func(f func()) { return func(f func()) {
stackTrace := debug.Stack() stackTrace := debug.Stack()
go func() { go func() {
defer HandlePanic(log, stackTrace, nil) defer HandlePanic(log, stackTrace)
f()
}()
}
}
// GoroutineWrapperFuncWithPanicHandler returns a goroutine wrapper function that handles panics,
// writes them to the log, and executes a handler function for panics.
func GoroutineWrapperFuncWithPanicHandler(log logs.Logger) func(func(), func()) {
return func(f func(), panicHandler func()) {
stackTrace := debug.Stack()
go func() {
defer HandlePanic(log, stackTrace, panicHandler)
f() f()
}() }()
} }
} }
// AfterFuncWrapperFunc returns a time.AfterFunc wrapper function that handles panics. // AfterFuncWrapperFunc returns a time.AfterFunc wrapper function that handles panics.
func AfterFuncWrapperFunc(log logs.Logger) func(d time.Duration, f func()) *time.Timer { func AfterFuncWrapperFunc(log *logs.Logger) func(d time.Duration, f func()) *time.Timer {
return func(d time.Duration, f func()) *time.Timer { return func(d time.Duration, f func()) *time.Timer {
stackTrace := debug.Stack() stackTrace := debug.Stack()
return time.AfterFunc(d, func() { return time.AfterFunc(d, func() {
defer HandlePanic(log, stackTrace, nil) defer HandlePanic(log, stackTrace)
f()
})
}
}
// AfterFuncWrapperFuncWithPanicHandler returns a time.AfterFunc wrapper function that handles panics,
// writes them to the log, and executes a handler function for panics.
func AfterFuncWrapperFuncWithPanicHandler(log logs.Logger) func(d time.Duration, f func(), panicHandler func()) *time.Timer {
return func(d time.Duration, f func(), panicHandler func()) *time.Timer {
stackTrace := debug.Stack()
return time.AfterFunc(d, func() {
defer HandlePanic(log, stackTrace, panicHandler)
f() f()
}) })
} }