diff --git a/fuzzing/coverage/coverage_tracer.go b/fuzzing/coverage/coverage_tracer.go index 2f1a47e9..d0084caf 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 Hash key is a contract's codehash (which uniquely identifies it), + // and the bool key is whether we're in contract init (true) or runtime (false) + // (since init vs runtime produces different results from getContractCoverageMapHash). + codeHashCache map[bool]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: map[bool]map[common.Hash]common.Hash{false: make(map[common.Hash]common.Hash), true: make(map[common.Hash]common.Hash)}, } nativeTracer := &tracers.Tracer{ Hooks: &tracing.Hooks{ @@ -159,11 +167,17 @@ 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 if codeSize > 0 { // Obtain our contract coverage map lookup hash. if callFrameState.lookupHash == nil { - lookupHash := getContractCoverageMapHash(code, callFrameState.create) + lookupHash, cacheHit := t.codeHashCache[isCreate][gethCodeHash] + if !cacheHit { + lookupHash = getContractCoverageMapHash(code, isCreate) + t.codeHashCache[isCreate][gethCodeHash] = lookupHash + } callFrameState.lookupHash = &lookupHash }