mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Fix adding breakpoints during run by setting a dummy tracing function (PY-24026)
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user