diff --git a/fuzzing/coverage/coverage_maps.go b/fuzzing/coverage/coverage_maps.go index 60db6fa8..59237669 100644 --- a/fuzzing/coverage/coverage_maps.go +++ b/fuzzing/coverage/coverage_maps.go @@ -16,14 +16,6 @@ type CoverageMaps struct { // maps represents a structure used to track every ContractCoverageMap by a given deployed address/lookup hash. maps map[common.Hash]map[common.Address]*ContractCoverageMap - // TODO comment this - // The assumption here is that geth codehash matches if and only if codehash matches - gethCodeHashToCodeHash map[common.Hash]*common.Hash - - // TODO comment this - // The assumption here is that geth codehash matches if and only if codehash matches - cachedGethCodeHash common.Hash - // cachedCodeAddress represents the last code address which coverage was updated for. This is used to prevent an // expensive lookup in maps. If cachedCodeHash does not match the current code address for which we are updating // coverage for, it, along with other cache variables are updated. @@ -44,9 +36,7 @@ type CoverageMaps struct { // NewCoverageMaps initializes a new CoverageMaps object. func NewCoverageMaps() *CoverageMaps { - maps := &CoverageMaps{ - gethCodeHashToCodeHash: make(map[common.Hash]*common.Hash), - } + maps := &CoverageMaps{} maps.Reset() return maps } @@ -56,7 +46,6 @@ func (cm *CoverageMaps) Reset() { cm.maps = make(map[common.Hash]map[common.Address]*ContractCoverageMap) cm.cachedCodeAddress = common.Address{} cm.cachedCodeHash = common.Hash{} - cm.cachedGethCodeHash = common.Hash{} cm.cachedMap = nil } @@ -181,7 +170,7 @@ func (cm *CoverageMaps) Update(coverageMaps *CoverageMaps) (bool, bool, error) { } // UpdateAt updates the hit count of a given program counter location within code coverage data. -func (cm *CoverageMaps) UpdateAt(codeAddress common.Address, gethCodeHash common.Hash, codeLookupHash common.Hash, codeSize int, pc uint64) (bool, error) { +func (cm *CoverageMaps) UpdateAt(codeAddress common.Address, codeLookupHash common.Hash, codeSize int, pc uint64) (bool, error) { // If the code size is zero, do nothing if codeSize == 0 { return false, nil @@ -196,7 +185,7 @@ func (cm *CoverageMaps) UpdateAt(codeAddress common.Address, gethCodeHash common ) // Try to obtain a coverage map from our cache - if cm.cachedMap != nil && cm.cachedCodeAddress == codeAddress && cm.cachedCodeHash == codeLookupHash && cm.cachedGethCodeHash == gethCodeHash { + if cm.cachedMap != nil && cm.cachedCodeAddress == codeAddress && cm.cachedCodeHash == codeLookupHash { coverageMap = cm.cachedMap } else { // If a coverage map lookup for this code hash doesn't exist, create the mapping. @@ -218,7 +207,6 @@ func (cm *CoverageMaps) UpdateAt(codeAddress common.Address, gethCodeHash common // Set our cached variables for faster coverage setting next time this method is called. cm.cachedMap = coverageMap cm.cachedCodeHash = codeLookupHash - cm.cachedGethCodeHash = gethCodeHash cm.cachedCodeAddress = codeAddress } diff --git a/fuzzing/coverage/coverage_tracer.go b/fuzzing/coverage/coverage_tracer.go index 9f80db25..d0084caf 100644 --- a/fuzzing/coverage/coverage_tracer.go +++ b/fuzzing/coverage/coverage_tracer.go @@ -53,8 +53,12 @@ type CoverageTracer struct { // nativeTracer is the underlying tracer used to capture EVM execution. nativeTracer *chain.TestChainTracer - // TODO comment - gethCodeHashToCodeHash map[common.Hash]*common.Hash + // 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. @@ -72,9 +76,9 @@ type coverageTracerCallFrameState struct { // NewCoverageTracer returns a new CoverageTracer. func NewCoverageTracer() *CoverageTracer { tracer := &CoverageTracer{ - coverageMaps: NewCoverageMaps(), - gethCodeHashToCodeHash: make(map[common.Hash]*common.Hash), - callFrameStates: make([]*coverageTracerCallFrameState, 0), + 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{ @@ -161,30 +165,24 @@ func (t *CoverageTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tr address := scope.Address() // We can cast OpContext to ScopeContext because that is the type passed to OnOpcode. scopeContext := scope.(*vm.ScopeContext) - gethCodeHash := scopeContext.Contract.CodeHash 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 { - if callFrameState.pendingCoverageMap.cachedGethCodeHash == gethCodeHash { - lookupHash := callFrameState.pendingCoverageMap.cachedCodeHash - callFrameState.lookupHash = &lookupHash - } else if t.gethCodeHashToCodeHash[gethCodeHash] != nil { - callFrameState.lookupHash = t.gethCodeHashToCodeHash[gethCodeHash] - } else if callFrameState.pendingCoverageMap.gethCodeHashToCodeHash[gethCodeHash] != nil { - callFrameState.lookupHash = t.gethCodeHashToCodeHash[gethCodeHash] - } else { - lookupHash := getContractCoverageMapHash(code, callFrameState.create) - callFrameState.lookupHash = &lookupHash - t.gethCodeHashToCodeHash[gethCodeHash] = &lookupHash - callFrameState.pendingCoverageMap.gethCodeHashToCodeHash[gethCodeHash] = &lookupHash + lookupHash, cacheHit := t.codeHashCache[isCreate][gethCodeHash] + if !cacheHit { + lookupHash = getContractCoverageMapHash(code, isCreate) + t.codeHashCache[isCreate][gethCodeHash] = lookupHash } + callFrameState.lookupHash = &lookupHash } // Record coverage for this location in our map. - _, coverageUpdateErr := callFrameState.pendingCoverageMap.UpdateAt(address, gethCodeHash, *callFrameState.lookupHash, codeSize, pc) + _, coverageUpdateErr := callFrameState.pendingCoverageMap.UpdateAt(address, *callFrameState.lookupHash, codeSize, pc) if coverageUpdateErr != nil { logging.GlobalLogger.Panic("Coverage tracer failed to update coverage map while tracing state", coverageUpdateErr) }