#
# 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()