# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy of # the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. # """Help utilities for debugging""" import sys from oslo_config import cfg from oslo_log import log as logging LOG = logging.getLogger(__name__) CONF = cfg.CONF __debug_state = None pydev_debug_opts = [ cfg.StrOpt("pydev_debug", choices=("disabled", "enabled", "auto"), default="disabled", help="Enable or disable pydev remote debugging. " "If value is 'auto' tries to connect to remote " "debugger server, but in case of error " "continues running with debugging disabled."), cfg.StrOpt("pydev_debug_host", help="Pydev debug server host (localhost by default)."), cfg.PortOpt("pydev_debug_port", default=5678, help="Pydev debug server port (5678 by default)."), cfg.StrOpt("pydev_path", help="Set path to pydevd library, used if pydevd is " "not found in python sys.path.") ] CONF.register_opts(pydev_debug_opts) def setup(): """ Analyze configuration for pydev remote debugging and establish connection to remote debugger service if needed @return: True if remote debugging was enabled successfully, otherwise - False """ global __debug_state if CONF.pydev_debug == "enabled": __debug_state = __setup_remote_pydev_debug( pydev_debug_host=CONF.pydev_debug_host, pydev_debug_port=CONF.pydev_debug_port, pydev_path=CONF.pydev_path) elif CONF.pydev_debug == "auto": __debug_state = __setup_remote_pydev_debug_safe( pydev_debug_host=CONF.pydev_debug_host, pydev_debug_port=CONF.pydev_debug_port, pydev_path=CONF.pydev_path) else: __debug_state = False def enabled(): """ @return: True if connection to remote debugger established, otherwise False """ assert __debug_state is not None, ("debug_utils are not initialized. " "Please call setup() method first") # if __debug_state is set and we have monkey patched # eventlet.thread, issue a warning. # You can't safely use eventlet.is_monkey_patched() on the # threading module so you have to do this little dance. # Discovered after much head scratching, see also # # http://stackoverflow.com/questions/32452110/ # does-eventlet-do-monkey-patch-for-threading-module # # note multi-line URL if __debug_state: import threading if threading.current_thread.__module__ == 'eventlet.green.threading': LOG.warning("Enabling debugging with eventlet monkey" " patched produce unexpected behavior.") return __debug_state def __setup_remote_pydev_debug_safe(pydev_debug_host=None, pydev_debug_port=5678, pydev_path=None): """ Safe version of __setup_remote_pydev_debug method. In error case returns False as result instead of Exception raising @see: __setup_remote_pydev_debug """ try: return __setup_remote_pydev_debug( pydev_debug_host=pydev_debug_host, pydev_debug_port=pydev_debug_port, pydev_path=pydev_path) except Exception as e: LOG.warning("Can't connect to remote debug server." " Continuing to work in standard mode." " Error: %s.", e) return False def __setup_remote_pydev_debug(pydev_debug_host=None, pydev_debug_port=None, pydev_path=None): """ Method connects to remote debug server, and attach current thread trace to debugger. Also thread.start_new_thread thread.start_new are patched to enable debugging of new threads @param pydev_debug_host: remote debug server host hame, 'localhost' if not specified or None @param pydev_debug_port: remote debug server port, 5678 if not specified or None @param pydev_path: optional path to pydevd library, used it pydevd is not found in python sys.path @return: True if debugging initialized, otherwise exception should be raised """ try: import pydevd LOG.debug("pydevd module was imported from system path") except ImportError: LOG.debug("Can't load pydevd module from system path. Try loading it " "from pydev_path: %s", pydev_path) assert pydev_path, "pydev_path is not set" if pydev_path not in sys.path: sys.path.append(pydev_path) import pydevd LOG.debug("pydevd module was imported from pydev_path: %s", pydev_path) pydevd.settrace( host=pydev_debug_host, port=pydev_debug_port, stdoutToServer=True, stderrToServer=True, trace_only_current_thread=False, suspend=False, ) return True