Skip to content

Commit

Permalink
Merge branch 'main' into resources-update-core-assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
schroeding committed Dec 1, 2024
2 parents 0fcb39b + b3debbe commit 93199de
Show file tree
Hide file tree
Showing 26 changed files with 395 additions and 76 deletions.
8 changes: 4 additions & 4 deletions .appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# This file is part of BenchExec, a framework for reliable benchmarking:
# https://github.com/sosy-lab/benchexec
#
# SPDX-FileCopyrightText: 2007-2020 Dirk Beyer <https://www.sosy-lab.org>
# SPDX-FileCopyrightText: 2007-2024 Dirk Beyer <https://www.sosy-lab.org>
#
# SPDX-License-Identifier: Apache-2.0

image: "Visual Studio 2022"

environment:
matrix:
- PYTHON: "C:\\Python38"
- PYTHON: "C:\\Python39"
- PYTHON: "C:\\Python312"

build: off

install:
- set PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%
# The package installation via our setup.py file uses easy_install, which fails to correctly install lxml.
# As some Python environments don't have lxml preinstalled, install it here to avoid errors during the execution in those cases.
- python -m pip install --user ".[dev]"

test_script:
Expand Down
7 changes: 2 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,5 @@
/BenchExec.egg-info
/.coverage*
/coverage
/.pytype
.idea
node_modules/
/contrib/vcloud/lib/ivy*.jar
/contrib/vcloud/lib/vcloud-jars
/coverage.xml
/.idea
93 changes: 75 additions & 18 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,54 +33,105 @@ stages:
- images
- test

.unit-tests: &unit-tests
.tests:cgroupsv1: &tests-cgroupsv1
stage: test
before_script:
# Create user, we do not want to test as root
- adduser --disabled-login --gecos "" $PRIMARY_USER
# Activate coverage for subprocesses
- printf 'import coverage\ncoverage.process_startup()\n' > "/usr/local/lib/python${PYTHON_VERSION}/site-packages/sitecustomize.py"
# Give $PRIMARY_USER permission to create cgroups
- test/for_each_of_my_cgroups.sh chgrp $PRIMARY_USER
- test/for_each_of_my_cgroups.sh chmod g+w $PRIMARY_USER
# Install pytest-cov (does not work with --user)
- pip install pytest-cov
# Install BenchExec with `dev` dependencies
- sudo -u $PRIMARY_USER pip install --user ".[dev]"
# Start lxcfs
- lxcfs /var/lib/lxcfs &
script:
- sudo -u $PRIMARY_USER
COVERAGE_PROCESS_START=.coveragerc
coverage run -m pytest
- sudo -u $PRIMARY_USER python -m pytest -ra --cov
after_script:
- sudo -u $PRIMARY_USER coverage combine
- sudo -u $PRIMARY_USER coverage report
- sudo -u $PRIMARY_USER coverage xml
tags:
- privileged
coverage: '/TOTAL.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
artifacts:
paths:
- coverage.xml
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml

tests:cgroupsv1:python-3.8:
<<: *tests-cgroupsv1
variables:
PYTHON_VERSION: '3.8'

tests:cgroupsv1:python-3.9:
<<: *tests-cgroupsv1
variables:
PYTHON_VERSION: '3.9'

tests:cgroupsv1:python-3.10:
<<: *tests-cgroupsv1
variables:
PYTHON_VERSION: '3.10'

tests:cgroupsv1:python-3.11:
<<: *tests-cgroupsv1
variables:
PYTHON_VERSION: '3.11'

tests:cgroupsv1:python-3.12:
<<: *tests-cgroupsv1
variables:
PYTHON_VERSION: '3.12'

unit-tests:python-3.8:
<<: *unit-tests
.tests:cgroupsv2: &tests-cgroupsv2
stage: test
before_script:
# Prepare cgroups
- test/setup_cgroupsv2_in_container.sh
# Install pytest-cov (does not work with --user)
- pip install pytest-cov
# Install BenchExec with `dev` dependencies
- pip install --user ".[dev]"
script:
- python -m pytest -ra --cov
after_script:
- coverage xml
tags:
- cgroupsv2
coverage: '/TOTAL.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml

