@@ -198,6 +198,10 @@ type BlockChainConfig struct {
198198
199199 // StateSizeTracking indicates whether the state size tracking is enabled.
200200 StateSizeTracking bool
201+
202+ // SlowBlockThreshold is the block execution time threshold beyond which
203+ // detailed statistics will be logged.
204+ SlowBlockThreshold time.Duration
201205}
202206
203207// DefaultConfig returns the default config.
@@ -337,7 +341,8 @@ type BlockChain struct {
337341 logger * tracing.Hooks
338342 stateSizer * state.SizeTracker // State size tracking
339343
340- lastForkReadyAlert time.Time // Last time there was a fork readiness print out
344+ lastForkReadyAlert time.Time // Last time there was a fork readiness print out
345+ slowBlockThreshold time.Duration // Block execution time threshold beyond which detailed statistics will be logged
341346}
342347
343348// NewBlockChain returns a fully initialised block chain using information
@@ -372,19 +377,20 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
372377 log .Info ("" )
373378
374379 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 ,
380+ chainConfig : chainConfig ,
381+ cfg : cfg ,
382+ db : db ,
383+ triedb : triedb ,
384+ triegc : prque.New [int64 , common.Hash ](nil ),
385+ chainmu : syncx .NewClosableMutex (),
386+ bodyCache : lru.NewCache [common.Hash , * types.Body ](bodyCacheLimit ),
387+ bodyRLPCache : lru.NewCache [common.Hash , rlp.RawValue ](bodyCacheLimit ),
388+ receiptsCache : lru.NewCache [common.Hash , []* types.Receipt ](receiptsCacheLimit ),
389+ blockCache : lru.NewCache [common.Hash , * types.Block ](blockCacheLimit ),
390+ txLookupCache : lru.NewCache [common.Hash , txLookup ](txLookupCacheLimit ),
391+ engine : engine ,
392+ logger : cfg .VmConfig .Tracer ,
393+ slowBlockThreshold : cfg .SlowBlockThreshold ,
388394 }
389395 bc .hc , err = NewHeaderChain (db , chainConfig , engine , bc .insertStopped )
390396 if err != nil {
@@ -1847,7 +1853,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
18471853 // still need re-execution to generate snapshots that are missing
18481854 case err != nil && ! errors .Is (err , ErrKnownBlock ):
18491855 stats .ignored += len (it .chain )
1850- bc .reportBlock (block , nil , err )
1856+ bc .reportBadBlock (block , nil , err )
18511857 return nil , it .index , err
18521858 }
18531859 // Track the singleton witness from this chain insertion (if any)
@@ -1915,6 +1921,14 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
19151921 if err != nil {
19161922 return nil , it .index , err
19171923 }
1924+ res .stats .reportMetrics ()
1925+
1926+ // Log slow block only if a single block is inserted (usually after the
1927+ // initial sync) to not overwhelm the users.
1928+ if len (chain ) == 1 {
1929+ res .stats .logSlow (block , bc .slowBlockThreshold )
1930+ }
1931+
19181932 // Report the import stats before returning the various results
19191933 stats .processed ++
19201934 stats .usedGas += res .usedGas
@@ -1975,15 +1989,20 @@ type blockProcessingResult struct {
19751989 procTime time.Duration
19761990 status WriteStatus
19771991 witness * stateless.Witness
1992+ stats * ExecuteStats
19781993}
19791994
19801995func (bpr * blockProcessingResult ) Witness () * stateless.Witness {
19811996 return bpr .witness
19821997}
19831998
1999+ func (bpr * blockProcessingResult ) Stats () * ExecuteStats {
2000+ return bpr .stats
2001+ }
2002+
19842003// ProcessBlock executes and validates the given block. If there was no error
19852004// it writes the block and associated state to database.
1986- func (bc * BlockChain ) ProcessBlock (parentRoot common.Hash , block * types.Block , setHead bool , makeWitness bool ) (_ * blockProcessingResult , blockEndErr error ) {
2005+ func (bc * BlockChain ) ProcessBlock (parentRoot common.Hash , block * types.Block , setHead bool , makeWitness bool ) (result * blockProcessingResult , blockEndErr error ) {
19872006 var (
19882007 err error
19892008 startTime = time .Now ()
@@ -2017,16 +2036,22 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
20172036 }
20182037 // Upload the statistics of reader at the end
20192038 defer func () {
2020- stats := prefetch .GetStats ()
2021- accountCacheHitPrefetchMeter .Mark (stats .AccountHit )
2022- accountCacheMissPrefetchMeter .Mark (stats .AccountMiss )
2023- storageCacheHitPrefetchMeter .Mark (stats .StorageHit )
2024- storageCacheMissPrefetchMeter .Mark (stats .StorageMiss )
2025- stats = process .GetStats ()
2026- accountCacheHitMeter .Mark (stats .AccountHit )
2027- accountCacheMissMeter .Mark (stats .AccountMiss )
2028- storageCacheHitMeter .Mark (stats .StorageHit )
2029- storageCacheMissMeter .Mark (stats .StorageMiss )
2039+ pStat := prefetch .GetStats ()
2040+ accountCacheHitPrefetchMeter .Mark (pStat .AccountCacheHit )
2041+ accountCacheMissPrefetchMeter .Mark (pStat .AccountCacheMiss )
2042+ storageCacheHitPrefetchMeter .Mark (pStat .StorageCacheHit )
2043+ storageCacheMissPrefetchMeter .Mark (pStat .StorageCacheMiss )
2044+
2045+ rStat := process .GetStats ()
2046+ accountCacheHitMeter .Mark (rStat .AccountCacheHit )
2047+ accountCacheMissMeter .Mark (rStat .AccountCacheMiss )
2048+ storageCacheHitMeter .Mark (rStat .StorageCacheHit )
2049+ storageCacheMissMeter .Mark (rStat .StorageCacheMiss )
2050+
2051+ if result != nil {
2052+ result .stats .StatePrefetchCacheStats = pStat
2053+ result .stats .StateReadCacheStats = rStat
2054+ }
20302055 }()
20312056
20322057 go func (start time.Time , throwaway * state.StateDB , block * types.Block ) {
@@ -2083,14 +2108,14 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
20832108 pstart := time .Now ()
20842109 res , err := bc .processor .Process (block , statedb , bc .cfg .VmConfig )
20852110 if err != nil {
2086- bc .reportBlock (block , res , err )
2111+ bc .reportBadBlock (block , res , err )
20872112 return nil , err
20882113 }
20892114 ptime := time .Since (pstart )
20902115
20912116 vstart := time .Now ()
20922117 if err := bc .validator .ValidateState (block , statedb , res , false ); err != nil {
2093- bc .reportBlock (block , res , err )
2118+ bc .reportBadBlock (block , res , err )
20942119 return nil , err
20952120 }
20962121 vtime := time .Since (vstart )
@@ -2124,26 +2149,28 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
21242149 }
21252150 }
21262151
2127- xvtime := time .Since (xvstart )
2128- proctime := time .Since (startTime ) // processing + validation + cross validation
2129-
2152+ var (
2153+ xvtime = time .Since (xvstart )
2154+ proctime = time .Since (startTime ) // processing + validation + cross validation
2155+ stats = & ExecuteStats {}
2156+ )
21302157 // Update the metrics touched during block processing and validation
2131- accountReadTimer . Update ( statedb .AccountReads ) // Account reads are complete(in processing)
2132- storageReadTimer . Update ( statedb .StorageReads ) // Storage reads are complete(in processing)
2133- if statedb .AccountLoaded != 0 {
2134- accountReadSingleTimer . Update ( statedb .AccountReads / time . Duration ( statedb . AccountLoaded ) )
2135- }
2136- if statedb . StorageLoaded != 0 {
2137- storageReadSingleTimer . Update ( statedb . StorageReads / time . Duration ( statedb .StorageLoaded ))
2138- }
2139- accountUpdateTimer . Update ( statedb . AccountUpdates ) // Account updates are complete(in validation)
2140- storageUpdateTimer . Update ( statedb . StorageUpdates ) // Storage updates are complete(in validation)
2141- accountHashTimer . Update (statedb .AccountHashes ) // Account hashes are complete(in validation )
2142- triehash := statedb .AccountHashes // The time spent on tries hashing
2143- trieUpdate := statedb . AccountUpdates + statedb . StorageUpdates // The time spent on tries update
2144- blockExecutionTimer . Update ( ptime - (statedb .AccountReads + statedb .StorageReads )) // The time spent on EVM processing
2145- blockValidationTimer . Update ( vtime - (triehash + trieUpdate )) // The time spent on block validation
2146- blockCrossValidationTimer . Update ( xvtime ) // The time spent on stateless cross validation
2158+ stats . AccountReads = statedb .AccountReads // Account reads are complete(in processing)
2159+ stats . StorageReads = statedb .StorageReads // Storage reads are complete(in processing)
2160+ stats . AccountUpdates = statedb .AccountUpdates // Account updates are complete(in validation)
2161+ stats . StorageUpdates = statedb .StorageUpdates // Storage updates are complete(in validation )
2162+ stats . AccountHashes = statedb . AccountHashes // Account hashes are complete(in validation)
2163+
2164+ stats . AccountLoaded = statedb .AccountLoaded
2165+ stats . AccountUpdated = statedb . AccountUpdated
2166+ stats . AccountDeleted = statedb . AccountDeleted
2167+ stats . StorageLoaded = statedb . StorageLoaded
2168+ stats . StorageUpdated = int (statedb .StorageUpdated . Load () )
2169+ stats . StorageDeleted = int ( statedb .StorageDeleted . Load ())
2170+
2171+ stats . Execution = ptime - (statedb .AccountReads + statedb .StorageReads ) // The time spent on EVM processing
2172+ stats . Validation = vtime - (statedb . AccountHashes + statedb . AccountUpdates + statedb . StorageUpdates ) // The time spent on block validation
2173+ stats . CrossValidation = xvtime // The time spent on stateless cross validation
21472174
21482175 // Write the block to the chain and get the status.
21492176 var (
@@ -2165,24 +2192,22 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
21652192 }
21662193
21672194 // Update the metrics touched during block commit
2168- accountCommitTimer .Update (statedb .AccountCommits ) // Account commits are complete, we can mark them
2169- storageCommitTimer .Update (statedb .StorageCommits ) // Storage commits are complete, we can mark them
2170- snapshotCommitTimer .Update (statedb .SnapshotCommits ) // Snapshot commits are complete, we can mark them
2171- triedbCommitTimer .Update (statedb .TrieDBCommits ) // Trie database commits are complete, we can mark them
2195+ stats .AccountCommits = statedb .AccountCommits // Account commits are complete, we can mark them
2196+ stats .StorageCommits = statedb .StorageCommits // Storage commits are complete, we can mark them
2197+ stats .SnapshotCommit = statedb .SnapshotCommits // Snapshot commits are complete, we can mark them
2198+ stats .TrieDBCommit = statedb .TrieDBCommits // Trie database commits are complete, we can mark them
2199+ stats .BlockWrite = time .Since (wstart ) - max (statedb .AccountCommits , statedb .StorageCommits ) /* concurrent */ - statedb .SnapshotCommits - statedb .TrieDBCommits
21722200
2173- blockWriteTimer .Update (time .Since (wstart ) - max (statedb .AccountCommits , statedb .StorageCommits ) /* concurrent */ - statedb .SnapshotCommits - statedb .TrieDBCommits )
21742201 elapsed := time .Since (startTime ) + 1 // prevent zero division
2175- blockInsertTimer .Update (elapsed )
2176-
2177- // TODO(rjl493456442) generalize the ResettingTimer
2178- mgasps := float64 (res .GasUsed ) * 1000 / float64 (elapsed )
2179- chainMgaspsMeter .Update (time .Duration (mgasps ))
2202+ stats .TotalTime = elapsed
2203+ stats .MgasPerSecond = float64 (res .GasUsed ) * 1000 / float64 (elapsed )
21802204
21812205 return & blockProcessingResult {
21822206 usedGas : res .GasUsed ,
21832207 procTime : proctime ,
21842208 status : status ,
21852209 witness : witness ,
2210+ stats : stats ,
21862211 }, nil
21872212}
21882213
@@ -2667,8 +2692,8 @@ func (bc *BlockChain) skipBlock(err error, it *insertIterator) bool {
26672692 return false
26682693}
26692694
2670- // reportBlock logs a bad block error.
2671- func (bc * BlockChain ) reportBlock (block * types.Block , res * ProcessResult , err error ) {
2695+ // reportBadBlock logs a bad block error.
2696+ func (bc * BlockChain ) reportBadBlock (block * types.Block , res * ProcessResult , err error ) {
26722697 var receipts types.Receipts
26732698 if res != nil {
26742699 receipts = res .Receipts
0 commit comments