Skip to content

Commit

Permalink
Properly stop at line 1 in frame eval mode. Fixes #995
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioz committed Jul 30, 2022
1 parent db43846 commit a294092
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 15 deletions.
12 changes: 6 additions & 6 deletions src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_cython.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,7 @@ cdef class PyDBFrame:
new_frame = frame
stop_reason = CMD_SET_FUNCTION_BREAK

elif not is_return and info.pydev_state != 2 and breakpoints_for_file is not None and line in breakpoints_for_file:
elif is_line and info.pydev_state != 2 and breakpoints_for_file is not None and line in breakpoints_for_file:
breakpoint = breakpoints_for_file[line]
new_frame = frame
stop = True
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,7 @@ def trace_dispatch(self, frame, event, arg):
new_frame = frame
stop_reason = CMD_SET_FUNCTION_BREAK

elif not is_return and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and line in breakpoints_for_file:
elif is_line and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and line in breakpoints_for_file:
breakpoint = breakpoints_for_file[line]
new_frame = frame
stop = True
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ def get_instructions_to_add(
'''
# Good reference to how things work regarding line numbers and jumps:
# https://github.com/python/cpython/blob/3.6/Objects/lnotab_notes.txt

# Usually use a stop line -1, but if that'd be 0, using line +1 is ok too.
spurious_line = stop_at_line - 1
if spurious_line <= 0:
spurious_line = stop_at_line + 1

label = Label()
return [
# -- if _pydev_needs_stop_at_break():
Expand All @@ -99,10 +105,10 @@ def get_instructions_to_add(
#
# Note that this has line numbers -1 so that when the NOP just below
# is executed we have a spurious line event.
Instr("LOAD_CONST", _pydev_stop_at_break, lineno=stop_at_line - 1),
Instr("LOAD_CONST", stop_at_line, lineno=stop_at_line - 1),
Instr("CALL_FUNCTION", 1, lineno=stop_at_line - 1),
Instr("POP_TOP", lineno=stop_at_line - 1),
Instr("LOAD_CONST", _pydev_stop_at_break, lineno=spurious_line),
Instr("LOAD_CONST", stop_at_line, lineno=spurious_line),
Instr("CALL_FUNCTION", 1, lineno=spurious_line),
Instr("POP_TOP", lineno=spurious_line),

# Reason for the NOP: Python will give us a 'line' trace event whenever we forward jump to
# the first instruction of a line, so, in the case where we haven't added a programmatic
Expand Down Expand Up @@ -259,6 +265,12 @@ def insert_pydevd_breaks(
# if code_to_modify.co_firstlineno in breakpoint_lines:
# return False, code_to_modify

for line in breakpoint_lines:
if line <= 0:
# The first line is line 1, so, a break at line 0 is not valid.
pydev_log.info('Trying to add breakpoint in invalid line: %s', line)
return False, code_to_modify

try:
b = bytecode.Bytecode.from_code(code_to_modify)

Expand Down
3 changes: 3 additions & 0 deletions src/debugpy/_vendored/pydevd/pydevd.py
Original file line number Diff line number Diff line change
Expand Up @@ -1995,6 +1995,9 @@ def do_wait_suspend(self, thread, frame, event, arg, exception_type=None): # @U
thread_id = get_current_thread_id(thread)

# print('do_wait_suspend %s %s %s %s %s %s (%s)' % (frame.f_lineno, frame.f_code.co_name, frame.f_code.co_filename, event, arg, constant_to_str(thread.additional_info.pydev_step_cmd), constant_to_str(thread.additional_info.pydev_original_step_cmd)))
# print('--- stack ---')
# print(traceback.print_stack(file=sys.stdout))
# print('--- end stack ---')

# Send the suspend message
message = thread.additional_info.pydev_message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def breaknow():


if '--fork-in-subprocess' in sys.argv:
popen = None
if sys.platform == 'win32':
popen = subprocess.Popen([sys.executable, __file__, '--forked'])
pid = popen.pid
Expand All @@ -20,6 +21,16 @@ def breaknow():
print('currently in pid: %s, ppid: %s' % (os.getpid(), ppid))
print('os.fork returned', pid)
breaknow()
# i.e.: wait so that we check for the retcode so that we don't get a traceback such as the one
# below (as that code in __del__ will only be called if returncode is None).
# Traceback (most recent call last):
# File "C:\hostedtoolcache\windows\Python\3.9.13\x64\lib\subprocess.py", line 1055, in __del__
# self._internal_poll(_deadstate=_maxsize)
# File "C:\hostedtoolcache\windows\Python\3.9.13\x64\lib\subprocess.py", line 1457, in _internal_poll
# if _WaitForSingleObject(self._handle, 0) == _WAIT_OBJECT_0:
# OSError: [WinError 6] The handle is invalid
if popen is not None:
popen.wait(20)

elif '--forked' in sys.argv:
try:
Expand Down
6 changes: 3 additions & 3 deletions src/debugpy/_vendored/pydevd/tests_python/test_debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -2274,9 +2274,9 @@ def test_case_method_single_line(case_setup):
writer.write_add_breakpoint(writer.get_line_index_with_content('Break here'), 'None')
writer.write_make_initial_run()

for _ in range(5):
# We'll hit the same breakpoint 5 times (method creation,
# (enter method, exit method) * 2.
for _ in range(3):
# We'll hit the same breakpoint 3 times (method creation,
# method line for each call).
hit = writer.wait_for_breakpoint_hit()

writer.write_run_thread(hit.thread_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,17 @@ def test_generator_code_cache(case_setup_force_frame_eval):
writer.write_run_thread(hit.thread_id)

writer.finished_ok = True


def test_break_line_1(case_setup_force_frame_eval):
with case_setup_force_frame_eval.test_file('_debugger_case_yield_from.py') as writer:
break1_line = 1
break1_id = writer.write_add_breakpoint(break1_line, 'None')
writer.write_make_initial_run()

hit = writer.wait_for_breakpoint_hit(line=break1_line)
assert hit.suspend_type == "frame_eval"

writer.write_run_thread(hit.thread_id)

writer.finished_ok = True

0 comments on commit a294092

Please sign in to comment.