Fix adding breakpoints during run by setting a dummy tracing function (PY-24026)

This commit is contained in:
Elizaveta Shashkova
2017-06-06 18:51:50 +03:00
parent 5b61081380
commit abfca5af46
15 changed files with 4041 additions and 4843 deletions
@@ -51,7 +51,7 @@ def _on_forked_process():
def _on_set_trace_for_new_thread(global_debugger):
if global_debugger is not None:
global_debugger.SetTrace(global_debugger.trace_dispatch, global_debugger.frame_eval_func)
global_debugger.SetTrace(global_debugger.trace_dispatch, global_debugger.frame_eval_func, global_debugger.dummy_trace_dispatch)
#===============================================================================
# Things related to monkey-patching
@@ -5,7 +5,7 @@ from _pydevd_bundle.pydevd_constants import STATE_RUN, PYTHON_SUSPEND, IS_JYTHON
from _pydevd_bundle.pydevd_frame import PyDBFrame
# ENDIF
version = 3
version = 4
if not hasattr(sys, '_current_frames'):
@@ -977,18 +977,6 @@ class InternalSetNextStatementThread(InternalThreadCommand):
t.additional_info.pydev_state = STATE_RUN
#=======================================================================================================================
# InternalSetTracingThread
#=======================================================================================================================
class InternalSetTracingThread(InternalThreadCommand):
"""Revert using tracing function inside thread, sometimes needed while frame evaluation debugging"""
def __init__(self, thread_id):
self.thread_id = thread_id
def do_it(self, dbg):
dbg.SetTrace(dbg.trace_dispatch)
#=======================================================================================================================
# InternalGetVariable
#=======================================================================================================================
@@ -1457,7 +1445,7 @@ def pydevd_find_thread_by_id(thread_id):
return None
def pydevd_check_frame_for_new_breakpoint(main_debugger, breakpoint, filepath):
def enable_tracing_in_frames(main_debugger):
""" If frame evaluation is enabled and breakpoint was added while running debug session there are two cases:
* the frame isn't under execution yet, we'll handle all its breakpoints in frame evaluation function
* the frame is already under execution, we need to enable old tracing function and disable it after exiting the frame
@@ -1479,34 +1467,8 @@ def pydevd_check_frame_for_new_breakpoint(main_debugger, breakpoint, filepath):
if additional_info is None:
continue
frame_executed = None
func_name = breakpoint.func_name
for frame in additional_info.iter_frames(t):
current_frame = frame
while current_frame is not None:
try:
# Make fast path faster!
abs_path_real_path_and_base = NORM_PATHS_AND_BASE_CONTAINER[current_frame.f_code.co_filename]
except:
abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(current_frame)
if abs_path_real_path_and_base[1] == filepath:
# compare functions' names
current_func = current_frame.f_code.co_name
if func_name == current_func or (func_name == "None" and current_func == "<module>"):
frame_executed = current_frame
break
current_frame = current_frame.f_back
if frame_executed is not None:
# SetTrace should be executed within the thread
thread_id = get_thread_id(t)
int_cmd = InternalSetTracingThread(thread_id)
main_debugger.post_internal_command(int_cmd, thread_id)
main_debugger.set_trace_for_frame_and_parents(frame_executed, overwrite_prev_trace=True)
if thread_id not in main_debugger.disable_tracing_after_exit_frames:
main_debugger.disable_tracing_after_exit_frames[thread_id] = set()
# tracing function inside frame shouldn't be removed until the program exits this frame
main_debugger.disable_tracing_after_exit_frames[thread_id].add(frame_executed)
main_debugger.set_trace_for_frame_and_parents(frame, overwrite_prev_trace=True)
main_debugger.set_use_code_extra(False)
except:
traceback.print_exc()
File diff suppressed because it is too large Load Diff
@@ -9,7 +9,7 @@ from _pydevd_bundle.pydevd_constants import STATE_RUN, PYTHON_SUSPEND, IS_JYTHON
# from _pydevd_bundle.pydevd_frame import PyDBFrame
# ENDIF
version = 3
version = 4
if not hasattr(sys, '_current_frames'):
@@ -557,16 +557,6 @@ cdef class PyDBFrame:
# trace function for showing return values after step over
can_skip = False
if main_debugger.frame_eval_func and event == 'return' and info.pydev_step_cmd == -1:
frames_set = main_debugger.disable_tracing_after_exit_frames.get(get_thread_id(thread), None)
if frames_set is not None:
if frame in frames_set:
frames_set.remove(frame)
if len(frames_set) == 0:
# there were some frames, but we exited all of them, stop tracing
main_debugger.disable_tracing_after_exit_frames.pop(get_thread_id(thread))
main_debugger.SetTrace(None)
# Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint,
# we will return nothing for the next trace
# also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway,
@@ -430,16 +430,6 @@ class PyDBFrame:
# trace function for showing return values after step over
can_skip = False
if main_debugger.frame_eval_func and event == 'return' and info.pydev_step_cmd == -1:
frames_set = main_debugger.disable_tracing_after_exit_frames.get(get_thread_id(thread), None)
if frames_set is not None:
if frame in frames_set:
frames_set.remove(frame)
if len(frames_set) == 0:
# there were some frames, but we exited all of them, stop tracing
main_debugger.disable_tracing_after_exit_frames.pop(get_thread_id(thread))
main_debugger.SetTrace(None)
# Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint,
# we will return nothing for the next trace
# also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway,
@@ -17,7 +17,7 @@ from _pydevd_bundle.pydevd_comm import CMD_RUN, CMD_VERSION, CMD_LIST_THREADS, C
CMD_REMOVE_EXCEPTION_BREAK, CMD_LOAD_SOURCE, CMD_ADD_DJANGO_EXCEPTION_BREAK, CMD_REMOVE_DJANGO_EXCEPTION_BREAK, \
CMD_EVALUATE_CONSOLE_EXPRESSION, InternalEvaluateConsoleExpression, InternalConsoleGetCompletions, \
CMD_RUN_CUSTOM_OPERATION, InternalRunCustomOperation, CMD_IGNORE_THROWN_EXCEPTION_AT, CMD_ENABLE_DONT_TRACE, \
CMD_SHOW_RETURN_VALUES, ID_TO_MEANING, CMD_GET_DESCRIPTION, InternalGetDescription, pydevd_check_frame_for_new_breakpoint
CMD_SHOW_RETURN_VALUES, ID_TO_MEANING, CMD_GET_DESCRIPTION, InternalGetDescription, enable_tracing_in_frames
from _pydevd_bundle.pydevd_constants import get_thread_id, IS_PY3K, DebugInfoHolder, dict_contains, dict_keys, \
STATE_RUN
@@ -328,7 +328,7 @@ def process_net_command(py_db, cmd_id, seq, text):
py_db.set_tracing_for_untraced_contexts(overwrite_prev_trace=True)
if py_db.frame_eval_func is not None:
pydevd_check_frame_for_new_breakpoint(py_db, breakpoint, file)
enable_tracing_in_frames(py_db)
elif cmd_id == CMD_REMOVE_BREAK:
#command to remove some breakpoint
@@ -67,11 +67,11 @@ def _internal_set_trace(tracing_func):
TracingFunctionHolder._original_tracing(tracing_func)
def SetTrace(tracing_func, frame_eval_func=None):
def SetTrace(tracing_func, frame_eval_func=None, dummy_tracing_func=None):
if tracing_func is not None and frame_eval_func is not None:
# There is no need to set tracing function if frame evaluation is available
frame_eval_func()
return
tracing_func = dummy_tracing_func
if TracingFunctionHolder._original_tracing is None:
#This may happen before replace_sys_set_trace_func is called.
@@ -1,5 +1,5 @@
try:
from _pydevd_frame_eval.pydevd_frame_evaluator import frame_eval_func, stop_frame_eval, set_use_code_extra
from _pydevd_frame_eval.pydevd_frame_evaluator import frame_eval_func, stop_frame_eval, set_use_code_extra, dummy_trace_dispatch
except ImportError:
try:
import struct
@@ -8,6 +8,7 @@ if sys.version_info[0] == 3 and sys.version_info[1] == 6:
frame_eval_func = None
stop_frame_eval = None
set_use_code_extra = None
dummy_trace_dispatch = None
USE_FRAME_EVAL = os.environ.get('PYDEVD_USE_FRAME_EVAL', None)
@@ -17,7 +18,8 @@ if USE_FRAME_EVAL == 'NO':
else:
if IS_PY36:
try:
from _pydevd_frame_eval.pydevd_frame_eval_cython_wrapper import frame_eval_func, stop_frame_eval, set_use_code_extra
from _pydevd_frame_eval.pydevd_frame_eval_cython_wrapper import frame_eval_func, stop_frame_eval, set_use_code_extra, \
dummy_trace_dispatch
except ImportError:
from _pydev_bundle.pydev_monkey import log_error_once
File diff suppressed because it is too large Load Diff
@@ -1,10 +1,9 @@
import dis
from _pydev_imps._pydev_saved_modules import threading
from _pydevd_bundle.pydevd_additional_thread_info import PyDBAdditionalThreadInfo
from _pydevd_bundle.pydevd_comm import get_global_debugger, CMD_THREAD_SUSPEND
from _pydevd_bundle.pydevd_constants import STATE_RUN, STATE_SUSPEND, get_thread_id
from _pydevd_bundle.pydevd_comm import get_global_debugger
from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE
from _pydevd_frame_eval.pydevd_frame_tracing import pydev_trace_code_wrapper, update_globals_dict
from _pydevd_frame_eval.pydevd_frame_tracing import pydev_trace_code_wrapper, update_globals_dict, dummy_tracing_holder
from _pydevd_frame_eval.pydevd_modify_bytecode import insert_code
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame, NORM_PATHS_AND_BASE_CONTAINER
@@ -34,12 +33,8 @@ def set_use_code_extra(new_value):
UseCodeExtraHolder.use_code_extra = new_value
def enable_tracing_debugger_for_frame(main_debugger, frame, thread_id):
main_debugger.SetTrace(main_debugger.trace_dispatch)
main_debugger.set_trace_for_frame_and_parents(frame)
if thread_id not in main_debugger.disable_tracing_after_exit_frames:
main_debugger.disable_tracing_after_exit_frames[thread_id] = set()
main_debugger.disable_tracing_after_exit_frames[thread_id].add(frame)
cpdef dummy_trace_dispatch(frame, str event, arg):
return None
cdef PyObject* get_bytecode_while_frame_eval(PyFrameObject *frame_obj, int exc):
@@ -105,12 +100,8 @@ cdef PyObject* get_bytecode_while_frame_eval(PyFrameObject *frame_obj, int exc):
additional_info.is_tracing = False
return _PyEval_EvalFrameDefault(frame_obj, exc)
main_debugger = get_global_debugger()
if (additional_info.pydev_state == STATE_SUSPEND and t.stop_reason == CMD_THREAD_SUSPEND) or \
(additional_info.pydev_state == STATE_RUN and main_debugger.disable_tracing_after_exit_frames):
main_debugger.process_internal_commands()
was_break = False
main_debugger = get_global_debugger()
breakpoints = main_debugger.breakpoints.get(abs_path_real_path_and_base[1])
code_object = frame.f_code
if breakpoints:
@@ -128,7 +119,7 @@ cdef PyObject* get_bytecode_while_frame_eval(PyFrameObject *frame_obj, int exc):
frame_obj.f_code = <PyCodeObject *> new_code
was_break = True
else:
enable_tracing_debugger_for_frame(main_debugger, frame, get_thread_id(t))
main_debugger.set_trace_for_frame_and_parents(frame)
was_break = False
break
if was_break:
@@ -159,6 +150,8 @@ cdef PyObject* get_bytecode_while_frame_eval(PyFrameObject *frame_obj, int exc):
def frame_eval_func():
cdef PyThreadState *state = PyThreadState_Get()
state.interp.eval_frame = get_bytecode_while_frame_eval
global dummy_tracing_holder
dummy_tracing_holder.set_trace_func(dummy_trace_dispatch)
def stop_frame_eval():
cdef PyThreadState *state = PyThreadState_Get()
@@ -7,6 +7,16 @@ from _pydevd_bundle.pydevd_comm import get_global_debugger, CMD_SET_BREAK
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame, NORM_PATHS_AND_BASE_CONTAINER
class DummyTracingHolder:
dummy_trace_func = None
def set_trace_func(self, trace_func):
self.dummy_trace_func = trace_func
dummy_tracing_holder = DummyTracingHolder()
def update_globals_dict(globals_dict):
new_globals = {'_pydev_stop_at_break': _pydev_stop_at_break}
globals_dict.update(new_globals)
@@ -81,7 +91,7 @@ def _pydev_stop_at_break():
if t.additional_info.is_tracing:
return
if t.additional_info.pydev_step_cmd == -1 and sys.gettrace() is None:
if t.additional_info.pydev_step_cmd == -1 and frame.f_trace in (None, dummy_tracing_holder.dummy_trace_func):
# do not handle breakpoints while stepping, because they're handled by old tracing function
t.additional_info.is_tracing = True
debugger = get_global_debugger()
+8 -22
View File
@@ -31,12 +31,12 @@ from _pydevd_bundle.pydevd_comm import CMD_SET_BREAK, CMD_SET_NEXT_STATEMENT, CM
PyDBDaemonThread, _queue, ReaderThread, GetGlobalDebugger, get_global_debugger, \
set_global_debugger, WriterThread, pydevd_find_thread_by_id, pydevd_log, \
start_client, start_server, InternalGetBreakpointException, InternalSendCurrExceptionTrace, \
InternalSendCurrExceptionTraceProceeded, InternalSetTracingThread
InternalSendCurrExceptionTraceProceeded
from _pydevd_bundle.pydevd_custom_frames import CustomFramesContainer, custom_frames_container_init
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame
from _pydevd_bundle.pydevd_kill_all_pydevd_threads import kill_all_pydev_threads
from _pydevd_bundle.pydevd_trace_dispatch import trace_dispatch as _trace_dispatch, global_cache_skips, global_cache_frame_skips
from _pydevd_frame_eval.pydevd_frame_eval_main import frame_eval_func, stop_frame_eval, set_use_code_extra
from _pydevd_frame_eval.pydevd_frame_eval_main import frame_eval_func, stop_frame_eval, set_use_code_extra, dummy_trace_dispatch
from _pydevd_bundle.pydevd_utils import save_main_module
from pydevd_concurrency_analyser.pydevd_concurrency_logger import ThreadingLogger, AsyncioLogger, send_message, cur_time
from pydevd_concurrency_analyser.pydevd_thread_wrappers import wrap_threads
@@ -248,9 +248,6 @@ class PyDB:
self.mpl_hooks_in_debug_console = False
self.mpl_modules_for_patching = {}
# this dict is used for frame evaluation, it holds thread_id -> set of frames
self.disable_tracing_after_exit_frames = {}
self._filename_to_not_in_scope = {}
self.first_breakpoint_reached = False
self.is_filter_enabled = pydevd_utils.is_filter_enabled()
@@ -673,12 +670,6 @@ class PyDB:
if stop_reason == CMD_SET_BREAK and self.suspend_on_breakpoint_exception:
self._send_breakpoint_condition_exception(thread)
if self.frame_eval_func is not None and stop_reason == CMD_THREAD_SUSPEND:
thread_id = get_thread_id(thread)
int_cmd = InternalSetTracingThread(thread_id)
self.post_internal_command(int_cmd, thread_id)
set_use_code_extra(False)
def _send_breakpoint_condition_exception(self, thread):
"""If conditional breakpoint raises an exception during evaluation
@@ -822,11 +813,7 @@ class PyDB:
info.pydev_state = STATE_RUN
if self.frame_eval_func is not None and info.pydev_state == STATE_RUN:
if info.pydev_step_cmd == -1:
if not self.disable_tracing_after_exit_frames.get(get_thread_id(thread), None):
# disable old tracing function without stepping commands
self.SetTrace(None)
else:
if info.pydev_step_cmd != -1:
if info.pydev_step_cmd == CMD_STEP_INTO or info.pydev_step_cmd == CMD_STEP_INTO_MY_CODE:
self.set_trace_for_frame_and_parents(frame)
# enable old tracing function for stepping
@@ -894,7 +881,7 @@ class PyDB:
def prepare_to_run(self):
''' Shared code to prepare debugging by installing traces and registering threads '''
self.patch_threads()
pydevd_tracing.SetTrace(self.trace_dispatch, self.frame_eval_func)
pydevd_tracing.SetTrace(self.trace_dispatch, self.frame_eval_func, self.dummy_trace_dispatch)
# There is no need to set tracing function if frame evaluation is available. Moreover, there is no need to patch thread
# functions, because frame evaluation function is set to all threads by default.
@@ -905,9 +892,6 @@ class PyDB:
def patch_threads(self):
if self.frame_eval_func is not None:
# There is no need to patch thread functions, because frame evaluation function is set to all threads by default.
return
try:
# not available in jython!
import threading
@@ -1057,6 +1041,8 @@ class PyDB:
trace_dispatch = _trace_dispatch
frame_eval_func = frame_eval_func
dummy_trace_dispatch = dummy_trace_dispatch
set_use_code_extra = set_use_code_extra
def set_debug(setup):
setup['DEBUG_RECORD_SOCKET_READS'] = True
@@ -1231,7 +1217,7 @@ def _locked_settrace(
# note that we do that through pydevd_tracing.SetTrace so that the tracing
# is not warned to the user!
pydevd_tracing.SetTrace(debugger.trace_dispatch, debugger.frame_eval_func)
pydevd_tracing.SetTrace(debugger.trace_dispatch, debugger.frame_eval_func, debugger.dummy_trace_dispatch)
if not trace_only_current_thread:
# Trace future threads?
@@ -1264,7 +1250,7 @@ def _locked_settrace(
additional_info = PyDBAdditionalThreadInfo()
t.additional_info = additional_info
pydevd_tracing.SetTrace(debugger.trace_dispatch, debugger.frame_eval_func)
pydevd_tracing.SetTrace(debugger.trace_dispatch, debugger.frame_eval_func, debugger.dummy_trace_dispatch)
if not trace_only_current_thread:
# Trace future threads?
@@ -350,13 +350,6 @@ def get_breakpoint(plugin, pydb, pydb_frame, frame, event, args):
def suspend(plugin, pydb, thread, frame, bp_type):
if bp_type == 'jinja2':
if pydb.frame_eval_func is not None:
thread_id = get_thread_id(thread)
if thread_id not in pydb.disable_tracing_after_exit_frames:
pydb.disable_tracing_after_exit_frames[thread_id] = set()
# tracing function inside frame shouldn't be removed until the program exits this frame
pydb.disable_tracing_after_exit_frames[thread_id].add(frame)
return _suspend_jinja2(pydb, thread, frame)
return None