diff options
author | Eddie Louie <eddie.louie@mongodb.com> | 2017-03-24 02:22:31 -0400 |
---|---|---|
committer | Eddie Louie <eddie.louie@mongodb.com> | 2017-03-31 18:21:53 -0400 |
commit | b6d2b5e5c6071f6b76072d15bf36f34babec2a14 (patch) | |
tree | c90bf25eee33d2cd43f7d0cd991c1a2d48425475 /buildscripts | |
parent | f6cbdfb8c5c52209f58562ccbe14013c72df3f40 (diff) | |
download | mongo-b6d2b5e5c6071f6b76072d15bf36f34babec2a14.tar.gz |
SERVER-21842 Generate report.json file and dump resmoke stacks on Windows
Diffstat (limited to 'buildscripts')
-rwxr-xr-x | buildscripts/hang_analyzer.py | 60 | ||||
-rwxr-xr-x | buildscripts/resmoke.py | 3 | ||||
-rw-r--r-- | buildscripts/resmokelib/sighandler.py | 73 |
3 files changed, 121 insertions, 15 deletions
diff --git a/buildscripts/hang_analyzer.py b/buildscripts/hang_analyzer.py index 45e34462fda..ffc5d6a79fa 100755 --- a/buildscripts/hang_analyzer.py +++ b/buildscripts/hang_analyzer.py @@ -27,6 +27,12 @@ import tempfile import time from distutils import spawn from optparse import OptionParser +_is_windows = (sys.platform == "win32") + +if _is_windows: + import win32event + import win32api + # Get relative imports to work when the package is not installed on the PYTHONPATH. if __name__ == "__main__" and __package__ is None: @@ -477,7 +483,7 @@ def get_hang_analyzers(): dbg = GDBDumper() jstack = JstackDumper() ps = SolarisProcessList() - elif os.name == 'nt' or (os.name == "posix" and sys.platform == "cygwin"): + elif _is_windows or sys.platform == "cygwin": dbg = WindowsDumper() jstack = JstackWindowsDumper() ps = WindowsProcessList() @@ -501,6 +507,33 @@ def check_dump_quota(quota, ext): return (size_sum <= quota) +def signal_event_object(logger, pid): + """Signal the Windows event object""" + + # Use unique event_name created. + event_name = "Global\\Mongo_Python_" + str(pid) + + try: + desired_access = win32event.EVENT_MODIFY_STATE + inherit_handle = False + task_timeout_handle = win32event.OpenEvent(desired_access, + inherit_handle, + event_name) + except win32event.error as err: + logger.info("Exception from win32event.OpenEvent with error: %s" % err) + return + + try: + win32event.SetEvent(task_timeout_handle) + except win32event.error as err: + logger.info("Exception from win32event.SetEvent with error: %s" % err) + finally: + win32api.CloseHandle(task_timeout_handle) + + logger.info("Waiting for process to report") + time.sleep(5) + + def signal_process(logger, pid, signalnum): """Signal process with signal, N/A on Windows""" try: @@ -530,8 +563,13 @@ def main(): root_logger.info("OS: %s" % platform.platform()) try: - distro = platform.linux_distribution() - root_logger.info("Linux Distribution: %s" % str(distro)) + if _is_windows or sys.platform == "cygwin": + distro = platform.win32_ver() + root_logger.info("Windows Distribution: %s" % str(distro)) + else: + distro = platform.linux_distribution() + root_logger.info("Linux Distribution: %s" % str(distro)) + except AttributeError: root_logger.warning("Cannot determine Linux distro since Python is too old") @@ -651,13 +689,23 @@ def main(): # TerminateProcess. # Note: The stacktrace output may be captured elsewhere (i.e. resmoke). for (pid, process_name) in [(p, pn) for (p, pn) in processes if pn in go_processes]: - root_logger.info("Sending signal SIGABRT to go process %s with PID %d" % (process_name, pid)) + root_logger.info("Sending signal SIGABRT to go process %s with PID %d" % + (process_name, pid)) signal_process(root_logger, pid, signal.SIGABRT) # Dump python processes after signalling them. for (pid, process_name) in [(p, pn) for (p, pn) in processes if pn.startswith("python")]: - root_logger.info("Sending signal SIGUSR1 to python process %s with PID %d" % (process_name, pid)) - signal_process(root_logger, pid, signal.SIGUSR1) + # On Windows, we set up an event object to wait on a signal. For Cygwin, we register + # a signal handler to wait for the signal since it supports POSIX signals. + if _is_windows: + root_logger.info("Calling SetEvent to signal python process %s with PID %d" % + (process_name, pid)) + signal_event_object(root_logger, pid) + else: + root_logger.info("Sending signal SIGUSR1 to python process %s with PID %d" % + (process_name, pid)) + signal_process(root_logger, pid, signal.SIGUSR1) + process_logger = get_process_logger(options.debugger_output, pid, process_name) dbg.dump_info( root_logger, diff --git a/buildscripts/resmoke.py b/buildscripts/resmoke.py index c5511ef1e4d..ca3eee80f85 100755 --- a/buildscripts/resmoke.py +++ b/buildscripts/resmoke.py @@ -148,7 +148,8 @@ def main(): interrupted = False suites = resmokelib.parser.get_suites(values, args) - # Register a signal handler so we can write the report file if the task times out. + # Register a signal handler or Windows event object so we can write the report file if the task + # times out. resmokelib.sighandler.register(resmoke_logger, suites) # Run the suite finder after the test suite parsing is complete. diff --git a/buildscripts/resmokelib/sighandler.py b/buildscripts/resmokelib/sighandler.py index 082deea7630..cbce75ca0f7 100644 --- a/buildscripts/resmokelib/sighandler.py +++ b/buildscripts/resmokelib/sighandler.py @@ -4,16 +4,25 @@ Utility to support asynchronously signaling the current process. from __future__ import absolute_import +import atexit +import os import signal import sys +import threading import traceback +_is_windows = (sys.platform == "win32") +if _is_windows: + import win32api + import win32event + from . import reportfile def register(logger, suites): """ - Registers a signal handler for the SIGUSR1 signal. + On Windows, set up an event object to wait for signal, otherwise, register a signal handler + for the SIGUSR1 signal. """ def _handle_sigusr1(signum, frame): @@ -22,22 +31,70 @@ def register(logger, suites): then write out the report file. """ - _dump_stacks(logger) + header_msg = "Dumping stacks due to SIGUSR1 signal" + + _dump_stacks(logger, header_msg) reportfile.write(suites) - try: + def _handle_set_event(event_handle): + """ + Windows event object handler that will dump the stacks of all threads and then write out + the report file. + """ + + while True: + try: + # Wait for task time out to dump stacks. + ret = win32event.WaitForSingleObject(event_handle, win32event.INFINITE) + if ret != win32event.WAIT_OBJECT_0: + logger.error("_handle_set_event WaitForSingleObject failed: %d" % ret) + return + except win32event.error as err: + logger.error("Exception from win32event.WaitForSingleObject with error: %s" % err) + else: + header_msg = "Dumping stacks due to signal from win32event.SetEvent" + + _dump_stacks(logger, header_msg) + reportfile.write(suites) + + + # On Windows spawn a thread to wait on an event object for signal to dump stacks. For Cygwin + # platforms, we use a signal handler since it supports POSIX signals. + if _is_windows: + # Create unique event_name. + event_name = "Global\\Mongo_Python_" + str(os.getpid()) + + try: + security_attributes = None + manual_reset = False + initial_state = False + task_timeout_handle = win32event.CreateEvent(security_attributes, + manual_reset, + initial_state, + event_name) + except win32event.error as err: + logger.error("Exception from win32event.CreateEvent with error: %s" % err) + return + + # Register to close event object handle on exit. + atexit.register(win32api.CloseHandle, task_timeout_handle) + + # Create thread. + event_handler_thread = threading.Thread(target=_handle_set_event, + kwargs={"event_handle": task_timeout_handle}, + name="windows_event_handler_thread") + event_handler_thread.daemon = True + event_handler_thread.start() + else: + # Otherwise register a signal handler signal.signal(signal.SIGUSR1, _handle_sigusr1) - except AttributeError: - logger.warn("Cannot catch signals on Windows") -def _dump_stacks(logger): +def _dump_stacks(logger, header_msg): """ Signal handler that will dump the stacks of all threads. """ - header_msg = "Dumping stacks due to SIGUSR1 signal" - sb = [] sb.append(header_msg) |