def set_cmdline_args(args: argparse.Namespace):
    print(args)
    global _MP_QUEUE
    if _MP_QUEUE:
        _MP_QUEUE.put(args.ssh_port)  # check that we don't get a conflict
    if args.interact and (args.internal_shard or args.internal_num_shards
                          or args.parallel_jobs):
        boot_cheribsd.failure("Cannot use --interact with ")
        sys.exit()
def flush_thread(f, qemu: pexpect.spawn):
    while True:
        if f:
            f.flush()
        i = qemu.expect([pexpect.TIMEOUT, "KDB: enter:"], timeout=30)
        if boot_cheribsd.PRETEND:
            time.sleep(1)
        elif i == 1:
            boot_cheribsd.failure("GOT KERNEL PANIC!", exit=False)
            boot_cheribsd.debug_kernel_panic(qemu)
            global KERNEL_PANIC
            KERNEL_PANIC = True
Example #3
0
def run_noop_test(qemu: pexpect.spawn, args: argparse.Namespace):
    import boot_cheribsd
    boot_cheribsd.success("Booted successfully")
    boot_cheribsd.run_cheribsd_command(qemu, "mount_smbfs --help", cheri_trap_fatal=False)
    boot_cheribsd.run_cheribsd_command(qemu, "/libexec/ld-cheri-elf.so.1 --help")
    poweroff_start = datetime.datetime.now()
    qemu.sendline("poweroff")
    i = qemu.expect(["Uptime:", pexpect.TIMEOUT, pexpect.EOF] + boot_cheribsd.FATAL_ERROR_MESSAGES, timeout=20)
    if i != 0:
        boot_cheribsd.failure("Poweroff " + ("timed out" if i == 1 else "failed"))
        return False
    i = qemu.expect([pexpect.TIMEOUT, pexpect.EOF], timeout=20)
    if i == 0:
        boot_cheribsd.failure("QEMU didn't exit after shutdown!")
        return False
    boot_cheribsd.success("Poweroff took: ", datetime.datetime.now() - poweroff_start)
    return True
