From c3c7aa730e81abd7879ef62aca421c1d4401764c Mon Sep 17 00:00:00 2001 From: Rodrigo Arias Date: Mon, 22 Aug 2022 16:40:08 +0200 Subject: [PATCH] Add support for runtime tests --- nix/nosv.nix | 67 ++++++++++++++++++++ test/CMakeLists.txt | 37 +++++------ test/macros.cmake | 70 +++++++++++++++++++++ test/match-error.sh | 29 +++++++++ test/{mp-driver.sh => ovni-driver.sh} | 33 +++++----- test/{driver.sh => rt/CMakeLists.txt} | 28 ++++----- test/rt/nosv.toml | 90 +++++++++++++++++++++++++++ test/rt/nosv/attach.c | 33 ++++++++++ 8 files changed, 335 insertions(+), 52 deletions(-) create mode 100644 nix/nosv.nix create mode 100644 test/macros.cmake create mode 100755 test/match-error.sh rename test/{mp-driver.sh => ovni-driver.sh} (65%) rename test/{driver.sh => rt/CMakeLists.txt} (56%) mode change 100755 => 100644 create mode 100644 test/rt/nosv.toml create mode 100644 test/rt/nosv/attach.c diff --git a/nix/nosv.nix b/nix/nosv.nix new file mode 100644 index 0000000..245f5f5 --- /dev/null +++ b/nix/nosv.nix @@ -0,0 +1,67 @@ +# Build with `nix-build nix/old-glibc.nix` +let + # Pin the nixpkgs + nixpkgsPath = builtins.fetchTarball { + # Descriptive name to make the store path easier to identify + name = "nixos-20.09"; + # Commit hash for nixos-20.09 as of 2021-01-11 + url = "https://github.com/nixos/nixpkgs/archive/41dddb1283733c4993cb6be9573d5cef937c1375.tar.gz"; + # Hash obtained using `nix-prefetch-url --unpack ` + sha256 = "1blbidbmxhaxar2x76nz72bazykc5yxi0algsbrhxgrsvijs4aiw"; + }; + + pkgs = import nixpkgsPath { }; + + ovni = pkgs.stdenv.mkDerivation rec { + name = "ovni"; + + buildInputs = with pkgs; [ cmake openmpi ]; + + # Prevent accidental reutilization of previous builds, as we are taking the + # current directory as-is + preConfigure = '' + rm -rf build install + + # There is no /bin/bash + patchShebangs test/*.sh + ''; + + cmakeBuildType = "Debug"; + cmakeFlags = [ "-DCMAKE_SKIP_BUILD_RPATH=OFF" ]; + buildFlags = [ "VERBOSE=1" ]; + preCheck = '' + export CTEST_OUTPUT_ON_FAILURE=1 + ''; + dontStrip = true; + doCheck = true; + checkTarget = "test"; + + src = ../.; + }; + + nosv = pkgs.stdenv.mkDerivation rec { + pname = "nosv"; + version = src.shortRev; + buildInputs = with pkgs; [ autoreconfHook pkg-config numactl ovni ]; + configureFlags = [ "--with-ovni=${ovni}" ]; + dontStrip = true; + src = builtins.fetchGit { + url = "ssh://git@gitlab-internal.bsc.es/nos-v/nos-v.git"; + ref = "master"; + }; + }; + + # Quick fix to avoid rebuilding every time the ovni source changes. + # Use this nosv' version below as dependency of ovni-rt + nosv' = /nix/store/rvnrbc7ibpw06jdilz6mha7szzxcr2mi-nosv-8936f3e; + + ovni-rt = ovni.overrideAttrs (old: { + __impure = true; + __noChroot = true; + buildInputs = old.buildInputs ++ [ nosv pkgs.strace ]; + cmakeFlags = old.cmakeFlags ++ [ "-DBUILD_RT_TESTING=ON" ]; + }); + +in + + ovni-rt diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9ec6aa7..4223d51 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,31 +17,22 @@ set(OVNI_TEST_SOURCE_DIR "${CMAKE_SOURCE_DIR}/test") set(OVNI_TEST_BUILD_DIR "${CMAKE_BINARY_DIR}/test") -macro(ovni_test name driver) - add_executable("${name}" "${name}.c") - target_link_libraries("${name}" ovni) - - add_test(NAME "${name}" - COMMAND "${OVNI_TEST_SOURCE_DIR}/${driver}" "${name}" - WORKING_DIRECTORY "${OVNI_TEST_BUILD_DIR}") - - set_property(TEST "${name}" PROPERTY RUN_SERIAL TRUE) - - if("${name}" MATCHES ".*-bad") - set_property(TEST "${name}" PROPERTY WILL_FAIL TRUE) - endif() - -endmacro() +include(macros.cmake) # Only run performance sensitive tests on Release builds if(CMAKE_BUILD_TYPE STREQUAL "Release") - ovni_test("flush-overhead" "driver.sh") + ovni_test(NAME flush-overhead) endif() -ovni_test("flush" "driver.sh") -ovni_test("mp-simple" "mp-driver.sh") -ovni_test("mp-rank" "mp-driver.sh") -ovni_test("nosv-nested-tasks" "driver.sh") -ovni_test("nosv-nested-tasks-bad" "driver.sh") -ovni_test("nosv-task-types" "mp-driver.sh") -ovni_test("nosv-pause" "mp-driver.sh") +ovni_test(NAME flush) +ovni_test(NAME mp-simple MP) +ovni_test(NAME mp-rank MP) +ovni_test(NAME nosv-nested-tasks) +ovni_test(NAME nosv-nested-tasks-bad + SHOULD_FAIL REGEX "fatal: unknown task type id 1") +ovni_test(NAME nosv-task-types MP) +ovni_test(NAME nosv-pause MP) + +if(BUILD_RT_TESTING) + add_subdirectory(rt) +endif() diff --git a/test/macros.cmake b/test/macros.cmake new file mode 100644 index 0000000..28ea376 --- /dev/null +++ b/test/macros.cmake @@ -0,0 +1,70 @@ +include(CMakeParseArguments) + +function(ovni_test) + set(switches MP SHOULD_FAIL) + set(single NPROC NAME REGEX) + set(multi SOURCE ENV) + + cmake_parse_arguments( + OVNI_TEST "${switches}" "${single}" "${multi}" ${ARGN}) + + if(NOT OVNI_TEST_NAME) + message(FATAL_ERROR "You must provide a test NAME") + endif(NOT OVNI_TEST_NAME) + + set(OVNI_TEST_NAME ${OVNI_TEST_NAME} PARENT_SCOPE) + + # Set default source if not given + if(NOT OVNI_TEST_SOURCE) + set(OVNI_TEST_SOURCE "${OVNI_TEST_NAME}.c") + #message("Setting default source to ${OVNI_TEST_SOURCE}") + endif() + + if(NOT OVNI_TEST_NPROC) + if(NOT OVNI_TEST_MP) + set(OVNI_TEST_NPROC 1) + else() + set(OVNI_TEST_NPROC 4) + endif() + endif() + + list(APPEND OVNI_TEST_ENV + "OVNI_NPROCS=${OVNI_TEST_NPROC}") + + list(APPEND OVNI_TEST_ENV + "OVNI_BUILD_DIR=${CMAKE_BINARY_DIR}") + + list(APPEND OVNI_TEST_ENV + "OVNI_CURRENT_DIR=${CMAKE_CURRENT_BINARY_DIR}") + + add_executable("${OVNI_TEST_NAME}" "${OVNI_TEST_SOURCE}") + target_link_libraries("${OVNI_TEST_NAME}" ovni) + + set(driver "${OVNI_TEST_SOURCE_DIR}/ovni-driver.sh") + + if(OVNI_TEST_SHOULD_FAIL) + if(NOT OVNI_TEST_REGEX) + message(FATAL_ERROR "You must provide a REGEX for a failing test") + endif() + # Custom error handler, as ctest doesn't behave as one would expect. + add_test(NAME "${OVNI_TEST_NAME}" + COMMAND + "${OVNI_TEST_SOURCE_DIR}/match-error.sh" + "${OVNI_TEST_REGEX}" + "${driver}" + "${OVNI_TEST_NAME}" + WORKING_DIRECTORY "${OVNI_TEST_BUILD_DIR}") + else() + add_test(NAME "${OVNI_TEST_NAME}" + COMMAND + "${driver}" + "${OVNI_TEST_NAME}" + WORKING_DIRECTORY "${OVNI_TEST_BUILD_DIR}") + endif() + + set_tests_properties("${OVNI_TEST_NAME}" + PROPERTIES + RUN_SERIAL TRUE + ENVIRONMENT "${OVNI_TEST_ENV}" + WORKING_DIRECTORY "${OVNI_TEST_BUILD_DIR}") +endfunction(ovni_test) diff --git a/test/match-error.sh b/test/match-error.sh new file mode 100755 index 0000000..1ffe563 --- /dev/null +++ b/test/match-error.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# This script return 0 if and only if the given program returns non-zero +# AND the regex matches the output + +# $1 = the regex as grep +# $2... The program + +regex="$1" +shift + +"${@}" 2>&1 | stdbuf -i0 -o0 tee /dev/stderr | grep -q "${regex}" + +rcprog=${PIPESTATUS[0]} rcgrep=${PIPESTATUS[2]} + +echo "rcprog='$rcprog' rcgrep='$rcgrep'" + +if [ "$rcprog" != 0 ] && [ "$rcgrep" = 0 ]; then + echo "ok: program failed and grep matched the error line" + exit 0 +else + if [ "$rcprog" = 0 ]; then + echo "error: program exited with 0 rather than failure" + fi + if [ "$rcgrep" != 0 ]; then + echo "error: regex \"${regex}\" not matched" + fi + exit 1 +fi diff --git a/test/mp-driver.sh b/test/ovni-driver.sh similarity index 65% rename from test/mp-driver.sh rename to test/ovni-driver.sh index a1248ed..eff17b8 100755 --- a/test/mp-driver.sh +++ b/test/ovni-driver.sh @@ -17,25 +17,30 @@ set -e -testname="$1" +dir=$(readlink -f "${OVNI_CURRENT_DIR}") +testname="$dir/$1" +workdir="${testname}.trace" +tracedir="${workdir}/ovni" +emubin="${OVNI_BUILD_DIR}/ovniemu" -if [ -z "$2" ]; then - NPROCS=4 -else - NPROCS="$2" -fi - -tracedir="ovni" -emubin=../ovniemu +mkdir -p "${workdir}" +cd "${workdir}" rm -rf "$tracedir" -for i in $(seq 1 $NPROCS); do - # Run the test in the background - OVNI_RANK=$(($i-1)) OVNI_NRANKS=$NPROCS "./$testname" & -done +if [ -z "$OVNI_NPROCS" ]; then + OVNI_NPROCS=1 +fi -wait +if [ "$OVNI_NPROCS" -gt 1 ]; then + for i in $(seq 1 "$OVNI_NPROCS"); do + # Run the test in the background + OVNI_RANK=$(($i-1)) OVNI_NRANKS=$OVNI_NPROCS "$testname" & + done + wait +else + "$testname" +fi # Then launch the emulator in lint mode "$emubin" -l "$tracedir" diff --git a/test/driver.sh b/test/rt/CMakeLists.txt old mode 100755 new mode 100644 similarity index 56% rename from test/driver.sh rename to test/rt/CMakeLists.txt index 0f0842b..1c610ff --- a/test/driver.sh +++ b/test/rt/CMakeLists.txt @@ -1,6 +1,5 @@ -#!/bin/sh # -# Copyright (c) 2021 Barcelona Supercomputing Center (BSC) +# Copyright (c) 2022 Barcelona Supercomputing Center (BSC) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,18 +14,17 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -set -e +find_library(nosv libnosv) +find_path(NOSV_INCLUDE_DIR nosv.h) -testname="$1" -tracedir="ovni" -emubin=../ovniemu +function(nosv_test) + ovni_test(${ARGN}) + target_link_libraries("${OVNI_TEST_NAME}" nosv) + target_include_directories("${OVNI_TEST_NAME}" + PUBLIC ${NOSV_INCLUDE_DIR}) + set_property(TEST "${OVNI_TEST_NAME}" APPEND + PROPERTY + ENVIRONMENT "NOSV_CONFIG=${OVNI_TEST_SOURCE_DIR}/rt/nosv.toml") +endfunction() -rm -rf "$tracedir" - -# Run the test -"./$testname" - -# Then launch the emulator in lint mode -"$emubin" -l "$tracedir" - -#rm -rf $tracedir +nosv_test(NAME nosv-attach SOURCE nosv/attach.c) diff --git a/test/rt/nosv.toml b/test/rt/nosv.toml new file mode 100644 index 0000000..8a8e772 --- /dev/null +++ b/test/rt/nosv.toml @@ -0,0 +1,90 @@ +# This file is part of nOS-V and is licensed under the terms contained in the COPYING file +# +# Copyright (C) 2021-2022 Barcelona Supercomputing Center (BSC) + +# Default nOS-V configuration file. +# Note that every process in the same nOS-V instance should load an identical configuration file, +# otherwise, the behaviour is unspecified + +# Shared memory configuration +[shared_memory] + # Name of the shared memory section. This can be leveraged to create two separate nOS-V instances + # in the same system. Generally this has to remain unchanged + name = "nosv" + # Size of the shared memory section. Choose powers of two + size = "2G" + # Start of the shared memory mapping + # This option should be memory address codified in hexadecimal (start with 0x) + start = 0x0000200000000000 + +# Scheduler configuration +# These parameters allow to fine tune the scheduler's behaviour +[scheduler] + # Number of logical CPUs mapped to a single scheduler SPSC for ready tasks + # Minimum is 1, there is no maximum. Try to choose numbers that divide evenly the number + # of CPUs in the system. + # A lower number will yield more scheduling throughput and is adequate when using + # multiple task creator processes. + # A higher number will yield lower scheduling latency and is adequate when using one or few + # task creator processes + cpus_per_queue = 1 + # Number of tasks that are grabbed off of a queue when processing ready tasks. + # Lowering the number may be better if there are very few ready tasks during execution. + # Increasing the batch may help if your CPU has bad single-thread performance. + # In general, the default value of 64 should result in a good trade-off + queue_batch = 64 + # Scheduler quantum in ns. This is a guideline for how long should we execute one process' tasks + # until we have to switch to the next one. However, as nOS-V tasks are not preemptable, it isn't enforced. + # This parameter is specially relevant for the nosv_schedpoint function, which will try to schedule + # each quantum ns. + # A lower value will cause more inter-process context switches but may provide more uniform progress, + # while a higher value will minimize context switches but may stall applications for longer + quantum_ns = 20000000 # nanoseconds + # Size of the queues that are used to place tasks into the scheduler + # A good value should be a multiple of queue_batch + in_queue_size = 256 + +# CPU Governor configuration +# Controls the policy that nOS-V follows to block idle CPUs to save energy and resources +[governor] + # There is a choice between three different governor policies: + # - hybrid: CPUs will spin for governor.spins before going to sleep when no work is available + # - idle: CPUs will sleep immediately when no work is available + # - busy: CPUs will never sleep + # In general, use idle when targeting minimum power usage, and busy when targeting maximum performance + # The default is hybrid as it provides a good balance between power and performance. + policy = "hybrid" + # Number of times a CPU will spin without sleeping in the "hybrid" policy. + # When "idle" or "busy" are selected, this setting is ignored + spins = 10000 + +# Debug options +[debug] + # Dump all the configuration options nOS-V is running with, its final parsed values and the + # path of the config file being used + dump_config = false + +# Hardware Counters configuration +[hwcounters] + # Whether to print verbose information if a backend is enabled + verbose = false + # The enabled HWCounter backends. Possible options: "papi", "none" + backend = "none" + # The list of PAPI counters to read. By default only "PAPI_TOT_INS" and "PAPI_TOT_CYC" + papi_events = [ + "PAPI_TOT_INS", + "PAPI_TOT_CYC" + ] + +# Enabling turbo will cause nOS-V to set architecture-specific optimization flags on every created +# or attached threads. In x86, this will cause the FTZ and DAZ flags of the SSE FPU to be enabled, +# causing a significant performance increase in floating-point applications, but disabling IEEE-754 +# compatibility. +[turbo] + enabled = false + +# Monitoring cappabilities and configuration. +[monitoring] + enabled = false + # Whether to print verbose information if monitoring is enabled + verbose = false diff --git a/test/rt/nosv/attach.c b/test/rt/nosv/attach.c new file mode 100644 index 0000000..4751564 --- /dev/null +++ b/test/rt/nosv/attach.c @@ -0,0 +1,33 @@ +#define _DEFAULT_SOURCE + +#include +#include + +#include "common.h" + +int main(void) +{ + nosv_init(); + + nosv_task_type_t type; + if(nosv_type_init(&type, NULL, NULL, NULL, "adopted", NULL, + NULL, NOSV_TYPE_INIT_EXTERNAL) != 0) + die("nosv_type_init failed\n"); + + nosv_task_t task; + if(nosv_attach(&task, type, 0, NULL, 0) != 0) + die("nosv_attach failed\n"); + + usleep(100); + + if(nosv_detach(0) != 0) + die("nosv_detach failed\n"); + + if(nosv_type_destroy(type, 0) != 0) + die("nosv_type_destroy failed\n"); + + if(nosv_shutdown() != 0) + die("nosv_shutdown failed\n"); + + return 0; +}