diff options
author | Eddie Louie <eddie.louie@mongodb.com> | 2017-03-24 02:22:31 -0400 |
---|---|---|
committer | Eddie Louie <eddie.louie@mongodb.com> | 2017-04-27 18:18:19 -0400 |
commit | 6f4805c92a9099431c0d694256f6ffd3d1451d24 (patch) | |
tree | c72b68072939ccbc11ac7054fcf2b104800c7d4c | |
parent | 4293ece343526a2795bac13105ac9ead723e872c (diff) | |
download | mongo-6f4805c92a9099431c0d694256f6ffd3d1451d24.tar.gz |
SERVER-21842 Generate report.json file and dump resmoke stacks on Windows
(cherry picked from commit b6d2b5e5c6071f6b76072d15bf36f34babec2a14)
-rwxr-xr-x | buildscripts/hang_analyzer.py | 63 | ||||
-rwxr-xr-x | buildscripts/resmoke.py | 3 | ||||
-rw-r--r-- | buildscripts/resmokelib/sighandler.py | 73 |
3 files changed, 122 insertions, 17 deletions
diff --git a/buildscripts/hang_analyzer.py b/buildscripts/hang_analyzer.py index ea708cc0ecd..ef99fb8461a 100755 --- a/buildscripts/hang_analyzer.py +++ b/buildscripts/hang_analyzer.py @@ -24,6 +24,12 @@ import threading import time from distutils import spawn from optparse import OptionParser +_is_windows = (sys.platform == "win32") + +if _is_windows: + import win32event + import win32api + if sys.platform == "win32": import win32process @@ -365,7 +371,7 @@ def get_hang_analyzers(): elif sys.platform.startswith("sunos"): dbg = GDBDumper() ps = SolarisProcessList() - elif os.name == 'nt' or (os.name == "posix" and sys.platform == "cygwin"): + elif _is_windows or sys.platform == "cygwin": dbg = WindowsDumper() ps = WindowsProcessList() elif sys.platform == "darwin": @@ -383,10 +389,37 @@ def is_interesting_process(p): return False -def signal_process(pid): - """Signal python process with SIGUSR1, N/A on Windows""" +def signal_event_object(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: + print "Exception from win32event.OpenEvent with error: " + str(err) + return + try: - os.kill(pid, signal.SIGUSR1) + win32event.SetEvent(task_timeout_handle) + except win32event.error as err: + print "Exception from win32event.SetEvent with error: " + str(err) + finally: + win32api.CloseHandle(task_timeout_handle) + + print "Waiting for process to report" + time.sleep(5) + + +def signal_process(pid, signalnum): + """Signal process with signal, N/A on Windows""" + try: + os.kill(pid, signalnum) print "Waiting for python process to report" time.sleep(5) @@ -417,8 +450,13 @@ def main(): print "OS: " + platform.platform() try: - distro = platform.linux_distribution() - print "Linux Distribution: " + str(distro) + if _is_windows or sys.platform == "cygwin": + distro = platform.win32_ver() + print "Windows Distribution: " + str(distro) + else: + distro = platform.linux_distribution() + print "Linux Distribution: " + str(distro) + except AttributeError: print "Cannot determine Linux distro since Python is too old" @@ -463,14 +501,23 @@ def main(): dbg.dump_info(process[0], process[1], sys.stdout) for process in [a for a in processes if a[1].startswith("python")]: - signal_process(process[0]) + # 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: + sys.stdout.write("Calling SetEvent to signal python process %s with PID %d\n" % + (process[1], process[0])) + signal_event_object(process[0]) + else: + sys.stdout.write("Sending signal SIGUSR1 to python process %s with PID %d\n" % + (process[1], process[0])) + signal_process(process[0], signal.SIGUSR1) dbg.dump_info(process[0], process[1], sys.stdout) # Suspend the timer so we can exit cleanly timer.cancel() - sys.stdout.write("Done analyzing processes for hangs\n") + sys.stdout.write("Done analyzing all processes for hangs\n") if __name__ == "__main__": main() diff --git a/buildscripts/resmoke.py b/buildscripts/resmoke.py index 40c88f9587d..837b0ec3b26 100755 --- a/buildscripts/resmoke.py +++ b/buildscripts/resmoke.py @@ -134,7 +134,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) try: 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) |