def run_tests_impl(qemu: boot_cheribsd.CheriBSDInstance,
                   args: argparse.Namespace, tempdir: str):
    qemu.EXIT_ON_KERNEL_PANIC = False  # since we run multiple threads we shouldn't use sys.exit()
    print("PID of QEMU:", qemu.pid)
    port = args.ssh_port
    user = "******"  # TODO: run these tests as non-root!
    libcxx_dir = Path(args.build_dir)
    (libcxx_dir / "tmp").mkdir(exist_ok=True)
    # TODO: move this to boot_cheribsd.py
    config_contents = """
Host cheribsd-test-instance
        User {user}
        HostName localhost
        Port {port}
        IdentityFile {ssh_key}
        # avoid errors due to changed host key:
        UserKnownHostsFile /dev/null
        StrictHostKeyChecking no
        # faster connection by reusing the existing one:
        ControlPath {home}/.ssh/controlmasters/%r@%h:%p
        # ConnectTimeout 30
        # ConnectionAttempts 3
        ControlMaster auto
""".format(user=user,
           port=port,
           ssh_key=Path(args.ssh_key).with_suffix(""),
           home=Path.home())
    # print("Writing ssh config: ", config_contents)
    with Path(tempdir, "config").open("w") as c:
        c.write(config_contents)
    Path(Path.home(), "controlmasters").mkdir(exist_ok=True)
    boot_cheribsd.run_host_command(["cat", str(Path(tempdir, "config"))])
    # Check that the config file works:
    boot_cheribsd.run_host_command([
        "ssh", "-F",
        str(Path(tempdir, "config")), "cheribsd-test-instance", "-p",
        str(port), "--", "echo", "connection successful"
    ],
                                   cwd=str(libcxx_dir))

    executor = 'SSHExecutorWithNFSMount("cheribsd-test-instance", username="******", port={port}, nfs_dir="{host_dir}", ' \
               'path_in_target="/mnt/tmp", extra_ssh_flags=["-F", "{tempdir}/config", "-n", "-4", "-t", "-t"], ' \
               'extra_scp_flags=["-F", "{tempdir}/config"])'.format(user=user, port=port, host_dir=str(libcxx_dir / "tmp"), tempdir=tempdir)

    print("Running libcxx_tests with executor", executor)
    # have to use -j1 + --single-process since otherwise CheriBSD might wedge
    lit_cmd = [
        str(libcxx_dir / "bin/llvm-lit"), "-j1", "-vv", "--single-process",
        "-Dexecutor=" + executor, "test"
    ]
    if args.lit_debug_output:
        lit_cmd.append("--debug")
    # This does not work since it doesn't handle running ssh commands....
    lit_cmd.append(
        "--timeout=120"
    )  # 2 minutes max per test (in case there is an infinite loop)
    xunit_file = None  # type: Path
    if args.xunit_output:
        lit_cmd.append("--xunit-xml-output")
        xunit_file = Path(args.xunit_output).absolute()
        if args.internal_shard:
            xunit_file = xunit_file.with_name("shard-" +
                                              str(args.internal_shard) + "-" +
                                              xunit_file.name)
        lit_cmd.append(str(xunit_file))
    qemu_logfile = None
    if args.internal_shard:
        assert args.internal_num_shards, "Invalid call!"
        lit_cmd.append("--num-shards=" + str(args.internal_num_shards))
        lit_cmd.append("--run-shard=" + str(args.internal_shard))
        if xunit_file:
            qemu_log_path = xunit_file.with_suffix(".output").absolute()
            boot_cheribsd.success("Writing QEMU output to ", qemu_log_path)
            qemu_logfile = qemu_log_path.open("w")
            qemu.logfile_read = qemu_logfile
            boot_cheribsd.run_cheribsd_command(qemu, "echo HELLO LOGFILE")
    # Fixme starting lit at the same time does not work!
    # TODO: add the polling to the main thread instead of having another thread?
    # start the qemu output flushing thread so that we can see the kernel panic
    t = threading.Thread(target=flush_thread, args=(qemu_logfile, qemu))
    t.daemon = True
    t.start()
    shard_prefix = "SHARD" + str(
        args.internal_shard) + ": " if args.internal_shard else ""
    try:
        boot_cheribsd.success("Starting llvm-lit: cd ", libcxx_dir, " && ",
                              " ".join(lit_cmd))
        boot_cheribsd.run_host_command(lit_cmd, cwd=str(libcxx_dir))
        # lit_proc = pexpect.spawnu(lit_cmd[0], lit_cmd[1:], echo=True, timeout=60, cwd=str(libcxx_dir))
        # TODO: get stderr!!
        # while lit_proc.isalive():
        lit_proc = None
        while False:
            line = lit_proc.readline()
            if shard_prefix:
                line = shard_prefix + line
            print(line)
            global KERNEL_PANIC
            # Abort once we detect a kernel panic
            if KERNEL_PANIC:
                lit_proc.sendintr()
            print(shard_prefix + lit_proc.read())
        print("Lit finished.")
        if lit_proc and lit_proc.exitstatus == 1:
            boot_cheribsd.failure(shard_prefix + "SOME TESTS FAILED",
                                  exit=False)
    except:
        raise
    finally:
        if qemu_logfile:
            qemu_logfile.flush()

    if False:
        # slow executor using scp:
        executor = 'SSHExecutor("localhost", username="******", port={port})'.format(
            user=user, port=port)
if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("--parallel-jobs",
                        metavar="N",
                        type=int,
                        help="Split up the testsuite into N parallel jobs")
    # Don't let this parser capture --help
    args, remainder = parser.parse_known_args(
        filter(lambda x: x != "-h" and x != "--help", sys.argv))
    # If parallel is set spawn N processes and use the lit --num-shards + --run-shard flags to split the work
    # Since a full run takes about 16 hours this should massively reduce the amount of time needed.

    if args.parallel_jobs and args.parallel_jobs != 1:
        if args.parallel_jobs < 1:
            boot_cheribsd.failure("Invalid number of parallel jobs: ",
                                  args.parallel_jobs,
                                  exit=True)
        starttime = datetime.datetime.now()
        boot_cheribsd.success("Running ", args.parallel_jobs, " parallel jobs")
        sem = Semaphore(value=0)  # ensure that we block immediately
        mp_q = Queue()
        processes = []
        for i in range(args.parallel_jobs):
            shard_num = i + 1
            p = Process(target=run_shard,
                        args=(mp_q, sem, shard_num, args.parallel_jobs))
            p.daemon = True  # kill process on parent exit
            p.name = "<LIBCXX test shard " + str(shard_num) + ">"
            p.start()
            processes.append(p)
            atexit.register(p.terminate)