From 7ffe38210970f055c4eac351c6913f30bbdbe060 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 8 Feb 2016 11:38:02 -0600 Subject: [PATCH 1/3] remote.eventlet: a new module implementing a remote profiling server in eventlet --- profiling/remote/eventlet.py | 148 +++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 profiling/remote/eventlet.py diff --git a/profiling/remote/eventlet.py b/profiling/remote/eventlet.py new file mode 100644 index 0000000..e74e516 --- /dev/null +++ b/profiling/remote/eventlet.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- +""" + profiling.remote.eventlet + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Implements a profiling server based on `eventlet`_. + + .. _eventlet: http://eventlet.net/ + + :copyright: (c) 2014-2016, What! Studio + :license: BSD, see LICENSE for more details. + +""" +from __future__ import absolute_import + +import socket + +import eventlet +from eventlet.semaphore import Semaphore + +from . import INTERVAL, LOG, PICKLE_PROTOCOL, ProfilingServer + + +__all__ = ['EventletProfilingServer'] + + +class StreamServer(object): + def __init__(self, listen_info, handle=None, backlog=None, + spawn='default', **ssl_args): + assert backlog is None + assert spawn == 'default' + + if ':' in listen_info[0]: + self.server = eventlet.listen(listen_info, + family=socket.AF_INET6) + else: + self.server = eventlet.listen(listen_info) + + if ssl_args: + def wrap_and_handle(sock, addr): + ssl_args.setdefault('server_side', True) + handle(ssl.wrap_socket(sock, **ssl_args), addr) + + self.handle = wrap_and_handle + else: + self.handle = handle + + def serve_forever(self): + while True: + sock, addr = self.server.accept() + spawn(self.handle, sock, addr) + + +class wrap_errors(object): + """Helper to make function return an exception, rather than raise it. + + Because every exception that is unhandled by greenlet will be logged, + it is desirable to prevent non-error exceptions from leaving a greenlet. + This can done with simple ``try``/``except`` construct:: + + def wrapped_func(*args, **kwargs): + try: + return func(*args, **kwargs) + except (A, B, C), ex: + return ex + + :class:`wrap_errors` provides a shortcut to write that in one line:: + + wrapped_func = wrap_errors((A, B, C), func) + + It also preserves ``__str__`` and ``__repr__`` of the original function. + """ + # QQQ could also support using wrap_errors as a decorator + + def __init__(self, errors, func): + """Make a new function from `func', such that it catches `errors' (an + Exception subclass, or a tuple of Exception subclasses) and return + it as a value. + """ + self.errors = errors + self.func = func + + def __call__(self, *args, **kwargs): + func = self.func + try: + return func(*args, **kwargs) + except self.errors, ex: + return ex + + def __str__(self): + return str(self.func) + + def __repr__(self): + return repr(self.func) + + def __getattr__(self, item): + return getattr(self.func, item) + + +class EventletProfilingServer(StreamServer, ProfilingServer): + """A profiling server implementation based on `eventlet`_. When you choose + it, you should set a :class:`profiling.timers.greenlet.GreenletTimer` for + the profiler's timer:: + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(('', 0)) + sock.listen(1) + + profiler = Profiler(GreenletTimer()) + server = EventletProfilingServer(sock, profiler) + server.serve_forever() + + .. _eventlet: http://eventlet.net/ + + """ + + def __init__(self, listener, profiler=None, interval=INTERVAL, + log=LOG, pickle_protocol=PICKLE_PROTOCOL, **server_kwargs): + StreamServer.__init__(self, listener, **server_kwargs) + ProfilingServer.__init__(self, profiler, interval, + log, pickle_protocol) + self.lock = Semaphore() + + def _send(self, sock, data): + sock.sendall(data) + + def _close(self, sock): + sock.close() + + def _addr(self, sock): + return sock.getsockname() + + def _start_profiling(self): + eventlet.spawn(self.profile_periodically) + + def _start_watching(self, sock): + disconnected = lambda x: self.disconnected(sock) + recv = wrap_errors(socket.error, sock.recv) + eventlet.spawn(recv, 1).link(disconnected) + + def profile_periodically(self): + with self.lock: + for __ in self.profiling(): + eventlet.sleep(self.interval) + + def handle(self, sock, addr=None): + self.connected(sock) From a0b645d72b730364a241016b4980460cf13007b9 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Tue, 9 Feb 2016 16:35:18 -0600 Subject: [PATCH 2/3] eventlet: fix up python3 support --- profiling/remote/eventlet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/profiling/remote/eventlet.py b/profiling/remote/eventlet.py index e74e516..ab3be8b 100644 --- a/profiling/remote/eventlet.py +++ b/profiling/remote/eventlet.py @@ -84,7 +84,7 @@ def __call__(self, *args, **kwargs): func = self.func try: return func(*args, **kwargs) - except self.errors, ex: + except self.errors as ex: return ex def __str__(self): From 9e1af53dc7c74c244dfbd2142e77cdb50977fbd6 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Tue, 9 Feb 2016 16:37:01 -0600 Subject: [PATCH 3/3] eventlet: more fixes --- profiling/remote/eventlet.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/profiling/remote/eventlet.py b/profiling/remote/eventlet.py index ab3be8b..75cf605 100644 --- a/profiling/remote/eventlet.py +++ b/profiling/remote/eventlet.py @@ -25,16 +25,11 @@ class StreamServer(object): - def __init__(self, listen_info, handle=None, backlog=None, + def __init__(self, server, handle=None, backlog=None, spawn='default', **ssl_args): assert backlog is None assert spawn == 'default' - - if ':' in listen_info[0]: - self.server = eventlet.listen(listen_info, - family=socket.AF_INET6) - else: - self.server = eventlet.listen(listen_info) + self.server = server if ssl_args: def wrap_and_handle(sock, addr):