github.com/aergoio/aergo@v1.3.1/chain/debugger.go (about)

     1  package chain
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strconv"
     7  	"sync"
     8  	"time"
     9  )
    10  
    11  type ErrDebug struct {
    12  	cond  StopCond
    13  	value int
    14  }
    15  
    16  type StopCond int
    17  
    18  // stop before swap chain
    19  const (
    20  	DEBUG_CHAIN_STOP StopCond = 0 + iota
    21  	DEBUG_CHAIN_RANDOM_STOP
    22  	DEBUG_CHAIN_BP_SLEEP
    23  	DEBUG_CHAIN_OTHER_SLEEP
    24  	DEBUG_SYNCER_CRASH
    25  	DEBUG_RAFT_SNAP_FREQ // change snap frequency after first snapshot
    26  )
    27  
    28  const (
    29  	DEBUG_CHAIN_STOP_INF = DEBUG_RAFT_SNAP_FREQ
    30  )
    31  
    32  var (
    33  	EnvNameStaticCrash     = "DEBUG_CHAIN_CRASH"       // 1 ~ 4
    34  	EnvNameRandomCrashTime = "DEBUG_RANDOM_CRASH_TIME" // 1 ~ 600000(=10min) ms
    35  	EnvNameChainBPSleep    = "DEBUG_CHAIN_BP_SLEEP"    // bp node sleeps before connecting block for each block (ms). used
    36  	EnvNameChainOtherSleep = "DEBUG_CHAIN_OTHER_SLEEP" // non bp node sleeps before connecting block for each block (ms).
    37  	EnvNameSyncCrash       = "DEBUG_SYNCER_CRASH"      // case 1
    38  	EnvNameRaftSnapFreq    = "DEBUG_RAFT_SNAP_FREQ"    // case 1
    39  )
    40  
    41  var stopConds = [...]string{
    42  	EnvNameStaticCrash,
    43  	EnvNameRandomCrashTime,
    44  	EnvNameChainBPSleep,
    45  	EnvNameChainOtherSleep,
    46  	EnvNameSyncCrash,
    47  	EnvNameRaftSnapFreq,
    48  }
    49  
    50  type DebugHandler func(value int) error
    51  
    52  func (c StopCond) String() string { return stopConds[c] }
    53  
    54  func (ec *ErrDebug) Error() string {
    55  	return fmt.Sprintf("stopped by debugger cond[%s]=%d", ec.cond.String(), ec.value)
    56  }
    57  
    58  type Debugger struct {
    59  	sync.RWMutex
    60  	condMap map[StopCond]int
    61  	isEnv   map[StopCond]bool
    62  }
    63  
    64  func newDebugger() *Debugger {
    65  	dbg := &Debugger{condMap: make(map[StopCond]int), isEnv: make(map[StopCond]bool)}
    66  
    67  	checkEnv := func(condName StopCond) {
    68  		envName := stopConds[condName]
    69  
    70  		envStr := os.Getenv(envName)
    71  		if len(envStr) > 0 {
    72  			val, err := strconv.Atoi(envStr)
    73  			if err != nil {
    74  				logger.Error().Err(err).Msgf("%s environment varialble must be integer", envName)
    75  				return
    76  			}
    77  			logger.Debug().Int("value", val).Msgf("env variable[%s] is set", envName)
    78  
    79  			dbg.Set(condName, val, true)
    80  		}
    81  	}
    82  
    83  	checkEnv(DEBUG_CHAIN_STOP)
    84  	checkEnv(DEBUG_CHAIN_RANDOM_STOP)
    85  	checkEnv(DEBUG_CHAIN_BP_SLEEP)
    86  	checkEnv(DEBUG_CHAIN_OTHER_SLEEP)
    87  	checkEnv(DEBUG_SYNCER_CRASH)
    88  	checkEnv(DEBUG_RAFT_SNAP_FREQ)
    89  
    90  	return dbg
    91  }
    92  
    93  func (debug *Debugger) Set(cond StopCond, value int, env bool) {
    94  	if debug == nil {
    95  		return
    96  	}
    97  
    98  	debug.Lock()
    99  	defer debug.Unlock()
   100  
   101  	logger.Debug().Int("cond", int(cond)).Str("name", stopConds[cond]).Int("val", value).Msg("set debug condition")
   102  
   103  	debug.condMap[cond] = value
   104  	debug.isEnv[cond] = env
   105  }
   106  
   107  func (debug *Debugger) Unset(cond StopCond) {
   108  	if debug == nil {
   109  		return
   110  	}
   111  
   112  	debug.Lock()
   113  	defer debug.Unlock()
   114  
   115  	logger.Debug().Str("cond", cond.String()).Msg("deubugger condition is unset")
   116  	delete(debug.condMap, cond)
   117  }
   118  
   119  func (debug *Debugger) clear() {
   120  	if debug == nil {
   121  		return
   122  	}
   123  
   124  	debug.Lock()
   125  	defer debug.Unlock()
   126  
   127  	debug.condMap = make(map[StopCond]int)
   128  	debug.isEnv = make(map[StopCond]bool)
   129  }
   130  
   131  func (debug *Debugger) Check(cond StopCond, value int, handler DebugHandler) error {
   132  	if debug == nil {
   133  		return nil
   134  	}
   135  
   136  	debug.Lock()
   137  	defer debug.Unlock()
   138  
   139  	if setVal, ok := debug.condMap[cond]; ok {
   140  		logger.Debug().Str("cond", stopConds[cond]).Int("val", setVal).Msg("check debug condition")
   141  
   142  		switch cond {
   143  		case DEBUG_CHAIN_STOP:
   144  			if setVal == value {
   145  				if debug.isEnv[cond] {
   146  					logger.Fatal().Str("cond", stopConds[cond]).Msg("shutdown by DEBUG_CHAIN_CRASH")
   147  				} else {
   148  					return &ErrDebug{cond: cond, value: value}
   149  				}
   150  			}
   151  
   152  		case DEBUG_CHAIN_RANDOM_STOP:
   153  			go crashRandom(setVal)
   154  			handleCrashRandom(setVal)
   155  
   156  		case DEBUG_CHAIN_OTHER_SLEEP, DEBUG_CHAIN_BP_SLEEP:
   157  			handleChainSleep(setVal)
   158  
   159  		case DEBUG_SYNCER_CRASH:
   160  			if setVal == value {
   161  				return handleSyncerCrash(setVal, cond)
   162  			}
   163  		case DEBUG_RAFT_SNAP_FREQ:
   164  			handler(setVal)
   165  		}
   166  	}
   167  
   168  	return nil
   169  }
   170  
   171  func handleChainSleep(sleepMils int) {
   172  	logger.Debug().Int("sleep(ms)", sleepMils).Msg("before chain sleep")
   173  
   174  	time.Sleep(time.Millisecond * time.Duration(sleepMils))
   175  
   176  	logger.Debug().Msg("after chain sleep")
   177  }
   178  
   179  func handleCrashRandom(waitMils int) {
   180  	logger.Debug().Int("after(ms)", waitMils).Msg("before random crash")
   181  
   182  	go crashRandom(waitMils)
   183  }
   184  
   185  func handleSyncerCrash(val int, cond StopCond) error {
   186  	if val >= 1 {
   187  		logger.Fatal().Int("val", val).Msg("sync crash by DEBUG_SYNC_CRASH")
   188  		return nil
   189  	} else {
   190  		return &ErrDebug{cond: cond, value: val}
   191  	}
   192  }
   193  
   194  func crashRandom(waitMils int) {
   195  	if waitMils <= 0 {
   196  		return
   197  	}
   198  
   199  	time.Sleep(time.Millisecond * time.Duration(waitMils))
   200  
   201  	logger.Debug().Msg("shutdown by DEBUG_RANDOM_CRASH_TIME")
   202  
   203  	os.Exit(100)
   204  }