Backport correctness package
This commit is contained in:
parent
fe71ef88c3
commit
ddb5b0b050
|
@ -188,6 +188,7 @@ if(WITH_PYTHON)
|
|||
add_subdirectory(bindings)
|
||||
endif()
|
||||
add_subdirectory(fdbbackup)
|
||||
add_subdirectory(contrib)
|
||||
add_subdirectory(tests)
|
||||
if(WITH_DOCUMENTATION)
|
||||
add_subdirectory(documentation)
|
||||
|
|
|
@ -61,6 +61,9 @@ function(add_fdb_test)
|
|||
set(this_test_timeout ${ADD_FDB_TEST_TIMEOUT})
|
||||
if(NOT this_test_timeout)
|
||||
set(this_test_timeout 3600)
|
||||
if(USE_VALGRIND_FOR_CTEST)
|
||||
set(this_test_timeout 36000)
|
||||
endif()
|
||||
endif()
|
||||
set(test_type "simulation")
|
||||
set(fdb_test_files_ "${fdb_test_files}")
|
||||
|
@ -84,6 +87,9 @@ function(add_fdb_test)
|
|||
if (NOT "${ADD_FDB_TEST_TEST_NAME}" STREQUAL "")
|
||||
set(test_name ${ADD_FDB_TEST_TEST_NAME})
|
||||
endif()
|
||||
if((NOT test_name MATCHES "${TEST_INCLUDE}") OR (test_name MATCHES "${TEST_EXCLUDE}"))
|
||||
return()
|
||||
endif()
|
||||
math(EXPR test_idx "${CURRENT_TEST_INDEX} + ${NUM_TEST_FILES}")
|
||||
set(CURRENT_TEST_INDEX "${test_idx}" PARENT_SCOPE)
|
||||
# set(<var> <value> PARENT_SCOPE) doesn't set the
|
||||
|
@ -107,7 +113,7 @@ function(add_fdb_test)
|
|||
set(BUGGIFY_OPTION "-B")
|
||||
endif()
|
||||
set(VALGRIND_OPTION "")
|
||||
if (USE_VALGRIND)
|
||||
if (USE_VALGRIND_FOR_CTEST)
|
||||
set(VALGRIND_OPTION "--use-valgrind")
|
||||
endif()
|
||||
list(TRANSFORM ADD_FDB_TEST_TEST_FILES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/")
|
||||
|
@ -117,6 +123,7 @@ function(add_fdb_test)
|
|||
-b ${PROJECT_BINARY_DIR}
|
||||
-t ${test_type}
|
||||
-O ${OLD_FDBSERVER_BINARY}
|
||||
--crash
|
||||
--aggregate-traces ${TEST_AGGREGATE_TRACES}
|
||||
--log-format ${TEST_LOG_FORMAT}
|
||||
--keep-logs ${TEST_KEEP_LOGS}
|
||||
|
@ -132,4 +139,239 @@ function(add_fdb_test)
|
|||
get_filename_component(test_dir ${test_dir_full} NAME)
|
||||
set_tests_properties(${test_name} PROPERTIES TIMEOUT ${this_test_timeout} LABELS "${test_dir}")
|
||||
endif()
|
||||
# set variables used for generating test packages
|
||||
set(TEST_NAMES ${TEST_NAMES} ${test_name} PARENT_SCOPE)
|
||||
set(TEST_FILES_${test_name} ${ADD_FDB_TEST_TEST_FILES} PARENT_SCOPE)
|
||||
set(TEST_TYPE_${test_name} ${test_type} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
if(NOT WIN32)
|
||||
set(TEST_PACKAGE_INCLUDE ".*" CACHE STRING "A regex of all tests that should be included in the test package")
|
||||
set(TEST_PACKAGE_EXCLUDE ".^" CACHE STRING "A regex of all tests that shouldn't be added to the test package")
|
||||
set(TEST_PACKAGE_ADD_DIRECTORIES "" CACHE STRING "A ;-separated list of directories. All files within each directory will be added to the test package")
|
||||
endif()
|
||||
|
||||
# This sets up a directory with the correctness files common to all correctness packages.
|
||||
# This function should be called with the following arguments:
|
||||
#
|
||||
# - OUT_DIR the directory where files will be staged
|
||||
# - CONTEXT the type of correctness package being built (e.g. 'valgrind correctness')
|
||||
function(stage_correctness_package)
|
||||
set(oneValueArgs OUT_DIR CONTEXT OUT_FILES)
|
||||
cmake_parse_arguments(STAGE "" "${oneValueArgs}" "" "${ARGN}")
|
||||
file(MAKE_DIRECTORY ${STAGE_OUT_DIR}/bin)
|
||||
string(LENGTH "${CMAKE_SOURCE_DIR}/tests/" base_length)
|
||||
foreach(test IN LISTS TEST_NAMES)
|
||||
if(("${TEST_TYPE_${test}}" STREQUAL "simulation") AND
|
||||
(${test} MATCHES ${TEST_PACKAGE_INCLUDE}) AND
|
||||
(NOT ${test} MATCHES ${TEST_PACKAGE_EXCLUDE}))
|
||||
foreach(file IN LISTS TEST_FILES_${test})
|
||||
string(SUBSTRING ${file} ${base_length} -1 rel_out_file)
|
||||
set(out_file ${STAGE_OUT_DIR}/tests/${rel_out_file})
|
||||
list(APPEND test_files ${out_file})
|
||||
add_custom_command(
|
||||
OUTPUT ${out_file}
|
||||
DEPENDS ${file}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${file} ${out_file}
|
||||
COMMENT "Copying ${STAGE_CONTEXT} test file ${rel_out_file}"
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
endforeach()
|
||||
foreach(dir IN LISTS TEST_PACKAGE_ADD_DIRECTORIES)
|
||||
file(GLOB_RECURSE files ${dir}/*)
|
||||
string(LENGTH ${dir} dir_len)
|
||||
foreach(file IN LISTS files)
|
||||
get_filename_component(src_dir ${file} DIRECTORY)
|
||||
# We need to make sure that ${src_dir} is at least
|
||||
# as long as ${dir}. Otherwise the later call to
|
||||
# SUBSTRING will fail
|
||||
set(src_dir "${src_dir}/")
|
||||
string(SUBSTRING ${src_dir} ${dir_len} -1 dest_dir)
|
||||
string(SUBSTRING ${file} ${dir_len} -1 rel_out_file)
|
||||
set(out_file ${STAGE_OUT_DIR}/${rel_out_file})
|
||||
list(APPEND external_files ${out_file})
|
||||
add_custom_command(
|
||||
OUTPUT ${out_file}
|
||||
DEPENDS ${file}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${file} ${out_file}
|
||||
COMMENT "Copying ${STAGE_CONTEXT} external file ${file}"
|
||||
)
|
||||
endforeach()
|
||||
endforeach()
|
||||
list(APPEND package_files ${STAGE_OUT_DIR}/bin/fdbserver
|
||||
${STAGE_OUT_DIR}/bin/coverage.fdbserver.xml
|
||||
${STAGE_OUT_DIR}/bin/coverage.fdbclient.xml
|
||||
${STAGE_OUT_DIR}/bin/coverage.fdbrpc.xml
|
||||
${STAGE_OUT_DIR}/bin/coverage.flow.xml
|
||||
${STAGE_OUT_DIR}/bin/TestHarness.exe
|
||||
${STAGE_OUT_DIR}/bin/TraceLogHelper.dll
|
||||
${STAGE_OUT_DIR}/CMakeCache.txt
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT ${package_files}
|
||||
DEPENDS ${CMAKE_BINARY_DIR}/CMakeCache.txt
|
||||
${CMAKE_BINARY_DIR}/packages/bin/fdbserver
|
||||
${CMAKE_BINARY_DIR}/bin/coverage.fdbserver.xml
|
||||
${CMAKE_BINARY_DIR}/lib/coverage.fdbclient.xml
|
||||
${CMAKE_BINARY_DIR}/lib/coverage.fdbrpc.xml
|
||||
${CMAKE_BINARY_DIR}/lib/coverage.flow.xml
|
||||
${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe
|
||||
${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/CMakeCache.txt ${STAGE_OUT_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/packages/bin/fdbserver
|
||||
${CMAKE_BINARY_DIR}/bin/coverage.fdbserver.xml
|
||||
${CMAKE_BINARY_DIR}/lib/coverage.fdbclient.xml
|
||||
${CMAKE_BINARY_DIR}/lib/coverage.fdbrpc.xml
|
||||
${CMAKE_BINARY_DIR}/lib/coverage.flow.xml
|
||||
${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe
|
||||
${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll
|
||||
${STAGE_OUT_DIR}/bin
|
||||
COMMENT "Copying files for ${STAGE_CONTEXT} package"
|
||||
)
|
||||
list(APPEND package_files ${test_files} ${external_files})
|
||||
if(STAGE_OUT_FILES)
|
||||
set(${STAGE_OUT_FILES} ${package_files} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(create_correctness_package)
|
||||
if(WIN32)
|
||||
return()
|
||||
endif()
|
||||
set(out_dir "${CMAKE_BINARY_DIR}/correctness")
|
||||
stage_correctness_package(OUT_DIR ${out_dir} CONTEXT "correctness" OUT_FILES package_files)
|
||||
set(tar_file ${CMAKE_BINARY_DIR}/packages/correctness-${CMAKE_PROJECT_VERSION}.tar.gz)
|
||||
add_custom_command(
|
||||
OUTPUT ${tar_file}
|
||||
DEPENDS ${package_files}
|
||||
${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/correctnessTest.sh
|
||||
${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/correctnessTimeout.sh
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/correctnessTest.sh
|
||||
${out_dir}/joshua_test
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/correctnessTimeout.sh
|
||||
${out_dir}/joshua_timeout
|
||||
COMMAND ${CMAKE_COMMAND} -E tar cfz ${tar_file} ${package_files}
|
||||
${out_dir}/joshua_test
|
||||
${out_dir}/joshua_timeout
|
||||
WORKING_DIRECTORY ${out_dir}
|
||||
COMMENT "Package correctness archive"
|
||||
)
|
||||
add_custom_target(package_tests ALL DEPENDS ${tar_file})
|
||||
add_dependencies(package_tests strip_only_fdbserver TestHarness)
|
||||
endfunction()
|
||||
|
||||
function(create_valgrind_correctness_package)
|
||||
if(WIN32)
|
||||
return()
|
||||
endif()
|
||||
if(USE_VALGRIND)
|
||||
set(out_dir "${CMAKE_BINARY_DIR}/valgrind_correctness")
|
||||
stage_correctness_package(OUT_DIR ${out_dir} CONTEXT "valgrind correctness" OUT_FILES package_files)
|
||||
set(tar_file ${CMAKE_BINARY_DIR}/packages/valgrind-${CMAKE_PROJECT_VERSION}.tar.gz)
|
||||
add_custom_command(
|
||||
OUTPUT ${tar_file}
|
||||
DEPENDS ${package_files}
|
||||
${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/valgrindTest.sh
|
||||
${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/valgrindTimeout.sh
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/valgrindTest.sh
|
||||
${out_dir}/joshua_test
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/valgrindTimeout.sh
|
||||
${out_dir}/joshua_timeout
|
||||
COMMAND ${CMAKE_COMMAND} -E tar cfz ${tar_file} ${package_files}
|
||||
${out_dir}/joshua_test
|
||||
${out_dir}/joshua_timeout
|
||||
WORKING_DIRECTORY ${out_dir}
|
||||
COMMENT "Package valgrind correctness archive"
|
||||
)
|
||||
add_custom_target(package_valgrind_tests ALL DEPENDS ${tar_file})
|
||||
add_dependencies(package_valgrind_tests strip_only_fdbserver TestHarness)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(package_bindingtester)
|
||||
if(WIN32 OR OPEN_FOR_IDE)
|
||||
return()
|
||||
elseif(APPLE)
|
||||
set(fdbcName "libfdb_c.dylib")
|
||||
else()
|
||||
set(fdbcName "libfdb_c.so")
|
||||
endif()
|
||||
set(bdir ${CMAKE_BINARY_DIR}/bindingtester)
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bindingtester)
|
||||
set(outfiles ${bdir}/fdbcli ${bdir}/fdbserver ${bdir}/${fdbcName} ${bdir}/joshua_test ${bdir}/joshua_timeout)
|
||||
add_custom_command(
|
||||
OUTPUT ${outfiles}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/CMakeCache.txt
|
||||
${CMAKE_BINARY_DIR}/packages/bin/fdbcli
|
||||
${CMAKE_BINARY_DIR}/packages/bin/fdbserver
|
||||
${CMAKE_BINARY_DIR}/packages/lib/${fdbcName}
|
||||
${bdir}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/bindingTest.sh ${bdir}/joshua_test
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/bindingTimeout.sh ${bdir}/joshua_timeout
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/localClusterStart.sh ${bdir}/localClusterStart.sh
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/bindingTestScript.sh ${bdir}/bindingTestScript.sh
|
||||
COMMENT "Copy executables and scripts to bindingtester dir")
|
||||
file(GLOB_RECURSE test_files ${CMAKE_SOURCE_DIR}/bindings/*)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_BINARY_DIR}/bindingtester.touch"
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bindingtester/tests
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/bindingtester/tests
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bindings ${CMAKE_BINARY_DIR}/bindingtester/tests
|
||||
COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_BINARY_DIR}/bindingtester.touch"
|
||||
COMMENT "Copy test files for bindingtester")
|
||||
|
||||
add_custom_target(copy_binding_output_files DEPENDS ${CMAKE_BINARY_DIR}/bindingtester.touch python_binding fdb_flow_tester)
|
||||
add_custom_command(
|
||||
TARGET copy_binding_output_files
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:fdb_flow_tester> ${bdir}/tests/flow/bin/fdb_flow_tester
|
||||
COMMENT "Copy Flow tester for bindingtester")
|
||||
|
||||
set(generated_binding_files python/fdb/fdboptions.py)
|
||||
if(WITH_JAVA)
|
||||
if(NOT FDB_RELEASE)
|
||||
set(prerelease_string "-PRERELEASE")
|
||||
else()
|
||||
set(prerelease_string "")
|
||||
endif()
|
||||
add_custom_command(
|
||||
TARGET copy_binding_output_files
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_BINARY_DIR}/packages/fdb-java-${CMAKE_PROJECT_VERSION}${prerelease_string}.jar
|
||||
${bdir}/tests/java/foundationdb-client.jar
|
||||
COMMENT "Copy Java bindings for bindingtester")
|
||||
add_dependencies(copy_binding_output_files fat-jar)
|
||||
add_dependencies(copy_binding_output_files foundationdb-tests)
|
||||
set(generated_binding_files ${generated_binding_files} java/foundationdb-tests.jar)
|
||||
endif()
|
||||
|
||||
if(WITH_GO AND NOT OPEN_FOR_IDE)
|
||||
add_dependencies(copy_binding_output_files fdb_go_tester fdb_go)
|
||||
add_custom_command(
|
||||
TARGET copy_binding_output_files
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/bindings/go/bin/_stacktester ${bdir}/tests/go/build/bin/_stacktester
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_BINARY_DIR}/bindings/go/src/github.com/apple/foundationdb/bindings/go/src/fdb/generated.go # SRC
|
||||
${bdir}/tests/go/src/fdb/ # DEST
|
||||
COMMENT "Copy generated.go for bindingtester")
|
||||
endif()
|
||||
|
||||
foreach(generated IN LISTS generated_binding_files)
|
||||
add_custom_command(
|
||||
TARGET copy_binding_output_files
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/bindings/${generated} ${bdir}/tests/${generated}
|
||||
COMMENT "Copy ${generated} to bindingtester")
|
||||
endforeach()
|
||||
|
||||
add_custom_target(copy_bindingtester_binaries
|
||||
DEPENDS ${outfiles} "${CMAKE_BINARY_DIR}/bindingtester.touch" copy_binding_output_files)
|
||||
add_dependencies(copy_bindingtester_binaries strip_only_fdbserver strip_only_fdbcli strip_only_fdb_c)
|
||||
set(tar_file ${CMAKE_BINARY_DIR}/packages/bindingtester-${CMAKE_PROJECT_VERSION}.tar.gz)
|
||||
add_custom_command(
|
||||
OUTPUT ${tar_file}
|
||||
COMMAND ${CMAKE_COMMAND} -E tar czf ${tar_file} *
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bindingtester
|
||||
COMMENT "Pack bindingtester")
|
||||
add_custom_target(bindingtester ALL DEPENDS ${tar_file})
|
||||
add_dependencies(bindingtester copy_bindingtester_binaries)
|
||||
endfunction()
|
||||
|
|
|
@ -130,21 +130,20 @@ function(strip_debug_symbols target)
|
|||
list(APPEND strip_command -o "${out_file}")
|
||||
add_custom_command(OUTPUT "${out_file}"
|
||||
COMMAND ${strip_command} $<TARGET_FILE:${target}>
|
||||
DEPENDS ${target}
|
||||
COMMENT "Stripping symbols from ${target}")
|
||||
add_custom_target(strip_only_${target} DEPENDS ${out_file})
|
||||
if(is_exec AND NOT APPLE)
|
||||
add_custom_command(OUTPUT "${out_file}.debug"
|
||||
DEPENDS strip_only_${target}
|
||||
COMMAND objcopy --verbose --only-keep-debug $<TARGET_FILE:${target}> "${out_file}.debug"
|
||||
COMMAND objcopy --verbose --add-gnu-debuglink="${out_file}.debug" "${out_file}"
|
||||
DEPENDS ${out_file}
|
||||
COMMENT "Copy debug symbols to ${out_name}.debug")
|
||||
list(APPEND out_files "${out_file}.debug")
|
||||
add_custom_target(strip_${target} DEPENDS "${out_file}.debug")
|
||||
else()
|
||||
add_custom_target(strip_${target})
|
||||
endif()
|
||||
add_dependencies(strip_${target} strip_only_${target})
|
||||
add_dependencies(strip_${target} ${target})
|
||||
endif()
|
||||
add_dependencies(strip_targets strip_${target})
|
||||
endfunction()
|
||||
|
||||
|
@ -185,12 +184,12 @@ function(add_flow_target)
|
|||
if(WIN32)
|
||||
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${generated}"
|
||||
COMMAND $<TARGET_FILE:actorcompiler> "${CMAKE_CURRENT_SOURCE_DIR}/${src}" "${CMAKE_CURRENT_BINARY_DIR}/${generated}" ${actor_compiler_flags}
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${src}"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${src}" ${actor_exe}
|
||||
COMMENT "Compile actor: ${src}")
|
||||
else()
|
||||
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${generated}"
|
||||
COMMAND ${MONO_EXECUTABLE} ${actor_exe} "${CMAKE_CURRENT_SOURCE_DIR}/${src}" "${CMAKE_CURRENT_BINARY_DIR}/${generated}" ${actor_compiler_flags} > /dev/null
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${src}"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${src}" ${actor_exe}
|
||||
COMMENT "Compile actor: ${src}")
|
||||
endif()
|
||||
else()
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
if(NOT WIN32)
|
||||
add_subdirectory(TraceLogHelper)
|
||||
add_subdirectory(TestHarness)
|
||||
endif()
|
|
@ -0,0 +1,23 @@
|
|||
# Overview
|
||||
|
||||
This directory provides the files needed to create a Joshua correctness bundle for testing FoundationDB.
|
||||
|
||||
Rigorous testing is central to our engineering process. The features of our core are challenging, requiring us to meet exacting standards of correctness and performance. Data guarantees and transactional integrity must be maintained not only during normal operations but over a broad range of failure scenarios. At the same time, we aim to achieve performance goals such as low latencies and near-linear scalability. To meet these challenges, we use a combined regime of robust simulation, live performance testing, and hardware-based failure testing.
|
||||
|
||||
# Joshua
|
||||
|
||||
Joshua is a powerful tool for testing system correctness. Our simulation technology, called Joshua, is enabled by and tightly integrated with `flow`, our programming language for actor-based concurrency. In addition to generating efficient production code, Flow works with Joshua for simulated execution.
|
||||
|
||||
The major goal of Joshua is to make sure that we find and diagnose issues in simulation rather than the real world. Joshua runs tens of thousands of simulations every night, each one simulating large numbers of component failures. Based on the volume of tests that we run and the increased intensity of the failures in our scenarios, we estimate that we have run the equivalent of roughly one trillion CPU-hours of simulation on FoundationDB.
|
||||
|
||||
Joshua is able to conduct a *deterministic* simulation of an entire FoundationDB cluster within a single-threaded process. Determinism is crucial in that it allows perfect repeatability of a simulated run, facilitating controlled experiments to home in on issues. The simulation steps through time, synchronized across the system, representing a larger amount of real time in a smaller amount of simulated time. In practice, our simulations usually have about a 10-1 factor of real-to-simulated time, which is advantageous for the efficiency of testing.
|
||||
|
||||
We run a broad range of simulations testing various aspects of the system. For example, we run a cycle test that uses key-values pairs arranged in a ring that executes transactions to change the values in a manner designed to maintain the ring's integrity, allowing a clear test of transactional isolation.
|
||||
|
||||
Joshua simulates all physical components of a FoundationDB system, beginning with the number and type of machines in the cluster. For example, Joshua models drive performance on each machine, including drive space and the possibility of the drive filling up. Joshua also models the network, allowing a small amount of code to specify delivery of packets.
|
||||
|
||||
We use Joshua to simulate failures modes at the network, machine, and datacenter levels, including connection failures, degradation of machine performance, machine shutdowns or reboots, machines coming back from the dead, etc. We stress-test all of these failure modes, failing machines at very short intervals, inducing unusually severe loads, and delaying communications channels.
|
||||
|
||||
For a while, there was an informal competition within the engineering team to design failures that found the toughest bugs and issues the most easily. After a period of one-upsmanship, the reigning champion is called "swizzle-clogging". To swizzle-clog, you first pick a random subset of nodes in the cluster. Then, you "clog" (stop) each of their network connections one by one over a few seconds. Finally, you unclog them in a random order, again one by one, until they are all up. This pattern seems to be particularly good at finding deep issues that only happen in the rarest real-world cases.
|
||||
|
||||
Joshua's success has surpassed our expectation and has been vital to our engineering team. It seems unlikely that we would have been able to build FoundationDB without this technology.
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash
|
||||
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
ulimit -S -c unlimited
|
||||
|
||||
unset FDB_NETWORK_OPTION_EXTERNAL_CLIENT_DIRECTORY
|
||||
WORKDIR="$(pwd)/tmp/$$"
|
||||
if [ ! -d "${WORKDIR}" ] ; then
|
||||
mkdir -p "${WORKDIR}"
|
||||
fi
|
||||
DEBUGLEVEL=0 DISPLAYERROR=1 RANDOMTEST=1 WORKDIR="${WORKDIR}" ${SCRIPTDIR}/bindingTestScript.sh 1
|
|
@ -0,0 +1,88 @@
|
|||
#/bin/bash
|
||||
SCRIPTDIR=$( cd "${BASH_SOURCE[0]%\/*}" && pwd )
|
||||
cwd="$(pwd)"
|
||||
BINDIR="${BINDIR:-${SCRIPTDIR}}"
|
||||
LIBDIR="${BINDIR}:${LD_LIBRARY_PATH}"
|
||||
SCRIPTID="${$}"
|
||||
SAVEONERROR="${SAVEONERROR:-1}"
|
||||
PYTHONDIR="${BINDIR}/tests/python"
|
||||
testScript="${BINDIR}/tests/bindingtester/run_binding_tester.sh"
|
||||
VERSION="1.9"
|
||||
|
||||
source ${SCRIPTDIR}/localClusterStart.sh
|
||||
|
||||
# Display syntax
|
||||
if [ "$#" -lt 1 ]
|
||||
then
|
||||
echo "bindingTestScript.sh <number of test cycles>"
|
||||
echo " version: ${VERSION}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cycles="${1}"
|
||||
|
||||
if [ "${DEBUGLEVEL}" -gt 0 ]
|
||||
then
|
||||
echo "Work dir: ${WORKDIR}"
|
||||
echo "Bin dir: ${BINDIR}"
|
||||
echo "Log dir: ${LOGDIR}"
|
||||
echo "Python path: ${PYTHONDIR}"
|
||||
echo "Lib dir: ${LIBDIR}"
|
||||
echo "Cluster String: ${FDBCLUSTERTEXT}"
|
||||
echo "Script Id: ${SCRIPTID}"
|
||||
echo "Version: ${VERSION}"
|
||||
fi
|
||||
|
||||
# Begin the cluster using the logic in localClusterStart.sh.
|
||||
startCluster
|
||||
|
||||
# Stop the cluster on exit
|
||||
trap "stopCluster" EXIT
|
||||
|
||||
# Display user message
|
||||
if [ "${status}" -ne 0 ]; then
|
||||
:
|
||||
elif ! displayMessage "Running binding tester"
|
||||
then
|
||||
echo 'Failed to display user message'
|
||||
let status="${status} + 1"
|
||||
|
||||
elif ! PYTHONPATH="${PYTHONDIR}" LD_LIBRARY_PATH="${LIBDIR}" FDB_CLUSTER_FILE="${FDBCONF}" LOGSTDOUT=1 CONSOLELOG="${WORKDIR}/console.log" "${testScript}" "${cycles}" "${WORKDIR}/errors/run.log"
|
||||
then
|
||||
if [ "${DEBUGLEVEL}" -gt 0 ]; then
|
||||
printf "\n%-16s %-40s \n" "$(date '+%F %H-%M-%S')" "Failed to complete binding tester in ${SECONDS} seconds."
|
||||
fi
|
||||
let status="${status} + 1"
|
||||
|
||||
elif [ "${DEBUGLEVEL}" -gt 0 ]; then
|
||||
printf "\n%-16s %-40s \n" "$(date '+%F %H-%M-%S')" "Completed binding tester in ${SECONDS} seconds"
|
||||
fi
|
||||
|
||||
# Display directory and log information, if an error occurred
|
||||
if [ "${status}" -ne 0 ]
|
||||
then
|
||||
ls "${WORKDIR}" &> "${LOGDIR}/dir.log"
|
||||
ps -eafwH &> "${LOGDIR}/process-preclean.log"
|
||||
if [ -f "${FDBCONF}" ]; then
|
||||
cp -f "${FDBCONF}" "${LOGDIR}/"
|
||||
fi
|
||||
# Display the severity errors
|
||||
if [ -d "${LOGDIR}" ]; then
|
||||
grep -ir 'Severity="40"' "${LOGDIR}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Save debug information files, environment, and log information, if an error occurred
|
||||
if [ "${status}" -ne 0 ] && [ "${SAVEONERROR}" -gt 0 ]; then
|
||||
ps -eafwH &> "${LOGDIR}/process-exit.log"
|
||||
netstat -na &> "${LOGDIR}/netstat.log"
|
||||
df -h &> "${LOGDIR}/disk.log"
|
||||
env &> "${LOGDIR}/env.log"
|
||||
fi
|
||||
|
||||
# Stop the cluster
|
||||
if stopCluster; then
|
||||
unset FDBSERVERID
|
||||
fi
|
||||
|
||||
exit "${status}"
|
|
@ -0,0 +1,28 @@
|
|||
#!/bin/bash -u
|
||||
|
||||
# Look for the start cluster log file.
|
||||
notstarted=0
|
||||
for file in `find . -name startcluster.log` ; do
|
||||
if [ -n "$(grep 'Could not create database' "${file}")" ] ; then
|
||||
echo "${file}:"
|
||||
cat "${file}"
|
||||
echo
|
||||
notstarted=1
|
||||
fi
|
||||
done
|
||||
|
||||
# Print information on how the server didn't start.
|
||||
if [ "${notstarted}" -gt 0 ] ; then
|
||||
for file in `find . -name fdbclient.log` ; do
|
||||
echo "${file}:"
|
||||
cat "${file}"
|
||||
echo
|
||||
done
|
||||
fi
|
||||
|
||||
# Print the test output.
|
||||
for file in `find . -name console.log` ; do
|
||||
echo "${file}:"
|
||||
cat "${file}"
|
||||
echo
|
||||
done
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
OLDBINDIR="${OLDBINDIR:-/app/deploy/global_data/oldBinaries}"
|
||||
mono bin/TestHarness.exe joshua-run "${OLDBINDIR}" false
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash -u
|
||||
for file in `find . -name 'trace*.xml'` ; do
|
||||
mono ./bin/TestHarness.exe summarize "${file}" summary.xml "" JoshuaTimeout true
|
||||
done
|
|
@ -0,0 +1,402 @@
|
|||
#!/bin/bash
|
||||
SCRIPTDIR="${SCRIPTDIR:-$( cd "${BASH_SOURCE[0]%\/*}" && pwd )}"
|
||||
DEBUGLEVEL="${DEBUGLEVEL:-1}"
|
||||
WORKDIR="${WORKDIR:-${SCRIPTDIR}/tmp/fdb.work}"
|
||||
LOGDIR="${WORKDIR}/log"
|
||||
ETCDIR="${WORKDIR}/etc"
|
||||
BINDIR="${BINDIR:-${SCRIPTDIR}}"
|
||||
FDBPORTSTART="${FDBPORTSTART:-4000}"
|
||||
FDBPORTTOTAL="${FDBPORTTOTAL:-1000}"
|
||||
SERVERCHECKS="${SERVERCHECKS:-10}"
|
||||
CONFIGUREWAIT="${CONFIGUREWAIT:-240}"
|
||||
FDBCONF="${ETCDIR}/fdb.cluster"
|
||||
LOGFILE="${LOGFILE:-${LOGDIR}/startcluster.log}"
|
||||
AUDITCLUSTER="${AUDITCLUSTER:-0}"
|
||||
AUDITLOG="${AUDITLOG:-/tmp/audit-cluster.log}"
|
||||
|
||||
# Initialize the variables
|
||||
status=0
|
||||
messagetime=0
|
||||
messagecount=0
|
||||
|
||||
# Do nothing, if cluster string is already defined
|
||||
if [ -n "${FDBCLUSTERTEXT}" ]
|
||||
then
|
||||
:
|
||||
# Otherwise, define the cluster text
|
||||
else
|
||||
# Define a random ip address and port on localhost
|
||||
if [ -z "${IPADDRESS}" ]; then
|
||||
let index2="${RANDOM} % 256"
|
||||
let index3="${RANDOM} % 256"
|
||||
let index4="(${RANDOM} % 255) + 1"
|
||||
IPADDRESS="127.${index2}.${index3}.${index4}"
|
||||
fi
|
||||
if [ -z "${FDBPORT}" ]; then
|
||||
let FDBPORT="(${RANDOM} % ${FDBPORTTOTAL}) + ${FDBPORTSTART}"
|
||||
fi
|
||||
FDBCLUSTERTEXT="${IPADDRESS}:${FDBPORT}"
|
||||
fi
|
||||
|
||||
function log
|
||||
{
|
||||
local status=0
|
||||
if [ "$#" -lt 1 ]
|
||||
then
|
||||
echo "Usage: log <message> [echo]"
|
||||
echo
|
||||
echo "Logs the message and timestamp to LOGFILE (${LOGFILE}) and, if the"
|
||||
echo "second argument is either not present or is set to 1, stdout."
|
||||
let status="${status} + 1"
|
||||
else
|
||||
# Log to stdout.
|
||||
if [ "$#" -lt 2 ] || [ "${2}" -ge 1 ]
|
||||
then
|
||||
echo "${1}"
|
||||
fi
|
||||
|
||||
# Log to file.
|
||||
datestr=$(date +"%Y-%m-%d %H:%M:%S (%s)")
|
||||
dir=$(dirname "${LOGFILE}")
|
||||
if ! [ -d "${dir}" ] && ! mkdir -p "${dir}"
|
||||
then
|
||||
echo "Could not create directory to log output."
|
||||
let status="${status} + 1"
|
||||
elif ! [ -f "${LOGFILE}" ] && ! touch "${LOGFILE}"
|
||||
then
|
||||
echo "Could not create file ${LOGFILE} to log output."
|
||||
let status="${status} + 1"
|
||||
elif ! echo "[ ${datestr} ] ${1}" >> "${LOGFILE}"
|
||||
then
|
||||
echo "Could not log output to ${LOGFILE}."
|
||||
let status="${status} + 1"
|
||||
fi
|
||||
fi
|
||||
|
||||
return "${status}"
|
||||
}
|
||||
|
||||
# Display a message for the user.
|
||||
function displayMessage
|
||||
{
|
||||
local status=0
|
||||
|
||||
if [ "$#" -lt 1 ]
|
||||
then
|
||||
echo "displayMessage <message>"
|
||||
let status="${status} + 1"
|
||||
elif ! log "${1}" 0
|
||||
then
|
||||
log "Could not write message to file."
|
||||
else
|
||||
# Increment the message counter
|
||||
let messagecount="${messagecount} + 1"
|
||||
|
||||
# Display successful message, if previous message
|
||||
if [ "${messagecount}" -gt 1 ]
|
||||
then
|
||||
# Determine the amount of transpired time
|
||||
let timespent="${SECONDS}-${messagetime}"
|
||||
|
||||
if [ "${DEBUGLEVEL}" -gt 0 ]; then
|
||||
printf "... done in %3d seconds\n" "${timespent}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Display message
|
||||
if [ "${DEBUGLEVEL}" -gt 0 ]; then
|
||||
printf "%-16s %-35s " "$(date "+%F %H-%M-%S")" "$1"
|
||||
fi
|
||||
|
||||
# Update the variables
|
||||
messagetime="${SECONDS}"
|
||||
fi
|
||||
|
||||
return "${status}"
|
||||
}
|
||||
|
||||
# Create the directories used by the server.
|
||||
function createDirectories
|
||||
{
|
||||
local status=0
|
||||
|
||||
# Display user message
|
||||
if ! displayMessage "Creating directories"
|
||||
then
|
||||
echo 'Failed to display user message'
|
||||
let status="${status} + 1"
|
||||
|
||||
elif ! mkdir -p "${LOGDIR}" "${ETCDIR}"
|
||||
then
|
||||
log "Failed to create directories"
|
||||
let status="${status} + 1"
|
||||
|
||||
# Display user message
|
||||
elif ! displayMessage "Setting file permissions"
|
||||
then
|
||||
log 'Failed to display user message'
|
||||
let status="${status} + 1"
|
||||
|
||||
elif ! chmod 755 "${BINDIR}/fdbserver" "${BINDIR}/fdbcli"
|
||||
then
|
||||
log "Failed to set file permissions"
|
||||
let status="${status} + 1"
|
||||
|
||||
else
|
||||
while read filepath
|
||||
do
|
||||
if [ -f "${filepath}" ] && [ ! -x "${filepath}" ]
|
||||
then
|
||||
# if [ "${DEBUGLEVEL}" -gt 1 ]; then
|
||||
# log " Enable executable: ${filepath}"
|
||||
# fi
|
||||
log " Enable executable: ${filepath}" "${DEBUGLEVEL}"
|
||||
if ! chmod 755 "${filepath}"
|
||||
then
|
||||
log "Failed to set executable for file: ${filepath}"
|
||||
let status="${status} + 1"
|
||||
fi
|
||||
fi
|
||||
done < <(find "${BINDIR}" -iname '*.py' -o -iname '*.rb' -o -iname 'fdb_flow_tester' -o -iname '_stacktester' -o -iname '*.js' -o -iname '*.sh' -o -iname '*.ksh')
|
||||
fi
|
||||
|
||||
return ${status}
|
||||
}
|
||||
|
||||
# Create a cluster file for the local cluster.
|
||||
function createClusterFile
|
||||
{
|
||||
local status=0
|
||||
|
||||
if [ "${status}" -ne 0 ]; then
|
||||
:
|
||||
# Display user message
|
||||
elif ! displayMessage "Creating Fdb Cluster file"
|
||||
then
|
||||
log 'Failed to display user message'
|
||||
let status="${status} + 1"
|
||||
else
|
||||
description=$(LC_CTYPE=C tr -dc A-Za-z0-9 < /dev/urandom 2> /dev/null | head -c 8)
|
||||
random_str=$(LC_CTYPE=C tr -dc A-Za-z0-9 < /dev/urandom 2> /dev/null | head -c 8)
|
||||
echo "${description}:${random_str}@${FDBCLUSTERTEXT}" > "${FDBCONF}"
|
||||
fi
|
||||
|
||||
if [ "${status}" -ne 0 ]; then
|
||||
:
|
||||
elif ! chmod 0664 "${FDBCONF}"; then
|
||||
log "Failed to set permissions on fdbconf: ${FDBCONF}"
|
||||
let status="${status} + 1"
|
||||
fi
|
||||
|
||||
return ${status}
|
||||
}
|
||||
|
||||
# Stop the Cluster from running.
|
||||
function stopCluster
|
||||
{
|
||||
local status=0
|
||||
|
||||
# Add an audit entry, if enabled
|
||||
if [ "${AUDITCLUSTER}" -gt 0 ]; then
|
||||
printf '%-15s (%6s) Stopping cluster %-20s (%6s): %s\n' "$(date +'%Y-%m-%d %H:%M:%S')" "${$}" "${FDBCLUSTERTEXT}" "${FDBSERVERID}" >> "${AUDITLOG}"
|
||||
fi
|
||||
if [ -z "${FDBSERVERID}" ]; then
|
||||
log 'FDB Server process is not defined'
|
||||
let status="${status} + 1"
|
||||
elif ! kill -0 "${FDBSERVERID}"; then
|
||||
log "Failed to locate FDB Server process (${FDBSERVERID})"
|
||||
let status="${status} + 1"
|
||||
elif "${BINDIR}/fdbcli" -C "${FDBCONF}" --exec "kill; kill ${FDBCLUSTERTEXT}; sleep 3" --timeout 120 &>> "${LOGDIR}/fdbcli-kill.log"
|
||||
then
|
||||
# Ensure that process is dead
|
||||
if ! kill -0 "${FDBSERVERID}" 2> /dev/null; then
|
||||
log "Killed cluster (${FDBSERVERID}) via cli"
|
||||
elif ! kill -9 "${FDBSERVERID}"; then
|
||||
log "Failed to kill FDB Server process (${FDBSERVERID}) via cli or kill command"
|
||||
let status="${status} + 1"
|
||||
else
|
||||
log "Forcibly killed FDB Server process (${FDBSERVERID}) since cli failed"
|
||||
fi
|
||||
elif ! kill -9 "${FDBSERVERID}"; then
|
||||
log "Failed to forcibly kill FDB Server process (${FDBSERVERID})"
|
||||
let status="${status} + 1"
|
||||
else
|
||||
log "Forcibly killed FDB Server process (${FDBSERVERID})"
|
||||
fi
|
||||
return "${status}"
|
||||
}
|
||||
|
||||
# Start the server running.
|
||||
function startFdbServer
|
||||
{
|
||||
local status=0
|
||||
|
||||
# Add an audit entry, if enabled
|
||||
if [ "${AUDITCLUSTER}" -gt 0 ]; then
|
||||
printf '%-15s (%6s) Starting cluster %-20s\n' "$(date +'%Y-%m-%d %H:%M:%S')" "${$}" "${FDBCLUSTERTEXT}" >> "${AUDITLOG}"
|
||||
fi
|
||||
|
||||
if ! displayMessage "Starting Fdb Server"
|
||||
then
|
||||
log 'Failed to display user message'
|
||||
let status="${status} + 1"
|
||||
|
||||
else
|
||||
"${BINDIR}/fdbserver" --knob_disable_posix_kernel_aio=1 -C "${FDBCONF}" -p "${FDBCLUSTERTEXT}" -L "${LOGDIR}" -d "${WORKDIR}/fdb/${$}" &> "${LOGDIR}/fdbserver.log" &
|
||||
if [ "${?}" -ne 0 ]
|
||||
then
|
||||
log "Failed to start FDB Server"
|
||||
let status="${status} + 1"
|
||||
else
|
||||
FDBSERVERID="${!}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${FDBSERVERID}" ]; then
|
||||
log "FDB Server start failed because no process"
|
||||
let status="${status} + 1"
|
||||
elif ! kill -0 "${FDBSERVERID}" ; then
|
||||
log "FDB Server start failed because process terminated unexpectedly"
|
||||
let status="${status} + 1"
|
||||
fi
|
||||
|
||||
return ${status}
|
||||
}
|
||||
|
||||
function getStatus
|
||||
{
|
||||
local status=0
|
||||
|
||||
if [ "${status}" -ne 0 ]; then
|
||||
:
|
||||
elif ! date &>> "${LOGDIR}/fdbclient.log"
|
||||
then
|
||||
log 'Failed to get date'
|
||||
let status="${status} + 1"
|
||||
elif ! "${BINDIR}/fdbcli" -C "${FDBCONF}" --exec 'status json' --timeout 120 &>> "${LOGDIR}/fdbclient.log"
|
||||
then
|
||||
log 'Failed to get status from fdbcli'
|
||||
let status="${status} + 1"
|
||||
elif ! date &>> "${LOGDIR}/fdbclient.log"
|
||||
then
|
||||
log 'Failed to get date'
|
||||
let status="${status} + 1"
|
||||
fi
|
||||
|
||||
return ${status}
|
||||
}
|
||||
|
||||
# Verify that the cluster is available.
|
||||
function verifyAvailable
|
||||
{
|
||||
local status=0
|
||||
|
||||
if [ -z "${FDBSERVERID}" ]; then
|
||||
log "FDB Server process is not defined."
|
||||
let status="${status} + 1"
|
||||
# Verify that the server is running.
|
||||
elif ! kill -0 "${FDBSERVERID}"
|
||||
then
|
||||
log "FDB server process (${FDBSERVERID}) is not running"
|
||||
let status="${status} + 1"
|
||||
# Display user message.
|
||||
elif ! displayMessage "Checking cluster availability"
|
||||
then
|
||||
log 'Failed to display user message'
|
||||
let status="${status} + 1"
|
||||
# Determine if status json says the database is available.
|
||||
else
|
||||
avail=`"${BINDIR}/fdbcli" -C "${FDBCONF}" --exec 'status json' --timeout "${SERVERCHECKS}" 2> /dev/null | grep -E '"database_available"|"available"' | grep 'true'`
|
||||
log "Avail value: ${avail}" "${DEBUGLEVEL}"
|
||||
if [[ -n "${avail}" ]] ; then
|
||||
:
|
||||
else
|
||||
let status="${status} + 1"
|
||||
fi
|
||||
fi
|
||||
return "${status}"
|
||||
}
|
||||
|
||||
# Configure the database on the server.
|
||||
function createDatabase
|
||||
{
|
||||
local status=0
|
||||
|
||||
if [ "${status}" -ne 0 ]; then
|
||||
:
|
||||
# Ensure that the server is running
|
||||
elif ! kill -0 "${FDBSERVERID}"
|
||||
then
|
||||
log "FDB server process: (${FDBSERVERID}) is not running"
|
||||
let status="${status} + 1"
|
||||
|
||||
# Display user message
|
||||
elif ! displayMessage "Creating database"
|
||||
then
|
||||
log 'Failed to display user message'
|
||||
let status="${status} + 1"
|
||||
elif ! echo "Client log:" &> "${LOGDIR}/fdbclient.log"
|
||||
then
|
||||
log 'Failed to create fdbclient.log'
|
||||
let status="${status} + 1"
|
||||
elif ! getStatus
|
||||
then
|
||||
log 'Failed to get status'
|
||||
let status="${status} + 1"
|
||||
|
||||
# Configure the database.
|
||||
else
|
||||
"${BINDIR}/fdbcli" -C "${FDBCONF}" --exec 'configure new single memory; status' --timeout "${CONFIGUREWAIT}" --log --log-dir "${LOGDIR}" &>> "${LOGDIR}/fdbclient.log"
|
||||
|
||||
if ! displayMessage "Checking if config succeeded"
|
||||
then
|
||||
log 'Failed to display user message.'
|
||||
fi
|
||||
|
||||
iteration=0
|
||||
while [[ "${iteration}" -lt "${SERVERCHECKS}" ]] && ! verifyAvailable
|
||||
do
|
||||
log "Database not created (iteration ${iteration})."
|
||||
let iteration="${iteration} + 1"
|
||||
done
|
||||
|
||||
if ! verifyAvailable
|
||||
then
|
||||
log "Failed to create database via cli"
|
||||
getStatus
|
||||
cat "${LOGDIR}/fdbclient.log"
|
||||
log "Ignoring -- moving on"
|
||||
#let status="${status} + 1"
|
||||
fi
|
||||
fi
|
||||
|
||||
return ${status}
|
||||
}
|
||||
|
||||
# Begin the local cluster from scratch.
|
||||
function startCluster
|
||||
{
|
||||
local status=0
|
||||
|
||||
if [ "${status}" -ne 0 ]; then
|
||||
:
|
||||
elif ! createDirectories
|
||||
then
|
||||
log "Could not create directories."
|
||||
let status="${status} + 1"
|
||||
elif ! createClusterFile
|
||||
then
|
||||
log "Could not create cluster file."
|
||||
let status="${status} + 1"
|
||||
elif ! startFdbServer
|
||||
then
|
||||
log "Could not start FDB server."
|
||||
let status="${status} + 1"
|
||||
elif ! createDatabase
|
||||
then
|
||||
log "Could not create database."
|
||||
let status="${status} + 1"
|
||||
fi
|
||||
|
||||
return ${status}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
OLDBINDIR="${OLDBINDIR:-/app/deploy/global_data/oldBinaries}"
|
||||
mono bin/TestHarness.exe joshua-run "${OLDBINDIR}" true
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash -u
|
||||
for file in `find . -name 'trace*.xml'` ; do
|
||||
for valgrindFile in `find . -name 'valgrind*.xml'` ; do
|
||||
mono ./bin/TestHarness.exe summarize "${file}" summary.xml "${valgrindFile}" JoshuaTimeout true
|
||||
done
|
||||
done
|
|
@ -0,0 +1,17 @@
|
|||
set(SRCS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Program.cs
|
||||
Properties/AssemblyInfo.cs)
|
||||
|
||||
set(TEST_HARNESS_REFERENCES
|
||||
"-r:System,System.Core,System.Xml.Linq,System.Data.DataSetExtensions,Microsoft.CSharp,System.Data,System.Xml,System.Runtime.Serialization,${TraceLogHelperDll}")
|
||||
|
||||
set(out_file ${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Program.cs.cmake ${CMAKE_CURRENT_BINARY_DIR}/Program.cs)
|
||||
|
||||
add_custom_command(OUTPUT ${out_file}
|
||||
COMMAND ${MCS_EXECUTABLE} ARGS ${TEST_HARNESS_REFERENCES} ${SRCS} "-target:exe" "-out:${out_file}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${SRCS} TraceLogHelper
|
||||
COMMENT "Compile TestHarness" VERBATIM)
|
||||
add_custom_target(TestHarness DEPENDS ${out_file})
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* AssemblyInfo.cs
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2020 Apple Inc. and the FoundationDB project authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("TestHarness")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Apple Inc.")]
|
||||
[assembly: AssemblyProduct("TestHarness")]
|
||||
[assembly: AssemblyCopyright("Copyright © Apple Inc. 2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("92d9df90-6d48-4558-bb25-d878ea69b4bd")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{6A6B7D20-EB7E-4768-BDE2-21FE0C32F17A}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>TestHarness</RootNamespace>
|
||||
<AssemblyName>TestHarness</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<OutputPath>$(SolutionDir)bin\$(Configuration)\</OutputPath>
|
||||
<IntermediateOutputPath>$(SystemDrive)\temp\msvcfdb\$(Configuration)\TestHarness\</IntermediateOutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisIgnoreBuiltInRuleSets>true</CodeAnalysisIgnoreBuiltInRuleSets>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TraceLogHelper\TraceLogHelper.csproj">
|
||||
<Project>{1FA45F13-1015-403C-9115-CEFFDD522B20}</Project>
|
||||
<Name>TraceLogHelper</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,20 @@
|
|||
set(SRCS
|
||||
Event.cs
|
||||
JsonParser.cs
|
||||
Properties/AssemblyInfo.cs
|
||||
TraceLogUtil.cs
|
||||
XmlParser.cs)
|
||||
|
||||
set(TRACE_LOG_HELPER_REFERENCES
|
||||
"-r:System,System.Core,System.Runtime.Serialization,System.Xml.Linq,System.Data.DataSetExtensions,Microsoft.CSharp,System.Data,System.Xml")
|
||||
|
||||
|
||||
set(out_file ${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll)
|
||||
|
||||
add_custom_command(OUTPUT ${out_file}
|
||||
COMMAND ${MCS_EXECUTABLE} ARGS ${TRACE_LOG_HELPER_REFERENCES} ${SRCS} "-target:library" "-out:${out_file}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${SRCS}
|
||||
COMMENT "Compile TraceLogHelper" VERBATIM)
|
||||
add_custom_target(TraceLogHelper DEPENDS ${out_file})
|
||||
set(TraceLogHelperDll "${out_file}" PARENT_SCOPE)
|
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* Event.cs
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2020 Apple Inc. and the FoundationDB project authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Dynamic;
|
||||
|
||||
namespace Magnesium
|
||||
{
|
||||
public enum Severity
|
||||
{
|
||||
SevDebug=5,
|
||||
SevInfo=10,
|
||||
SevWarn=20,
|
||||
SevWarnAlways=30,
|
||||
SevError=40,
|
||||
};
|
||||
|
||||
public class Event
|
||||
{
|
||||
public double Time { get; set; }
|
||||
public Severity Severity { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Machine { get; set; }
|
||||
public string ID { get; set; }
|
||||
public string WorkerDesc { get { return Machine + " " + ID; } }
|
||||
public string TraceFile { get; set; }
|
||||
public System.Xml.Linq.XElement original { get; set; }
|
||||
static MyExpando emptyDetails = new MyExpando(new Dictionary<string, Object>());
|
||||
MyExpando _Details = emptyDetails;
|
||||
public dynamic Details { get{ return _Details; } }
|
||||
public IDictionary<string,Object> DDetails { get { return _Details._members; }
|
||||
set {
|
||||
_Details = new MyExpando(value);
|
||||
//foreach(var v in value)
|
||||
// _Details.SetMember(v.Key, v.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public Event ShallowCopy()
|
||||
{
|
||||
return (Event)MemberwiseClone();
|
||||
}
|
||||
|
||||
class MyExpando : DynamicObject
|
||||
{
|
||||
public MyExpando(IDictionary<string, Object> init)
|
||||
{
|
||||
_members = init;
|
||||
}
|
||||
|
||||
public IDictionary<string, Object> _members =
|
||||
new Dictionary<string, Object>();
|
||||
|
||||
/// <summary>
|
||||
/// When a new property is set,
|
||||
/// add the property name and value to the dictionary
|
||||
/// </summary>
|
||||
public override bool TrySetMember
|
||||
(SetMemberBinder binder, Object value)
|
||||
{
|
||||
if (!_members.ContainsKey(binder.Name))
|
||||
_members.Add(binder.Name, value);
|
||||
else
|
||||
_members[binder.Name] = value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetMember(string name, Object value)
|
||||
{
|
||||
if (!_members.ContainsKey(name))
|
||||
_members.Add(name, value);
|
||||
else
|
||||
_members[name] = value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When user accesses something, return the value if we have it
|
||||
/// </summary>
|
||||
public override bool TryGetMember
|
||||
(GetMemberBinder binder, out Object result)
|
||||
{
|
||||
if (_members.ContainsKey(binder.Name))
|
||||
{
|
||||
result = _members[binder.Name];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.TryGetMember(binder, out result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If a property value is a delegate, invoke it
|
||||
/// </summary>
|
||||
public override bool TryInvokeMember
|
||||
(InvokeMemberBinder binder, Object[] args, out Object result)
|
||||
{
|
||||
if (_members.ContainsKey(binder.Name)
|
||||
&& _members[binder.Name] is Delegate)
|
||||
{
|
||||
result = (_members[binder.Name] as Delegate).DynamicInvoke(args);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.TryInvokeMember(binder, args, out result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Return all dynamic member names
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
public override IEnumerable<string> GetDynamicMemberNames()
|
||||
{
|
||||
return _members.Keys;
|
||||
}
|
||||
}
|
||||
|
||||
public string FormatTestError(bool includeDetails)
|
||||
{
|
||||
string s = Type;
|
||||
if (Type == "InternalError")
|
||||
s = string.Format("{0} {1} {2}", Type, Details.File, Details.Line);
|
||||
else if (Type == "TestFailure")
|
||||
s = string.Format("{0} {1}", Type, Details.Reason);
|
||||
else if (Type == "ValgrindError")
|
||||
s = string.Format("{0} {1}", Type, Details.What);
|
||||
else if (Type == "ExitCode")
|
||||
s = string.Format("{0} 0x{1:x}", Type, int.Parse(Details.Code));
|
||||
else if (Type == "StdErrOutput")
|
||||
s = string.Format("{0}: {1}", Type, Details.Output);
|
||||
else if (Type == "BTreeIntegrityCheck")
|
||||
s = string.Format("{0}: {1}", Type, Details.ErrorDetail);
|
||||
if (DDetails.ContainsKey("Error"))
|
||||
s += " " + Details.Error;
|
||||
if (DDetails.ContainsKey("WinErrorCode"))
|
||||
s += " " + Details.WinErrorCode;
|
||||
if (DDetails.ContainsKey("LinuxErrorCode"))
|
||||
s += " " + Details.LinuxErrorCode;
|
||||
if (DDetails.ContainsKey("Status"))
|
||||
s += " Status=" + Details.Status;
|
||||
if (DDetails.ContainsKey("In"))
|
||||
s += " In " + Details.In;
|
||||
if (DDetails.ContainsKey("SQLiteError"))
|
||||
s += string.Format(" SQLiteError={0}({1})", Details.SQLiteError, Details.SQLiteErrorCode);
|
||||
if (DDetails.ContainsKey("Details") && includeDetails)
|
||||
s += ": " + Details.Details;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public class TestPlan : Event
|
||||
{
|
||||
public string TestUID;
|
||||
public string TestFile;
|
||||
public int randomSeed;
|
||||
public bool Buggify;
|
||||
public bool DeterminismCheck;
|
||||
public string OldBinary;
|
||||
|
||||
};
|
||||
|
||||
public class Test : TestPlan
|
||||
{
|
||||
public string SourceVersion;
|
||||
public double SimElapsedTime;
|
||||
public double RealElapsedTime;
|
||||
public bool ok;
|
||||
public int passed, failed;
|
||||
public int randomUnseed;
|
||||
public long peakMemUsage;
|
||||
|
||||
public Event[] events; // Summarized events during the test
|
||||
};
|
||||
|
||||
public struct AreaGraphPoint
|
||||
{
|
||||
public double X { get; set; }
|
||||
public double Y { get; set; }
|
||||
};
|
||||
|
||||
public struct LineGraphPoint
|
||||
{
|
||||
public double X { get; set; }
|
||||
public double Y { get; set; }
|
||||
public object Category { get; set; }
|
||||
};
|
||||
|
||||
public class Interval
|
||||
{
|
||||
public double Begin { get; set; }
|
||||
public double End { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string Color { get; set; }
|
||||
public string Detail { get; set; }
|
||||
public object Object { get; set; }
|
||||
};
|
||||
|
||||
public class MachineRole : Interval
|
||||
{
|
||||
public string Machine { get; set; }
|
||||
public string Role { get; set; }
|
||||
};
|
||||
|
||||
public class Location
|
||||
{
|
||||
public string Name;
|
||||
public int Y;
|
||||
};
|
||||
|
||||
//Specifies the type of LocationTime being used
|
||||
public enum LocationTimeOp
|
||||
{
|
||||
//Designates a location in the code at a particular time
|
||||
Normal = 0,
|
||||
|
||||
//Designates that this LocationTime maps one event ID to another
|
||||
MapId
|
||||
};
|
||||
|
||||
//Struct which houses a location in the code and a time which that location was hit
|
||||
//The ID signifies the particular pass through the code
|
||||
//Some of these objects act as special markers used to connect one ID to another
|
||||
public struct LocationTime
|
||||
{
|
||||
public int id;
|
||||
public double Time;
|
||||
public Location Loc;
|
||||
|
||||
public LocationTimeOp locationTimeOp;
|
||||
|
||||
//If locationTimeOp == MapId, then this will hold the id of the event that should come after the id specified in the id field
|
||||
public int childId;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* JsonParser.cs
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2020 Apple Inc. and the FoundationDB project authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization.Json;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.XPath;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Magnesium
|
||||
{
|
||||
public static class JsonParser
|
||||
{
|
||||
static Random r = new Random();
|
||||
|
||||
public static IEnumerable<Event> Parse(System.IO.Stream stream, string file,
|
||||
bool keepOriginalElement = false, double startTime = -1, double endTime = Double.MaxValue,
|
||||
double samplingFactor = 1.0)
|
||||
{
|
||||
using (var reader = new System.IO.StreamReader(stream))
|
||||
{
|
||||
string line;
|
||||
while((line = reader.ReadLine()) != null)
|
||||
{
|
||||
XElement root = XElement.Load(JsonReaderWriterFactory.CreateJsonReader(new MemoryStream(Encoding.UTF8.GetBytes(line)), new XmlDictionaryReaderQuotas()));
|
||||
Event ev = null;
|
||||
try
|
||||
{
|
||||
ev = ParseEvent(root, file, keepOriginalElement, startTime, endTime, samplingFactor);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception(string.Format("Failed to parse JSON {0}", root), e);
|
||||
}
|
||||
if (ev != null) yield return ev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Event ParseEvent(XElement xEvent, string file, bool keepOriginalElement, double startTime, double endTime, double samplingFactor)
|
||||
{
|
||||
if (samplingFactor != 1.0 && r.NextDouble() > samplingFactor)
|
||||
return null;
|
||||
|
||||
XElement trackLatestElement = xEvent.XPathSelectElement("//TrackLatestType");
|
||||
bool rolledEvent = trackLatestElement != null && trackLatestElement.Value.Equals("Rolled");
|
||||
String timeElement = (rolledEvent) ? "OriginalTime" : "Time";
|
||||
double eventTime = double.Parse(xEvent.XPathSelectElement("//" + timeElement).Value);
|
||||
|
||||
if (eventTime < startTime || eventTime > endTime)
|
||||
return null;
|
||||
|
||||
return new Event {
|
||||
Severity = (Severity)int.Parse(xEvent.XPathSelectElement("//Severity").ValueOrDefault("40")),
|
||||
Type = string.Intern(xEvent.XPathSelectElement("//Type").Value),
|
||||
Time = eventTime,
|
||||
Machine = string.Intern(xEvent.XPathSelectElement("//Machine").Value),
|
||||
ID = string.Intern(xEvent.XPathSelectElement("//ID").ValueOrDefault("0")),
|
||||
TraceFile = file,
|
||||
DDetails = xEvent.Elements()
|
||||
.Where(a=>a.Name != "Type" && a.Name != "Time" && a.Name != "Machine" && a.Name != "ID" && a.Name != "Severity" && (!rolledEvent || a.Name != "OriginalTime"))
|
||||
.ToDictionary(a=>string.Intern(a.Name.LocalName), a=>(object)a.Value),
|
||||
original = keepOriginalElement ? xEvent : null
|
||||
};
|
||||
}
|
||||
|
||||
private static string ValueOrDefault( this XElement attr, string def ) {
|
||||
if (attr == null) return def;
|
||||
else return attr.Value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* AssemblyInfo.cs
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2020 Apple Inc. and the FoundationDB project authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("TraceLogHelper")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Apple Inc.")]
|
||||
[assembly: AssemblyProduct("TraceLogHelper")]
|
||||
[assembly: AssemblyCopyright("Copyright © Apple Inc. 2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("ec8f2fdc-25fe-4958-b807-c30bd1da341b")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{1FA45F13-1015-403C-9115-CEFFDD522B20}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>TraceLogHelper</RootNamespace>
|
||||
<AssemblyName>TraceLogHelper</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Event.cs" />
|
||||
<Compile Include="JsonParser.cs" />
|
||||
<Compile Include="TraceLogUtil.cs" />
|
||||
<Compile Include="XmlParser.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* TraceLogUtil.cs
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2020 Apple Inc. and the FoundationDB project authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Magnesium
|
||||
{
|
||||
public static class TraceLogUtil
|
||||
{
|
||||
public static IEnumerable<Event> IdentifyFailedTestPlans(IEnumerable<Event> events)
|
||||
{
|
||||
var failedPlans = new Dictionary<string, TestPlan>();
|
||||
foreach (var ev in events)
|
||||
{
|
||||
var tp = ev as TestPlan;
|
||||
if (tp == null || tp.TestUID == "")
|
||||
{
|
||||
yield return ev;
|
||||
continue;
|
||||
}
|
||||
var t = tp as Test;
|
||||
if (t == null)
|
||||
{
|
||||
if (!failedPlans.ContainsKey(tp.TestUID + tp.TraceFile))
|
||||
{
|
||||
failedPlans.Add(tp.TestUID + tp.TraceFile, tp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
failedPlans.Remove(tp.TestUID + tp.TraceFile);
|
||||
if ((tp.TraceFile != null) && tp.TraceFile.EndsWith("-2.txt")) failedPlans.Remove(tp.TestUID + tp.TraceFile.Split('-')[0] + "-1.txt");
|
||||
yield return ev;
|
||||
}
|
||||
}
|
||||
foreach (var p in failedPlans.Values)
|
||||
yield return new Test
|
||||
{
|
||||
Type = "FailedTestPlan",
|
||||
Time = p.Time,
|
||||
Machine = p.Machine,
|
||||
TestUID = p.TestUID,
|
||||
TestFile = p.TestFile,
|
||||
randomSeed = p.randomSeed,
|
||||
Buggify = p.Buggify,
|
||||
DeterminismCheck = p.DeterminismCheck,
|
||||
OldBinary = p.OldBinary,
|
||||
events = new Event[] {
|
||||
new Event {
|
||||
Severity = Severity.SevWarnAlways,
|
||||
Type = "TestNotSummarized",
|
||||
Time = p.Time,
|
||||
Machine = p.Machine
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* XmlParser.cs
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2020 Apple Inc. and the FoundationDB project authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Magnesium
|
||||
{
|
||||
public static class XmlParser
|
||||
{
|
||||
static Random r = new Random();
|
||||
|
||||
public static IEnumerable<Event> Parse(System.IO.Stream stream, string file,
|
||||
bool keepOriginalElement = false, double startTime = -1, double endTime = Double.MaxValue,
|
||||
double samplingFactor = 1.0)
|
||||
{
|
||||
using (var reader = XmlReader.Create(stream))
|
||||
{
|
||||
reader.ReadToDescendant("Trace");
|
||||
reader.Read();
|
||||
foreach (var xev in StreamElements(reader))
|
||||
{
|
||||
Event ev = null;
|
||||
try
|
||||
{
|
||||
if (xev.Name == "Event")
|
||||
ev = ParseEvent(xev, file, keepOriginalElement, startTime, endTime, samplingFactor);
|
||||
else if (xev.Name == "Test")
|
||||
ev = ParseTest(xev, file, keepOriginalElement);
|
||||
else if (xev.Name == "TestPlan")
|
||||
ev = ParseTestPlan(xev, file, keepOriginalElement);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception(string.Format("Failed to parse XML {0}", xev), e);
|
||||
}
|
||||
if (ev != null) yield return ev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Event ParseEvent(XElement xEvent, string file, bool keepOriginalElement, double startTime, double endTime, double samplingFactor)
|
||||
{
|
||||
if (samplingFactor != 1.0 && r.NextDouble() > samplingFactor)
|
||||
return null;
|
||||
|
||||
XAttribute trackLatestAttribute = xEvent.Attribute("TrackLatestType");
|
||||
bool rolledEvent = trackLatestAttribute != null && trackLatestAttribute.Value.Equals("Rolled");
|
||||
String timeAttribute = (rolledEvent) ? "OriginalTime" : "Time";
|
||||
double eventTime = double.Parse(xEvent.Attribute(timeAttribute).Value);
|
||||
|
||||
if (eventTime < startTime || eventTime > endTime)
|
||||
return null;
|
||||
|
||||
return new Event {
|
||||
Severity = (Severity)int.Parse(xEvent.Attribute("Severity").ValueOrDefault("40")),
|
||||
Type = string.Intern(xEvent.Attribute("Type").Value),
|
||||
Time = eventTime,
|
||||
Machine = string.Intern(xEvent.Attribute("Machine").Value),
|
||||
ID = string.Intern(xEvent.Attribute("ID").ValueOrDefault("0")),
|
||||
TraceFile = file,
|
||||
DDetails = xEvent.Attributes()
|
||||
.Where(a=>a.Name != "Type" && a.Name != "Time" && a.Name != "Machine" && a.Name != "ID" && a.Name != "Severity" && (!rolledEvent || a.Name != "OriginalTime"))
|
||||
.ToDictionary(a=>string.Intern(a.Name.LocalName), a=>(object)a.Value),
|
||||
original = keepOriginalElement ? xEvent : null,
|
||||
};
|
||||
}
|
||||
|
||||
private static string ValueOrDefault( this XAttribute attr, string def ) {
|
||||
if (attr == null) return def;
|
||||
else return attr.Value;
|
||||
}
|
||||
|
||||
private static TestPlan ParseTestPlan(XElement xTP, string file, bool keepOriginalElement)
|
||||
{
|
||||
var time = double.Parse(xTP.Attribute("Time").ValueOrDefault("0"));
|
||||
var machine = xTP.Attribute("Machine").ValueOrDefault("");
|
||||
return new TestPlan
|
||||
{
|
||||
TraceFile = file,
|
||||
Type = "TestPlan",
|
||||
Time = time,
|
||||
Machine = machine,
|
||||
TestUID = xTP.Attribute("TestUID").ValueOrDefault(""),
|
||||
TestFile = xTP.Attribute("TestFile").ValueOrDefault(""),
|
||||
randomSeed = int.Parse(xTP.Attribute("RandomSeed").ValueOrDefault("0")),
|
||||
Buggify = xTP.Attribute("BuggifyEnabled").ValueOrDefault("1") != "0",
|
||||
DeterminismCheck = xTP.Attribute("DeterminismCheck").ValueOrDefault("1") != "0",
|
||||
OldBinary = xTP.Attribute("OldBinary").ValueOrDefault(""),
|
||||
original = keepOriginalElement ? xTP : null,
|
||||
};
|
||||
}
|
||||
|
||||
private static Test ParseTest(XElement xTest, string file, bool keepOriginalElement)
|
||||
{
|
||||
var time = double.Parse(xTest.Attribute("Time").ValueOrDefault("0"));
|
||||
var machine = xTest.Attribute("Machine").ValueOrDefault("");
|
||||
return new Test
|
||||
{
|
||||
TraceFile = file,
|
||||
Type = "Test",
|
||||
Time = time,
|
||||
Machine = machine,
|
||||
TestUID = xTest.Attribute("TestUID").ValueOrDefault(""),
|
||||
TestFile = xTest.Attribute("TestFile").ValueOrDefault(""),
|
||||
SourceVersion = xTest.Attribute("SourceVersion").ValueOrDefault(""),
|
||||
ok = bool.Parse(xTest.Attribute("OK").ValueOrDefault("false")),
|
||||
randomSeed = int.Parse(xTest.Attribute("RandomSeed").ValueOrDefault("0")),
|
||||
randomUnseed = int.Parse(xTest.Attribute("RandomUnseed").ValueOrDefault("0")),
|
||||
SimElapsedTime = double.Parse(xTest.Attribute("SimElapsedTime").ValueOrDefault("0")),
|
||||
RealElapsedTime = double.Parse(xTest.Attribute("RealElapsedTime").ValueOrDefault("0")),
|
||||
passed = int.Parse(xTest.Attribute("Passed").ValueOrDefault("0")),
|
||||
failed = int.Parse(xTest.Attribute("Failed").ValueOrDefault("0")),
|
||||
peakMemUsage = long.Parse(xTest.Attribute("PeakMemory").ValueOrDefault("0")),
|
||||
Buggify = xTest.Attribute("BuggifyEnabled").ValueOrDefault("1") != "0",
|
||||
DeterminismCheck = xTest.Attribute("DeterminismCheck").ValueOrDefault("1") != "0",
|
||||
OldBinary = xTest.Attribute("OldBinary").ValueOrDefault(""),
|
||||
original = keepOriginalElement ? xTest : null,
|
||||
events = xTest.Elements().Select(e =>
|
||||
new Event {
|
||||
Severity = (Severity)int.Parse(e.Attribute("Severity").ValueOrDefault("0")),
|
||||
Type = e.Name.LocalName,
|
||||
Time = time,
|
||||
Machine = machine,
|
||||
DDetails = e.Attributes()
|
||||
.Where(a => a.Name != "Type" && a.Name != "Time" && a.Name != "Machine" && a.Name != "Severity")
|
||||
.ToDictionary(a => a.Name.LocalName, a => (object)a.Value)
|
||||
}).ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
private static T Try<T>(Func<T> action, Func<Exception, T> onError, Func<bool> isEOF)
|
||||
{
|
||||
try
|
||||
{
|
||||
return action();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (isEOF())
|
||||
return onError(e);
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<XElement> StreamElements(this XmlReader reader)
|
||||
{
|
||||
while (!reader.EOF)
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
XElement node = null;
|
||||
try
|
||||
{
|
||||
node = XElement.ReadFrom(reader) as XElement;
|
||||
}
|
||||
catch (Exception) { break; }
|
||||
if (node != null)
|
||||
yield return node;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
reader.Read();
|
||||
}
|
||||
catch (Exception) { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,8 @@ set(TEST_KEEP_LOGS "FAILED" CACHE STRING "Which logs to keep (NONE, FAILED, ALL)
|
|||
set(TEST_KEEP_SIMDIR "NONE" CACHE STRING "Which simfdb directories to keep (NONE, FAILED, ALL)")
|
||||
set(TEST_AGGREGATE_TRACES "NONE" CACHE STRING "Create aggregated trace files (NONE, FAILED, ALL)")
|
||||
set(TEST_LOG_FORMAT "xml" CACHE STRING "Format for test trace files (xml, json)")
|
||||
set(TEST_INCLUDE ".*" CACHE STRING "Include only tests that match the given regex")
|
||||
set(TEST_EXCLUDE ".^" CACHE STRING "Exclude all tests matching the given regex")
|
||||
|
||||
# for the restart test we optimally want to use the last stable fdbserver
|
||||
# to test upgrades
|
||||
|
@ -219,6 +221,12 @@ if(WITH_PYTHON)
|
|||
add_fdb_test(TEST_FILES status/single_process_too_many_config_params.txt)
|
||||
|
||||
verify_testing()
|
||||
if (NOT OPEN_FOR_IDE AND NOT WIN32)
|
||||
create_correctness_package()
|
||||
if (USE_VALGRIND)
|
||||
create_valgrind_correctness_package()
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Python not found, won't configure ctest")
|
||||
endif()
|
||||
|
|
Loading…
Reference in New Issue