Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify saving tx result in coverage #1158

Merged
merged 1 commit into from
Jan 7, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 12 additions & 15 deletions lib/Echidna/Exec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import Echidna.Symbolic (forceBuf)
import Echidna.Transaction
import Echidna.Types (ExecException(..), Gas, fromEVM, emptyAccount)
import Echidna.Types.Config (Env(..), EConfig(..), UIConf(..), OperationMode(..), OutputFormat(Text))
import Echidna.Types.Coverage (CoverageInfo)
import Echidna.Types.Signature (getBytecodeMetadata, lookupBytecodeMetadata)
import Echidna.Types.Solidity (SolConf(..))
import Echidna.Types.Tx (TxCall(..), Tx, TxResult(..), call, dst, initialTimestamp, initialBlockNumber, getResult)
Expand Down Expand Up @@ -237,7 +238,7 @@ execTx
execTx vm tx = runStateT (execTxWith (fromEVM exec) tx) vm

-- | A type alias for the context we carry while executing instructions
type CoverageContext = (Bool, Maybe (BS.ByteString, Int))
type CoverageContext = (Bool, Maybe (VMut.IOVector CoverageInfo, Int))

-- | Execute a transaction, logging coverage at every step.
execTxWithCov
Expand All @@ -257,17 +258,13 @@ execTxWithCov tx = do

-- Update the last valid location with the transaction result
grew' <- liftIO $ case lastLoc of
Just (meta, pc) -> do
cov <- readIORef covRef
case Map.lookup meta cov of
Nothing -> pure False -- shouldn't happen
Just vec -> do
let txResultBit = fromEnum $ getResult $ fst r
VMut.read vec pc >>= \case
(opIx, depths, txResults) | not (txResults `testBit` txResultBit) -> do
VMut.write vec pc (opIx, depths, txResults `setBit` txResultBit)
pure True -- we count this as new coverage
_ -> pure False
Just (vec, pc) -> do
let txResultBit = fromEnum $ getResult $ fst r
VMut.read vec pc >>= \case
(opIx, depths, txResults) | not (txResults `testBit` txResultBit) -> do
VMut.write vec pc (opIx, depths, txResults `setBit` txResultBit)
pure True -- we count this as new coverage
_ -> pure False
_ -> pure False

pure (r, grew || grew')
Expand Down Expand Up @@ -314,7 +311,7 @@ execTxWithCov tx = do

VMut.write vec' pc (opIx, fromIntegral depth, 0 `setBit` fromEnum Stop)

writeIORef covContextRef (True, Just (meta, pc))
writeIORef covContextRef (True, Just (vec', pc))
else do
-- TODO: should we collect the coverage here? Even if there is no
-- bytecode for external contract, we could have a "virtual" location
Expand All @@ -328,9 +325,9 @@ execTxWithCov tx = do
VMut.read vec pc >>= \case
(_, depths, results) | depth < 64 && not (depths `testBit` depth) -> do
VMut.write vec pc (opIx, depths `setBit` depth, results `setBit` fromEnum Stop)
writeIORef covContextRef (True, Just (meta, pc))
writeIORef covContextRef (True, Just (vec, pc))
_ ->
modifyIORef' covContextRef $ \(new, _) -> (new, Just (meta, pc))
modifyIORef' covContextRef $ \(new, _) -> (new, Just (vec, pc))

-- | Get the VM's current execution location
currentCovLoc vm = (vm.state.pc, fromMaybe 0 $ vmOpIx vm, length vm.frames)
Expand Down