# # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(DIR ${PARENT_DIR}/${TEST_NAME}) function(setup) execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${PARENT_DIR}/${TEST_NAME}) execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${PARENT_DIR}/${TEST_NAME}) execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${BIN_DIR}) execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${BIN_DIR}) endfunction() function(print_logs) message(STATUS "Test ${TEST_NAME}:") if(EXISTS ${BIN_DIR}/${TEST_NAME}.out) file(READ ${BIN_DIR}/${TEST_NAME}.out OUT) message(STATUS "Stdout:\n${OUT}") endif() if(EXISTS ${BIN_DIR}/${TEST_NAME}.err) file(READ ${BIN_DIR}/${TEST_NAME}.err ERR) message(STATUS "Stderr:\n${ERR}") endif() if(EXISTS ${BIN_DIR}/${TEST_NAME}.pmreorder) file(READ ${BIN_DIR}/${TEST_NAME}.pmreorder PMEMREORDER) message(STATUS "Pmreorder:\n${PMEMREORDER}") endif() endfunction() # Performs cleanup and log matching. function(finish) print_logs() if(EXISTS ${SRC_DIR}/${TEST_NAME}.err.match) match(${BIN_DIR}/${TEST_NAME}.err ${SRC_DIR}/${TEST_NAME}.err.match) endif() if(EXISTS ${SRC_DIR}/${TEST_NAME}.out.match) match(${BIN_DIR}/${TEST_NAME}.out ${SRC_DIR}/${TEST_NAME}.out.match) endif() execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory ${PARENT_DIR}/${TEST_NAME}) endfunction() # Verifies ${log_file} matches ${match_file} using "match". function(match log_file match_file) execute_process(COMMAND ${PERL_EXECUTABLE} ${MATCH_SCRIPT} -o ${log_file} ${match_file} RESULT_VARIABLE MATCH_ERROR) if(MATCH_ERROR) message(FATAL_ERROR "Log does not match: ${MATCH_ERROR}") endif() endfunction() # Verifies file exists function(check_file_exists file) if(NOT EXISTS ${file}) message(FATAL_ERROR "${file} doesn't exist") endif() endfunction() # Verifies file doesn't exist function(check_file_doesnt_exist file) if(EXISTS ${file}) message(FATAL_ERROR "${file} exists") endif() endfunction() # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=810295 # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=780173 # https://bugs.kde.org/show_bug.cgi?id=303877 # # valgrind issues an unsuppressable warning when exceeding # the brk segment, causing matching failures. We can safely # ignore it because malloc() will fallback to mmap() anyway. # # list of ingored warnings should match with the list provided by PMDK: # https://github.com/pmem/pmdk/blob/master/src/test/unittest/unittest.sh function(valgrind_ignore_warnings valgrind_log) execute_process(COMMAND bash "-c" "cat ${valgrind_log} | grep -v \ -e \"WARNING: Serious error when reading debug info\" \ -e \"When reading debug info from \" \ -e \"Ignoring non-Dwarf2/3/4 block in .debug_info\" \ -e \"Last block truncated in .debug_info; ignoring\" \ -e \"parse_CU_Header: is neither DWARF2 nor DWARF3 nor DWARF4\" \ -e \"brk segment overflow\" \ -e \"see section Limitations in user manual\" \ -e \"Warning: set address range perms: large range\"\ -e \"further instances of this message will not be shown\"\ > ${valgrind_log}.tmp mv ${valgrind_log}.tmp ${valgrind_log}") endfunction() function(execute_common expect_success output_file name) if(TESTS_USE_FORCED_PMEM) set(ENV{PMEM_IS_PMEM_FORCE} 1) endif() if(${TRACER} STREQUAL pmemcheck) if(TESTS_USE_FORCED_PMEM) # pmemcheck runs really slow with pmem, disable it set(ENV{PMEM_IS_PMEM_FORCE} 0) endif() set(TRACE valgrind --error-exitcode=99 --tool=pmemcheck) set(ENV{LIBPMEMOBJ_CPP_TRACER_PMEMCHECK} 1) elseif(${TRACER} STREQUAL memcheck) set(TRACE valgrind --error-exitcode=99 --tool=memcheck --leak-check=full --suppressions=${TEST_ROOT_DIR}/ld.supp --suppressions=${TEST_ROOT_DIR}/memcheck-stdcpp.supp --suppressions=${TEST_ROOT_DIR}/memcheck-libunwind.supp) set(ENV{LIBPMEMOBJ_CPP_TRACER_MEMCHECK} 1) elseif(${TRACER} STREQUAL helgrind) set(TRACE valgrind --error-exitcode=99 --tool=helgrind) set(ENV{LIBPMEMOBJ_CPP_TRACER_HELGRIND} 1) elseif(${TRACER} STREQUAL drd) set(TRACE valgrind --error-exitcode=99 --tool=drd) set(ENV{LIBPMEMOBJ_CPP_TRACER_DRD} 1) elseif(${TRACER} MATCHES "none.*") # nothing else() message(FATAL_ERROR "Unknown tracer '${TRACER}'") endif() if (NOT $ENV{CGDB}) if (NOT WIN32) set(TRACE timeout -s SIGALRM -k 200s 180s ${TRACE}) endif() endif() string(REPLACE ";" " " TRACE_STR "${TRACE}") message(STATUS "Executing: ${TRACE_STR} ${name} ${ARGN}") set(cmd ${TRACE} ${name} ${ARGN}) if($ENV{CGDB}) find_program(KONSOLE NAMES konsole) find_program(GNOME_TERMINAL NAMES gnome-terminal) find_program(CGDB NAMES cgdb) if (NOT KONSOLE AND NOT GNOME_TERMINAL) message(FATAL_ERROR "konsole or gnome-terminal not found.") elseif (NOT CGDB) message(FATAL_ERROR "cdgb not found.") elseif(NOT (${TRACER} STREQUAL none)) message(FATAL_ERROR "Cannot use cgdb with ${TRACER}") else() if (KONSOLE) set(cmd konsole -e cgdb --args ${cmd}) elseif(GNOME_TERMINAL) set(cmd gnome-terminal --tab --active --wait -- cgdb --args ${cmd}) endif() endif() endif() if(${output_file} STREQUAL none) execute_process(COMMAND ${cmd} OUTPUT_QUIET RESULT_VARIABLE res) else() execute_process(COMMAND ${cmd} RESULT_VARIABLE res OUTPUT_FILE ${BIN_DIR}/${TEST_NAME}.out ERROR_FILE ${BIN_DIR}/${TEST_NAME}.err) endif() # memcheck and pmemcheck match files should follow name pattern: # testname_testcasenr_memcheck/pmemcheck.err.match # If they do exist, ignore test result - it will be verified during # log matching in finish() function. if(EXISTS ${SRC_DIR}/${TEST_NAME}.err.match) valgrind_ignore_warnings(${BIN_DIR}/${TEST_NAME}.err) # pmemcheck is a special snowflake and it doesn't set exit code when # it detects an error, so we have to look at its output if match file # was not found. else() if(${TRACER} STREQUAL pmemcheck) if(NOT EXISTS ${BIN_DIR}/${TEST_NAME}.err) message(FATAL_ERROR "${TEST_NAME}.err not found.") endif() file(READ ${BIN_DIR}/${TEST_NAME}.err PMEMCHECK_ERR) message(STATUS "Stderr:\n${PMEMCHECK_ERR}\nEnd of stderr") if(NOT PMEMCHECK_ERR MATCHES "ERROR SUMMARY: 0") message(FATAL_ERROR "${TRACE} ${name} ${ARGN} failed: ${res}") endif() endif() if(res AND expect_success) print_logs() message(FATAL_ERROR "${TRACE} ${name} ${ARGN} failed: ${res}") endif() if(NOT res AND NOT expect_success) print_logs() message(FATAL_ERROR "${TRACE} ${name} ${ARGN} unexpectedly succeeded: ${res}") endif() endif() if(${TRACER} STREQUAL pmemcheck) unset(ENV{LIBPMEMOBJ_CPP_TRACER_PMEMCHECK}) elseif(${TRACER} STREQUAL memcheck) unset(ENV{LIBPMEMOBJ_CPP_TRACER_MEMCHECK}) elseif(${TRACER} STREQUAL helgrind) unset(ENV{LIBPMEMOBJ_CPP_TRACER_HELGRIND}) elseif(${TRACER} STREQUAL drd) unset(ENV{LIBPMEMOBJ_CPP_TRACER_DRD}) endif() if(TESTS_USE_FORCED_PMEM) unset(ENV{PMEM_IS_PMEM_FORCE}) endif() endfunction() function(check_target name) if(NOT EXISTS ${name}) message(FATAL_ERROR "Tests were not found! If not built, run make first.") endif() endfunction() # Generic command executor which handles failures and prints command output # to specified file. function(execute_with_output out name) check_target(${name}) execute_common(true ${out} ${name} ${ARGN}) endfunction() # Generic command executor which handles failures but ignores output. function(execute_ignore_output name) check_target(${name}) execute_common(true none ${name} ${ARGN}) endfunction() # Executes test command ${name} and verifies its status. # First argument of the command is test directory name. # Optional function arguments are passed as consecutive arguments to # the command. function(execute name) check_target(${name}) execute_common(true ${TRACER}_${TESTCASE} ${name} ${ARGN}) endfunction() # Executes command ${name} and creates a storelog. # First argument is pool file. # Second argument is test executable. # Optional function arguments are passed as consecutive arguments to # the command. function(pmreorder_create_store_log pool name) check_target(${name}) if(NOT (${TRACER} STREQUAL none)) message(FATAL_ERROR "Pmreorder test must be run without any tracer.") endif() configure_file(${pool} ${pool}.copy COPYONLY) set(ENV{PMREORDER_EMIT_LOG} 1) if(DEFINED ENV{PMREORDER_STACKTRACE_DEPTH}) set(PMREORDER_STACKTRACE_DEPTH $ENV{PMREORDER_STACKTRACE_DEPTH}) set(PMREORDER_STACKTRACE "yes") else() set(PMREORDER_STACKTRACE_DEPTH 1) set(PMREORDER_STACKTRACE "no") endif() set(cmd valgrind --tool=pmemcheck -q --log-stores=yes --print-summary=no --log-file=${BIN_DIR}/${TEST_NAME}.storelog --log-stores-stacktraces=${PMREORDER_STACKTRACE} --log-stores-stacktraces-depth=${PMREORDER_STACKTRACE_DEPTH} --expect-fence-after-clflush=yes ${name} ${ARGN}) execute_common(true ${TRACER}_${TESTCASE} ${cmd}) unset(ENV{PMREORDER_EMIT_LOG}) file(REMOVE ${pool}) configure_file(${pool}.copy ${pool} COPYONLY) endfunction() # Executes pmreorder. # First argument is expected result. # Second argument is engine type. # Third argument is path to configure file. # Fourth argument is path to the checker program. # Optional function arguments are passed as consecutive arguments to # the command. function(pmreorder_execute expect_success engine conf_file name) check_target(${name}) if(NOT (${TRACER} STREQUAL none)) message(FATAL_ERROR "Pmreorder test must be run without any tracer.") endif() set(ENV{PMEMOBJ_COW} 1) set(cmd pmreorder -l ${BIN_DIR}/${TEST_NAME}.storelog -o ${BIN_DIR}/${TEST_NAME}.pmreorder -r ${engine} -p "${name} ${ARGN}" -x ${conf_file}) execute_common(${expect_success} ${TRACER}_${TESTCASE} ${cmd}) unset(ENV{PMEMOBJ_COW}) endfunction()