Backport correctness package

This commit is contained in:
Andrew Noyes 2021-01-15 00:30:31 +00:00
parent fe71ef88c3
commit ddb5b0b050
25 changed files with 3320 additions and 13 deletions

View File

@ -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)

View File

@ -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}
@ -127,9 +134,244 @@ function(add_fdb_test)
${VALGRIND_OPTION}
${ADD_FDB_TEST_TEST_FILES}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
get_filename_component(test_dir_full ${first_file} DIRECTORY)
if(NOT ${test_dir_full} STREQUAL "")
get_filename_component(test_dir ${test_dir_full} NAME)
set_tests_properties(${test_name} PROPERTIES TIMEOUT ${this_test_timeout} LABELS "${test_dir}")
endif()
get_filename_component(test_dir_full ${first_file} DIRECTORY)
if(NOT ${test_dir_full} STREQUAL "")
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()

View File

@ -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")
add_custom_target(strip_${target} DEPENDS "${out_file}.debug")
else()
add_custom_target(strip_${target})
add_dependencies(strip_${target} strip_only_${target})
endif()
add_dependencies(strip_${target} strip_only_${target})
add_dependencies(strip_${target} ${target})
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()

4
contrib/CMakeLists.txt Normal file
View File

@ -0,0 +1,4 @@
if(NOT WIN32)
add_subdirectory(TraceLogHelper)
add_subdirectory(TestHarness)
endif()

23
contrib/Joshua/README.md Normal file
View File

@ -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.

View File

@ -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

View File

@ -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}"

View File

@ -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

View File

@ -0,0 +1,3 @@
#!/bin/sh
OLDBINDIR="${OLDBINDIR:-/app/deploy/global_data/oldBinaries}"
mono bin/TestHarness.exe joshua-run "${OLDBINDIR}" false

View File

@ -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

View File

@ -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}
}

View File

@ -0,0 +1,3 @@
#!/bin/sh
OLDBINDIR="${OLDBINDIR:-/app/deploy/global_data/oldBinaries}"
mono bin/TestHarness.exe joshua-run "${OLDBINDIR}" true

View File

@ -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

View File

@ -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

View File

@ -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")]

View File

@ -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>

View File

@ -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)

View File

@ -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;
};
}

View File

@ -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;
}
}
}

View File

@ -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")]

View File

@ -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>

View File

@ -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
}
}
};
}
}
}

View File

@ -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; }
}
}
}
}
}

View File

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