diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md index da2f89824af81..696e9542253a4 100644 --- a/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixos/doc/manual/release-notes/rl-2405.section.md @@ -92,6 +92,11 @@ Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` for Pi - [Handheld Daemon](https://github.com/hhd-dev/hhd), support for gaming handhelds like the Legion Go, ROG Ally, and GPD Win. Available as [services.handheld-daemon](#opt-services.handheld-daemon.enable). +- [BenchExec](https://github.com/sosy-lab/benchexec), a framework for reliable benchmarking and resource measurement, available as [programs.benchexec](#opt-programs.benchexec.enable), + As well as related programs + [CPU Energy Meter](https://github.com/sosy-lab/cpu-energy-meter), available as [programs.cpu-energy-meter](#opt-programs.cpu-energy-meter.enable), and + [PQoS Wrapper](https://gitlab.com/sosy-lab/software/pqos-wrapper), available as [programs.pqos-wrapper](#opt-programs.pqos-wrapper.enable). + - [Guix](https://guix.gnu.org), a functional package manager inspired by Nix. Available as [services.guix](#opt-services.guix.enable). - [PhotonVision](https://photonvision.org/), a free, fast, and easy-to-use computer vision solution for the FIRSTĀ® Robotics Competition. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index b14b83a8119ac..f446fa13ea26f 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -158,6 +158,7 @@ ./programs/bash/ls-colors.nix ./programs/bash/undistract-me.nix ./programs/bcc.nix + ./programs/benchexec.nix ./programs/browserpass.nix ./programs/calls.nix ./programs/captive-browser.nix @@ -167,6 +168,7 @@ ./programs/chromium.nix ./programs/clash-verge.nix ./programs/cnping.nix + ./programs/cpu-energy-meter.nix ./programs/command-not-found/command-not-found.nix ./programs/coolercontrol.nix ./programs/criu.nix @@ -250,6 +252,7 @@ ./programs/pantheon-tweaks.nix ./programs/partition-manager.nix ./programs/plotinus.nix + ./programs/pqos-wrapper.nix ./programs/projecteur.nix ./programs/proxychains.nix ./programs/qdmr.nix diff --git a/nixos/modules/programs/benchexec.nix b/nixos/modules/programs/benchexec.nix new file mode 100644 index 0000000000000..652670c117ea3 --- /dev/null +++ b/nixos/modules/programs/benchexec.nix @@ -0,0 +1,98 @@ +{ lib +, pkgs +, config +, options +, ... +}: +let + cfg = config.programs.benchexec; + opt = options.programs.benchexec; + + filterUsers = x: + if builtins.isString x then config.users.users ? ${x} else + if builtins.isInt x then x else + throw "filterUsers expects string (username) or int (UID)"; + + uid = x: + if builtins.isString x then config.users.users.${x}.uid else + if builtins.isInt x then x else + throw "uid expects string (username) or int (UID)"; +in +{ + options.programs.benchexec = { + enable = lib.mkEnableOption "BenchExec"; + package = lib.options.mkPackageOption pkgs "benchexec" { }; + + users = lib.options.mkOption { + type = with lib.types; listOf (either str int); + description = '' + Users that intend to use BenchExec. + Provide usernames of users that are configured via {option}`${options.users.users}` as string, + and UIDs of "mutable users" as integers. + Control group delegation will be configured via systemd. + For more information, see . + ''; + default = [ ]; + example = lib.literalExpression '' + [ + "alice" # username of a user configured via ${options.users.users} + 1007 # UID of a mutable user + ] + ''; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = (map + (user: { + assertion = config.users.users ? ${user}; + message = '' + The user '${user}' intends to use BenchExec (via `${opt.users}`), but is not configured via `${options.users.users}`. + ''; + }) + (builtins.filter builtins.isString cfg.users) + ) ++ (map + (id: { + assertion = config.users.mutableUsers; + message = '' + The user with UID '${id}' intends to use BenchExec (via `${opt.users}`), but mutable users are disabled via `${options.users.mutableUsers}`. + ''; + }) + (builtins.filter builtins.isInt cfg.users) + ) ++ [ + { + assertion = config.systemd.enableUnifiedCgroupHierarchy == true; + message = '' + The BenchExec module `${opt.enable}` only supports control groups 2 (`${options.systemd.enableUnifiedCgroupHierarchy} = true`). + ''; + } + ]; + + environment.systemPackages = [ cfg.package ]; + + # See . + systemd.services = builtins.listToAttrs (map + (user: { + name = "user@${builtins.toString (uid user)}"; + value = { + serviceConfig.Delegate = "yes"; + overrideStrategy = "asDropin"; + }; + }) + (builtins.filter filterUsers cfg.users)); + + # See . + virtualisation.lxc.lxcfs.enable = lib.mkDefault true; + + # See . + programs = { + cpu-energy-meter.enable = lib.mkDefault true; + pqos-wrapper.enable = lib.mkDefault true; + }; + + # See . + security.unprivilegedUsernsClone = true; + }; + + meta.maintainers = with lib.maintainers; [ lorenzleutgeb ]; +} diff --git a/nixos/modules/programs/cpu-energy-meter.nix b/nixos/modules/programs/cpu-energy-meter.nix new file mode 100644 index 0000000000000..653ec067492d7 --- /dev/null +++ b/nixos/modules/programs/cpu-energy-meter.nix @@ -0,0 +1,27 @@ +{ config +, lib +, pkgs +, ... +}: { + options.programs.cpu-energy-meter = { + enable = lib.mkEnableOption "CPU Energy Meter"; + package = lib.mkPackageOption pkgs "cpu-energy-meter" { }; + }; + + config = + let + cfg = config.programs.cpu-energy-meter; + in + lib.mkIf cfg.enable { + hardware.cpu.x86.msr.enable = true; + + security.wrappers.${cfg.package.meta.mainProgram} = { + owner = "nobody"; + group = config.hardware.cpu.x86.msr.group; + source = lib.getExe cfg.package; + capabilities = "cap_sys_rawio=ep"; + }; + }; + + meta.maintainers = with lib.maintainers; [ lorenzleutgeb ]; +} diff --git a/nixos/modules/programs/pqos-wrapper.nix b/nixos/modules/programs/pqos-wrapper.nix new file mode 100644 index 0000000000000..82023e67a2ae2 --- /dev/null +++ b/nixos/modules/programs/pqos-wrapper.nix @@ -0,0 +1,27 @@ +{ config +, lib +, pkgs +, ... +}: +let + cfg = config.programs.pqos-wrapper; +in +{ + options.programs.pqos-wrapper = { + enable = lib.mkEnableOption "PQoS Wrapper for BenchExec"; + package = lib.mkPackageOption pkgs "pqos-wrapper" { }; + }; + + config = lib.mkIf cfg.enable { + hardware.cpu.x86.msr.enable = true; + + security.wrappers.${cfg.package.meta.mainProgram} = { + owner = "nobody"; + group = config.hardware.cpu.x86.msr.group; + source = lib.getExe cfg.package; + capabilities = "cap_sys_rawio=eip"; + }; + }; + + meta.maintainers = with lib.maintainers; [ lorenzleutgeb ]; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index c6ec2474e6052..ccde1a9feb2ad 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -144,6 +144,7 @@ in { bcachefs = handleTestOn ["x86_64-linux" "aarch64-linux"] ./bcachefs.nix {}; beanstalkd = handleTest ./beanstalkd.nix {}; bees = handleTest ./bees.nix {}; + benchexec = handleTest ./benchexec.nix {}; binary-cache = handleTest ./binary-cache.nix {}; bind = handleTest ./bind.nix {}; bird = handleTest ./bird.nix {}; diff --git a/nixos/tests/benchexec.nix b/nixos/tests/benchexec.nix new file mode 100644 index 0000000000000..3fc9ebc2c35f5 --- /dev/null +++ b/nixos/tests/benchexec.nix @@ -0,0 +1,54 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: +let + user = "alice"; +in +{ + name = "benchexec"; + + nodes.benchexec = { + imports = [ ./common/user-account.nix ]; + + programs.benchexec = { + enable = true; + users = [ user ]; + }; + }; + + testScript = { ... }: + let + runexec = lib.getExe' pkgs.benchexec "runexec"; + echo = builtins.toString pkgs.benchexec; + test = lib.getExe (pkgs.writeShellApplication rec { + name = "test"; + meta.mainProgram = name; + text = "echo '${echo}'"; + }); + wd = "/tmp"; + stdout = "${wd}/runexec.out"; + stderr = "${wd}/runexec.err"; + in + '' + start_all() + machine.wait_for_unit("multi-user.target") + benchexec.succeed(''''\ + systemd-run \ + --property='StandardOutput=file:${stdout}' \ + --property='StandardError=file:${stderr}' \ + --unit=runexec --wait --user --machine='${user}@' \ + --working-directory ${wd} \ + '${runexec}' \ + --debug \ + --read-only-dir / \ + --hidden-dir /home \ + '${test}' \ + '''') + benchexec.succeed("grep -s '${echo}' ${wd}/output.log") + benchexec.succeed("test \"$(grep -Ec '((start|wall|cpu)time|memory)=' ${stdout})\" = 4") + benchexec.succeed("! grep -E '(WARNING|ERROR)' ${stderr}") + ''; + + interactive.nodes.benchexec.services.kmscon = { + enable = true; + fonts = [{ name = "Fira Code"; package = pkgs.fira-code; }]; + }; +}) diff --git a/pkgs/by-name/be/benchexec/package.nix b/pkgs/by-name/be/benchexec/package.nix new file mode 100644 index 0000000000000..abc04b97cb8de --- /dev/null +++ b/pkgs/by-name/be/benchexec/package.nix @@ -0,0 +1,62 @@ +{ lib +, fetchFromGitHub +, python3 +, libseccomp +, nixosTests +, testers +, benchexec +}: +python3.pkgs.buildPythonApplication rec { + pname = "benchexec"; + version = "3.21"; + + src = fetchFromGitHub { + owner = "sosy-lab"; + repo = "benchexec"; + rev = version; + hash = "sha256-bE3brmmLHZQakDKvd47I1hm9Dcsu6DrSeJyjWWtEZWI="; + }; + + pyproject = true; + + nativeBuildInputs = with python3.pkgs; [ setuptools ]; + + # NOTE: CPU Energy Meter is not added, + # because BenchExec should call the wrapper configured + # via `security.wrappers.cpu-energy-meter` + # in `programs.cpu-energy-meter`, which will have the required + # capabilities to access MSR. + # If we add `cpu-energy-meter` here, BenchExec will instead call an executable + # without `CAP_SYS_RAWIO` and fail. + propagatedBuildInputs = with python3.pkgs; [ + coloredlogs + lxml + pystemd + pyyaml + ]; + + makeWrapperArgs = [ "--set-default LIBSECCOMP ${lib.getLib libseccomp}/lib/libseccomp.so" ]; + + passthru.tests = + let + testVersion = result: testers.testVersion { + command = "${result} --version"; + package = benchexec; + }; + in + { + nixos = nixosTests.benchexec; + benchexec-version = testVersion "benchexec"; + runexec-version = testVersion "runexec"; + table-generator-version = testVersion "table-generator"; + containerexec-version = testVersion "containerexec"; + }; + + meta = with lib; { + description = "A Framework for Reliable Benchmarking and Resource Measurement."; + homepage = "https://github.com/sosy-lab/benchexec"; + maintainers = with maintainers; [ lorenzleutgeb ]; + license = licenses.asl20; + mainProgram = "benchexec"; + }; +} diff --git a/pkgs/by-name/cp/cpu-energy-meter/package.nix b/pkgs/by-name/cp/cpu-energy-meter/package.nix new file mode 100644 index 0000000000000..6d31da594645c --- /dev/null +++ b/pkgs/by-name/cp/cpu-energy-meter/package.nix @@ -0,0 +1,40 @@ +{ lib +, stdenv +, fetchFromGitHub +, libcap +}: +stdenv.mkDerivation rec { + pname = "cpu-energy-meter"; + version = "1.2"; + + src = fetchFromGitHub { + owner = "sosy-lab"; + repo = "cpu-energy-meter"; + rev = version; + hash = "sha256-QW65Z8mRYLHcyLeOtNAHjwPNWAUP214wqIYclK+whFw="; + }; + + postPatch = '' + substituteInPlace Makefile \ + --replace "DESTDIR :=" "DESTDIR := $out" \ + --replace "PREFIX := /usr/local" "PREFIX :=" + ''; + + buildInputs = [ libcap ]; + + env.NIX_CFLAGS_COMPILE = "-fcommon"; + + postInstall = '' + install -Dm444 -t $out/etc/udev/rules.d $src/debian/additional_files/59-msr.rules + ''; + + meta = with lib; { + description = "A tool for measuring energy consumption of Intel CPUs"; + homepage = "https://github.com/sosy-lab/cpu-energy-meter"; + changelog = "https://github.com/sosy-lab/cpu-energy-meter/blob/main/CHANGELOG.md"; + maintainers = with maintainers; [ lorenzleutgeb ]; + license = licenses.bsd3; + platforms = [ "x86_64-linux" ]; + mainProgram = "cpu-energy-meter"; + }; +} diff --git a/pkgs/by-name/pq/pqos-wrapper/package.nix b/pkgs/by-name/pq/pqos-wrapper/package.nix new file mode 100644 index 0000000000000..d0235fedc3a71 --- /dev/null +++ b/pkgs/by-name/pq/pqos-wrapper/package.nix @@ -0,0 +1,28 @@ +{ lib +, intel-cmt-cat +, fetchFromGitLab +, python3 +}: +python3.pkgs.buildPythonApplication rec { + pname = "pqos-wrapper"; + version = "unstable-2022-01-31"; + + src = fetchFromGitLab { + group = "sosy-lab"; + owner = "software"; + repo = pname; + rev = "ce816497a07dcb4b931652b98359e4601a292b15"; + hash = "sha256-SaYr6lVucpJjVtGgxRbDGYbOoBwdfEDVKtvD+M1L0o4="; + }; + + makeWrapperArgs = [ "--prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath [ intel-cmt-cat ]}" ]; + + meta = with lib; { + description = "Wrapper for Intel PQoS for the purpose of using it in BenchExec"; + homepage = "https://gitlab.com/sosy-lab/software/pqos-wrapper"; + maintainers = with maintainers; [ lorenzleutgeb ]; + license = licenses.asl20; + platforms = [ "x86_64-linux" ]; + mainProgram = "pqos_wrapper"; + }; +}