PROTON-1798: cmake runtime-check improvements Usage notes: - new CMake variable: RUNTIME_CHECK, choose from [memcheck helgrind asan tsan OFF] - defaults to 'memcheck' if available, else OFF - old ENABLE_ variables for valgrind/sanitizers are deprecated - example_test scripts check for stderr output including from killed processes
Implementation details: - moved all runtime-check setup code to seprate runtime-check.cmake - tool-agnostic internal CMake variables for running tests - removed all valgrind-specific code outside of runtime-check.cmake - example_test.py check stderr as well as exit status to catch broker issues. NOTE: asan,tsan not yet working for python/ruby bindings, they are disabled in san builds. See tests/preload_asan.sh for current status of the work. NOTE: Some python soak tests for obscure messenger features were removed, they have faulty start-up timing logic and can fail under valgrind. We can restore them if needed but we'll need to fix the -X feature of msgr-recv to report ready only after connections are remote open. Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/27edd9ac Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/27edd9ac Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/27edd9ac Branch: refs/heads/go1 Commit: 27edd9aca3b2b1078089ceaa2a387f0016ac6f0a Parents: 7885bd3 Author: Alan Conway <acon...@redhat.com> Authored: Fri Sep 7 13:20:42 2018 -0400 Committer: Alan Conway <acon...@redhat.com> Committed: Mon Sep 10 15:41:24 2018 -0400 ---------------------------------------------------------------------- CMakeLists.txt | 71 +++------------ c/examples/CMakeLists.txt | 7 +- c/examples/example_test.py | 58 +++++------- c/tests/CMakeLists.txt | 4 +- c/tests/fuzz/CMakeLists.txt | 2 +- c/tests/threaderciser.supp | 18 ---- c/tests/threaderciser.tsupp | 5 - cpp/CMakeLists.txt | 2 +- cpp/examples/CMakeLists.txt | 14 ++- cpp/examples/broker.cpp | 4 +- cpp/examples/example_test.py | 42 ++------- misc/config.sh.in | 6 -- python/CMakeLists.txt | 8 +- python/tests/proton_tests/common.py | 35 +------ python/tests/proton_tests/soak.py | 83 ++--------------- python/tests/proton_tests/ssl.py | 33 ------- python/tox.ini.in | 2 +- ruby/CMakeLists.txt | 8 +- runtime_check.cmake | 123 +++++++++++++++++++++++++ scripts/env.py | 5 - tests/preload_asan.sh | 51 +++++++++++ tests/py/test_subprocess.py | 105 +++++++++++++++++++++ tests/valgrind.supp | 151 +++++++++++++++++++++++++++++++ 23 files changed, 517 insertions(+), 320 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/CMakeLists.txt b/CMakeLists.txt index 560dc05..105f22e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,9 @@ find_package (CyrusSASL) enable_testing () +# Set up runtime checks (valgrind, sanitizers etc.) +include(runtime_check.cmake) + ## Variables used across components set (PN_ENV_SCRIPT "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/scripts/env.py") @@ -188,23 +191,6 @@ if (CMAKE_C_COMPILER_ID MATCHES "Clang") set (CXX_WARNING_FLAGS "${COMPILE_WARNING_FLAGS} -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-float-equal -Wno-padded -Wno-sign-conversion -Wno-switch-enum -Wno-weak-vtables -Wno-exit-time-destructors -Wno-global-constructors -Wno-shorten-64-to-32 -Wno-documentation -Wno-documentation-unknown-command -Wno-old-style-cast -Wno-missing-noreturn") endif() -# Sanitizer flags apply to to both GNU and clang, C and C++ -if(ENABLE_SANITIZERS) - set(SANITIZE_FLAGS "-g -fno-omit-frame-pointer -fsanitize=address -fsanitize=leak -fsanitize=undefined") -endif() -if(ENABLE_TSAN) - set(SANITIZE_FLAGS "-g -fno-omit-frame-pointer -fsanitize=thread") -endif() -if (SANITIZE_FLAGS) - mark_as_advanced(SANITIZE_FLAGS) - if(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZE_FLAGS}") - endif() - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZE_FLAGS}") - endif() -endif() - if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") if (NOT CMAKE_OSX_ARCHITECTURES) set(CMAKE_OSX_ARCHITECTURES "x86_64") @@ -300,41 +286,6 @@ endforeach() set (PROTON_SHARE ${SHARE_INSTALL_DIR}/proton-${PN_VERSION}) # End of variables used during install -# Can't do valgrind and coverage at athe same time - coverage takes precedence -if (CMAKE_BUILD_TYPE MATCHES "Coverage") - message(STATUS "Building for coverage analysis; no run-time error detection") -else () - find_program(VALGRIND_EXECUTABLE valgrind DOC "Location of the valgrind program") - mark_as_advanced (VALGRIND_EXECUTABLE) - - option(ENABLE_VALGRIND "Use valgrind to detect run-time problems" ON) - if (ENABLE_VALGRIND) - if (VALGRIND_EXECUTABLE) - set (VALGRIND_SUPPRESSIONS ${CMAKE_SOURCE_DIR}/c/tests/valgrind.supp CACHE STRING "Default valgrind suppressions") - set (VALGRIND_OPTIONS "--error-exitcode=42 --quiet --leak-check=full --trace-children=yes" CACHE STRING "Default valgrind options") - mark_as_advanced(VALGRIND_SUPPRESSIONS VALGRIND_OPTIONS) - set (VALGRIND_ENV "VALGRIND=${VALGRIND_EXECUTABLE}" "VALGRIND_ARGS=${VALGRIND_OPTIONS} --suppressions=${VALGRIND_SUPPRESSIONS}") - separate_arguments(VALGRIND_OPTIONS_LIST UNIX_COMMAND ${VALGRIND_OPTIONS}) - set (memcheck-cmd ${VALGRIND_EXECUTABLE} ${VALGRIND_OPTIONS_LIST} "--suppressions=${VALGRIND_SUPPRESSIONS}") - set (racecheck-cmd ${VALGRIND_EXECUTABLE} --tool=helgrind --error-exitcode=42 --quiet) - else () - message(STATUS "Can't locate the valgrind command; no run-time error detection") - endif () - endif () -endif () - -# Options to enable sanitizing compile flags. Compile flags are set in c/CMakeLists.txt -option(ENABLE_SANITIZERS "Compile with sanitizers (ASan, UBSan, TSan); incompatible with Valgrind" OFF) -option(ENABLE_TSAN "Compile with Thread Sanitizer (TSan); incompatible with Valgrind" OFF) -if (ENABLE_SANITIZERS OR ENABLE_TSAN) - set(DISABLE ENABLE_VALGRIND ENABLE_UNDEFINED_ERROR BUILD_GO) - message(STATUS "Building with sanitizers; disables ${DISABLE}") - foreach(d ${DISABLE}) - set(${d} OFF CACHE BOOL "Disabled to run sanitizers" FORCE) - endforeach() - unset(VALGRIND_ENV) -endif() - # Set result to a native search path - used by examples and binding tests. # args after result are directories or search paths. macro(set_search_path result) @@ -379,11 +330,11 @@ endif() find_program(GO_EXE go) mark_as_advanced(GO_EXE) if (GO_EXE) - if(WIN32) + set (DEFAULT_GO ON) + if(WIN32 OR RUNTIME_CHECK) # Go on windows requires gcc tool chain + # Go does not work with C-based runtime checkers. set (DEFAULT_GO OFF) - else() - set (DEFAULT_GO ON) endif() endif (GO_EXE) @@ -405,15 +356,17 @@ if(SWIG_FOUND) # Prerequisites for Python wrapper: find_package (PythonLibs ${PYTHON_VERSION_STRING} EXACT) - if (PYTHONLIBS_FOUND) + # TODO aconway 2018-09-07: get python binding tests working with sanitizers + if (PYTHONLIBS_FOUND AND NOT SANITIZE_FLAGS) set (DEFAULT_PYTHON ON) - endif (PYTHONLIBS_FOUND) + endif () # Prerequisites for Ruby: find_package(Ruby) - if (RUBY_FOUND) + # TODO aconway 2018-09-07: get ruby binding tests working with sanitizers + if (RUBY_FOUND AND NOT SANITIZE_FLAGS) set (DEFAULT_RUBY ON) - endif (RUBY_FOUND) + endif () endif() # To kick-start a build with just a few bindings enabled by default, e.g. ruby and go: http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/c/examples/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/c/examples/CMakeLists.txt b/c/examples/CMakeLists.txt index 6128f62..b04e444 100644 --- a/c/examples/CMakeLists.txt +++ b/c/examples/CMakeLists.txt @@ -45,6 +45,9 @@ endif() add_test( NAME c-example-tests - COMMAND ${PN_ENV_SCRIPT} "PATH=${test_path}" ${VALGRIND_ENV} -- - ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example_test.py -v + COMMAND ${PN_ENV_SCRIPT} + "PATH=${test_path}" + "PYTHONPATH=${CMAKE_SOURCE_DIR}/tests/py" + ${TEST_ENV} -- + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example_test.py -v WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/c/examples/example_test.py ---------------------------------------------------------------------- diff --git a/c/examples/example_test.py b/c/examples/example_test.py index 1834989..35a8993 100644 --- a/c/examples/example_test.py +++ b/c/examples/example_test.py @@ -20,31 +20,14 @@ # Run the C examples and verify that they behave as expected. # Example executables must be in PATH -import unittest, sys, time, re +import unittest -import subprocess +from test_subprocess import Popen, TestProcessError, check_output -class Server(subprocess.Popen): +class Server(Popen): def __init__(self, *args, **kwargs): - self.kill_me = kwargs.pop('kill_me', False) - kwargs.update({'universal_newlines': True, - 'stdout': subprocess.PIPE}) super(Server, self).__init__(*args, **kwargs) - - def __enter__(self): - line = self.stdout.readline() - self.port = re.search("listening on ([0-9]+)$", line).group(1) - return self - - def __exit__(self, *args): - if self.kill_me: - self.kill() - self.stdout.close() # Doesn't get closed if killed - self.wait() - -def check_output(*args, **kwargs): - kwargs.update({'universal_newlines': True}) - return subprocess.check_output(*args, **kwargs) + self.port = self.expect("listening on ([0-9]+)$").group(1) MESSAGES=10 @@ -64,6 +47,10 @@ class ExampleTest(unittest.TestCase): """Run an example with standard arguments, return output""" return check_output([name, "", port, "xtest", str(messages)]) + def startex(self, name, port, messages=MESSAGES): + """Start an example sub-process with standard arguments""" + return Popen([name, "", port, "xtest", str(messages)]) + def test_send_receive(self): """Send first then receive""" with Broker() as b: @@ -73,20 +60,21 @@ class ExampleTest(unittest.TestCase): def test_receive_send(self): """Start receiving first, then send.""" with Broker() as b: + r = self.startex("receive", b.port) self.assertEqual(send_expect(), self.runex("send", b.port)) - self.assertMultiLineEqual(receive_expect(), self.runex("receive", b.port)) + self.assertMultiLineEqual(receive_expect(), r.communicate()[0]) def test_send_direct(self): """Send to direct server""" - with Server(["direct", "", "0"]) as d: - self.assertEqual(send_expect(), self.runex("send", d.port)) - self.assertMultiLineEqual(receive_expect(), d.communicate()[0]) + d = Server(["direct", "", "0"]) + self.assertEqual(send_expect(), self.runex("send", d.port)) + self.assertMultiLineEqual(receive_expect(), d.communicate()[0]) def test_receive_direct(self): """Receive from direct server""" - with Server(["direct", "", "0"]) as d: - self.assertMultiLineEqual(receive_expect(), self.runex("receive", d.port)) - self.assertEqual("10 messages sent and acknowledged\n", d.communicate()[0]) + d = Server(["direct", "", "0"]) + self.assertMultiLineEqual(receive_expect(), self.runex("receive", d.port)) + self.assertEqual("10 messages sent and acknowledged\n", d.communicate()[0]) def test_send_abort_broker(self): """Sending aborted messages to a broker""" @@ -101,13 +89,13 @@ class ExampleTest(unittest.TestCase): def test_send_abort_direct(self): """Send aborted messages to the direct server""" - with Server(["direct", "", "0", "examples", "20"]) as d: - self.assertEqual(send_expect(), self.runex("send", d.port)) - self.assertEqual(send_abort_expect(), self.runex("send-abort", d.port)) - self.assertEqual(send_expect(), self.runex("send", d.port)) - expect = receive_expect_messages() + "Message aborted\n"*MESSAGES + receive_expect_messages()+receive_expect_total(20) - self.maxDiff = None - self.assertMultiLineEqual(expect, d.communicate()[0]) + d = Server(["direct", "", "0", "examples", "20"]) + self.assertEqual(send_expect(), self.runex("send", d.port)) + self.assertEqual(send_abort_expect(), self.runex("send-abort", d.port)) + self.assertEqual(send_expect(), self.runex("send", d.port)) + expect = receive_expect_messages() + "Message aborted\n"*MESSAGES + receive_expect_messages()+receive_expect_total(20) + self.maxDiff = None + self.assertMultiLineEqual(expect, d.communicate()[0]) def test_send_ssl_receive(self): """Send with SSL, then receive""" http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/c/tests/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/c/tests/CMakeLists.txt b/c/tests/CMakeLists.txt index 87dfdb6..a4bfb67 100644 --- a/c/tests/CMakeLists.txt +++ b/c/tests/CMakeLists.txt @@ -32,7 +32,7 @@ macro (pn_add_c_test_nolib test) if (BUILD_WITH_CXX) set_source_files_properties (${ARGN} PROPERTIES LANGUAGE CXX) endif (BUILD_WITH_CXX) - add_test(NAME ${test} COMMAND ${test_env} ${memcheck-cmd} $<TARGET_FILE:${test}>) + add_test(NAME ${test} COMMAND ${test_env} ${TEST_EXE_PREFIX_CMD} $<TARGET_FILE:${test}>) endmacro (pn_add_c_test_nolib) # Add test with qpid-proton-core linked @@ -58,7 +58,7 @@ if(HAS_PROACTOR) pn_add_c_test (c-proactor-tests proactor.c) target_link_libraries (c-proactor-tests qpid-proton-proactor) - # TODO Enable by default when races are cleared up + # TODO Enable by default when races and xcode problems are cleared up option(THREADERCISER "Run the threaderciser concurrency tests" OFF) if (THREADERCISER) pn_add_c_test(c-threaderciser threaderciser.c) http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/c/tests/fuzz/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/c/tests/fuzz/CMakeLists.txt b/c/tests/fuzz/CMakeLists.txt index 19825b6..5d4187d 100644 --- a/c/tests/fuzz/CMakeLists.txt +++ b/c/tests/fuzz/CMakeLists.txt @@ -37,7 +37,7 @@ macro (pn_add_fuzz_test test) if (FUZZ_REGRESSION_TESTS) # StandaloneFuzzTargetMain cannot walk directory trees file(GLOB_RECURSE files ${CMAKE_CURRENT_SOURCE_DIR}/${test}/*) - add_test (NAME ${test} COMMAND ${memcheck-cmd} $<TARGET_FILE:${test}> ${files}) + add_test (NAME ${test} COMMAND ${TEST_EXE_PREFIX_CMD} $<TARGET_FILE:${test}> ${files}) else () add_test (NAME ${test} COMMAND $<TARGET_FILE:${test}> -runs=1 ${CMAKE_CURRENT_SOURCE_DIR}/${test}>) endif () http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/c/tests/threaderciser.supp ---------------------------------------------------------------------- diff --git a/c/tests/threaderciser.supp b/c/tests/threaderciser.supp deleted file mode 100644 index d14ff61..0000000 --- a/c/tests/threaderciser.supp +++ /dev/null @@ -1,18 +0,0 @@ -{ - IGNORE: benign race in pni_log_enabled - Helgrind:Race - fun:pni_log_enabled -} - -{ - IGNORE: NSS library poking around in its own data segment upsets helgrind - Helgrind:Race - fun:strpbrk - fun:_nss_files_parse_servent -} -{ - IGNORE: NSS library poking around in its own text segment upsets helgrind - Helgrind:Race - fun:* - fun:_nss_files_getservbyname_r -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/c/tests/threaderciser.tsupp ---------------------------------------------------------------------- diff --git a/c/tests/threaderciser.tsupp b/c/tests/threaderciser.tsupp deleted file mode 100644 index 18ceb23..0000000 --- a/c/tests/threaderciser.tsupp +++ /dev/null @@ -1,5 +0,0 @@ -# TSAN suppressions for threaderciser - -# Benign race in pni_log_enabled -race:pni_log_enabled - http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/cpp/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 0dcede2..a06e67d 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -242,7 +242,7 @@ macro(add_cpp_test test) "PATH=$<TARGET_FILE_DIR:qpid-proton>" $<TARGET_FILE:${test}> ${ARGN}) else () - add_test (NAME cpp-${test} COMMAND ${memcheck-cmd} $<TARGET_FILE:${test}> ${ARGN}) + add_test (NAME cpp-${test} COMMAND ${TEST_EXE_PREFIX_CMD} $<TARGET_FILE:${test}> ${ARGN}) endif () endmacro(add_cpp_test) http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/cpp/examples/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 18d922e..ba18d83 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -104,7 +104,7 @@ if(HAS_ENOUGH_CPP11) endif() # Add a test with the correct environment to find test executables and valgrind. -macro(add_cpp_test name) +macro(add_cpp_example_test name) if(WIN32) set(test_path "$<TARGET_FILE_DIR:broker>;$<TARGET_FILE_DIR:qpid-proton>;$<TARGET_FILE_DIR:qpid-proton-cpp>") else(WIN32) @@ -112,13 +112,17 @@ macro(add_cpp_test name) endif(WIN32) add_test( NAME ${name} - COMMAND ${PN_ENV_SCRIPT} "PATH=${test_path}" ${VALGRIND_ENV} - "HAS_CPP11=$<$<BOOL:${HAS_ENOUGH_CPP11}>:1>" -- ${ARGN} + COMMAND ${PN_ENV_SCRIPT} + "PATH=${test_path}" + "PYTHONPATH=${CMAKE_SOURCE_DIR}/tests/py" + "HAS_CPP11=$<$<BOOL:${HAS_ENOUGH_CPP11}>:1>" + ${TEST_ENV} -- + ${ARGN} ) endmacro() -add_cpp_test(cpp-example-container ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example_test.py -v ContainerExampleTest) +add_cpp_example_test(cpp-example-container ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example_test.py -v ContainerExampleTest) if (NOT SSL_IMPL STREQUAL none) -add_cpp_test(cpp-example-container-ssl ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example_test.py -v ContainerExampleSSLTest) +add_cpp_example_test(cpp-example-container-ssl ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example_test.py -v ContainerExampleSSLTest) endif() http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/cpp/examples/broker.cpp ---------------------------------------------------------------------- diff --git a/cpp/examples/broker.cpp b/cpp/examples/broker.cpp index d45309e..b15ac39 100644 --- a/cpp/examples/broker.cpp +++ b/cpp/examples/broker.cpp @@ -355,7 +355,7 @@ public: } void on_error(const proton::error_condition& e) OVERRIDE { - std::cerr << "error: " << e.what() << std::endl; + std::cout << "protocol error: " << e.what() << std::endl; } // The container calls on_transport_close() last. @@ -429,7 +429,7 @@ int main(int argc, char **argv) { } catch (const example::bad_option& e) { std::cout << opts << std::endl << e.what() << std::endl; } catch (const std::exception& e) { - std::cerr << "broker shutdown: " << e.what() << std::endl; + std::cout << "broker shutdown: " << e.what() << std::endl; } return 1; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/cpp/examples/example_test.py ---------------------------------------------------------------------- diff --git a/cpp/examples/example_test.py b/cpp/examples/example_test.py index edcde27..38a9a6e 100644 --- a/cpp/examples/example_test.py +++ b/cpp/examples/example_test.py @@ -20,40 +20,21 @@ # Run the C++ examples and verify that they behave as expected. # Example executables must be in PATH -import unittest, sys, time, re, shutil, os +import unittest, sys, shutil, os +from test_subprocess import Popen, TestProcessError, check_output from os.path import dirname from string import Template -import subprocess - -class Server(subprocess.Popen): +class Server(Popen): + """A process that prints 'listening on <port>' to stdout""" def __init__(self, *args, **kwargs): - self.port = None - self.kill_me = kwargs.pop('kill_me', False) - kwargs.update({'universal_newlines': True, - 'stdout': subprocess.PIPE}) super(Server, self).__init__(*args, **kwargs) - - def __enter__(self): - return self - - def __exit__(self, *args): - if self.kill_me: - self.kill() - self.stdout.close() # Doesn't get closed if killed - self.wait() + self.port = self.expect("listening on ([0-9]+)$").group(1) @property def addr(self): - if not self.port: - line = self.stdout.readline() - self.port = re.search("listening on ([0-9]+)$", line).group(1) return ":%s/example" % self.port -def check_output(*args, **kwargs): - kwargs.update({'universal_newlines': True}) - return subprocess.check_output(*args, **kwargs) - def _cyrusSetup(conf_dir): """Write out simple SASL config.tests """ @@ -77,12 +58,9 @@ mech_list: EXTERNAL DIGEST-MD5 SCRAM-SHA-1 CRAM-MD5 PLAIN ANONYMOUS # Globally initialize Cyrus SASL configuration _cyrusSetup('sasl-conf') -def wait_listening(p): - return re.search(b"listening on ([0-9]+)$", p.stdout.readline()).group(1) - class Broker(Server): - def __init__(self): - super(Broker, self).__init__(["broker", "-a", "//:0"], kill_me=True) + def __init__(self): + super(Broker, self).__init__(["broker", "-a", "//:0"], kill_me=True) CLIENT_EXPECT="""Twas brillig, and the slithy toves => TWAS BRILLIG, AND THE SLITHY TOVES Did gire and gymble in the wabe. => DID GIRE AND GYMBLE IN THE WABE. @@ -104,7 +82,7 @@ class ContainerExampleTest(unittest.TestCase): self.assertMultiLineEqual(recv_expect(), check_output(["simple_recv", "-a", Broker.addr])) def test_simple_recv_send(self): - recv = Server(["simple_recv", "-a", Broker.addr]) + recv = Popen(["simple_recv", "-a", Broker.addr]) self.assertMultiLineEqual("all messages confirmed\n", check_output(["simple_send", "-a", Broker.addr])) self.assertMultiLineEqual(recv_expect(), recv.communicate()[0]) @@ -119,8 +97,8 @@ class ContainerExampleTest(unittest.TestCase): self.assertMultiLineEqual("all messages confirmed\n", send.communicate()[0]) def test_request_response(self): - with Server(["server", Broker.addr, "example"], kill_me=True) as server: - self.assertIn("connected to", server.stdout.readline()) + with Popen(["server", Broker.addr, "example"], kill_me=True) as server: + server.expect("connected to %s" % Broker.addr) self.assertMultiLineEqual(CLIENT_EXPECT, check_output(["client", "-a", Broker.addr])) def test_request_response_direct(self): http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/misc/config.sh.in ---------------------------------------------------------------------- diff --git a/misc/config.sh.in b/misc/config.sh.in index a61c757..dd23602 100755 --- a/misc/config.sh.in +++ b/misc/config.sh.in @@ -52,12 +52,6 @@ export LD_LIBRARY_PATH="$(merge_paths $PROTON_BUILD $LD_LIBRARY_PATH)" # Test applications export PATH="$(merge_paths $PATH $PROTON_BUILD/c/tools $PROTON_HOME/python/tests)" -# Can the test harness use valgrind? -if [[ -x "$(type -p valgrind)" && "@ENABLE_VALGRIND@" == "ON" ]] ; then - export VALGRIND=$(type -p valgrind) - export VALGRIND_ARGS="@VALGRIND_OPTIONS@" -fi - # Can the test harness use saslpasswd2? if [[ -x "$(type -p saslpasswd2)" ]] ; then export SASLPASSWD=$(type -p saslpasswd2) http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/python/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index d63ab96..181128c 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -208,8 +208,8 @@ add_test (NAME python-test COMMAND ${PN_ENV_SCRIPT} "PATH=${py_path}" "PYTHONPATH=${py_pythonpath}" "SASLPASSWD=${CyrusSASL_Saslpasswd_EXECUTABLE}" - ${VALGRIND_ENV} - ${PYTHON_EXECUTABLE} -- ${python_coverage_options} "${py_tests}/proton-test") + ${TEST_ENV} + ${TEST_WRAP_PREFIX_CMD} ${PYTHON_EXECUTABLE} -- ${python_coverage_options} "${py_tests}/proton-test") set_tests_properties(python-test PROPERTIES PASS_REGULAR_EXPRESSION "Totals: .* 0 failed") check_python_module("tox" TOX_MODULE_FOUND) @@ -237,8 +237,8 @@ else () "PATH=${py_path}" "SASLPASSWD=${CyrusSASL_Saslpasswd_EXECUTABLE}" "SWIG=${SWIG_EXECUTABLE}" - ${VALGRIND_ENV} -- - ${PYTHON_EXECUTABLE} -m tox) + ${TEST_ENV} -- + ${TEST_WRAP_PREFIX_CMD} ${PYTHON_EXECUTABLE} -m tox) set_tests_properties(python-tox-test PROPERTIES PASS_REGULAR_EXPRESSION "Totals: .* ignored, 0 failed" http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/python/tests/proton_tests/common.py ---------------------------------------------------------------------- diff --git a/python/tests/proton_tests/common.py b/python/tests/proton_tests/common.py index fd4decc..a56a2c0 100644 --- a/python/tests/proton_tests/common.py +++ b/python/tests/proton_tests/common.py @@ -281,7 +281,7 @@ class MessengerApp(object): del cmd[0:1] cmd.insert(0, foundfile) cmd.insert(0, sys.executable) - self._process = Popen(cmd, stdout=PIPE, stderr=STDOUT, + self._process = Popen(cmd, stdout=PIPE, bufsize=4096, universal_newlines=True) except OSError: e = sys.exc_info()[1] @@ -425,7 +425,8 @@ class MessengerReceiver(MessengerApp): # command string? def _build_command(self): - self._cmdline = self._command + self._cmdline = os.environ.get("TEST_EXE_PREFIX", "").split() + self._cmdline += self._command self._do_common_options() self._cmdline += ["-X", "READY"] assert self.subscriptions, "Missing subscriptions, required for receiver!" @@ -468,47 +469,17 @@ class MessengerSenderC(MessengerSender): MessengerSender.__init__(self) self._command = ["msgr-send"] -def setup_valgrind(self): - if "VALGRIND" not in os.environ: - raise Skipped("Skipping test - $VALGRIND not set.") - super(type(self), self).__init__() - self._command = [os.environ["VALGRIND"]] + os.environ["VALGRIND_ARGS"].split(' ') + self._command - -class MessengerSenderValgrind(MessengerSenderC): - """ Run the C sender under Valgrind - """ - def __init__(self, suppressions=None): - setup_valgrind(self) - class MessengerReceiverC(MessengerReceiver): def __init__(self): MessengerReceiver.__init__(self) self._command = ["msgr-recv"] -class MessengerReceiverValgrind(MessengerReceiverC): - """ Run the C receiver under Valgrind - """ - def __init__(self, suppressions=None): - setup_valgrind(self) - class ReactorSenderC(MessengerSender): def __init__(self): MessengerSender.__init__(self) self._command = ["reactor-send"] -class ReactorSenderValgrind(ReactorSenderC): - """ Run the C sender under Valgrind - """ - def __init__(self, suppressions=None): - setup_valgrind(self) - class ReactorReceiverC(MessengerReceiver): def __init__(self): MessengerReceiver.__init__(self) self._command = ["reactor-recv"] - -class ReactorReceiverValgrind(ReactorReceiverC): - """ Run the C receiver under Valgrind - """ - def __init__(self, suppressions=None): - setup_valgrind(self) http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/python/tests/proton_tests/soak.py ---------------------------------------------------------------------- diff --git a/python/tests/proton_tests/soak.py b/python/tests/proton_tests/soak.py index 5e103fd..e015db5 100644 --- a/python/tests/proton_tests/soak.py +++ b/python/tests/proton_tests/soak.py @@ -24,9 +24,7 @@ import os from .common import Test, Skipped, free_tcp_ports, \ MessengerReceiverC, MessengerSenderC, \ - MessengerReceiverValgrind, MessengerSenderValgrind, \ ReactorReceiverC, ReactorSenderC, \ - ReactorReceiverValgrind, ReactorSenderValgrind, \ isSSLPresent # @@ -192,52 +190,14 @@ class MessengerTests(AppTests): self._do_test(iterations) - def _do_relay_test(self, receiver, relay, sender, domain="amqp"): - """ Send N messages to a receiver, which replies to each and forwards - each of them to different receiver. - Parameters: - iterations - repeat the senders this many times - target_count - # targets to send to - send_count = # messages sent to each target - send_batch - wait for replies after this many messages sent - forward_count - forward to this many targets - """ - iterations = self.iterations - send_count = self.send_count - target_count = self.target_count - send_batch = self.send_batch - forward_count = self.forward_count - - send_total = send_count * target_count - receive_total = send_total * iterations - - port = free_tcp_ports()[0] - - receiver.subscriptions = ["%s://~0.0.0.0:%s" % (domain, port)] - receiver.receive_count = receive_total - receiver.send_reply = True - # forward to 'relay' - uses two links - # ## THIS FAILS: - # receiver.forwards = ["amqp://Relay/%d" % j for j in range(forward_count)] - receiver.forwards = ["%s://Relay" % domain] - receiver.timeout = MessengerTests._timeout - self.receivers.append( receiver ) - - relay.subscriptions = ["%s://0.0.0.0:%s" % (domain, port)] - relay.name = "Relay" - relay.receive_count = receive_total - relay.timeout = MessengerTests._timeout - self.receivers.append( relay ) - - # send to 'receiver' - sender.targets = ["%s://0.0.0.0:%s/X%dY" % (domain, port, j) for j in range(target_count)] - sender.send_count = send_total - sender.get_reply = True - sender.timeout = MessengerTests._timeout - self.senders.append( sender ) - - self._do_test(iterations) - + # Removed messenger "relay" tests. The test start-up is faulty: + # msgr-recv prints it's -X ready message when it starts to open a + # connection but it does not wait for the remote open. The relay + # tests depends on mapping a container name from an incoming + # connection. They can fail under if the sender starts before the + # connection is complete (esp. valgrind with SSL connections) We + # could fix the tests but since messenger is deprecated it does + # not seem worthwhile. def _do_star_topology_test(self, r_factory, s_factory, domain="amqp"): """ @@ -291,10 +251,6 @@ class MessengerTests(AppTests): self._ssl_check() self._do_oneway_test(MessengerReceiverC(), MessengerSenderC(), "amqps") - def test_oneway_valgrind(self): - self.valgrind_test() - self._do_oneway_test(MessengerReceiverValgrind(), MessengerSenderValgrind()) - def test_echo_C(self): self._do_echo_test(MessengerReceiverC(), MessengerSenderC()) @@ -302,21 +258,6 @@ class MessengerTests(AppTests): self._ssl_check() self._do_echo_test(MessengerReceiverC(), MessengerSenderC(), "amqps") - def test_echo_valgrind(self): - self.valgrind_test() - self._do_echo_test(MessengerReceiverValgrind(), MessengerSenderValgrind()) - - def test_relay_C(self): - self._do_relay_test(MessengerReceiverC(), MessengerReceiverC(), MessengerSenderC()) - - def test_relay_C_SSL(self): - self._ssl_check() - self._do_relay_test(MessengerReceiverC(), MessengerReceiverC(), MessengerSenderC(), "amqps") - - def test_relay_valgrind(self): - self.valgrind_test() - self._do_relay_test(MessengerReceiverValgrind(), MessengerReceiverValgrind(), MessengerSenderValgrind()) - def test_star_topology_C(self): self._do_star_topology_test( MessengerReceiverC, MessengerSenderC ) @@ -324,13 +265,5 @@ class MessengerTests(AppTests): self._ssl_check() self._do_star_topology_test( MessengerReceiverC, MessengerSenderC, "amqps" ) - def test_star_topology_valgrind(self): - self.valgrind_test() - self._do_star_topology_test( MessengerReceiverValgrind, MessengerSenderValgrind ) - def test_oneway_reactor(self): self._do_oneway_test(ReactorReceiverC(), ReactorSenderC()) - - def test_oneway_reactor_valgrind(self): - self.valgrind_test() - self._do_oneway_test(ReactorReceiverValgrind(), ReactorSenderValgrind()) http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/python/tests/proton_tests/ssl.py ---------------------------------------------------------------------- diff --git a/python/tests/proton_tests/ssl.py b/python/tests/proton_tests/ssl.py index fcc3795..9419292 100644 --- a/python/tests/proton_tests/ssl.py +++ b/python/tests/proton_tests/ssl.py @@ -882,39 +882,6 @@ class SslTest(common.Test): receiver.wait() assert receiver.status() == 0, "Command '%s' failed" % str(receiver.cmdline()) - def DISABLED_test_defaults_valgrind(self): - """ Run valgrind over a simple SSL connection (no certificates) - """ - # the openssl libraries produce far too many valgrind errors to be - # useful. AFAIK, there is no way to wriate a valgrind suppression - # expression that will ignore all errors from a given library. - # Until we can, skip this test. - port = common.free_tcp_ports()[0] - - receiver = common.MessengerReceiverValgrind() - receiver.subscriptions = ["amqps://~127.0.0.1:%s" % port] - receiver.receive_count = 1 - receiver.timeout = self.timeout - receiver.start() - - sender = common.MessengerSenderValgrind() - sender.targets = ["amqps://127.0.0.1:%s/X" % port] - sender.send_count = 1 - sender.timeout = self.timeout - sender.start() - sender.wait() - assert sender.status() == 0, "Command '%s' failed" % str(sender.cmdline()) - - receiver.wait() - assert receiver.status() == 0, "Command '%s' failed" % str(receiver.cmdline()) - - # self.server_domain.set_credentials(self._testpath("client-certificate.pem"), - # self._testpath("client-private-key.pem"), - # "client-password") - - # self.client_domain.set_trusted_ca_db(self._testpath("ca-certificate.pem")) - # self.client_domain.set_peer_authentication( SSLDomain.VERIFY_PEER ) - def test_singleton(self): """Verify that only a single instance of SSL can exist per Transport""" transport = Transport() http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/python/tox.ini.in ---------------------------------------------------------------------- diff --git a/python/tox.ini.in b/python/tox.ini.in index c514078..ec4a77d 100644 --- a/python/tox.ini.in +++ b/python/tox.ini.in @@ -13,7 +13,7 @@ passenv = PKG_CONFIG_PATH CFLAGS SASLPASSWD - VALGRIND + TEST_EXE_PREFIX commands = @CMAKE_SOURCE_DIR@/python/tests/proton-test '{posargs:--ignore-file=@CMAKE_SOURCE_DIR@/python/tests/tox-blacklist}' deps = http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/ruby/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/ruby/CMakeLists.txt b/ruby/CMakeLists.txt index e205250..eb99907 100644 --- a/ruby/CMakeLists.txt +++ b/ruby/CMakeLists.txt @@ -125,14 +125,18 @@ to_native_path("${bin};${c_lib_dir};$ENV{PATH}" PATH) execute_process(COMMAND ${RUBY_EXECUTABLE} -r minitest -e "" RESULT_VARIABLE result OUTPUT_QUIET ERROR_QUIET) if (result EQUAL 0) # Have minitest - set(test_env ${PN_ENV_SCRIPT} -- "PATH=${PATH}" "RUBYLIB=${RUBYLIB}" "SASLPASSWD=${CyrusSASL_Saslpasswd_EXECUTABLE}") + set(test_env ${PN_ENV_SCRIPT} -- + "PATH=${PATH}" + "RUBYLIB=${RUBYLIB}" + "SASLPASSWD=${CyrusSASL_Saslpasswd_EXECUTABLE}" + ${TEST_ENV}) macro(add_ruby_test script) get_filename_component(name ${script} NAME_WE) string(REPLACE "_" "-" name "ruby-${name}") add_test( NAME ${name} - COMMAND ${test_env} ${RUBY_EXECUTABLE} ${script} -v + COMMAND ${test_env} ${TEST_WRAP_PREFIX_CMD} ${RUBY_EXECUTABLE} ${script} -v ${ARGN}) endmacro() http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/runtime_check.cmake ---------------------------------------------------------------------- diff --git a/runtime_check.cmake b/runtime_check.cmake new file mode 100644 index 0000000..e1d76c3 --- /dev/null +++ b/runtime_check.cmake @@ -0,0 +1,123 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +# Configuration for code analysis tools: runtime checking and coverage. + +# Any test that needs runtime checks should use TEST_EXE_PREFIX and TEST_ENV. +# Normally they are set as a result of the RUNTIME_CHECK value, +# but can be set directly for unsupported tools or unusual flags +# e.g. -DTEST_EXE_PREFIX=rr or -DTEST_EXE_PREFIX="valgrind --tool=massif" + +set(TEST_EXE_PREFIX "" CACHE STRING "Prefix for test executable command line") +set(TEST_WRAP_PREFIX "" CACHE STRING "Prefix for interpreter tests (e.g. python, ruby) that load proton as an extension") +set(TEST_ENV "" CACHE STRING "Extra environment for tests: name1=value1;name2=value2") +mark_as_advanced(TEST_EXE_PREFIX TEST_WRAP_PREFIX TEST_ENV) + +# Check for valgrind +find_program(VALGRIND_EXECUTABLE valgrind DOC "location of valgrind program") +set(VALGRIND_SUPPRESSIONS "${CMAKE_SOURCE_DIR}/tests/valgrind.supp" CACHE STRING "Suppressions file for valgrind") +set(VALGRIND_COMMON_ARGS "--error-exitcode=42 --quiet --suppressions=${VALGRIND_SUPPRESSIONS}") +mark_as_advanced(VALGRIND_EXECUTABLE VALGRIND_SUPPRESSIONS VALGRIND_COMMON_ARGS) + +# Check for compiler sanitizers +if((CMAKE_C_COMPILER_ID MATCHES "GNU" + AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 4.8) + OR (CMAKE_C_COMPILER_ID MATCHES "Clang" + AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 4.1)) + set(HAS_SANITIZERS TRUE) +endif() + +# Valid values for RUNTIME_CHECK +set(runtime_checks OFF asan tsan memcheck helgrind) + +# Set the default +if(NOT CMAKE_BUILD_TYPE MATCHES "Coverage" AND VALGRIND_EXECUTABLE) + set(RUNTIME_CHECK_DEFAULT "memcheck") +endif() + +# Deprecated options to enable runtime checks +macro(deprecated_enable_check old new doc) + option(${old} ${doc} OFF) + if (${old}) + message("WARNING: option ${old} is deprecated, use RUNTIME_CHECK=${new} instead") + set(RUNTIME_CHECK_DEFAULT ${new}) + endif() +endmacro() +deprecated_enable_check(ENABLE_VALGRIND memcheck "Use valgrind to detect run-time problems") +deprecated_enable_check(ENABLE_SANITIZERS asan "Compile with memory sanitizers (asan, ubsan)") +deprecated_enable_check(ENABLE_TSAN tsan "Compile with thread sanitizer (tsan)") + +set(RUNTIME_CHECK ${RUNTIME_CHECK_DEFAULT} CACHE string "Enable runtime checks. Valid values: ${runtime_checks}") + +if(CMAKE_BUILD_TYPE MATCHES "Coverage" AND RUNTIME_CHECK) + message(FATAL_ERROR "Cannot set RUNTIME_CHECK with CMAKE_BUILD_TYPE=Coverage") +endif() + +macro(assert_has_sanitizers) + if(NOT HAS_SANITIZERS) + message(FATAL_ERROR "compiler sanitizers are not available") + endif() +endmacro() + +macro(assert_has_valgrind) + if(NOT VALGRIND_EXECUTABLE) + message(FATAL_ERROR "valgrind is not available") + endif() +endmacro() + +if(RUNTIME_CHECK STREQUAL "memcheck") + assert_has_valgrind() + message(STATUS "Runtime memory checker: valgrind memcheck") + set(TEST_EXE_PREFIX "${VALGRIND_EXECUTABLE} --tool=memcheck --leak-check=full ${VALGRIND_COMMON_ARGS}") + # FIXME aconway 2018-09-06: NO TEST_WRAP_PREFIX, need --trace-children + many suppressions + +elseif(RUNTIME_CHECK STREQUAL "helgrind") + assert_has_valgrind() + message(STATUS "Runtime race checker: valgrind helgrind") + set(TEST_EXE_PREFIX "${VALGRIND_EXECUTABLE} --tool=helgrind ${VALGRIND_COMMON_ARGS}") + # FIXME aconway 2018-09-06: NO TEST_WRAP_PREFIX, need --trace-children + many suppressions + +elseif(RUNTIME_CHECK STREQUAL "asan") + assert_has_sanitizers() + message(STATUS "Runtime memory checker: gcc/clang memory sanitizers") + set(SANITIZE_FLAGS "-g -fno-omit-frame-pointer -fsanitize=address,undefined") + set(TEST_WRAP_PREFIX "${CMAKE_SOURCE_DIR}/tests/preload_asan.sh $<TARGET_FILE:qpid-proton-core>") + +elseif(RUNTIME_CHECK STREQUAL "tsan") + assert_has_sanitizers() + message(STATUS "Runtime race checker: gcc/clang thread sanitizer") + set(SANITIZE_FLAGS "-g -fno-omit-frame-pointer -fsanitize=thread") + +elseif(RUNTIME_CHECK) + message(FATAL_ERROR "'RUNTIME_CHECK=${RUNTIME_CHECK}' is invalid, valid values: ${runtime_checks}") +endif() + +if(SANITIZE_FLAGS) + set(ENABLE_UNDEFINED_ERROR OFF CACHE BOOL "Disabled for sanitizers" FORCE) + string(APPEND CMAKE_C_FLAGS " ${SANITIZE_FLAGS}") + string(APPEND CMAKE_CXX_FLAGS "${SANITIZE_FLAGS}") +endif() + +if(TEST_EXE_PREFIX) + # Add TEST_EXE_PREFIX to TEST_ENV so test runner scripts can use it. + list(APPEND TEST_ENV "TEST_EXE_PREFIX=${TEST_EXE_PREFIX}") + # Make a CMake-list form of TEST_EXE_PREFIX for add_test() commands + separate_arguments(TEST_EXE_PREFIX_CMD UNIX_COMMAND "${TEST_EXE_PREFIX}") +endif() +separate_arguments(TEST_WRAP_PREFIX_CMD UNIX_COMMAND "${TEST_WRAP_PREFIX}") http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/scripts/env.py ---------------------------------------------------------------------- diff --git a/scripts/env.py b/scripts/env.py index 04fa8bb..14df6d1 100644 --- a/scripts/env.py +++ b/scripts/env.py @@ -58,11 +58,6 @@ def main(argv=None): if len(args) == 0 or len(args[0]) == 0: raise Exception("Error: syntax error in command arguments") - if new_env.get("VALGRIND") and new_env.get("VALGRIND_ALL"): - # Python generates a lot of possibly-lost errors that are not errors, don't show them. - args = [new_env.get("VALGRIND"), "--show-reachable=no", "--show-possibly-lost=no", - "--error-exitcode=42"] + args - p = subprocess.Popen(args, env=new_env) return p.wait() http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/tests/preload_asan.sh ---------------------------------------------------------------------- diff --git a/tests/preload_asan.sh b/tests/preload_asan.sh new file mode 100755 index 0000000..e4928f9 --- /dev/null +++ b/tests/preload_asan.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +if test $# -lt 2; then + echo <<EOF +usage: $0 LIB EXE [args ...] +Get the libasan linked to LIB and preload it to run `EXE args ...` +EOF +fi + +LIB=$1; shift +EXE=$2 + +case $EXE in + *ruby*|*.rb|*python*|*.py) + # ruby has spurious leaks and causes asan errors. + # + # python tests have many leaks that may be real, but need to be + # analysed & fixed or suppressed before turning this on + + # Disable link order check to run with limited sanitizing + # Still seeing problems. + export ASAN_OPTIONS=verify_asan_link_order=0 + ;; + *) + # Preload the asan library linked to LIB. Note we need to + # check the actual linkage, there may be multiple asan lib + # versions installed and we must use the same one. + libasan=$(ldd $LIB | awk "/(libasan\\.so[.0-9]*)/ { print \$1 }") + export LD_PRELOAD="$libasan:$LD_PRELOAD" + ;; +esac + +exec "$@" http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/tests/py/test_subprocess.py ---------------------------------------------------------------------- diff --git a/tests/py/test_subprocess.py b/tests/py/test_subprocess.py new file mode 100644 index 0000000..1c7d2b9 --- /dev/null +++ b/tests/py/test_subprocess.py @@ -0,0 +1,105 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# + +# Extends the subprocess module to use runtime checkers, and report stderr output. + +import subprocess, re, os, tempfile + +from subprocess import PIPE + +class TestProcessError(Exception): + def __init__(self, proc, what, output=None): + self.output = output + sep = "\n%s stderr(%s) %s\n" %("_" * 32, proc.pid, "_" * 32) + error = sep + proc.error + sep if proc.error else "" + super(TestProcessError, self).__init__("%s pid=%s exit=%s: %s%s" % ( + proc.cmd, proc.pid, proc.returncode, what, error)) + +class Popen(subprocess.Popen): + """ + Adds TEST_EXE_PREFIX to the command and checks stderr for runtime checker output. + In a 'with' statement it runs check_wait() on exit from the block, or + check_kill() if initialized with kill_me=True + """ + + def __init__(self, *args, **kwargs): + """ + Takes all args and kwargs of subprocess.Popen except stdout, stderr, universal_newlines + kill_me=True runs check_kill() in __exit__() instead of check_wait() + """ + self.cmd = args[0] + self.on_exit = self.check_kill if kwargs.pop('kill_me', False) else self.check_wait + self.errfile = tempfile.NamedTemporaryFile(delete=False) + kwargs.update({'universal_newlines': True, 'stdout': PIPE, 'stderr': self.errfile}) + args = ((os.environ.get("TEST_EXE_PREFIX", "").split() + args[0]),) + args[1:] + super(Popen, self).__init__(*args, **kwargs) + + def check_wait(self): + if self.wait() or self.error: + raise TestProcessError(self, "check_wait") + + def communicate(self, *args, **kwargs): + result = super(Popen, self).communicate(*args, **kwargs) + if self.returncode or self.error: + raise TestProcessError(self, "check_communicate", result[0]) + return result + + def check_kill(self): + """Raise if process has already exited, kill and raise if self.error is not empty""" + if self.poll() is None: + self.kill() + self.wait() + self.stdout.close() # Doesn't get closed if killed + if self.error: + raise TestProcessError(self, "check_kill found error output") + else: + raise TestProcessError(self, "check_kill process not running") + + def expect(self, pattern): + line = self.stdout.readline() + match = re.search(pattern, line) + if not match: + raise Exception("%s: can't find '%s' in '%s'" % (self.cmd, pattern, line)) + return match + + @property + def error(self): + """Return stderr as string, may only be used after process has terminated.""" + assert(self.poll is not None) + if not hasattr(self, "_error"): + self.errfile.close() # Not auto-deleted + with open(self.errfile.name) as f: # Re-open to read + self._error = f.read().strip() + os.unlink(self.errfile.name) + return self._error + + def __enter__(self): + return self + + def __exit__(self, *args): + self.on_exit() + +def check_output(*args, **kwargs): + return Popen(*args, **kwargs).communicate()[0] + +class Server(Popen): + """A process that prints 'listening on <port>' to stdout""" + def __init__(self, *args, **kwargs): + super(Server, self).__init__(*args, **kwargs) + self.port = self.expect("listening on ([0-9]+)$").group(1) http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/27edd9ac/tests/valgrind.supp ---------------------------------------------------------------------- diff --git a/tests/valgrind.supp b/tests/valgrind.supp new file mode 100644 index 0000000..3fee095 --- /dev/null +++ b/tests/valgrind.supp @@ -0,0 +1,151 @@ +{ + SSL does a number of uninitialized accesses (expected) 1 + Memcheck:Cond + fun:BN_bin2bn + obj:* + obj:* +} + +{ + SSL does a number of uninitialized accesses (expected) 2 + Memcheck:Cond + fun:BN_num_bits_word + fun:BN_num_bits +} + +{ + SSL does a number of uninitialized accesses (expected) 3 + Memcheck:Value8 + fun:BN_num_bits_word + fun:BN_num_bits + fun:BN_mod_exp_mont_consttime + obj:* + fun:ssl3_ctx_ctrl +} + +{ + SSL does a number of uninitialized accesses (expected) 4 + Memcheck:Value8 + fun:BN_mod_exp_mont_consttime + obj:* + fun:ssl3_ctx_ctrl +} + +{ + SSL does a number of uninitialized accesses (FreeBSD version) + Memcheck:Value8 + fun:BN_num_bits_word + fun:BN_num_bits + fun:BN_mod_exp_mont_consttime + fun:BN_mod_exp_mont + obj:*libcrypto.so* + fun:ssl3_ctx_ctrl +} + +{ + SSL does a number of uninitialized accesses (FreeBSD version) + Memcheck:Value8 + fun:BN_mod_exp_mont_consttime + fun:BN_mod_exp_mont + obj:*libcrypto.so* + fun:ssl3_ctx_ctrl +} + +{ + SSL does a number of uninitialized accesses (expected) 5 + Memcheck:Value4 + fun:BN_mod_exp_mont_consttime + fun:BN_mod_exp_mont + obj:* + obj:* +} + +{ + SSL does a number of uninitialized accesses (expected) 6 + Memcheck:Value4 + fun:BN_num_bits_word + fun:BN_mod_exp_mont_consttime + fun:BN_mod_exp_mont + obj:* + obj:* +} + +{ + SSL does a number of uninitialized accesses (expected) 7 + Memcheck:Cond + fun:ASN1_STRING_set + fun:ASN1_mbstring_ncopy + fun:ASN1_mbstring_copy +} + +{ + Since we can never safely uninitialize SSL, allow this + Memcheck:Leak + fun:_vgrZU_libcZdsoZa_realloc + fun:CRYPTO_realloc + fun:lh_insert + obj:/lib64/libcrypto.so.0.9.8e + fun:ERR_load_strings + fun:ERR_load_X509V3_strings + fun:ERR_load_crypto_strings + fun:SSL_load_error_strings +} + +{ + Since we can never safely uninitialize SSL, allow this + Memcheck:Leak + fun:_vgrZU_libcZdsoZa_malloc + fun:CRYPTO_malloc + fun:lh_new + fun:OBJ_NAME_init + fun:OBJ_NAME_add + fun:EVP_add_cipher + fun:SSL_library_init +} + +{ + Since we can never safely uninitialize SSL, allow this + Memcheck:Leak + fun:malloc + obj:* + fun:CRYPTO_malloc +} + +{ + Known memory leak in cyrus-sasl (fixed in 2.1.26) + Memcheck:Leak + fun:malloc + fun:* + fun:sasl_config_init + fun:sasl_server_init +} + +{ + Known bug in glibc which tries to free ipv6 related static when getaddrinfo used + Memcheck:Free + fun:free + fun:__libc_freeres + fun:_vgnU_freeres + fun:__run_exit_handlers + fun:exit +} + +{ + Benign race in pni_log_enabled + Helgrind:Race + fun:pni_log_enabled +} + +{ + NSS library poking around in its own data segment upsets helgrind + Helgrind:Race + fun:strpbrk + fun:_nss_files_parse_servent +} + +{ + NSS library poking around in its own text segment upsets helgrind + Helgrind:Race + fun:* + fun:_nss_files_getservbyname_r +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org