Skip to content

Commit 0c56792

Browse files
committed
core, cmd, eth: log detailed statistics for slow block
1 parent 1487a85 commit 0c56792

File tree

11 files changed

+242
-66
lines changed

11 files changed

+242
-66
lines changed

cmd/geth/chaincmd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`,
9696
utils.CacheNoPrefetchFlag,
9797
utils.CachePreimagesFlag,
9898
utils.NoCompactionFlag,
99+
utils.LogSlowBlockFlag,
99100
utils.MetricsEnabledFlag,
100101
utils.MetricsEnabledExpensiveFlag,
101102
utils.MetricsHTTPFlag,

cmd/geth/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ var (
155155
utils.BeaconGenesisTimeFlag,
156156
utils.BeaconCheckpointFlag,
157157
utils.BeaconCheckpointFileFlag,
158+
utils.LogSlowBlockFlag,
158159
}, utils.NetworkFlags, utils.DatabaseFlags)
159160

160161
rpcFlags = []cli.Flag{

cmd/utils/flags.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,11 @@ var (
651651
Usage: "Disables db compaction after import",
652652
Category: flags.LoggingCategory,
653653
}
654+
LogSlowBlockFlag = &cli.Uint64Flag{
655+
Name: "debug.logslowblock",
656+
Usage: "The block execution speed threshold (Mgas/s) below which detailed statistics are logged",
657+
Category: flags.LoggingCategory,
658+
}
654659

655660
// MISC settings
656661
SyncTargetFlag = &cli.StringFlag{
@@ -1699,6 +1704,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
16991704
if ctx.IsSet(LogNoHistoryFlag.Name) {
17001705
cfg.LogNoHistory = true
17011706
}
1707+
if ctx.IsSet(LogSlowBlockFlag.Name) {
1708+
cfg.SlowBlockThreshold = ctx.Uint64(LogSlowBlockFlag.Name)
1709+
}
17021710
if ctx.IsSet(LogExportCheckpointsFlag.Name) {
17031711
cfg.LogExportCheckpoints = ctx.String(LogExportCheckpointsFlag.Name)
17041712
}

core/blockchain.go

Lines changed: 83 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ var (
111111
errChainStopped = errors.New("blockchain is stopped")
112112
errInvalidOldChain = errors.New("invalid old chain")
113113
errInvalidNewChain = errors.New("invalid new chain")
114+
115+
// slowBlockProcessThreshold defines the threshold (10 Mgas per second) for
116+
// considering a block execution as slow. Detailed performance metrics will be
117+
// printed if debug mode is enabled.
118+
slowBlockProcessThreshold = float64(10)
114119
)
115120

116121
var (
@@ -198,6 +203,10 @@ type BlockChainConfig struct {
198203

199204
// StateSizeTracking indicates whether the state size tracking is enabled.
200205
StateSizeTracking bool
206+
207+
// SlowBlockThreshold is the block execution speed threshold (Mgas/s)
208+
// below which detailed statistics are logged.
209+
SlowBlockThreshold uint64
201210
}
202211

203212
// DefaultConfig returns the default config.
@@ -338,6 +347,7 @@ type BlockChain struct {
338347
stateSizer *state.SizeTracker // State size tracking
339348

340349
lastForkReadyAlert time.Time // Last time there was a fork readiness print out
350+
slowBlockThreshold uint64 // Block execution speed threshold (Mgas/s) below which detailed statistics are logged
341351
}
342352

343353
// NewBlockChain returns a fully initialised block chain using information
@@ -372,19 +382,20 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
372382
log.Info("")
373383

374384
bc := &BlockChain{
375-
chainConfig: chainConfig,
376-
cfg: cfg,
377-
db: db,
378-
triedb: triedb,
379-
triegc: prque.New[int64, common.Hash](nil),
380-
chainmu: syncx.NewClosableMutex(),
381-
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
382-
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
383-
receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit),
384-
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
385-
txLookupCache: lru.NewCache[common.Hash, txLookup](txLookupCacheLimit),
386-
engine: engine,
387-
logger: cfg.VmConfig.Tracer,
385+
chainConfig: chainConfig,
386+
cfg: cfg,
387+
db: db,
388+
triedb: triedb,
389+
triegc: prque.New[int64, common.Hash](nil),
390+
chainmu: syncx.NewClosableMutex(),
391+
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
392+
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
393+
receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit),
394+
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
395+
txLookupCache: lru.NewCache[common.Hash, txLookup](txLookupCacheLimit),
396+
engine: engine,
397+
logger: cfg.VmConfig.Tracer,
398+
slowBlockThreshold: cfg.SlowBlockThreshold,
388399
}
389400
bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.insertStopped)
390401
if err != nil {
@@ -1842,7 +1853,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
18421853
// still need re-execution to generate snapshots that are missing
18431854
case err != nil && !errors.Is(err, ErrKnownBlock):
18441855
stats.ignored += len(it.chain)
1845-
bc.reportBlock(block, nil, err)
1856+
bc.reportBadBlock(block, nil, err)
18461857
return nil, it.index, err
18471858
}
18481859
// Track the singleton witness from this chain insertion (if any)
@@ -1910,6 +1921,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
19101921
if err != nil {
19111922
return nil, it.index, err
19121923
}
1924+
res.stats.reportMetrics()
1925+
res.stats.logSlow(block, bc.slowBlockThreshold)
1926+
19131927
// Report the import stats before returning the various results
19141928
stats.processed++
19151929
stats.usedGas += res.usedGas
@@ -1970,15 +1984,20 @@ type blockProcessingResult struct {
19701984
procTime time.Duration
19711985
status WriteStatus
19721986
witness *stateless.Witness
1987+
stats *ExecuteStats
19731988
}
19741989

19751990
func (bpr *blockProcessingResult) Witness() *stateless.Witness {
19761991
return bpr.witness
19771992
}
19781993

1994+
func (bpr *blockProcessingResult) Stats() *ExecuteStats {
1995+
return bpr.stats
1996+
}
1997+
19791998
// ProcessBlock executes and validates the given block. If there was no error
19801999
// it writes the block and associated state to database.
1981-
func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) {
2000+
func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (result *blockProcessingResult, blockEndErr error) {
19822001
var (
19832002
err error
19842003
startTime = time.Now()
@@ -2012,16 +2031,22 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
20122031
}
20132032
// Upload the statistics of reader at the end
20142033
defer func() {
2015-
stats := prefetch.GetStats()
2016-
accountCacheHitPrefetchMeter.Mark(stats.AccountHit)
2017-
accountCacheMissPrefetchMeter.Mark(stats.AccountMiss)
2018-
storageCacheHitPrefetchMeter.Mark(stats.StorageHit)
2019-
storageCacheMissPrefetchMeter.Mark(stats.StorageMiss)
2020-
stats = process.GetStats()
2021-
accountCacheHitMeter.Mark(stats.AccountHit)
2022-
accountCacheMissMeter.Mark(stats.AccountMiss)
2023-
storageCacheHitMeter.Mark(stats.StorageHit)
2024-
storageCacheMissMeter.Mark(stats.StorageMiss)
2034+
pStat := prefetch.GetStats()
2035+
accountCacheHitPrefetchMeter.Mark(pStat.AccountHit)
2036+
accountCacheMissPrefetchMeter.Mark(pStat.AccountMiss)
2037+
storageCacheHitPrefetchMeter.Mark(pStat.StorageHit)
2038+
storageCacheMissPrefetchMeter.Mark(pStat.StorageMiss)
2039+
2040+
rStat := process.GetStats()
2041+
accountCacheHitMeter.Mark(rStat.AccountHit)
2042+
accountCacheMissMeter.Mark(rStat.AccountMiss)
2043+
storageCacheHitMeter.Mark(rStat.StorageHit)
2044+
storageCacheMissMeter.Mark(rStat.StorageMiss)
2045+
2046+
if result != nil {
2047+
result.stats.StatePrefetchCacheStats = pStat
2048+
result.stats.StateReadCacheStats = rStat
2049+
}
20252050
}()
20262051

20272052
go func(start time.Time, throwaway *state.StateDB, block *types.Block) {
@@ -2078,14 +2103,14 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
20782103
pstart := time.Now()
20792104
res, err := bc.processor.Process(block, statedb, bc.cfg.VmConfig)
20802105
if err != nil {
2081-
bc.reportBlock(block, res, err)
2106+
bc.reportBadBlock(block, res, err)
20822107
return nil, err
20832108
}
20842109
ptime := time.Since(pstart)
20852110

20862111
vstart := time.Now()
20872112
if err := bc.validator.ValidateState(block, statedb, res, false); err != nil {
2088-
bc.reportBlock(block, res, err)
2113+
bc.reportBadBlock(block, res, err)
20892114
return nil, err
20902115
}
20912116
vtime := time.Since(vstart)
@@ -2119,26 +2144,28 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
21192144
}
21202145
}
21212146

2122-
xvtime := time.Since(xvstart)
2123-
proctime := time.Since(startTime) // processing + validation + cross validation
2124-
2147+
var (
2148+
xvtime = time.Since(xvstart)
2149+
proctime = time.Since(startTime) // processing + validation + cross validation
2150+
stats = &ExecuteStats{}
2151+
)
21252152
// Update the metrics touched during block processing and validation
2126-
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
2127-
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing)
2128-
if statedb.AccountLoaded != 0 {
2129-
accountReadSingleTimer.Update(statedb.AccountReads / time.Duration(statedb.AccountLoaded))
2130-
}
2131-
if statedb.StorageLoaded != 0 {
2132-
storageReadSingleTimer.Update(statedb.StorageReads / time.Duration(statedb.StorageLoaded))
2133-
}
2134-
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
2135-
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
2136-
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
2137-
triehash := statedb.AccountHashes // The time spent on tries hashing
2138-
trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
2139-
blockExecutionTimer.Update(ptime - (statedb.AccountReads + statedb.StorageReads)) // The time spent on EVM processing
2140-
blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
2141-
blockCrossValidationTimer.Update(xvtime) // The time spent on stateless cross validation
2153+
stats.AccountReads = statedb.AccountReads // Account reads are complete(in processing)
2154+
stats.StorageReads = statedb.StorageReads // Storage reads are complete(in processing)
2155+
stats.AccountUpdates = statedb.AccountUpdates // Account updates are complete(in validation)
2156+
stats.StorageUpdates = statedb.StorageUpdates // Storage updates are complete(in validation)
2157+
stats.AccountHashes = statedb.AccountHashes // Account hashes are complete(in validation)
2158+
2159+
stats.AccountLoaded = statedb.AccountLoaded
2160+
stats.AccountUpdated = statedb.AccountUpdated
2161+
stats.AccountDeleted = statedb.AccountDeleted
2162+
stats.StorageLoaded = statedb.StorageLoaded
2163+
stats.StorageUpdated = int(statedb.StorageUpdated.Load())
2164+
stats.StorageDeleted = int(statedb.StorageDeleted.Load())
2165+
2166+
stats.Execution = ptime - (statedb.AccountReads + statedb.StorageReads) // The time spent on EVM processing
2167+
stats.Validation = vtime - (statedb.AccountHashes + statedb.AccountUpdates + statedb.StorageUpdates) // The time spent on block validation
2168+
stats.CrossValidation = xvtime // The time spent on stateless cross validation
21422169

21432170
// Write the block to the chain and get the status.
21442171
var (
@@ -2160,24 +2187,22 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
21602187
}
21612188

21622189
// Update the metrics touched during block commit
2163-
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
2164-
storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them
2165-
snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them
2166-
triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
2190+
stats.AccountCommits = statedb.AccountCommits // Account commits are complete, we can mark them
2191+
stats.StorageCommits = statedb.StorageCommits // Storage commits are complete, we can mark them
2192+
stats.SnapshotCommit = statedb.SnapshotCommits // Snapshot commits are complete, we can mark them
2193+
stats.TrieDBCommit = statedb.TrieDBCommits // Trie database commits are complete, we can mark them
2194+
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits
21672195

2168-
blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits)
21692196
elapsed := time.Since(startTime) + 1 // prevent zero division
2170-
blockInsertTimer.Update(elapsed)
2171-
2172-
// TODO(rjl493456442) generalize the ResettingTimer
2173-
mgasps := float64(res.GasUsed) * 1000 / float64(elapsed)
2174-
chainMgaspsMeter.Update(time.Duration(mgasps))
2197+
stats.TotalTime = elapsed
2198+
stats.MgasPerSecond = float64(res.GasUsed) * 1000 / float64(elapsed)
21752199

21762200
return &blockProcessingResult{
21772201
usedGas: res.GasUsed,
21782202
procTime: proctime,
21792203
status: status,
21802204
witness: witness,
2205+
stats: stats,
21812206
}, nil
21822207
}
21832208

@@ -2649,8 +2674,8 @@ func (bc *BlockChain) skipBlock(err error, it *insertIterator) bool {
26492674
return false
26502675
}
26512676

2652-
// reportBlock logs a bad block error.
2653-
func (bc *BlockChain) reportBlock(block *types.Block, res *ProcessResult, err error) {
2677+
// reportBadBlock logs a bad block error.
2678+
func (bc *BlockChain) reportBadBlock(block *types.Block, res *ProcessResult, err error) {
26542679
var receipts types.Receipts
26552680
if res != nil {
26562681
receipts = res.Receipts

0 commit comments

Comments
 (0)