Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix multithreading stepping in 3.12 and later #1798

Merged
merged 3 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ That will generate a log from the test run.

Logging the test output can be tricky so here's some information on how to debug the tests.

#### Running pydevd tests inside of VS code

You can also run the pydevd tests inside of VS code using the test explorer (and debug the pytest code). To do so, set PYTHONPATH=. and open the `src/debugpy/_vendored/pydevd` folder in VS code. The test explorer should find all of the pydevd tests.

#### How to add more logging

The pydevd tests log everything to the console and to a text file during the test. If you scroll up in the console, it should show the log file it read the logs from:
Expand Down
7 changes: 7 additions & 0 deletions src/debugpy/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this file supposed to be in source control? It might be, I just wasn't sure since it was in the .vscode directory

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be. It's common settings. It makes debugpy tests runnable from the test runner in VS code.

"python.testing.pytestArgs": [
"."
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
Original file line number Diff line number Diff line change
Expand Up @@ -1084,7 +1084,12 @@ def _return_event(code, instruction, retval):
if func_code_info.plugin_return_stepping:
_plugin_stepping(py_db, step_cmd, "return", frame, thread_info)
return


if info.pydev_state == STATE_SUSPEND:
# We're already suspended, don't handle any more events on this thread.
_do_wait_suspend(py_db, thread_info, frame, "return", None)
return

# Python line stepping
stop_frame = info.pydev_step_stop
if step_cmd in (CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE, CMD_STEP_INTO_COROUTINE):
Expand Down
3,773 changes: 1,922 additions & 1,851 deletions src/debugpy/_vendored/pydevd/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.c

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,12 @@ cdef _return_event(code, instruction, retval):
if func_code_info.plugin_return_stepping:
_plugin_stepping(py_db, step_cmd, "return", frame, thread_info)
return


if info.pydev_state == STATE_SUSPEND:
# We're already suspended, don't handle any more events on this thread.
_do_wait_suspend(py_db, thread_info, frame, "return", None)
return

# Python line stepping
stop_frame = info.pydev_step_stop
if step_cmd in (CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE, CMD_STEP_INTO_COROUTINE):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@
"""

import threading
import requests
import time

event0 = threading.Event()
event1 = threading.Event()
event2 = threading.Event()
event3 = threading.Event()

def request_get(url):
# return "abc"
with requests.get(url) as data:
return data.text

def _thread1():
_event1_set = False
Expand All @@ -19,6 +25,7 @@ def _thread1():
while not event0.is_set():
event0.wait(timeout=0.001)

time.sleep(.1)
event1.set() # Break thread 1
_event1_set = True

Expand All @@ -33,6 +40,9 @@ def _thread2():
event0.set()

while not event1.is_set():
# Do something interesting that takes a while. This verifies we
# only get stop events for the thread with a breakpoint.
print(len(request_get("https://dns.google//")))
event1.wait(timeout=0.001)

event2.set()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3359,6 +3359,9 @@ def test_step_next_step_in_multi_threads(case_setup_dap, stepping_resumes_all_th
thread_name_to_id = dict((t["name"], t["id"]) for t in response.body.threads)
assert json_hit.thread_id == thread_name_to_id["thread1"]

stopped_events = json_facade.mark_messages(StoppedEvent)
assert len(stopped_events) == 1

timeout_at = time.time() + 30
checks = 0

Expand Down
2 changes: 2 additions & 0 deletions tests/debug/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,8 @@ def wait_for_stop(
assert len(expected_frames) <= len(frames)
assert expected_frames == frames[0 : len(expected_frames)]

assert len(frames) > 0

fid = frames[0]("id", int)
return StopInfo(stopped, frames, tid, fid)

Expand Down
Loading