diff options
author | Luke Chen <luke.chen@mongodb.com> | 2023-04-27 13:52:46 +1000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-04-27 04:23:54 +0000 |
commit | afc718c315bf713c6a3d79ebcfac23a35b36f1a5 (patch) | |
tree | aacb147ba7ced5bba97c43d8fac06d1f86cb47f6 | |
parent | c974b94d01f529df754554605e5ea3e4d675aaf4 (diff) | |
download | mongo-afc718c315bf713c6a3d79ebcfac23a35b36f1a5.tar.gz |
Import wiredtiger: a7dc285de9c56a5d66522e8887543b9b4c157f09 from branch mongodb-6.0
ref: 282fe2bab6..a7dc285de9
for: 6.0.6
WT-8932 Call wait on forked child from Python tests and add extra debugging for PIDs
12 files changed, 88 insertions, 11 deletions
diff --git a/src/third_party/wiredtiger/import.data b/src/third_party/wiredtiger/import.data index 39ed9e47ff2..5bde51e352a 100644 --- a/src/third_party/wiredtiger/import.data +++ b/src/third_party/wiredtiger/import.data @@ -2,5 +2,5 @@ "vendor": "wiredtiger", "github": "wiredtiger/wiredtiger.git", "branch": "mongodb-6.0", - "commit": "282fe2bab684f89ea92edf111461ccbe1b469375" + "commit": "a7dc285de9c56a5d66522e8887543b9b4c157f09" } diff --git a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/PKG-INFO b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/PKG-INFO index edb06bf7a42..edb06bf7a42 100644 --- a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/PKG-INFO +++ b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/PKG-INFO diff --git a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/README b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/README new file mode 100644 index 00000000000..e9d53be96d1 --- /dev/null +++ b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/README @@ -0,0 +1,4 @@ +We have modified concurrencytest.py from the original concurrencytest-0.1.2 to wait for the child processes +preventing defunct processes and to prefix output with the running PID for debuggability.o + +Future changes such as updating concurrencytest must take this into account. diff --git a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/concurrencytest.egg-info/PKG-INFO b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/concurrencytest.egg-info/PKG-INFO index edb06bf7a42..edb06bf7a42 100644 --- a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/concurrencytest.egg-info/PKG-INFO +++ b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/concurrencytest.egg-info/PKG-INFO diff --git a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/concurrencytest.egg-info/SOURCES.txt b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/concurrencytest.egg-info/SOURCES.txt index bf9f692ad1f..bf9f692ad1f 100644 --- a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/concurrencytest.egg-info/SOURCES.txt +++ b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/concurrencytest.egg-info/SOURCES.txt diff --git a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/concurrencytest.egg-info/dependency_links.txt b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/concurrencytest.egg-info/dependency_links.txt index 8b137891791..8b137891791 100644 --- a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/concurrencytest.egg-info/dependency_links.txt +++ b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/concurrencytest.egg-info/dependency_links.txt diff --git a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/concurrencytest.egg-info/requires.txt b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/concurrencytest.egg-info/requires.txt index 537ebcbac33..537ebcbac33 100644 --- a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/concurrencytest.egg-info/requires.txt +++ b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/concurrencytest.egg-info/requires.txt diff --git a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/concurrencytest.egg-info/top_level.txt b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/concurrencytest.egg-info/top_level.txt index cfc96e6db71..cfc96e6db71 100644 --- a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/concurrencytest.egg-info/top_level.txt +++ b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/concurrencytest.egg-info/top_level.txt diff --git a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/concurrencytest.py b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/concurrencytest.py index b3cb52d48d3..f9049ce8518 100644 --- a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/concurrencytest.py +++ b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/concurrencytest.py @@ -23,7 +23,9 @@ Unix only. import os import sys +import time import traceback +from threading import Thread import unittest from itertools import cycle from multiprocessing import cpu_count @@ -40,9 +42,29 @@ _all__ = [ 'partition_tests', ] +# This file has been modified from the original concurrencytest-0.1.2 to wait for the child processes +# preventing defunct processes and to prefix output with the running PID for debuggability. CPU_COUNT = cpu_count() +def wait_for_children(pids): + while pids: + try: + # As Windows doesn't support -1 for all children, loop through each child pid explicitly. + for child_pid in pids: + pid, exit_status = os.waitpid(child_pid, os.WNOHANG) + exit_code = os.waitstatus_to_exitcode(exit_status) + if exit_code != 0: + pids.remove(pid) + if exit_code > 0: + print("[pid:{}]: Unexpected exit ({}) for child process ({})".format(os.getpid(), exit_code, pid)) + else: + print("[pid:{}]: Unexpected exit by signal ({}) for child process ({})".format(os.getpid(), abs(exit_code), pid)) + except ChildProcessError: + # No children processes. + break + # Sleep as the waipid is non blocking. + time.sleep(5) def fork_for_tests(concurrency_num=CPU_COUNT): """Implementation of `make_tests` used to construct `ConcurrentTestSuite`. @@ -61,6 +83,7 @@ def fork_for_tests(concurrency_num=CPU_COUNT): test_blocks = partition_tests(suite, concurrency_num) # Clear the tests from the original suite so it doesn't keep them alive suite._tests[:] = [] + pids = [] for process_tests in test_blocks: process_suite = unittest.TestSuite(process_tests) # Also clear each split list so new suite has only reference @@ -79,6 +102,8 @@ def fork_for_tests(concurrency_num=CPU_COUNT): subunit_result = AutoTimingTestResultDecorator( TestProtocolClient(stream) ) + # Set the pid tag for the parent to log with this information. + subunit_result.tags(["pid:" + str(os.getpid())], []) process_suite.run(subunit_result) except: # Try and report traceback on stream, but exit with error @@ -87,7 +112,7 @@ def fork_for_tests(concurrency_num=CPU_COUNT): # written in one go to avoid interleaving lines from # multiple failing children. try: - stream.write(traceback.format_exc()) + print("[pid:{}]: {}".format(os.getpid(), traceback.format_exc())) finally: os._exit(1) os._exit(0) @@ -96,6 +121,10 @@ def fork_for_tests(concurrency_num=CPU_COUNT): stream = os.fdopen(c2pread, 'rb', 1) test = ProtocolTestCase(stream) result.append(test) + pids.append(pid) + # Monitor our children to prevent leaving <defunct> processes around. + wait_thread = Thread(target = wait_for_children, args = (pids, )) + wait_thread.start() return result return do_fork @@ -113,7 +142,6 @@ def partition_tests(suite, count): partition.append(test) return partitions - if __name__ == '__main__': import time diff --git a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/setup.cfg b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/setup.cfg index 861a9f55426..861a9f55426 100644 --- a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/setup.cfg +++ b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/setup.cfg diff --git a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/setup.py b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/setup.py index 447dc4110fe..447dc4110fe 100644 --- a/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2/setup.py +++ b/src/third_party/wiredtiger/test/3rdparty/concurrencytest-0.1.2-locally-modified/setup.py diff --git a/src/third_party/wiredtiger/test/suite/wttest.py b/src/third_party/wiredtiger/test/suite/wttest.py index edd22e346c7..7d94cda9bc8 100755 --- a/src/third_party/wiredtiger/test/suite/wttest.py +++ b/src/third_party/wiredtiger/test/suite/wttest.py @@ -42,7 +42,7 @@ except ImportError: import unittest from contextlib import contextmanager -import errno, glob, os, re, shutil, sys, time, traceback +import errno, glob, os, re, shutil, sys, threading, time, traceback, types import wiredtiger, wtscenario, wthooks def shortenWithEllipsis(s, maxlen): @@ -180,6 +180,38 @@ class ExtensionList(list): ext = '' if extarg == None else '=' + extarg self.append(dirname + '/' + name + ext) +# Custom result class that will prefix the pid in text output (including if it's a child). +# Only enabled when we are in verbose mode so we don't check that here. +class PidAwareTextTestResult(unittest.TextTestResult): + _thread_prefix = threading.local() + + def __init__(self, stream, descriptions, verbosity): + super(PidAwareTextTestResult, self).__init__(stream, descriptions, verbosity) + self._thread_prefix.value = "[pid:{}]: ".format(os.getpid()) + + def tags(self, new_tags, gone_tags): + # We attach the PID to the thread so we only need the new_tags. + for tag in new_tags: + if tag.startswith("pid:"): + pid = tag[len("pid:"):] + self._thread_prefix.value = "[pid:{}/{}]: ".format(os.getpid(), pid) + + def startTest(self, test): + self.stream.write(self._thread_prefix.value) + super(PidAwareTextTestResult, self).startTest(test) + + def getDescription(self, test): + return str(test.shortDescription()) + + def printErrorList(self, flavour, errors): + for test, err in errors: + self.stream.writeln(self.separator1) + self.stream.writeln("%s%s: %s" % (self._thread_prefix.value, + flavour, self.getDescription(test))) + self.stream.writeln(self.separator2) + self.stream.writeln("%s%s" % (self._thread_prefix.value, err)) + self.stream.flush() + class WiredTigerTestCase(unittest.TestCase): _globalSetup = False _printOnceSeen = {} @@ -586,9 +618,9 @@ class WiredTigerTestCase(unittest.TestCase): elapsed = time.time() - self.starttime if elapsed > 0.001 and WiredTigerTestCase._verbose >= 2: - print("%s: %.2f seconds" % (str(self), elapsed)) + print("[pid:{}]: {}: {:.2f} seconds".format(os.getpid(), str(self), elapsed)) if (not passed) and (not self.skipped): - print("ERROR in " + str(self)) + print("[pid:{}]: ERROR in {}".format(os.getpid(), str(self))) self.pr('FAIL') self.pr('preserving directory ' + self.testdir) if WiredTigerTestCase._verbose > 2: @@ -834,7 +866,7 @@ class WiredTigerTestCase(unittest.TestCase): @staticmethod def prout(s): - os.write(WiredTigerTestCase._dupout, str.encode(s + '\n')) + os.write(WiredTigerTestCase._dupout, str.encode("[pid:{}]: {}\n".format(os.getpid(), s))) def pr(self, s): """ @@ -873,7 +905,7 @@ class WiredTigerTestCase(unittest.TestCase): def tty(message): if WiredTigerTestCase._ttyDescriptor == None: WiredTigerTestCase._ttyDescriptor = open('/dev/tty', 'w') - WiredTigerTestCase._ttyDescriptor.write(message + '\n') + WiredTigerTestCase._ttyDescriptor.write("[pid:{}]: {}\n".format(os.getpid(), message)) def ttyVerbose(self, level, message): WiredTigerTestCase.ttyVerbose(level, message) @@ -935,6 +967,16 @@ def islongtest(): def getseed(): return WiredTigerTestCase._seeds +# We have to override the ThreadsafeForwardingResult implementation of tags so it gets set immediately +# which allows us to set the pid of the process on our output stream to make debugging easier. +def immediate_tags(self, new_tags, gone_tags): + self.result.tags(new_tags, gone_tags) + +def wrap_result_for_tags(thread_safe_result, thread_number): + # We use this technique to override the method instead of extending the class as it allows for less changes. + thread_safe_result.tags = types.MethodType(immediate_tags, thread_safe_result) + return thread_safe_result + def runsuite(suite, parallel): suite_to_run = suite if parallel > 1: @@ -942,16 +984,19 @@ def runsuite(suite, parallel): if not WiredTigerTestCase._globalSetup: WiredTigerTestCase.globalSetup() WiredTigerTestCase._concurrent = True - suite_to_run = ConcurrentTestSuite(suite, fork_for_tests(parallel)) + suite_to_run = ConcurrentTestSuite(suite, fork_for_tests(parallel), wrap_result=wrap_result_for_tags) try: if WiredTigerTestCase._randomseed: WiredTigerTestCase.prout("Starting test suite with seedw={0} and seedz={1}. Rerun this test with -seed {0}.{1} to get the same randomness" .format(str(WiredTigerTestCase._seeds[0]), str(WiredTigerTestCase._seeds[1]))) + result_class = None + if WiredTigerTestCase._verbose > 1: + result_class = PidAwareTextTestResult return unittest.TextTestRunner( - verbosity=WiredTigerTestCase._verbose).run(suite_to_run) + verbosity=WiredTigerTestCase._verbose, resultclass=result_class).run(suite_to_run) except BaseException as e: # This should not happen for regular test errors, unittest should catch everything - print('ERROR: running test: ', e) + print("[pid:{}]: ERROR: running test: {}".format(os.getpid(), e)) raise e def run(name='__main__'): |