Skip to content

Commit

Permalink
Add tool_pid argument to parent_setup_fn
Browse files Browse the repository at this point in the history
  • Loading branch information
charmoniumQ committed Feb 13, 2024
1 parent bae5804 commit b8af129
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 10 deletions.
23 changes: 20 additions & 3 deletions benchexec/baseexecutor.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,24 @@ def handle_basic_executor_options(options, parser):
class BaseExecutor(object):
"""Class for starting and handling processes."""

def __init__(self):
def __init__(
self,
child_setup_fn=None,
parent_setup_fn=None,
parent_cleanup_fn=None,
):
"""
See BaseExecutor._start_execution for a description of the parameters.
"""
self.child_setup_fn = (
child_setup_fn if child_setup_fn is None else util.dummy_fn
)
self.parent_setup_fn = (
parent_setup_fn if parent_setup_fn is None else util.dummy_fn
)
self.parent_cleanup_fn = (
parent_cleanup_fn if parent_cleanup_fn is None else util.dummy_fn
)
self.PROCESS_KILLED = False
# killing process is triggered asynchronously, need a lock for synchronization
self.SUB_PROCESS_PIDS_LOCK = threading.Lock()
Expand All @@ -75,8 +92,8 @@ def _start_execution(
parent_cleanup_fn,
):
"""Actually start the tool and the measurements.
@param parent_setup_fn a function without parameters that is called in the parent process
immediately before the tool is started
@param parent_setup_fn a function that is called in the parent process
immediately before the tool is started. The keyword-arguments passed may differ in subclasses.
@param child_setup_fn a function without parameters that is called in the child process
before the tool is started
@param parent_cleanup_fn a function that is called in the parent process
Expand Down
26 changes: 20 additions & 6 deletions benchexec/containerexecutor.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,12 +346,16 @@ def __init__(
self._uid = (
uid
if uid is not None
else container.CONTAINER_UID if container_system_config else os.getuid()
else container.CONTAINER_UID
if container_system_config
else os.getuid()
)
self._gid = (
gid
if gid is not None
else container.CONTAINER_GID if container_system_config else os.getgid()
else container.CONTAINER_GID
if container_system_config
else os.getgid()
)
self._allow_network = network_access
self._env_override = {}
Expand Down Expand Up @@ -475,9 +479,9 @@ def execute_run(
cgroups=cgroups,
output_dir=output_dir,
result_files_patterns=result_files_patterns,
child_setup_fn=util.dummy_fn,
parent_setup_fn=util.dummy_fn,
parent_cleanup_fn=util.dummy_fn,
child_setup_fn=self.child_setup_fn,
parent_setup_fn=self.parent_setup_fn,
parent_cleanup_fn=self.parent_cleanup_fn,
)

with self.SUB_PROCESS_PIDS_LOCK:
Expand Down Expand Up @@ -647,6 +651,9 @@ def grandchild():
# Signal readiness to parent by sending our PID
# and wait until parent is also ready
os.write(to_parent, str(my_outer_pid).encode())
logging.debug(
"Grandchild: Sent my pid; waiting for grandparent to be ready"
)
received = os.read(from_parent, 1)
assert received == MARKER_PARENT_COMPLETED, received

Expand Down Expand Up @@ -974,11 +981,18 @@ def check_child_exit_code():
if use_cgroup_ns:
cgroups = cgroups.create_fresh_child_cgroup_for_delegation()

# Do parent setup
logging.debug("Calling parent_setup_fn)")
parent_setup = parent_setup_fn(
child_pid=child_pid,
grandchild_pid=grandchild_pid,
)

# start measurements
cgroups.add_task(grandchild_pid)
parent_setup = parent_setup_fn()

# Signal grandchild that setup is finished
logging.debug("Telling grandchild we are ready")
os.write(to_grandchild, MARKER_PARENT_COMPLETED)

# Copy file descriptor, otherwise we could not close from_grandchild in
Expand Down
2 changes: 1 addition & 1 deletion benchexec/runexecutor.py
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@ def _execute(
# Disable energy measurements because we use only parts of a CPU
packages = None

def preParent():
def preParent(**kwargs):
"""Setup that is executed in the parent process immediately before the actual tool is started."""
# start measurements
if self._energy_measurement is not None and packages:
Expand Down
17 changes: 17 additions & 0 deletions benchexec/test_runexecutor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,23 @@ def test_result_file_log_limit(self):
count_msg = next(msg for msg in log.output if " output files matched" in msg)
self.assertIn(f"{file_count} output files matched", count_msg)

def test_parent_fns(self):
if not os.path.exists("/bin/sh"):
self.skipTest("missing /bin/sh")

def parent_setup_fn(*, tool_pid, **kwargs):
# I don't want to require psutil just for this
# I'll just read the procfs
assert os.path.exists("/proc/{tool_pid}")
return 12345

def parent_cleanup_fn(parent_setup, exit_code, path):
assert parent_setup == 12345
assert exit_code == 123

self.setUp(parent_setup_fn=parent_setup_fn, parent_cleanup_fn=parent_cleanup_fn)
self.execute_run("/bin/sh", "-c", "exit 123")

def test_file_count_limit(self):
if not os.path.exists("/bin/sh"):
self.skipTest("missing /bin/sh")
Expand Down

0 comments on commit b8af129

Please sign in to comment.