[IMP] dumpstack dumps greenlets when running in evented mode.
authorChristophe Simonis <chs@openerp.com>
Mon, 25 Nov 2013 15:59:56 +0000 (16:59 +0100)
committerChristophe Simonis <chs@openerp.com>
Mon, 25 Nov 2013 15:59:56 +0000 (16:59 +0100)
(forward port lost during previous commit)

bzr revid: chs@openerp.com-20131125155956-x92vq9w4r9ov4vcc

openerp/service/server.py
openerp/tools/misc.py

index 1945734..87f1001 100644 (file)
@@ -17,7 +17,6 @@ import subprocess
 import sys
 import threading
 import time
-import traceback
 
 import werkzeug.serving
 
@@ -33,7 +32,7 @@ except ImportError:
 import openerp
 import openerp.tools.config as config
 from openerp.release import nt_service_name
-from openerp.tools.misc import stripped_sys_argv
+from openerp.tools.misc import stripped_sys_argv, dumpstacks
 
 import wsgi_server
 
@@ -183,25 +182,6 @@ class CommonServer(object):
         # runtime
         self.pid = os.getpid()
 
-    def dumpstacks(self):
-        """ Signal handler: dump a stack trace for each existing thread."""
-        # code from http://stackoverflow.com/questions/132058/getting-stack-trace-from-a-running-python-application#answer-2569696
-        # modified for python 2.5 compatibility
-        threads_info = dict([(th.ident, {'name': th.name,
-                                        'uid': getattr(th,'uid','n/a')})
-                                    for th in threading.enumerate()])
-        code = []
-        for threadId, stack in sys._current_frames().items():
-            thread_info = threads_info.get(threadId)
-            code.append("\n# Thread: %s (id:%s) (uid:%s)" % \
-                        (thread_info and thread_info['name'] or 'n/a',
-                         threadId,
-                         thread_info and thread_info['uid'] or 'n/a'))
-            for filename, lineno, name, line in traceback.extract_stack(stack):
-                code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
-                if line:
-                    code.append("  %s" % (line.strip()))
-        _logger.info("\n".join(code))
 
     def close_socket(self, sock):
         """ Closes a socket instance cleanly
@@ -243,9 +223,6 @@ class ThreadedServer(CommonServer):
             # restart on kill -HUP
             openerp.phoenix = True
             self.quit_signals_received += 1
-        elif sig == signal.SIGQUIT:
-            # dump stacks on kill -3
-            self.dumpstacks()
 
     def cron_thread(self, number):
         while True:
@@ -295,7 +272,7 @@ class ThreadedServer(CommonServer):
             signal.signal(signal.SIGTERM, self.signal_handler)
             signal.signal(signal.SIGCHLD, self.signal_handler)
             signal.signal(signal.SIGHUP, self.signal_handler)
-            signal.signal(signal.SIGQUIT, self.signal_handler)
+            signal.signal(signal.SIGQUIT, dumpstacks)
         elif os.name == 'nt':
             import win32api
             win32api.SetConsoleCtrlHandler(lambda sig: signal_handler(sig, None), 1)
@@ -372,6 +349,10 @@ class GeventServer(CommonServer):
     def start(self):
         import gevent
         from gevent.wsgi import WSGIServer
+
+        if os.name == 'posix':
+            signal.signal(signal.SIGQUIT, dumpstacks)
+
         gevent.spawn(self.watch_parent)
         self.httpd = WSGIServer((self.interface, self.port), self.app)
         _logger.info('Evented Service (longpolling) running on %s:%s', self.interface, self.port)
@@ -558,9 +539,10 @@ class PreforkServer(CommonServer):
         signal.signal(signal.SIGTERM, self.signal_handler)
         signal.signal(signal.SIGHUP, self.signal_handler)
         signal.signal(signal.SIGCHLD, self.signal_handler)
-        signal.signal(signal.SIGQUIT, self.signal_handler)
         signal.signal(signal.SIGTTIN, self.signal_handler)
         signal.signal(signal.SIGTTOU, self.signal_handler)
+        signal.signal(signal.SIGQUIT, dumpstacks)
+
         # listen to socket
         self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
index ea5bf6a..20cb510 100644 (file)
@@ -41,6 +41,7 @@ from itertools import islice, izip, groupby
 from lxml import etree
 from which import which
 from threading import local
+import traceback
 
 try:
     from html2text import html2text
@@ -50,9 +51,10 @@ except ImportError:
 from config import config
 from cache import *
 
+import openerp
 # get_encodings, ustr and exception_to_unicode were originally from tools.misc.
 # There are moved to loglevels until we refactor tools.
-from openerp.loglevels import get_encodings, ustr, exception_to_unicode
+from openerp.loglevels import get_encodings, ustr, exception_to_unicode     # noqa
 
 _logger = logging.getLogger(__name__)
 
@@ -1067,4 +1069,42 @@ def stripped_sys_argv(*strip_args):
 
     return [x for i, x in enumerate(args) if not strip(args, i)]
 
+
+def dumpstacks(sig, frame):
+    """ Signal handler: dump a stack trace for each existing thread."""
+    code = []
+
+    def extract_stack(stack):
+        for filename, lineno, name, line in traceback.extract_stack(stack):
+            yield 'File: "%s", line %d, in %s' % (filename, lineno, name)
+            if line:
+                yield "  %s" % (line.strip(),)
+
+    # code from http://stackoverflow.com/questions/132058/getting-stack-trace-from-a-running-python-application#answer-2569696
+    # modified for python 2.5 compatibility
+    threads_info = dict([(th.ident, {'name': th.name, 'uid': getattr(th, 'uid', 'n/a')})
+                        for th in threading.enumerate()])
+    for threadId, stack in sys._current_frames().items():
+        thread_info = threads_info.get(threadId)
+        code.append("\n# Thread: %s (id:%s) (uid:%s)" %
+                    (thread_info and thread_info['name'] or 'n/a',
+                     threadId,
+                     thread_info and thread_info['uid'] or 'n/a'))
+        for line in extract_stack(stack):
+            code.append(line)
+
+    if openerp.evented:
+        # code from http://stackoverflow.com/questions/12510648/in-gevent-how-can-i-dump-stack-traces-of-all-running-greenlets
+        import gc
+        from greenlet import greenlet
+        for ob in gc.get_objects():
+            if not isinstance(ob, greenlet) or not ob:
+                continue
+            code.append("\n# Greenlet: %r" % (ob,))
+            for line in extract_stack(ob.gr_frame):
+                code.append(line)
+
+    _logger.info("\n".join(code))
+
+
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: