diff --git a/openerp/service/server.py b/openerp/service/server.py index 19457341b04..87f10010645 100644 --- a/openerp/service/server.py +++ b/openerp/service/server.py @@ -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) diff --git a/openerp/tools/misc.py b/openerp/tools/misc.py index ea5bf6a185b..20cb510d956 100644 --- a/openerp/tools/misc.py +++ b/openerp/tools/misc.py @@ -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: