1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
# 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
|