diff --git a/fuzzing/coverage/coverage_tracer.go b/fuzzing/coverage/coverage_tracer.go index 2f1a47e9..0cbe0785 100644 --- a/fuzzing/coverage/coverage_tracer.go +++ b/fuzzing/coverage/coverage_tracer.go @@ -52,6 +52,13 @@ type CoverageTracer struct { // nativeTracer is the underlying tracer used to capture EVM execution. nativeTracer *chain.TestChainTracer + + // codeHashCache is a cache for values returned by getContractCoverageMapHash, + // so that this expensive calculation doesn't need to be done every opcode. + // The [2] array is to differentiate between contract init (0) vs runtime (1), + // since init vs runtime produces different results from getContractCoverageMapHash. + // The Hash key is a contract's codehash, which uniquely identifies it. + codeHashCache [2]map[common.Hash]common.Hash } // coverageTracerCallFrameState tracks state across call frames in the tracer. @@ -71,6 +78,7 @@ func NewCoverageTracer() *CoverageTracer { tracer := &CoverageTracer{ coverageMaps: NewCoverageMaps(), callFrameStates: make([]*coverageTracerCallFrameState, 0), + codeHashCache: [2]map[common.Hash]common.Hash{make(map[common.Hash]common.Hash), make(map[common.Hash]common.Hash)}, } nativeTracer := &tracers.Tracer{ Hooks: &tracing.Hooks{ @@ -159,11 +167,23 @@ func (t *CoverageTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tr scopeContext := scope.(*vm.ScopeContext) code := scopeContext.Contract.Code codeSize := len(code) + isCreate := callFrameState.create + gethCodeHash := scopeContext.Contract.CodeHash + + cacheArrayKey := 1 + if isCreate { + cacheArrayKey = 0 + } + if codeSize > 0 { // Obtain our contract coverage map lookup hash. if callFrameState.lookupHash == nil { - lookupHash := getContractCoverageMapHash(code, callFrameState.create) + lookupHash, cacheHit := t.codeHashCache[cacheArrayKey][gethCodeHash] + if !cacheHit { + lookupHash = getContractCoverageMapHash(code, isCreate) + t.codeHashCache[cacheArrayKey][gethCodeHash] = lookupHash + } callFrameState.lookupHash = &lookupHash }