tests:cgroupsv2:python-3.8:
<<: *tests-cgroupsv2
variables:
PYTHON_VERSION: '3.8'

unit-tests:python-3.9:
<<: *unit-tests
tests:cgroupsv2:python-3.9:
<<: *tests-cgroupsv2
variables:
PYTHON_VERSION: '3.9'

unit-tests:python-3.10:
<<: *unit-tests
tests:cgroupsv2:python-3.10:
<<: *tests-cgroupsv2
variables:
PYTHON_VERSION: '3.10'

unit-tests:python-3.11:
<<: *unit-tests
tests:cgroupsv2:python-3.11:
<<: *tests-cgroupsv2
variables:
PYTHON_VERSION: '3.11'

tests:cgroupsv2:python-3.12:
<<: *tests-cgroupsv2
variables:
PYTHON_VERSION: '3.12'

# Static checks
check-format:
stage: test
Expand Down Expand Up @@ -228,3 +279,9 @@ build-docker:test:python-3.11:
variables:
DOCKERFILE: test/Dockerfile.python-3.11
IMAGE: test:python-3.11

build-docker:test:python-3.12:
extends: .build-docker
variables:
DOCKERFILE: test/Dockerfile.python-3.12
IMAGE: test:python-3.12
9 changes: 5 additions & 4 deletions benchexec/baseexecutor.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ def _start_execution(
@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
immediately after the tool terminated, with three parameters:
immediately after the tool terminated, with four parameters:
the result of parent_setup_fn, the result of the executed process as ProcessExitCode,
and the base path for looking up files as parameter values
the base path for looking up files as parameter values,
and the cgroup of the tool
@return: a tuple of PID of process and a blocking function, which waits for the process
and a triple of the exit code and the resource usage of the process
and the result of parent_cleanup_fn (do not use os.wait)
Expand Down Expand Up @@ -125,11 +126,11 @@ def wait_and_get_result():
exitcode, ru_child = self._wait_for_process(p.pid, args[0])

parent_cleanup = parent_cleanup_fn(
parent_setup, util.ProcessExitCode.from_raw(exitcode), ""
parent_setup, util.ProcessExitCode.from_raw(exitcode), "", cgroups
)
return exitcode, ru_child, parent_cleanup

return p.pid, wait_and_get_result
return p.pid, cgroups, wait_and_get_result

def _wait_for_process(self, pid, name):
"""Wait for the given process to terminate.
Expand Down
8 changes: 6 additions & 2 deletions benchexec/cgroups.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,11 @@ def handle_errors(self, critical_cgroups):
pass

@abstractmethod
def create_fresh_child_cgroup(self, subsystems):
def create_fresh_child_cgroup(self, subsystems, prefix=None):
pass

@abstractmethod
def create_fresh_child_cgroup_for_delegation(self):
pass

@abstractmethod
Expand Down Expand Up @@ -352,7 +356,7 @@ def add_task(self, pid):
def kill_all_tasks(self):
pass

def create_fresh_child_cgroup(self, subsystems):
def create_fresh_child_cgroup(self, subsystems, prefix=None):
return self

def create_fresh_child_cgroup_for_delegation(self):
Expand Down
16 changes: 14 additions & 2 deletions benchexec/cgroupsv1.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ def get_group_name(gid):
else:
sys.exit(_ERROR_MSG_OTHER) # e.g., subsystem not mounted

def create_fresh_child_cgroup(self, subsystems):
def create_fresh_child_cgroup(self, subsystems, prefix=CGROUP_NAME_PREFIX):
"""
Create child cgroups of the current cgroup for at least the given subsystems.
@return: A Cgroup instance representing the new child cgroup(s).
Expand All @@ -374,7 +374,7 @@ def create_fresh_child_cgroup(self, subsystems):
]
continue

cgroup = tempfile.mkdtemp(prefix=CGROUP_NAME_PREFIX, dir=parentCgroup)
cgroup = tempfile.mkdtemp(prefix=prefix, dir=parentCgroup)
createdCgroupsPerSubsystem[subsystem] = cgroup
createdCgroupsPerParent[parentCgroup] = cgroup

Expand All @@ -395,6 +395,18 @@ def copy_parent_to_child(name):

return CgroupsV1(createdCgroupsPerSubsystem)

def create_fresh_child_cgroup_for_delegation(self, prefix="delegate_"):
"""
Create a child cgroup with all controllers.
On cgroupsv1 there is no difference to a regular child cgroup.
"""
child_cgroup = self.create_fresh_child_cgroup(self.subsystems.keys(), prefix)
assert (
self.subsystems.keys() == child_cgroup.subsystems.keys()
), "delegation failed for at least one controller"

return child_cgroup

def add_task(self, pid):
"""
Add a process to the cgroups represented by this instance.
Expand Down
21 changes: 14 additions & 7 deletions benchexec/cgroupsv2.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def initialize():
else:
# No usable cgroup. We might still be able to continue if we actually
# do not require cgroups for benchmarking. So we do not fail here
# but return an instance that will on produce an error later.
# but return an instance that will only produce an error later.
return CgroupsV2({})

# Now we are the only process in this cgroup. In order to make it usable for
Expand Down Expand Up @@ -562,6 +562,12 @@ def raise_error(e):
# On cgroupsv2, frozen processes can still be killed, so this is all we need to
# do.
util.write_file("1", self.path, "cgroup.freeze", force=True)
# According to Lennart Poettering, this is not enough:
# https://lwn.net/Articles/855312/
# But we never encountered any problems, and new kernels have cgroup.kill
# anyway (since 5.14). So it is probably not worth to fix it and instead
# we just eventually require kernel 5.14 for cgroupsv2
# (before 5.19 memory measurements are missing as well).
keep_child = self._delegated_to.path if self._delegated_to else None
for child_cgroup in recursive_child_cgroups(self.path):
kill_all_tasks_in_cgroup(child_cgroup)
Expand All @@ -587,12 +593,13 @@ def read_max_mem_usage(self):
return None

def _read_pressure_stall_information(self, subsystem):
for line in open(self.path / (subsystem + ".pressure")):
if line.startswith("some "):
for item in line.split(" ")[1:]:
k, v = item.split("=")
if k == "total":
return Decimal(v) / 1_000_000
with open(self.path / (subsystem + ".pressure")) as pressure_file:
for line in pressure_file:
if line.startswith("some "):
for item in line.split(" ")[1:]:
k, v = item.split("=")
if k == "total":
return Decimal(v) / 1_000_000
return None

def read_mem_pressure(self):
Expand Down
13 changes: 12 additions & 1 deletion benchexec/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@
DIR_MODES = [DIR_HIDDEN, DIR_READ_ONLY, DIR_OVERLAY, DIR_FULL_ACCESS]
"""modes how a directory can be mounted in the container"""

LXCFS_PROC_DIR = b"/var/lib/lxcfs/proc"
LXCFS_BASE_DIR = b"/var/lib/lxcfs"
LXCFS_PROC_DIR = LXCFS_BASE_DIR + b"/proc"
SYS_CPU_DIR = b"/sys/devices/system/cpu"

_CLONE_NESTED_CALLBACK = ctypes.CFUNCTYPE(ctypes.c_int)
"""Type for callback of execute_in_namespace, nested in our primary callback."""
Expand Down Expand Up @@ -968,6 +970,15 @@ def setup_container_system_config(basedir, mountdir, dir_modes):
{"h": CONTAINER_HOME, "p": os.path.dirname(CONTAINER_HOME)},
)

# Virtualize CPU info with LXCFS if directory is not hidden nor full-access
if (
os.access(LXCFS_BASE_DIR + SYS_CPU_DIR, os.R_OK)
and determine_directory_mode(dir_modes, SYS_CPU_DIR) == DIR_READ_ONLY
):
make_bind_mount(
LXCFS_BASE_DIR + SYS_CPU_DIR, mountdir + SYS_CPU_DIR, private=True
)


def is_container_system_config_file(file):
"""Determine whether a given file is one of the files created by
Expand Down
Loading

0 comments on commit 93199de

Please sign in to comment.