mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Provide pydevd.log_to() to change the file to log to. WIP #1030
This commit is contained in:
parent
ea423ae598
commit
3272dace18
15 changed files with 309 additions and 101 deletions
|
|
@ -1,4 +1,5 @@
|
|||
from _pydevd_bundle.pydevd_constants import DebugInfoHolder, SHOW_COMPILE_CYTHON_COMMAND_LINE, NULL, LOG_TIME
|
||||
from _pydevd_bundle.pydevd_constants import DebugInfoHolder, SHOW_COMPILE_CYTHON_COMMAND_LINE, NULL, LOG_TIME, \
|
||||
ForkSafeLock
|
||||
from contextlib import contextmanager
|
||||
import traceback
|
||||
import os
|
||||
|
|
@ -6,18 +7,19 @@ import sys
|
|||
|
||||
|
||||
class _LoggingGlobals(object):
|
||||
|
||||
_warn_once_map = {}
|
||||
_debug_stream_filename = None
|
||||
_debug_stream = sys.stderr
|
||||
_debug_stream = NULL
|
||||
_debug_stream_initialized = False
|
||||
_initialize_lock = ForkSafeLock()
|
||||
|
||||
|
||||
def initialize_debug_stream(reinitialize=False):
|
||||
'''
|
||||
:param bool reinitialize:
|
||||
Reinitialize is used to update the debug stream after a fork (thus, if it wasn't
|
||||
initialized, we don't need to do anything).
|
||||
initialized, we don't need to do anything, just wait for the first regular log call
|
||||
to initialize).
|
||||
'''
|
||||
if reinitialize:
|
||||
if not _LoggingGlobals._debug_stream_initialized:
|
||||
|
|
@ -26,32 +28,69 @@ def initialize_debug_stream(reinitialize=False):
|
|||
if _LoggingGlobals._debug_stream_initialized:
|
||||
return
|
||||
|
||||
_LoggingGlobals._debug_stream_initialized = True
|
||||
with _LoggingGlobals._initialize_lock:
|
||||
# Initialization is done lazilly, so, it's possible that multiple threads try to initialize
|
||||
# logging.
|
||||
|
||||
# Note: we cannot initialize with sys.stderr because when forking we may end up logging things in 'os' calls.
|
||||
_LoggingGlobals._debug_stream = NULL
|
||||
_LoggingGlobals._debug_stream_filename = None
|
||||
# Check initial conditions again after obtaining the lock.
|
||||
if reinitialize:
|
||||
if not _LoggingGlobals._debug_stream_initialized:
|
||||
return
|
||||
else:
|
||||
if _LoggingGlobals._debug_stream_initialized:
|
||||
return
|
||||
|
||||
if not DebugInfoHolder.PYDEVD_DEBUG_FILE:
|
||||
_LoggingGlobals._debug_stream = sys.stderr
|
||||
else:
|
||||
# Add pid to the filename.
|
||||
try:
|
||||
dirname = os.path.dirname(DebugInfoHolder.PYDEVD_DEBUG_FILE)
|
||||
basename = os.path.basename(DebugInfoHolder.PYDEVD_DEBUG_FILE)
|
||||
try:
|
||||
os.makedirs(dirname)
|
||||
except:
|
||||
pass # Ignore error if it already exists.
|
||||
_LoggingGlobals._debug_stream_initialized = True
|
||||
|
||||
name, ext = os.path.splitext(basename)
|
||||
debug_file = os.path.join(dirname, name + '.' + str(os.getpid()) + ext)
|
||||
_LoggingGlobals._debug_stream = open(debug_file, 'w')
|
||||
_LoggingGlobals._debug_stream_filename = debug_file
|
||||
except:
|
||||
# Note: we cannot initialize with sys.stderr because when forking we may end up logging things in 'os' calls.
|
||||
_LoggingGlobals._debug_stream = NULL
|
||||
_LoggingGlobals._debug_stream_filename = None
|
||||
|
||||
if not DebugInfoHolder.PYDEVD_DEBUG_FILE:
|
||||
_LoggingGlobals._debug_stream = sys.stderr
|
||||
# Don't fail when trying to setup logging, just show the exception.
|
||||
traceback.print_exc()
|
||||
else:
|
||||
# Add pid to the filename.
|
||||
try:
|
||||
target_file = DebugInfoHolder.PYDEVD_DEBUG_FILE
|
||||
debug_file = _compute_filename_with_pid(target_file)
|
||||
_LoggingGlobals._debug_stream = open(debug_file, 'w')
|
||||
_LoggingGlobals._debug_stream_filename = debug_file
|
||||
except Exception:
|
||||
_LoggingGlobals._debug_stream = sys.stderr
|
||||
# Don't fail when trying to setup logging, just show the exception.
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def _compute_filename_with_pid(target_file, pid=None):
|
||||
# Note: used in tests.
|
||||
dirname = os.path.dirname(target_file)
|
||||
basename = os.path.basename(target_file)
|
||||
try:
|
||||
os.makedirs(dirname)
|
||||
except Exception:
|
||||
pass # Ignore error if it already exists.
|
||||
|
||||
name, ext = os.path.splitext(basename)
|
||||
if pid is None:
|
||||
pid = os.getpid()
|
||||
return os.path.join(dirname, '%s.%s%s' % (name, pid, ext))
|
||||
|
||||
|
||||
def log_to(log_file:str, log_level:int=3) -> None:
|
||||
with _LoggingGlobals._initialize_lock:
|
||||
# Can be set directly.
|
||||
DebugInfoHolder.DEBUG_TRACE_LEVEL = log_level
|
||||
|
||||
if DebugInfoHolder.PYDEVD_DEBUG_FILE != log_file:
|
||||
# Note that we don't need to reset it unless it actually changed
|
||||
# (would be the case where it's set as an env var in a new process
|
||||
# and a subprocess initializes logging to the same value).
|
||||
_LoggingGlobals._debug_stream = NULL
|
||||
_LoggingGlobals._debug_stream_filename = None
|
||||
|
||||
DebugInfoHolder.PYDEVD_DEBUG_FILE = log_file
|
||||
|
||||
_LoggingGlobals._debug_stream_initialized = False
|
||||
|
||||
|
||||
def list_log_files(pydevd_debug_file):
|
||||
|
|
@ -71,28 +110,33 @@ def log_context(trace_level, stream):
|
|||
'''
|
||||
To be used to temporarily change the logging settings.
|
||||
'''
|
||||
original_trace_level = DebugInfoHolder.DEBUG_TRACE_LEVEL
|
||||
original_debug_stream = _LoggingGlobals._debug_stream
|
||||
original_pydevd_debug_file = DebugInfoHolder.PYDEVD_DEBUG_FILE
|
||||
original_debug_stream_filename = _LoggingGlobals._debug_stream_filename
|
||||
original_initialized = _LoggingGlobals._debug_stream_initialized
|
||||
with _LoggingGlobals._initialize_lock:
|
||||
original_trace_level = DebugInfoHolder.DEBUG_TRACE_LEVEL
|
||||
original_debug_stream = _LoggingGlobals._debug_stream
|
||||
original_pydevd_debug_file = DebugInfoHolder.PYDEVD_DEBUG_FILE
|
||||
original_debug_stream_filename = _LoggingGlobals._debug_stream_filename
|
||||
original_initialized = _LoggingGlobals._debug_stream_initialized
|
||||
|
||||
DebugInfoHolder.DEBUG_TRACE_LEVEL = trace_level
|
||||
_LoggingGlobals._debug_stream = stream
|
||||
_LoggingGlobals._debug_stream_initialized = True
|
||||
DebugInfoHolder.DEBUG_TRACE_LEVEL = trace_level
|
||||
_LoggingGlobals._debug_stream = stream
|
||||
_LoggingGlobals._debug_stream_initialized = True
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
DebugInfoHolder.DEBUG_TRACE_LEVEL = original_trace_level
|
||||
_LoggingGlobals._debug_stream = original_debug_stream
|
||||
DebugInfoHolder.PYDEVD_DEBUG_FILE = original_pydevd_debug_file
|
||||
_LoggingGlobals._debug_stream_filename = original_debug_stream_filename
|
||||
_LoggingGlobals._debug_stream_initialized = original_initialized
|
||||
with _LoggingGlobals._initialize_lock:
|
||||
DebugInfoHolder.DEBUG_TRACE_LEVEL = original_trace_level
|
||||
_LoggingGlobals._debug_stream = original_debug_stream
|
||||
DebugInfoHolder.PYDEVD_DEBUG_FILE = original_pydevd_debug_file
|
||||
_LoggingGlobals._debug_stream_filename = original_debug_stream_filename
|
||||
_LoggingGlobals._debug_stream_initialized = original_initialized
|
||||
|
||||
|
||||
import time
|
||||
_last_log_time = time.time()
|
||||
|
||||
# Set to True to show pid in each logged message (usually the file has it, but sometimes it's handy).
|
||||
_LOG_PID = False
|
||||
|
||||
|
||||
def _pydevd_log(level, msg, *args):
|
||||
'''
|
||||
|
|
@ -120,6 +164,10 @@ def _pydevd_log(level, msg, *args):
|
|||
msg = '%.2fs - %s\n' % (time_diff, msg,)
|
||||
else:
|
||||
msg = '%s\n' % (msg,)
|
||||
|
||||
if _LOG_PID:
|
||||
msg = '<%s> - %s\n' % (os.getpid(), msg,)
|
||||
|
||||
try:
|
||||
try:
|
||||
initialize_debug_stream() # Do it as late as possible
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import re
|
|||
import sys
|
||||
from _pydev_bundle._pydev_saved_modules import threading
|
||||
from _pydevd_bundle.pydevd_constants import get_global_debugger, IS_WINDOWS, IS_JYTHON, get_current_thread_id, \
|
||||
sorted_dict_repr, set_global_debugger
|
||||
sorted_dict_repr, set_global_debugger, DebugInfoHolder
|
||||
from _pydev_bundle import pydev_log
|
||||
from contextlib import contextmanager
|
||||
from _pydevd_bundle import pydevd_constants
|
||||
|
|
@ -68,6 +68,13 @@ def _get_setup_updated_with_protocol_and_ppid(setup, is_exec=False):
|
|||
|
||||
else:
|
||||
pydev_log.debug('Unexpected protocol: %s', protocol)
|
||||
|
||||
if DebugInfoHolder.PYDEVD_DEBUG_FILE:
|
||||
setup['log-file'] = DebugInfoHolder.PYDEVD_DEBUG_FILE
|
||||
|
||||
if DebugInfoHolder.DEBUG_TRACE_LEVEL:
|
||||
setup['log-level'] = DebugInfoHolder.DEBUG_TRACE_LEVEL
|
||||
|
||||
return setup
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -551,8 +551,7 @@ class PyDevdAPI(object):
|
|||
if not supported_type:
|
||||
raise NameError(breakpoint_type)
|
||||
|
||||
if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
|
||||
pydev_log.debug('Added breakpoint:%s - line:%s - func_name:%s\n', canonical_normalized_filename, line, func_name)
|
||||
pydev_log.debug('Added breakpoint:%s - line:%s - func_name:%s\n', canonical_normalized_filename, line, func_name)
|
||||
|
||||
if canonical_normalized_filename in file_to_id_to_breakpoint:
|
||||
id_to_pybreakpoint = file_to_id_to_breakpoint[canonical_normalized_filename]
|
||||
|
|
@ -672,10 +671,10 @@ class PyDevdAPI(object):
|
|||
else:
|
||||
try:
|
||||
id_to_pybreakpoint = file_to_id_to_breakpoint.get(canonical_normalized_filename, {})
|
||||
if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
|
||||
if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1:
|
||||
existing = id_to_pybreakpoint[breakpoint_id]
|
||||
pydev_log.info('Removed breakpoint:%s - line:%s - func_name:%s (id: %s)\n' % (
|
||||
canonical_normalized_filename, existing.line, existing.func_name.encode('utf-8'), breakpoint_id))
|
||||
canonical_normalized_filename, existing.line, existing.func_name, breakpoint_id))
|
||||
|
||||
del id_to_pybreakpoint[breakpoint_id]
|
||||
py_db.consolidate_breakpoints(canonical_normalized_filename, id_to_pybreakpoint, file_to_line_to_breakpoints)
|
||||
|
|
|
|||
|
|
@ -274,13 +274,14 @@ class ReaderThread(PyDBDaemonThread):
|
|||
if hasattr(line, 'decode'):
|
||||
line = line.decode('utf-8')
|
||||
|
||||
if DebugInfoHolder.DEBUG_RECORD_SOCKET_READS:
|
||||
pydev_log.critical(u'debugger: received >>%s<<\n' % (line,))
|
||||
if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 3:
|
||||
pydev_log.debug('debugger: received >>%s<<\n', line)
|
||||
|
||||
args = line.split(u'\t', 2)
|
||||
args = line.split('\t', 2)
|
||||
try:
|
||||
cmd_id = int(args[0])
|
||||
pydev_log.debug('Received command: %s %s\n' % (ID_TO_MEANING.get(str(cmd_id), '???'), line,))
|
||||
if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 3:
|
||||
pydev_log.debug('Received command: %s %s\n', ID_TO_MEANING.get(str(cmd_id), '???'), line)
|
||||
self.process_command(cmd_id, int(args[1]), args[2])
|
||||
except:
|
||||
if sys is not None and pydev_log_exception is not None: # Could happen at interpreter shutdown
|
||||
|
|
@ -1351,12 +1352,12 @@ def internal_get_completions(dbg, seq, thread_id, frame_id, act_tok, line=-1, co
|
|||
try:
|
||||
remove_path = None
|
||||
try:
|
||||
qualifier = u''
|
||||
qualifier = ''
|
||||
if column >= 0:
|
||||
token_and_qualifier = extract_token_and_qualifier(act_tok, line, column)
|
||||
act_tok = token_and_qualifier[0]
|
||||
if act_tok:
|
||||
act_tok += u'.'
|
||||
act_tok += '.'
|
||||
qualifier = token_and_qualifier[1]
|
||||
|
||||
frame = dbg.find_frame(thread_id, frame_id)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
|
||||
class ArgHandlerWithParam:
|
||||
|
|
@ -68,8 +69,11 @@ ACCEPTED_ARG_HANDLERS = [
|
|||
ArgHandlerWithParam('access-token'),
|
||||
ArgHandlerWithParam('client-access-token'),
|
||||
|
||||
# Logging
|
||||
ArgHandlerWithParam('log-file'),
|
||||
ArgHandlerWithParam('log-level', int, 0),
|
||||
|
||||
ArgHandlerBool('server'),
|
||||
ArgHandlerBool('DEBUG_RECORD_SOCKET_READS'),
|
||||
ArgHandlerBool('multiproc'), # Used by PyCharm (reuses connection: ssh tunneling)
|
||||
ArgHandlerBool('multiprocess'), # Used by PyDev (creates new connection to ide)
|
||||
ArgHandlerBool('save-signatures'),
|
||||
|
|
@ -132,6 +136,8 @@ def process_command_line(argv):
|
|||
setup['file'] = ''
|
||||
setup['qt-support'] = ''
|
||||
|
||||
initial_argv = tuple(argv)
|
||||
|
||||
i = 0
|
||||
del argv[0]
|
||||
while i < len(argv):
|
||||
|
|
@ -169,10 +175,9 @@ def process_command_line(argv):
|
|||
i = len(argv) # pop out, file is our last argument
|
||||
|
||||
elif argv[i] == '--DEBUG':
|
||||
from pydevd import set_debug
|
||||
del argv[i]
|
||||
set_debug(setup)
|
||||
sys.stderr.write('pydevd: --DEBUG parameter deprecated. Use `--debug-level=3` instead.\n')
|
||||
|
||||
else:
|
||||
raise ValueError("Unexpected option: " + argv[i])
|
||||
raise ValueError("Unexpected option: %s when processing: %s" % (argv[i], initial_argv))
|
||||
return setup
|
||||
|
||||
|
|
|
|||
|
|
@ -39,10 +39,6 @@ class DebugInfoHolder:
|
|||
# General information
|
||||
DEBUG_TRACE_LEVEL = 0 # 0 = critical, 1 = info, 2 = debug, 3 = verbose
|
||||
|
||||
# Flags to debug specific points of the code.
|
||||
DEBUG_RECORD_SOCKET_READS = False
|
||||
DEBUG_TRACE_BREAKPOINTS = -1
|
||||
|
||||
PYDEVD_DEBUG_FILE = None
|
||||
|
||||
|
||||
|
|
@ -292,7 +288,6 @@ DEFAULT_VALUE = "__pydevd_value_async"
|
|||
ASYNC_EVAL_TIMEOUT_SEC = 60
|
||||
NEXT_VALUE_SEPARATOR = "__pydev_val__"
|
||||
BUILTINS_MODULE_NAME = 'builtins'
|
||||
SHOW_DEBUG_INFO_ENV = is_true_in_env(('PYCHARM_DEBUG', 'PYDEV_DEBUG', 'PYDEVD_DEBUG'))
|
||||
|
||||
# Pandas customization.
|
||||
PANDAS_MAX_ROWS = as_int_in_env('PYDEVD_PANDAS_MAX_ROWS', 60)
|
||||
|
|
@ -335,11 +330,11 @@ EXCEPTION_TYPE_UNHANDLED = 'UNHANDLED'
|
|||
EXCEPTION_TYPE_USER_UNHANDLED = 'USER_UNHANDLED'
|
||||
EXCEPTION_TYPE_HANDLED = 'HANDLED'
|
||||
|
||||
SHOW_DEBUG_INFO_ENV = is_true_in_env(('PYCHARM_DEBUG', 'PYDEV_DEBUG', 'PYDEVD_DEBUG'))
|
||||
|
||||
if SHOW_DEBUG_INFO_ENV:
|
||||
# show debug info before the debugger start
|
||||
DebugInfoHolder.DEBUG_RECORD_SOCKET_READS = True
|
||||
DebugInfoHolder.DEBUG_TRACE_LEVEL = 3
|
||||
DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS = 1
|
||||
|
||||
DebugInfoHolder.PYDEVD_DEBUG_FILE = os.getenv('PYDEVD_DEBUG_FILE')
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ class PyDevJsonCommandProcessor(object):
|
|||
return NetCommand(CMD_RETURN, 0, error_response, is_json=True)
|
||||
|
||||
else:
|
||||
if DebugInfoHolder.DEBUG_RECORD_SOCKET_READS and DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1:
|
||||
if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1:
|
||||
pydev_log.info('Process %s: %s\n' % (
|
||||
request.__class__.__name__, json.dumps(request.to_dict(update_ids_to_dap=True), indent=4, sort_keys=True),))
|
||||
|
||||
|
|
|
|||
|
|
@ -64,11 +64,11 @@ from _pydevd_bundle.pydevd_extension_api import DebuggerEventHandler
|
|||
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame, remove_exception_from_frame
|
||||
from _pydevd_bundle.pydevd_net_command_factory_xml import NetCommandFactory
|
||||
from _pydevd_bundle.pydevd_trace_dispatch import (
|
||||
trace_dispatch as _trace_dispatch, global_cache_skips, global_cache_frame_skips, fix_top_level_trace_and_get_trace_func)
|
||||
trace_dispatch as _trace_dispatch, global_cache_skips, global_cache_frame_skips, fix_top_level_trace_and_get_trace_func, USING_CYTHON)
|
||||
from _pydevd_bundle.pydevd_utils import save_main_module, is_current_thread_main_thread, \
|
||||
import_attr_from_module
|
||||
from _pydevd_frame_eval.pydevd_frame_eval_main import (
|
||||
frame_eval_func, dummy_trace_dispatch)
|
||||
frame_eval_func, dummy_trace_dispatch, USING_FRAME_EVAL)
|
||||
import pydev_ipython # @UnusedImport
|
||||
from _pydevd_bundle.pydevd_source_mapping import SourceMapping
|
||||
from _pydevd_bundle.pydevd_concurrency_analyser.pydevd_concurrency_logger import ThreadingLogger, AsyncioLogger, send_concurrency_message, cur_time
|
||||
|
|
@ -1814,22 +1814,19 @@ class PyDB(object):
|
|||
if eb.notify_on_unhandled_exceptions:
|
||||
cp = self.break_on_uncaught_exceptions.copy()
|
||||
cp[exception] = eb
|
||||
if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
|
||||
pydev_log.critical("Exceptions to hook on terminate: %s.", cp)
|
||||
pydev_log.info("Exceptions to hook on terminate: %s.", cp)
|
||||
self.break_on_uncaught_exceptions = cp
|
||||
|
||||
if eb.notify_on_handled_exceptions:
|
||||
cp = self.break_on_caught_exceptions.copy()
|
||||
cp[exception] = eb
|
||||
if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
|
||||
pydev_log.critical("Exceptions to hook always: %s.", cp)
|
||||
pydev_log.info("Exceptions to hook always: %s.", cp)
|
||||
self.break_on_caught_exceptions = cp
|
||||
|
||||
if eb.notify_on_user_unhandled_exceptions:
|
||||
cp = self.break_on_user_uncaught_exceptions.copy()
|
||||
cp[exception] = eb
|
||||
if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
|
||||
pydev_log.critical("Exceptions to hook on user uncaught code: %s.", cp)
|
||||
pydev_log.info("Exceptions to hook on user uncaught code: %s.", cp)
|
||||
self.break_on_user_uncaught_exceptions = cp
|
||||
|
||||
return eb
|
||||
|
|
@ -2600,12 +2597,6 @@ def send_json_message(msg):
|
|||
return True
|
||||
|
||||
|
||||
def set_debug(setup):
|
||||
setup['DEBUG_RECORD_SOCKET_READS'] = True
|
||||
setup['DEBUG_TRACE_BREAKPOINTS'] = 1
|
||||
setup['DEBUG_TRACE_LEVEL'] = 3
|
||||
|
||||
|
||||
def enable_qt_support(qt_support_mode):
|
||||
from _pydev_bundle import pydev_monkey_qt
|
||||
pydev_monkey_qt.patch_qt(qt_support_mode)
|
||||
|
|
@ -3239,14 +3230,40 @@ for handler in pydevd_extension_utils.extensions_of_type(DebuggerEventHandler):
|
|||
handler.on_debugger_modules_loaded(debugger_version=__version__)
|
||||
|
||||
|
||||
def log_to(log_file:str, log_level=3) -> None:
|
||||
'''
|
||||
In pydevd it's possible to log by setting the following environment variables:
|
||||
|
||||
PYDEVD_DEBUG=1 (sets the default log level to 3 along with other default options)
|
||||
PYDEVD_DEBUG_FILE=</path/to/file.log>
|
||||
|
||||
Note that the file will have the pid of the process added to it (so, logging to
|
||||
/path/to/file.log would actually start logging to /path/to/file.<pid>.log -- if subprocesses are
|
||||
logged, each new subprocess will have the logging set to its own pid).
|
||||
|
||||
Usually setting the environment variable is preferred as it'd log information while
|
||||
pydevd is still doing its imports and not just after this method is called, but on
|
||||
cases where this is hard to do this function may be called to set the tracing after
|
||||
pydevd itself is already imported.
|
||||
'''
|
||||
pydev_log.log_to(log_file, log_level)
|
||||
|
||||
|
||||
def _log_initial_info():
|
||||
pydev_log.debug("Initial arguments: %s", (sys.argv,))
|
||||
pydev_log.debug("Current pid: %s", os.getpid())
|
||||
pydev_log.debug("Using cython: %s", USING_CYTHON)
|
||||
pydev_log.debug("Using frame eval: %s", USING_FRAME_EVAL)
|
||||
pydev_log.debug("Using gevent mode: %s / imported gevent module support: %s", SUPPORT_GEVENT, bool(pydevd_gevent_integration))
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# main
|
||||
#=======================================================================================================================
|
||||
def main():
|
||||
|
||||
# parse the command line. --file is our last argument that is required
|
||||
pydev_log.debug("Initial arguments: %s", (sys.argv,))
|
||||
pydev_log.debug("Current pid: %s", os.getpid())
|
||||
_log_initial_info()
|
||||
try:
|
||||
from _pydevd_bundle.pydevd_command_line_handling import process_command_line
|
||||
setup = process_command_line(sys.argv)
|
||||
|
|
@ -3255,6 +3272,24 @@ def main():
|
|||
pydev_log.exception()
|
||||
usage(1)
|
||||
|
||||
log_trace_level = setup.get('log-level')
|
||||
|
||||
# Note: the logging info could've been changed (this would happen if this is a
|
||||
# subprocess and the value in the environment variable does not match the value in the
|
||||
# argument because the user used `pydevd.log_to` instead of supplying the environment
|
||||
# variable). If this is the case, update the logging info and re-log some information
|
||||
# in the new target.
|
||||
new_debug_file = setup.get('log-file')
|
||||
if new_debug_file and DebugInfoHolder.PYDEVD_DEBUG_FILE != new_debug_file:
|
||||
# The debug file can't be set directly, we need to use log_to() so that the a
|
||||
# new stream is actually created for the new file.
|
||||
log_to(new_debug_file, log_trace_level if log_trace_level is not None else 3)
|
||||
_log_initial_info() # The redirection info just changed, log it again.
|
||||
|
||||
elif log_trace_level is not None:
|
||||
# The log file was not specified
|
||||
DebugInfoHolder.DEBUG_TRACE_LEVEL = log_trace_level
|
||||
|
||||
if setup['print-in-debugger-startup']:
|
||||
try:
|
||||
pid = ' (pid: %s)' % os.getpid()
|
||||
|
|
@ -3267,13 +3302,6 @@ def main():
|
|||
|
||||
pydevd_vm_type.setup_type(setup.get('vm_type', None))
|
||||
|
||||
if SHOW_DEBUG_INFO_ENV:
|
||||
set_debug(setup)
|
||||
|
||||
DebugInfoHolder.DEBUG_RECORD_SOCKET_READS = setup.get('DEBUG_RECORD_SOCKET_READS', DebugInfoHolder.DEBUG_RECORD_SOCKET_READS)
|
||||
DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS = setup.get('DEBUG_TRACE_BREAKPOINTS', DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS)
|
||||
DebugInfoHolder.DEBUG_TRACE_LEVEL = setup.get('DEBUG_TRACE_LEVEL', DebugInfoHolder.DEBUG_TRACE_LEVEL)
|
||||
|
||||
port = setup['port']
|
||||
host = setup['client']
|
||||
f = setup['file']
|
||||
|
|
|
|||
|
|
@ -168,8 +168,6 @@ def attach(port, host, protocol=''):
|
|||
if py_db is not None:
|
||||
py_db.dispose_and_kill_all_pydevd_threads(wait=False)
|
||||
|
||||
# pydevd.DebugInfoHolder.DEBUG_RECORD_SOCKET_READS = True
|
||||
# pydevd.DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS = 3
|
||||
# pydevd.DebugInfoHolder.DEBUG_TRACE_LEVEL = 3
|
||||
pydevd.settrace(
|
||||
port=port,
|
||||
|
|
|
|||
|
|
@ -418,7 +418,7 @@ def case_setup_multiprocessing(debugger_runner_simple):
|
|||
|
||||
def update_command_line_args(writer, args):
|
||||
ret = debugger_unittest.AbstractWriterThread.update_command_line_args(writer, args)
|
||||
ret.insert(ret.index('--DEBUG_RECORD_SOCKET_READS'), '--multiprocess')
|
||||
ret.insert(ret.index('--client'), '--multiprocess')
|
||||
return ret
|
||||
|
||||
WriterThread.update_command_line_args = update_command_line_args
|
||||
|
|
|
|||
|
|
@ -397,7 +397,6 @@ class DebuggerRunner(object):
|
|||
localhost = pydev_localhost.get_localhost()
|
||||
ret = [
|
||||
writer.get_pydevd_file(),
|
||||
'--DEBUG_RECORD_SOCKET_READS',
|
||||
]
|
||||
|
||||
if not IS_PY36_OR_GREATER or not IS_CPYTHON or not TEST_CYTHON:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
from _pydev_bundle import pydev_log
|
||||
import os
|
||||
import io
|
||||
|
||||
|
||||
def gen_debug_info():
|
||||
from _pydevd_bundle.pydevd_constants import DebugInfoHolder
|
||||
dct = {}
|
||||
for name in (
|
||||
'PYDEVD_DEBUG_FILE',
|
||||
'DEBUG_TRACE_LEVEL',
|
||||
):
|
||||
dct[name] = getattr(DebugInfoHolder, name)
|
||||
|
||||
return dct
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if '-print-debug' in sys.argv:
|
||||
info = gen_debug_info() # break on 2nd process
|
||||
pydev_log.info('Something in print-debug')
|
||||
|
||||
print('>>> print-debug pid: %s' % os.getpid())
|
||||
print(json.dumps(info))
|
||||
|
||||
else:
|
||||
# Note: when running tests we usually have logging setup,
|
||||
# so, we create a context so that our changes are restored
|
||||
# when it finishes (as the `log_to` function will just reset
|
||||
# whatever is there).
|
||||
s = io.StringIO()
|
||||
with pydev_log.log_context(trace_level=3, stream=s):
|
||||
target_log_file = os.getenv('TARGET_LOG_FILE')
|
||||
|
||||
pydev_log.log_to(target_log_file, 1)
|
||||
new_debug_info = gen_debug_info()
|
||||
subprocess_pid = None
|
||||
with subprocess.Popen(
|
||||
[sys.executable, __file__, '-print-debug'],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
) as process:
|
||||
subprocess_pid = process.pid
|
||||
stdout, stderr = process.communicate(input)
|
||||
|
||||
output = stdout.decode('utf-8')
|
||||
pydev_log.info('Something in initial')
|
||||
|
||||
log_contents = open(pydev_log._compute_filename_with_pid(target_log_file)).read()
|
||||
assert 'Something in initial' in log_contents, 'Did not find "Something in initial" in %s' % (log_contents,)
|
||||
|
||||
log_contents = open(pydev_log._compute_filename_with_pid(target_log_file, pid=subprocess_pid)).read()
|
||||
assert 'Something in print-debug' in log_contents, 'Did not find "Something in print-debug" in %s' % (log_contents,)
|
||||
|
||||
output = ''.join(output.splitlines(keepends=True)[1:]) # Remove the first line
|
||||
loaded_debug_info = json.loads(output)
|
||||
assert loaded_debug_info == new_debug_info, 'Expected %s. Found: %s' % (new_debug_info, loaded_debug_info)
|
||||
print('>>> Initial pid: %s' % os.getpid())
|
||||
print(output)
|
||||
print('TEST SUCEEDED')
|
||||
|
|
@ -3836,10 +3836,12 @@ def test_step_over_my_code_global_setting_and_explicit_include(case_setup):
|
|||
def test_access_token(case_setup):
|
||||
|
||||
def update_command_line_args(self, args):
|
||||
args.insert(2, '--access-token')
|
||||
args.insert(3, 'bar123')
|
||||
args.insert(2, '--client-access-token')
|
||||
args.insert(3, 'foo234')
|
||||
i = args.index('--client')
|
||||
assert i > 0
|
||||
args.insert(i, '--access-token')
|
||||
args.insert(i + 1, 'bar123')
|
||||
args.insert(i, '--client-access-token')
|
||||
args.insert(i + 1, 'foo234')
|
||||
return args
|
||||
|
||||
with case_setup.test_file('_debugger_case_print.py', update_command_line_args=update_command_line_args) as writer:
|
||||
|
|
|
|||
|
|
@ -4130,8 +4130,8 @@ def test_ppid(case_setup, pyfile):
|
|||
|
||||
def update_command_line_args(writer, args):
|
||||
ret = debugger_unittest.AbstractWriterThread.update_command_line_args(writer, args)
|
||||
ret.insert(ret.index('--DEBUG_RECORD_SOCKET_READS'), '--ppid')
|
||||
ret.insert(ret.index('--DEBUG_RECORD_SOCKET_READS'), '22')
|
||||
ret.insert(ret.index('--client'), '--ppid')
|
||||
ret.insert(ret.index('--client'), '22')
|
||||
return ret
|
||||
|
||||
with case_setup.test_file(
|
||||
|
|
@ -4937,7 +4937,7 @@ def test_no_subprocess_patching(case_setup_multiprocessing, apply_multiprocessin
|
|||
|
||||
def update_command_line_args(writer, args):
|
||||
ret = debugger_unittest.AbstractWriterThread.update_command_line_args(writer, args)
|
||||
ret.insert(ret.index('--DEBUG_RECORD_SOCKET_READS'), '--multiprocess')
|
||||
ret.insert(ret.index('--client'), '--multiprocess')
|
||||
if apply_multiprocessing_patch:
|
||||
ret.append('apply-multiprocessing-patch')
|
||||
return ret
|
||||
|
|
@ -6304,6 +6304,72 @@ def test_ipython_stepping_step_in(case_setup):
|
|||
writer.finished_ok = True
|
||||
|
||||
|
||||
def test_logging_api(case_setup_multiprocessing, tmpdir):
|
||||
import threading
|
||||
from tests_python.debugger_unittest import AbstractWriterThread
|
||||
|
||||
log_file = str(tmpdir.join('pydevd_in_test_logging.log'))
|
||||
|
||||
def get_environ(self):
|
||||
env = os.environ.copy()
|
||||
env["TARGET_LOG_FILE"] = log_file
|
||||
return env
|
||||
|
||||
with case_setup_multiprocessing.test_file(
|
||||
'_debugger_case_logging.py',
|
||||
get_environ=get_environ
|
||||
) as writer:
|
||||
json_facade = JsonFacade(writer)
|
||||
json_facade.write_launch()
|
||||
|
||||
break1_line = writer.get_line_index_with_content("break on 2nd process")
|
||||
json_facade.write_set_breakpoints([break1_line])
|
||||
|
||||
server_socket = writer.server_socket
|
||||
secondary_finished_ok = [False]
|
||||
|
||||
class SecondaryProcessWriterThread(AbstractWriterThread):
|
||||
|
||||
TEST_FILE = writer.get_main_filename()
|
||||
_sequence = -1
|
||||
|
||||
class SecondaryProcessThreadCommunication(threading.Thread):
|
||||
|
||||
def run(self):
|
||||
from tests_python.debugger_unittest import ReaderThread
|
||||
server_socket.listen(1)
|
||||
self.server_socket = server_socket
|
||||
new_sock, addr = server_socket.accept()
|
||||
|
||||
reader_thread = ReaderThread(new_sock)
|
||||
reader_thread.name = ' *** Multiprocess Reader Thread'
|
||||
reader_thread.start()
|
||||
|
||||
writer2 = SecondaryProcessWriterThread()
|
||||
writer2.reader_thread = reader_thread
|
||||
writer2.sock = new_sock
|
||||
json_facade2 = JsonFacade(writer2)
|
||||
|
||||
json_facade2.write_set_breakpoints([break1_line, ])
|
||||
json_facade2.write_make_initial_run()
|
||||
|
||||
json_facade2.wait_for_thread_stopped()
|
||||
json_facade2.write_continue()
|
||||
secondary_finished_ok[0] = True
|
||||
|
||||
secondary_process_thread_communication = SecondaryProcessThreadCommunication()
|
||||
secondary_process_thread_communication.start()
|
||||
time.sleep(.1)
|
||||
|
||||
json_facade.write_make_initial_run()
|
||||
secondary_process_thread_communication.join(10)
|
||||
if secondary_process_thread_communication.is_alive():
|
||||
raise AssertionError('The SecondaryProcessThreadCommunication did not finish')
|
||||
|
||||
assert secondary_finished_ok[0]
|
||||
writer.finished_ok = True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main(['-k', 'test_replace_process', '-s'])
|
||||
|
||||
|
|
|
|||
|
|
@ -255,10 +255,7 @@ def _check_tracing_other_threads():
|
|||
import pydevd_tracing
|
||||
import time
|
||||
from tests_python.debugger_unittest import wait_for_condition
|
||||
try:
|
||||
import _thread
|
||||
except ImportError:
|
||||
import thread as _thread
|
||||
import _thread
|
||||
|
||||
# This method is called in a subprocess, so, make sure we exit properly even if we somehow
|
||||
# deadlock somewhere else.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue