summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEddie Louie <eddie.louie@mongodb.com>2017-03-24 02:22:31 -0400
committerEddie Louie <eddie.louie@mongodb.com>2017-03-31 18:21:53 -0400
commitb6d2b5e5c6071f6b76072d15bf36f34babec2a14 (patch)
treec90bf25eee33d2cd43f7d0cd991c1a2d48425475
parentf6cbdfb8c5c52209f58562ccbe14013c72df3f40 (diff)
downloadmongo-b6d2b5e5c6071f6b76072d15bf36f34babec2a14.tar.gz
SERVER-21842 Generate report.json file and dump resmoke stacks on Windows
-rwxr-xr-xbuildscripts/hang_analyzer.py60
-rwxr-xr-xbuildscripts/resmoke.py3
-rw-r--r--buildscripts/resmokelib/sighandler.py73
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)