Mentions légales du service

Skip to content
Snippets Groups Projects
runtest.py 5.92 KiB
import argparse
import subprocess
import os
import json
import signal
import re
from datetime import datetime
from pathlib import Path

parser = argparse.ArgumentParser(description="A description")
parser.add_argument(
    "pkg", help="The package which is tested. Should be packages/<pkg_name>.py e.g. packages/algpath.py")
parser.add_argument(
    "data", help="The system which is tested. Shoud be data/<test_name>.json e.g. data/linear_dense-10_all-paths_1.json")
parser.add_argument("-p", "--perf", action='store_true', default=False,
                    help="perf stat the benchmark and put the result in perflog.txt")
parser.add_argument("-t", "--timeout", nargs="?", const="300",
                    help="To run the benchmark with a timeout. Should be written as <number><unit> where <unit> may be nothing (this implicitely means seconds), s, m or h, e.g. 1h")
parser.add_argument("-m", "--mem", nargs="?", const="8G",
                    help="Maximum amount of memory used. Should be written as <number><unit> where <unit> may be nothing, K, M or G, e.g. 100M")
parser.add_argument("-n", "--norun", action='store_true',
                    default=False, help="To only generate script and command")
parser.add_argument("-c", "--core", nargs="?", const="1",
                    help="Core on which the job should be ran")
args = parser.parse_args()

pkg_path = Path(args.pkg)
data_path = Path(args.data)
tmp_data_str = f"file {data_path} does not exist"
tmp_pkg_str = f"file {pkg_path} does not exist"

assert data_path.exists(), tmp_data_str
assert pkg_path.exists(), tmp_pkg_str

data = data_path.stem
pkg = pkg_path.stem

pkg_binary_dict = {"homotopycontinuation": "julia", "sirocco": "sage",
                   "macaulay2": "M2", "algpath": "algpath", "adaptive": "packages/adaptive"}

print(f"Benchmarking {pkg} on {data_path}" + args.perf*" (with perf stat)")

print("Creating benchmark folder...")
bench_path = (Path("benchmarks").joinpath(
    *data_path.with_suffix("").parts[1:]) / pkg)
bench_path.mkdir(parents=True, exist_ok=True)

print("Generating script and command...")

auxi = Path("/".join([".." for _ in bench_path.parts]))

if pkg in ["adaptive"]:
    pkg_binary_path = auxi / pkg_binary_dict[pkg]
else:
    pkg_binary_path = pkg_binary_dict[pkg]

os.chdir(bench_path)
subprocess.run(["python3", f"{auxi / pkg_path}",
               f"{auxi / data_path}", f"{pkg_binary_path}"])
subprocess.run(["chmod", "+x", str("command.sh")])
# assert 1 == 0

if not args.norun:
    print(f"Timeout: {args.timeout}")
    print(f"Maximum amount of memory used: {args.mem}")
    info_file = open("info.json", "w")

    # Functions to parse input
    def memory_parsing(s):
        auxi = re.split('(\d+)', s)
        val = int(auxi[1])
        try:
            unit = auxi[2]
        except:
            return val
        else:
            match unit:
                case "K":
                    return val*(2**10)
                case "M":
                    return val*(2**20)
                case "G":
                    return val*(2**30)
                case _:
                    return val

    def timeout_parsing(s):
        auxi = re.split('(\d+)', s)
        val = int(auxi[1])
        try:
            unit = auxi[2]
        except:
            return val
        else:
            match unit:
                case "s":
                    return val
                case "m":
                    return val*60
                case "h":
                    return val*3600
                case _:
                    return val

    if args.timeout != None:
        timeout_s = timeout_parsing(args.timeout)
    else:
        timeout_s = None

    if args.mem != None:
        memory_b = memory_parsing(args.mem)
    else:
        memory_b = None

    memory_max = str(args.mem)
    info_dict = {
        "datetime": str(datetime.now()),
        "return code": None,
        "timeout": timeout_s,
        "timeout error": False,
        "memory": memory_b,
        "memory error": False,
        "script error": False,
        "killed manually": True,
    }

    json.dump(info_dict, info_file, indent=2)
    info_file.close()

    out_file = open("out.json", "w")
    log_file = open("log.txt", "w")

    print("Running script...")

    cmd = ["systemd-run", "--scope", "--user", "-p", f"MemoryMax={memory_b}", "-p", "MemorySwapMax=0"]*(
        memory_b != None) + ["perf", "stat", "-o", "perflog.txt"]*args.perf + ["taskset", args.core, "./command.sh"]

    p = subprocess.Popen(cmd, start_new_session=True,
                         stdout=out_file, stderr=log_file)
    try:
        _, _ = p.communicate(timeout=timeout_s)
        log_file.close()
        log_file = open("log.txt", "r")
        log = log_file.read()
        try:
            user_unit = re.findall(r"run.*scope", log)[0]
            log_systemd = subprocess.run(
                ["journalctl", "--user-unit", user_unit], capture_output=True).stdout
        except:
            log_systemd = ""

        if "A process of this unit has been killed by the OOM killer" in str(log_systemd):
            info_dict["memory error"] = True
        elif p.returncode != 0:
            info_dict["script error"] = True

    except subprocess.TimeoutExpired:
        print(f'Timeout ({args.timeout})',)
        print('Terminating the whole process group...')
        os.killpg(os.getpgid(p.pid), signal.SIGTERM)
        _, _ = p.communicate()
        info_dict["timeout error"] = True
    except:
        os.killpg(os.getpgid(p.pid), signal.SIGTERM)
        _, _ = p.communicate()
        

    out_file.close()
    log_file.close()

    info_dict["killed manually"] = False
    info_dict["return code"] = p.returncode
    info_file = open("info.json", "w")
    json.dump(info_dict, info_file, indent=2)
    info_file.close()

    out_file = open("out.json", "r")
    try:
        result = json.load(out_file)
    except:
        out_file.close()
    else:
        out_file.close()
        out_file = open("out.json", "w")
        json.dump(result, out_file, indent=2)