Skip to content

Commit

Permalink
Mitigate large memory consumption when replaying corpus (#725)
Browse files Browse the repository at this point in the history
  • Loading branch information
ggrieco-tob authored Mar 4, 2022
1 parent e52802c commit a729ef9
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 10 deletions.
22 changes: 12 additions & 10 deletions lib/Echidna/Campaign.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import Echidna.Test
import Echidna.Transaction
import Echidna.Shrink (shrinkSeq)
import Echidna.Types.Campaign
import Echidna.Types.Corpus (InitialCorpus)
import Echidna.Types.Coverage (coveragePoints)
import Echidna.Types.Test
import Echidna.Types.Buffer (viewBuffer)
Expand Down Expand Up @@ -194,17 +195,17 @@ addToCorpus n res = unless (null rtxs) $ hasLens . corpus %= DS.insert (toIntege
-- | Generate a new sequences of transactions, either using the corpus or with randomly created transactions
randseq :: ( MonadRandom m, MonadReader x m, MonadState y m
, Has TxConf x, Has TestConf x, Has CampaignConf x, Has GenDict y, Has Campaign y)
=> Int -> Map Addr Contract -> World -> m [Tx]
randseq ql o w = do
=> InitialCorpus -> Int -> Map Addr Contract -> World -> m [Tx]
randseq (n,txs) ql o w = do
ca <- use hasLens
cs <- view $ hasLens . mutConsts
txConf :: TxConf <- view hasLens
let ctxs = ca ^. corpus
-- TODO: include reproducer when optimizing
--rs = filter (not . null) $ map (view testReproducer) $ ca ^. tests
p = ca ^. ncallseqs
if length ctxs > p then -- Replay the transactions in the corpus, if we are executing the first iterations
return . snd $ DS.elemAt p ctxs
if n > p then -- Replay the transactions in the corpus, if we are executing the first iterations
return $ txs !! p
else do
memo <- use $ hasLens . bcMemo
-- Randomly generate new random transactions
Expand All @@ -222,8 +223,8 @@ randseq ql o w = do
-- constantly checking if we've solved any tests or can shrink known solves. Update coverage as a result
callseq :: ( MonadCatch m, MonadRandom m, MonadReader x m, MonadState y m
, Has SolConf x, Has TestConf x, Has TxConf x, Has CampaignConf x, Has DappInfo x, Has Campaign y, Has GenDict y)
=> VM -> World -> Int -> m ()
callseq v w ql = do
=> InitialCorpus -> VM -> World -> Int -> m ()
callseq ic v w ql = do
-- First, we figure out whether we need to execute with or without coverage optimization and gas info,
-- and pick our execution function appropriately
coverageEnabled <- isJust <$> view (hasLens . knownCoverage)
Expand All @@ -233,7 +234,7 @@ callseq v w ql = do
-- Then, we get the current campaign state
ca <- use hasLens
-- Then, we generate the actual transaction in the sequence
is <- randseq ql old w
is <- randseq ic ql old w
-- We then run each call sequentially. This gives us the result of each call, plus a new state
(res, s) <- runStateT (evalSeq w v ef is) (v, ca)
let new = s ^. _1 . env . EVM.contracts
Expand Down Expand Up @@ -291,20 +292,21 @@ campaign u vm w ts d txs = do
mempty
effectiveGenDict
False
(DS.fromList $ map (1,) txs)
DS.empty
0
memo
)
where
-- "mapMaybe ..." is to get a list of all contracts
ic = (length txs, txs)
memo = makeBytecodeMemo . mapMaybe (viewBuffer . (^. bytecode)) . elems $ (vm ^. env . EVM.contracts)
step = runUpdate (updateTest w vm Nothing) >> lift u >> runCampaign
runCampaign = use (hasLens . tests . to (fmap (view testState))) >>= update
update c = do
CampaignConf tl sof _ q sl _ _ _ _ _ <- view hasLens
Campaign { _ncallseqs } <- view hasLens <$> get
if | sof && any (\case Solved -> True; Failed _ -> True; _ -> False) c -> lift u
| any (\case Open n -> n < tl; _ -> False) c -> callseq vm w q >> step
| any (\case Open n -> n < tl; _ -> False) c -> callseq ic vm w q >> step
| any (\case Large n -> n < sl; _ -> False) c -> step
| null c && (q * _ncallseqs) < tl -> callseq vm w q >> step
| null c && (q * _ncallseqs) < tl -> callseq ic vm w q >> step
| otherwise -> lift u
1 change: 1 addition & 0 deletions lib/Echidna/Types/Corpus.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Echidna.Types.Corpus where
import Data.Set (Set, size)
import Echidna.Types.Tx (Tx)

type InitialCorpus = (Int, [[Tx]])
type Corpus = Set (Integer, [Tx])

corpusSize :: Corpus -> Int
Expand Down

0 comments on commit a729ef9

Please sign in to comment.