From 8ea9ecc3a123a9fbe3b03cff99dbaa32f408e0a7 Mon Sep 17 00:00:00 2001 From: Artur Cygan Date: Mon, 20 Mar 2023 13:02:47 +0100 Subject: [PATCH 1/3] Add logs for compilation and running slither --- lib/Echidna.hs | 2 +- lib/Echidna/Processor.hs | 15 +++++++++++---- lib/Echidna/Solidity.hs | 6 +++++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/Echidna.hs b/lib/Echidna.hs index fac9bc506..4d67d8a30 100644 --- a/lib/Echidna.hs +++ b/lib/Echidna.hs @@ -54,7 +54,7 @@ prepareContract env contracts solFiles specifiedContract seed = do funs -- run processors - slitherInfo <- runSlither (NE.head solFiles) solConf.cryticArgs + slitherInfo <- runSlither (NE.head solFiles) solConf case find (< minSupportedSolcVersion) slitherInfo.solcVersions of Just outdatedVersion -> throwM $ OutdatedSolcVersion outdatedVersion Nothing -> pure () diff --git a/lib/Echidna/Processor.hs b/lib/Echidna/Processor.hs index 5eae27c56..a16c0ebfc 100644 --- a/lib/Echidna/Processor.hs +++ b/lib/Echidna/Processor.hs @@ -3,6 +3,7 @@ module Echidna.Processor where import Control.Exception (Exception) +import Control.Monad (unless) import Control.Monad.Catch (MonadThrow(..)) import Data.Aeson ((.:), (.:?), (.!=), eitherDecode, parseJSON, withEmbeddedJSON, withObject) import Data.Aeson.Types (FromJSON, Parser, Value(String)) @@ -19,8 +20,9 @@ import Data.Set (Set) import Data.Set qualified as Set import Data.Text (pack, isSuffixOf) import System.Directory (findExecutable) -import System.Process (StdStream(..), readCreateProcessWithExitCode, proc, std_err) import System.Exit (ExitCode(..)) +import System.IO (hFlush, stdout) +import System.Process (StdStream(..), readCreateProcessWithExitCode, proc, std_err) import Text.Read (readMaybe) import EVM.ABI (AbiValue(..)) @@ -28,6 +30,7 @@ import EVM.Types (Addr(..)) import Echidna.ABI (hashSig, makeNumAbiValues, makeArrayAbiValues) import Echidna.Types.Signature (ContractName, FunctionName, FunctionHash) +import Echidna.Types.Solidity (SolConf(..)) -- | Things that can go wrong trying to run a processor. Read the 'Show' -- instance for more detailed explanations. @@ -122,14 +125,18 @@ instance FromJSON SlitherInfo where _ -> pure Nothing -- Slither processing -runSlither :: FilePath -> [String] -> IO SlitherInfo -runSlither fp extraArgs = if ".vy" `isSuffixOf` pack fp then return noInfo else do +runSlither :: FilePath -> SolConf -> IO SlitherInfo +runSlither fp solConf = if ".vy" `isSuffixOf` pack fp then return noInfo else do mp <- findExecutable "slither" case mp of Nothing -> throwM $ ProcessorNotFound "slither" "You should install it using 'pip3 install slither-analyzer --user'" Just path -> do - let args = ["--ignore-compile", "--print", "echidna", "--json", "-"] ++ extraArgs ++ [fp] + let args = ["--ignore-compile", "--print", "echidna", "--json", "-"] ++ solConf.cryticArgs ++ [fp] + unless solConf.quiet $ do + putStr $ "Running slither on " <> fp <> "... " + hFlush stdout (ec, out, err) <- readCreateProcessWithExitCode (proc path args) {std_err = Inherit} "" + unless solConf.quiet $ putStrLn "Done!" case ec of ExitSuccess -> case eitherDecode (BSL.pack out) of diff --git a/lib/Echidna/Solidity.hs b/lib/Echidna/Solidity.hs index b3c0a7680..8d0cce3a7 100644 --- a/lib/Echidna/Solidity.hs +++ b/lib/Echidna/Solidity.hs @@ -24,7 +24,7 @@ import System.Directory (doesDirectoryExist, doesFileExist, findExecutable, list import System.Process (StdStream(..), readCreateProcessWithExitCode, proc, std_err) import System.Exit (ExitCode(..)) import System.FilePath (joinPath, splitDirectories, ()) -import System.IO (openFile, IOMode(..)) +import System.IO (openFile, IOMode(..), hFlush, stdout) import System.Info (os) import EVM hiding (Env, env, contract, contracts, path) @@ -88,8 +88,12 @@ compileContracts solConf fp = do stderr <- if solConf.quiet then UseHandle <$> openFile nullFilePath WriteMode else pure Inherit + unless solConf.quiet $ do + putStr $ "Compiling " <> x <> "... " + hFlush stdout (ec, out, err) <- readCreateProcessWithExitCode (proc path $ (solConf.cryticArgs ++ solargs) |> x) {std_err = stderr} "" + unless solConf.quiet $ putStrLn "Done!" case ec of ExitSuccess -> readSolcBatch "crytic-export" ExitFailure _ -> throwM $ CompileFailure out err From b69d6224797626fb75acf97f68c7329b11e5d2a8 Mon Sep 17 00:00:00 2001 From: Artur Cygan Date: Mon, 20 Mar 2023 13:31:43 +0100 Subject: [PATCH 2/3] Move action measurement to measureIO --- lib/Echidna/Processor.hs | 10 +++------- lib/Echidna/Solidity.hs | 12 +++++------- lib/Echidna/Utility.hs | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 lib/Echidna/Utility.hs diff --git a/lib/Echidna/Processor.hs b/lib/Echidna/Processor.hs index a16c0ebfc..97a01c38f 100644 --- a/lib/Echidna/Processor.hs +++ b/lib/Echidna/Processor.hs @@ -3,7 +3,6 @@ module Echidna.Processor where import Control.Exception (Exception) -import Control.Monad (unless) import Control.Monad.Catch (MonadThrow(..)) import Data.Aeson ((.:), (.:?), (.!=), eitherDecode, parseJSON, withEmbeddedJSON, withObject) import Data.Aeson.Types (FromJSON, Parser, Value(String)) @@ -21,7 +20,6 @@ import Data.Set qualified as Set import Data.Text (pack, isSuffixOf) import System.Directory (findExecutable) import System.Exit (ExitCode(..)) -import System.IO (hFlush, stdout) import System.Process (StdStream(..), readCreateProcessWithExitCode, proc, std_err) import Text.Read (readMaybe) @@ -31,6 +29,7 @@ import EVM.Types (Addr(..)) import Echidna.ABI (hashSig, makeNumAbiValues, makeArrayAbiValues) import Echidna.Types.Signature (ContractName, FunctionName, FunctionHash) import Echidna.Types.Solidity (SolConf(..)) +import Echidna.Utility (measureIO) -- | Things that can go wrong trying to run a processor. Read the 'Show' -- instance for more detailed explanations. @@ -132,11 +131,8 @@ runSlither fp solConf = if ".vy" `isSuffixOf` pack fp then return noInfo else do Nothing -> throwM $ ProcessorNotFound "slither" "You should install it using 'pip3 install slither-analyzer --user'" Just path -> do let args = ["--ignore-compile", "--print", "echidna", "--json", "-"] ++ solConf.cryticArgs ++ [fp] - unless solConf.quiet $ do - putStr $ "Running slither on " <> fp <> "... " - hFlush stdout - (ec, out, err) <- readCreateProcessWithExitCode (proc path args) {std_err = Inherit} "" - unless solConf.quiet $ putStrLn "Done!" + (ec, out, err) <- measureIO solConf.quiet ("Running slither on " <> fp) $ do + readCreateProcessWithExitCode (proc path args) {std_err = Inherit} "" case ec of ExitSuccess -> case eitherDecode (BSL.pack out) of diff --git a/lib/Echidna/Solidity.hs b/lib/Echidna/Solidity.hs index 8d0cce3a7..edd860480 100644 --- a/lib/Echidna/Solidity.hs +++ b/lib/Echidna/Solidity.hs @@ -24,7 +24,7 @@ import System.Directory (doesDirectoryExist, doesFileExist, findExecutable, list import System.Process (StdStream(..), readCreateProcessWithExitCode, proc, std_err) import System.Exit (ExitCode(..)) import System.FilePath (joinPath, splitDirectories, ()) -import System.IO (openFile, IOMode(..), hFlush, stdout) +import System.IO (openFile, IOMode(..)) import System.Info (os) import EVM hiding (Env, env, contract, contracts, path) @@ -46,6 +46,7 @@ import Echidna.Types.Solidity import Echidna.Types.Test (EchidnaTest(..)) import Echidna.Types.Tx (basicTx, createTxWithValue, unlimitedGasPerBlock, initialTimestamp, initialBlockNumber) import Echidna.Types.World (World(..)) +import Echidna.Utility (measureIO) -- | Given a list of source caches (SourceCaches) and an optional contract name, -- select one that includes that contract (if possible). Otherwise, use the first source @@ -88,12 +89,9 @@ compileContracts solConf fp = do stderr <- if solConf.quiet then UseHandle <$> openFile nullFilePath WriteMode else pure Inherit - unless solConf.quiet $ do - putStr $ "Compiling " <> x <> "... " - hFlush stdout - (ec, out, err) <- readCreateProcessWithExitCode - (proc path $ (solConf.cryticArgs ++ solargs) |> x) {std_err = stderr} "" - unless solConf.quiet $ putStrLn "Done!" + (ec, out, err) <- measureIO solConf.quiet ("Compiling " <> x) $ do + readCreateProcessWithExitCode + (proc path $ (solConf.cryticArgs ++ solargs) |> x) {std_err = stderr} "" case ec of ExitSuccess -> readSolcBatch "crytic-export" ExitFailure _ -> throwM $ CompileFailure out err diff --git a/lib/Echidna/Utility.hs b/lib/Echidna/Utility.hs new file mode 100644 index 000000000..d1bfc13c0 --- /dev/null +++ b/lib/Echidna/Utility.hs @@ -0,0 +1,14 @@ +module Echidna.Utility where + +import Control.Monad (unless) +import Data.Time (diffUTCTime, getCurrentTime) +import System.IO (hFlush, stdout) + +measureIO :: Bool -> String -> IO b -> IO b +measureIO quiet message action = do + unless quiet $ putStr (message <> "... ") >> hFlush stdout + t0 <- getCurrentTime + ret <- action + t1 <- getCurrentTime + unless quiet $ putStrLn $ "Done! (" <> show (diffUTCTime t1 t0) <> ")" + pure ret From 31093e478bb0ae856499549b8b96b00cda869ee3 Mon Sep 17 00:00:00 2001 From: Artur Cygan Date: Mon, 20 Mar 2023 13:42:50 +0100 Subject: [PATCH 3/3] Measure saving coverage --- src/Main.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Main.hs b/src/Main.hs index adfd22ae4..eaa6a5f38 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -56,6 +56,7 @@ import Echidna.UI import Echidna.Output.Source import Echidna.Output.Corpus import Echidna.Solidity (compileContracts, selectSourceCache) +import Echidna.Utility (measureIO) import Etherscan qualified main :: IO () @@ -127,8 +128,10 @@ main = withUtf8 $ withCP65001 $ do Nothing -> pure () - saveTxs (dir "reproducers") (filter (not . null) $ (.testReproducer) <$> campaign._tests) - saveTxs (dir "coverage") (snd <$> Set.toList campaign._corpus) + measureIO cfg.solConf.quiet "Saving test reproducers" $ + saveTxs (dir "reproducers") (filter (not . null) $ (.testReproducer) <$> campaign._tests) + measureIO cfg.solConf.quiet "Saving corpus" $ + saveTxs (dir "coverage") (snd <$> Set.toList campaign._corpus) -- TODO: Add another option to config for saving coverage report