Skip to content

Commit

Permalink
Merge branch 'main' into embrace-uv
Browse files Browse the repository at this point in the history
  • Loading branch information
karmacoma-eth authored Dec 4, 2024
2 parents 6c3af34 + d119a5e commit c1368ef
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 7 deletions.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ dependencies = [
"sortedcontainers>=2.4.0",
"toml>=0.10.2",
"z3-solver==4.12.6.0",
"eth_hash[pysha3]>=0.7.0"
"eth_hash[pysha3]>=0.7.0",
"rich>=13.9.4"
]
dynamic = ["version"]

Expand Down
22 changes: 16 additions & 6 deletions src/halmos/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
from concurrent.futures import ThreadPoolExecutor
from copy import deepcopy
from dataclasses import asdict, dataclass
from datetime import timedelta
from enum import Enum
from importlib import metadata

from rich.status import Status
from z3 import (
Z3_OP_CONCAT,
BitVec,
Expand Down Expand Up @@ -733,14 +735,22 @@ def future_callback(future_model):
f"# of potential paths involving assertion violations: {len(future_models)} / {len(result_exs)} (--solver-threads {args.solver_threads})"
)

if args.early_exit:
while not (
len(counterexamples) > 0 or all([fm.done() for fm in future_models])
):
time.sleep(1)
# display assertion solving progress
if not args.no_status or args.early_exit:
with Status("") as status:
while True:
if args.early_exit and len(counterexamples) > 0:
break
done = sum(fm.done() for fm in future_models)
total = len(future_models)
if done == total:
break
elapsed = timedelta(seconds=int(timer.elapsed()))
status.update(f"[{elapsed}] solving queries: {done} / {total}")
time.sleep(0.1)

if args.early_exit:
thread_pool.shutdown(wait=False, cancel_futures=True)

else:
thread_pool.shutdown(wait=True)

Expand Down
6 changes: 6 additions & 0 deletions src/halmos/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@ class Config:
short="st",
)

no_status: bool = arg(
help="disable progress display",
global_default=False,
group=debugging,
)

debug: bool = arg(
help="run in debug mode",
global_default=False,
Expand Down
30 changes: 30 additions & 0 deletions src/halmos/sevm.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
from collections.abc import Callable, Iterator
from copy import deepcopy
from dataclasses import dataclass, field
from datetime import timedelta
from functools import reduce
from timeit import default_timer as timer
from typing import (
Any,
ForwardRef,
Expand All @@ -14,6 +16,7 @@
)

from eth_hash.auto import keccak
from rich.status import Status
from z3 import (
UGE,
UGT,
Expand Down Expand Up @@ -144,6 +147,7 @@

# TODO: make this configurable
MAX_MEMORY_SIZE = 2**20
PULSE_INTERVAL = 2**13

FOUNDRY_CALLER = 0x1804C8AB1F12E6BBF3894D4083F33E07309D1F38
FOUNDRY_ORIGIN = FOUNDRY_CALLER
Expand Down Expand Up @@ -1655,6 +1659,10 @@ class Worklist:
def __init__(self):
self.stack = []

# status data
self.completed_paths = 0
self.start_time = timer()

def push(self, ex: Exec, step: int):
self.stack.append(WorklistItem(ex, step))

Expand Down Expand Up @@ -2061,6 +2069,7 @@ def callback(new_ex: Exec, stack, step_id):
if subcall.is_stuck():
# internal errors abort the current path,
# so we don't need to add it to the worklist
stack.completed_paths += 1
yield new_ex
return

Expand Down Expand Up @@ -2402,6 +2411,7 @@ def callback(new_ex: Exec, stack, step_id):

if subcall.is_stuck():
# internal errors abort the current path,
stack.completed_paths += 1
yield new_ex
return

Expand Down Expand Up @@ -2630,13 +2640,18 @@ def gen_nested_ite(curr: int) -> BitVecRef:
return ZeroExt(248, gen_nested_ite(0))

def run(self, ex0: Exec) -> Iterator[Exec]:
with Status("") as status:
yield from self._run(ex0, status)

def _run(self, ex0: Exec, status: Status) -> Iterator[Exec]:
step_id: int = 0
stack: Worklist = Worklist()
stack.push(ex0, 0)

def finalize(ex: Exec):
# if it's at the top-level, there is no callback; yield the current execution state
if ex.callback is None:
stack.completed_paths += 1
yield ex

# otherwise, execute the callback to return to the parent execution context
Expand All @@ -2651,6 +2666,20 @@ def finalize(ex: Exec):
prev_step_id: int = item.step
step_id += 1

# display progress
if not self.options.no_status and step_id % PULSE_INTERVAL == 0:
elapsed = timer() - stack.start_time
speed = step_id / elapsed

# hh:mm:ss
elapsed_fmt = timedelta(seconds=int(elapsed))

status.update(
f"[{elapsed_fmt}] {speed:.0f} ops/s"
f" | completed paths: {stack.completed_paths}"
f" | outstanding paths: {len(stack)}"
)

if not ex.path.is_activated():
ex.path.activate()

Expand Down Expand Up @@ -3121,6 +3150,7 @@ def finalize(ex: Exec):
if not ex.is_halted():
# return data shouldn't be None, as it is considered being stuck
ex.halt(data=ByteVec(), error=err)
stack.completed_paths += 1
yield ex # early exit; do not call finalize()
continue

Expand Down

0 comments on commit c1368ef

Please sign in to comment.