Make set next statement work properly for frame eval debugger (PY-26101)

This commit is contained in:
fitermay
2018-03-13 12:54:48 +03:00
committed by Elizaveta Shashkova
parent f9fcc6f846
commit e9af010573
4 changed files with 51 additions and 25 deletions
@@ -560,7 +560,7 @@ class PyDBFrame:
breakpoint = breakpoints_for_file[line]
new_frame = frame
stop = True
if step_cmd == CMD_STEP_OVER and stop_frame is frame and (is_line or is_return):
if (step_cmd == CMD_STEP_OVER or step_cmd == CMD_SET_NEXT_STATEMENT) and stop_frame is frame and (is_line or is_return):
stop = False #we don't stop on breakpoint if we have to stop by step-over (it will be processed later)
elif plugin_manager is not None and main_debugger.has_plugin_line_breaks:
result = plugin_manager.get_breakpoint(main_debugger, self, frame, event, self._args)
@@ -3,7 +3,7 @@ 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
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, dummy_tracing_holder
from _pydevd_frame_eval.pydevd_frame_tracing import create_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
@@ -107,13 +107,16 @@ cdef PyObject* get_bytecode_while_frame_eval(PyFrameObject *frame_obj, int exc):
code_object = frame.f_code
if breakpoints:
breakpoints_to_update = []
injected_code_size = 0
for offset, line in dis.findlinestarts(code_object):
if line in breakpoints:
breakpoint = breakpoints[line]
if code_object not in breakpoint.code_objects:
# This check is needed for generator functions, because after each yield the new frame is created
# but the former code object is used
success, new_code = insert_code(frame.f_code, pydev_trace_code_wrapper.__code__, line)
injected_code = create_code_wrapper(offset + injected_code_size)
success, new_code = insert_code(frame.f_code, injected_code , line)
injected_code_size += len(injected_code.co_code)
if success:
breakpoints_to_update.append(breakpoint)
Py_INCREF(new_code)
@@ -1,12 +1,14 @@
from types import CodeType
import sys
from _pydev_bundle import pydev_log
from _pydev_imps._pydev_saved_modules import threading
from _pydevd_bundle.pydevd_comm import get_global_debugger, CMD_SET_BREAK
from _pydevd_bundle.pydevd_comm import get_global_debugger, CMD_SET_BREAK, CMD_SET_NEXT_STATEMENT
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame, NORM_PATHS_AND_BASE_CONTAINER
from _pydevd_bundle.pydevd_frame import handle_breakpoint_condition, handle_breakpoint_expression
import opcode
class DummyTracingHolder:
dummy_trace_func = None
@@ -54,7 +56,7 @@ def _pydev_stop_at_break():
frame = sys._getframe(1)
t = threading.currentThread()
if t.additional_info.is_tracing:
return
return False
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
@@ -74,16 +76,40 @@ def _pydev_stop_at_break():
except KeyError:
pydev_log.debug("Couldn't find breakpoint in the file {} on line {}".format(frame.f_code.co_filename, line))
t.additional_info.is_tracing = False
return
return False
if breakpoint and handle_breakpoint(frame, t, debugger, breakpoint):
pydev_log.debug("Suspending at breakpoint in file: {} on line {}".format(frame.f_code.co_filename, line))
debugger.set_suspend(t, CMD_SET_BREAK)
debugger.do_wait_suspend(t, frame, 'line', None, "frame_eval")
t.additional_info.is_tracing = False
return t.additional_info.pydev_step_cmd == CMD_SET_NEXT_STATEMENT
return False
def pydev_trace_code_wrapper():
# import this module again, because it's inserted inside user's code
global _pydev_stop_at_break
_pydev_stop_at_break()
return _pydev_stop_at_break()
def create_code_wrapper(offset):
co = pydev_trace_code_wrapper.__code__
# 0 + offset LOAD_GLOBAL 0 (_pydev_stop_at_break)
# 2 + offset CALL_FUNCTION 0
# 4 + offset POP_JUMP_IF_TRUE offset
byte_code = [116, 0, 131, 0]
if offset > 0xFF:
byte_code += [opcode.EXTENDED_ARG, offset >> 8]
byte_code += [115, offset & 0xFF]
#below code is just function trailer and gets removed
byte_code += [100, 0, 83, 0]
return CodeType(
co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
co.co_stacksize,
co.co_flags,
bytes(byte_code),
co.co_consts, co.co_names, co.co_varnames, co.co_filename,
co.co_name, co.co_firstlineno, co.co_lnotab, co.co_freevars,
co.co_cellvars)
+13 -16
View File
@@ -750,14 +750,10 @@ class PyDB:
if curr_func_name == func_name:
line = next_line
if frame.f_lineno == line:
stop = True
else:
if frame.f_trace is None:
frame.f_trace = self.trace_dispatch
frame.f_lineno = line
frame.f_trace = None
stop = True
if frame.f_trace is None:
frame.f_trace = self.trace_dispatch
frame.f_lineno = line
stop = True
else:
response_msg = "jump is available only within the bottom frame"
return stop, old_line, response_msg
@@ -856,14 +852,15 @@ class PyDB:
info.pydev_message = ''
if stop:
info.pydev_state = STATE_RUN
# `f_line` should be assigned within a tracing function, so, we can't assign it here
# for the frame evaluation debugger. For tracing debugger it will be assigned, but we should
# revert the previous value, because both debuggers should behave the same way
try:
self.set_next_statement(frame, event, info.pydev_func_name, old_line)
except:
pass
cmd = self.cmd_factory.make_thread_run_message(get_thread_id(thread), info.pydev_step_cmd)
self.writer.add_command(cmd)
if suspend_type == "trace":
info.pydev_state = STATE_SUSPEND
thread.stop_reason= CMD_SET_NEXT_STATEMENT
self.do_wait_suspend(thread, frame, event, arg, "trace")
else:
info.pydev_step_stop = frame
return
else:
info.pydev_step_cmd = -1
info.pydev_state = STATE_SUSPEND