Author: philip Date: Mon Mar 20 01:22:52 2023 New Revision: 1908546 URL: http://svn.apache.org/viewvc?rev=1908546&view=rev Log: Add suport for running valgrind during 'make check'.
* Makefile.in (check): Add options. * build/run_tests.py (_init_c_tests, _init_py_tests): Add options. (_maybe_prepend_valgrind): New. (_run_c_test): Prepend valgrind. (create_parser): Add options. * subversion/tests/cmdline/svntest/main.py (libtool_script): New variable. (open_pipe): Prepend valgrind. (TestSpawningThread): Add options. (_create_parser): Add options. * subversion/tests/README: Describe valgrind testing.. Modified: subversion/trunk/Makefile.in subversion/trunk/build/run_tests.py subversion/trunk/subversion/tests/README subversion/trunk/subversion/tests/cmdline/svntest/main.py Modified: subversion/trunk/Makefile.in URL: http://svn.apache.org/viewvc/subversion/trunk/Makefile.in?rev=1908546&r1=1908545&r2=1908546&view=diff ============================================================================== --- subversion/trunk/Makefile.in (original) +++ subversion/trunk/Makefile.in Mon Mar 20 01:22:52 2023 @@ -632,6 +632,12 @@ check: bin @TRANSFORM_LIBTOOL_SCRIPTS@ $ if test "$(STORE_PRISTINE)" != ""; then \ flags="--store-pristine $(STORE_PRISTINE) $$flags"; \ fi; \ + if test "$(VALGRIND)" != ""; then \ + flags="--valgrind $(VALGRIND) $$flags"; \ + fi; \ + if test "$(VALGRIND_OPTS)" != ""; then \ + flags="--valgrind-opts $(VALGRIND_OPTS) $$flags"; \ + fi; \ LD_LIBRARY_PATH='$(auth_plugin_dirs):$(LD_LIBRARY_PATH)' \ $(PYTHON) $(top_srcdir)/build/run_tests.py \ --config-file $(top_srcdir)/subversion/tests/tests.conf \ Modified: subversion/trunk/build/run_tests.py URL: http://svn.apache.org/viewvc/subversion/trunk/build/run_tests.py?rev=1908546&r1=1908545&r2=1908546&view=diff ============================================================================== --- subversion/trunk/build/run_tests.py (original) +++ subversion/trunk/build/run_tests.py Mon Mar 20 01:22:52 2023 @@ -266,6 +266,10 @@ class TestHarness: cmdline.append('--parallel') if self.opts.store_pristine is not None: cmdline.append('--store-pristine=%s' % self.opts.store_pristine) + if self.opts.valgrind is not None: + cmdline.append('--valgrind=%s' % self.opts.valgrind) + if self.opts.valgrind_opts is not None: + cmdline.append('--valgrind-opts=%s' % self.opts.valgrind_opts) self.c_test_cmdline = cmdline @@ -335,6 +339,10 @@ class TestHarness: cmdline.append('--allow-remote-http-connection') if self.opts.store_pristine is not None: cmdline.append('--store-pristine=%s' % self.opts.store_pristine) + if self.opts.valgrind is not None: + cmdline.append('--valgrind=%s' % self.opts.valgrind) + if self.opts.valgrind_opts is not None: + cmdline.append('--valgrind-opts=%s' % self.opts.valgrind_opts) self.py_test_cmdline = cmdline @@ -814,6 +822,17 @@ class TestHarness: log.write('FAIL: %s: Unknown test failure (%s).\n' % (progbase, test_failed)) + def _maybe_prepend_valgrind(self, cmdline, progbase): + if self.opts.valgrind: + if (progbase in self.opts.valgrind.split(',') + or 'C' in self.opts.valgrind.split(',')): + valgrind = [os.path.join(self.builddir, 'libtool'), '--mode=execute', + 'valgrind', '--quiet', '--error-exitcode=1'] + if self.opts.valgrind_opts: + valgrind += self.opts.valgrind_opts.split(' ') + cmdline = valgrind + cmdline + return cmdline + def _run_c_test(self, progabs, progdir, progbase, test_nums, dot_count): 'Run a c test, escaping parameters as required.' if self.opts.list_tests and self.opts.milestone_filter: @@ -849,6 +868,7 @@ class TestHarness: self.dots_written = dots tests_completed = 0 + cmdline = self._maybe_prepend_valgrind(cmdline, progbase) with Popen(cmdline, stdout=subprocess.PIPE, stderr=self.log) as prog: line = prog.stdout.readline() while line: @@ -1095,6 +1115,10 @@ def create_parser(): help='Run tests that connect to remote HTTP(S) servers') parser.add_option('--store-pristine', action='store', type='str', help='Set the WC pristine mode') + parser.add_option('--valgrind', action='store', + help='programs to run under valgrind') + parser.add_option('--valgrind-opts', action='store', + help='options to pass valgrind') parser.set_defaults(set_log_level=None) return parser Modified: subversion/trunk/subversion/tests/README URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/README?rev=1908546&r1=1908545&r2=1908546&view=diff ============================================================================== --- subversion/trunk/subversion/tests/README (original) +++ subversion/trunk/subversion/tests/README Mon Mar 20 01:22:52 2023 @@ -261,6 +261,72 @@ Testing Over DAV Please see subversion/tests/cmdline/README for how to run the command-line client test suite against a remote repository. + +Testing with valgrind +--------------------- + +The test suite has support for valgrind. For this to be useful +Subversion must be compiled with pool debugging enabled by +adding -DAPR_POOL_DEBUG to CFLAGS. It is sufficient to compile +just Subversion with pool debugging but compiling apr, apr-util +and serf will cause more of the code to be checked. + +Valgrind can be used on the C test programs, and/or on svn, +svnlook, svnadmin, etc. invoked during the python tests. To test +the C programs use: + + make check VALGRIND=C + +To test svn during the python tests use: + + make check SKIP_C_TESTS=1 VALGRIND=svn + +To test svnlook during the python tests use: + + make check VALGRIND=svnlook TESTS=subversion/tests/cmdline/svnlook_tests.py + +The VALGRIND settings can be combined: + + make check PARALLEL=1 VALGRIND=C,svn,svnlook + +Any valgrind error causes diagnostic output on stderr but the program +will continue to run. For the C tests valgrind is invoked with the +option --error-exitcode=1 so that the although the individual +tests within each C test program may PASS the test as a whole will +FAIL. For the python tests the test harness parses stderr and detects +the valgrind diagnostic, so individual tests will FAIL. In both cases +the stderr diagnostic output from valgrind is available in tests.log. + +If valgind is producing unwanted diagnostics in some of the system +libraries then create a suppression file and pass it as: + + make check VALGRIND=svn VALGRIND_OPTS=--suppressions=$PWD/vg.supp + +Running valgrind in the testsuite will only check the client-side code. +To check the server-side code invoke the server using valgrind and +arrange for it to remain in the foreground so valgrind output will +appear on stderr in the terminal. For svnserve something like: + + ./libtool --mode=execute valgrind \ + subversion/svnserve/svnserve -Tdr subversion/tests/cmdline --foreground + make check BASE_URL=svn://localhost # in another terminal + +For apache use a threaded MPM and then something like: + + valgrind httpd -X -f /path/to/cfg + make check BASE_URL=http://localhost:8888 # in another terminal + +Note that using valgrind will make the testsuite much slower; expect more +than an order of magnitude slowdown if using valgrind on svn during the +python tests. Using the ASAN/MSAN/LSAN sanitizers in gcc/clang is an +alternative; they will catch similar types of bugs and are much faster. +The sanitizers also benefit from pool debugging. + +Debian's 1:3.19.0-1 version of valgrind seems to have a problem with +the Dwarf 5 debugging info produced by Debian's compilers, so compile +with -gdwarf-4 if using -g. + + Conclusion ---------- Modified: subversion/trunk/subversion/tests/cmdline/svntest/main.py URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/svntest/main.py?rev=1908546&r1=1908545&r2=1908546&view=diff ============================================================================== --- subversion/trunk/subversion/tests/cmdline/svntest/main.py (original) +++ subversion/trunk/subversion/tests/cmdline/svntest/main.py Mon Mar 20 01:22:52 2023 @@ -211,6 +211,9 @@ svnauthz_validate_binary = os.path.abspa ) svnmover_binary = os.path.abspath('../../../tools/dev/svnmover/svnmover' + _exe) +# Where to find the libtool script created during build +libtool_script = os.path.abspath('../../../libtool') + # Location to the pristine repository, will be calculated from test_area_url # when we know what the user specified for --url. pristine_greek_repos_url = None @@ -508,6 +511,13 @@ def open_pipe(command, bufsize=-1, stdin if command[0].endswith('.py'): command.insert(0, sys.executable) + if options.valgrind: + if os.path.basename(command[0]) in options.valgrind.split(','): + valgrind = [libtool_script, '--mode=execute', 'valgrind', '--quiet'] + if options.valgrind_opts is not None: + valgrind += options.valgrind_opts.split(' ') + command = valgrind + command + command_string = command[0] + ' ' + ' '.join(map(_quote_arg, command[1:])) if not stdin: @@ -1856,6 +1866,10 @@ class TestSpawningThread(threading.Threa args.append('--bin=' + options.svn_bin) if options.store_pristine: args.append('--store-pristine=' + options.store_pristine) + if options.valgrind: + args.append('--valgrind=' + options.valgrind) + if options.valgrind_opts: + args.append('--valgrind-opts=' + options.valgrind_opts) result, stdout_lines, stderr_lines = spawn_process(command, 0, False, None, *args) @@ -2297,6 +2311,10 @@ def _create_parser(usage=None): help='Run tests that connect to remote HTTP(S) servers') parser.add_option('--store-pristine', action='store', type='str', help='Set the WC pristine mode') + parser.add_option('--valgrind', action='store', + help='programs to run under valgrind') + parser.add_option('--valgrind-opts', action='store', + help='options to pass to valgrind') # most of the defaults are None, but some are other values, set them here parser.set_defaults(