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-04-27 18:18:19 -0400
commit6f4805c92a9099431c0d694256f6ffd3d1451d24 (patch)
treec72b68072939ccbc11ac7054fcf2b104800c7d4c
parent4293ece343526a2795bac13105ac9ead723e872c (diff)
downloadmongo-6f4805c92a9099431c0d694256f6ffd3d1451d24.tar.gz
SERVER-21842 Generate report.json file and dump resmoke stacks on Windows
(cherry picked from commit b6d2b5e5c6071f6b76072d15bf36f34babec2a14)
-rwxr-xr-xbuildscripts/hang_analyzer.py63
-rwxr-xr-xbuildscripts/resmoke.py3
-rw-r--r--buildscripts/resmokelib/sighandler.py73
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)