mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
186 lines
6 KiB
Python
186 lines
6 KiB
Python
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
# Licensed under the MIT License. See LICENSE in the project root
|
|
# for license information.
|
|
|
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
|
|
import pytest
|
|
import sys
|
|
import time
|
|
|
|
from tests import debug
|
|
|
|
|
|
@pytest.mark.parametrize("count", [1, 3])
|
|
def test_thread_count(pyfile, target, run, count):
|
|
@pyfile
|
|
def code_to_debug():
|
|
import debug_me # noqa
|
|
import threading
|
|
import time
|
|
import sys
|
|
|
|
stop = False
|
|
|
|
def worker(tid, offset):
|
|
i = 0
|
|
global stop
|
|
while not stop:
|
|
time.sleep(0.01)
|
|
i += 1
|
|
|
|
threads = []
|
|
if sys.argv[1] != "1":
|
|
for i in [111, 222]:
|
|
thread = threading.Thread(target=worker, args=(i, len(threads)))
|
|
threads.append(thread)
|
|
thread.start()
|
|
print("check here") # @bp
|
|
stop = True
|
|
|
|
with debug.Session() as session:
|
|
with run(session, target(code_to_debug, args=[str(count)])):
|
|
session.set_breakpoints(code_to_debug, all)
|
|
|
|
session.wait_for_stop()
|
|
threads = session.request("threads")
|
|
assert len(threads["threads"]) == count
|
|
session.request_continue()
|
|
|
|
|
|
@pytest.mark.parametrize("resume", ["default", "resume_all", "resume_one"])
|
|
def test_step_multi_threads(pyfile, target, run, resume):
|
|
@pyfile
|
|
def code_to_debug():
|
|
# After breaking on the thread 1, thread 2 should pause waiting for the event1 to be set,
|
|
# so, when we step return on thread 1, the program should finish if all threads are resumed
|
|
# or should keep waiting for the thread 2 to run if only thread 1 is resumed.
|
|
|
|
import debug_me # noqa
|
|
import threading
|
|
|
|
event0 = threading.Event()
|
|
event1 = threading.Event()
|
|
event2 = threading.Event()
|
|
event3 = threading.Event()
|
|
|
|
def _thread1():
|
|
while not event0.is_set():
|
|
event0.wait(timeout=0.001)
|
|
event1.set() # @break_thread_1
|
|
while not event2.is_set():
|
|
event2.wait(timeout=0.001)
|
|
# Note: we can only get here if thread 2 is also released.
|
|
event3.set()
|
|
|
|
def _thread2():
|
|
event0.set()
|
|
while not event1.is_set():
|
|
event1.wait(timeout=0.001)
|
|
event2.set()
|
|
while not event3.is_set():
|
|
event3.wait(timeout=0.001)
|
|
|
|
threads = [
|
|
threading.Thread(target=_thread1, name="thread1"),
|
|
threading.Thread(target=_thread2, name="thread2"),
|
|
]
|
|
for t in threads:
|
|
t.start()
|
|
|
|
for t in threads:
|
|
t.join()
|
|
|
|
with debug.Session() as session:
|
|
if resume == "resume_all":
|
|
session.config["steppingResumesAllThreads"] = True
|
|
elif resume == "resume_one":
|
|
session.config["steppingResumesAllThreads"] = False
|
|
|
|
with run(session, target(code_to_debug)):
|
|
session.set_breakpoints(code_to_debug, all)
|
|
|
|
stop = session.wait_for_stop()
|
|
threads = session.request("threads")
|
|
assert len(threads["threads"]) == 3
|
|
|
|
thread_name_to_id = {t["name"]: t["id"] for t in threads["threads"]}
|
|
assert stop.thread_id == thread_name_to_id["thread1"]
|
|
|
|
if resume == "resume_one":
|
|
session.request("stepOut", {"threadId": stop.thread_id})
|
|
# Wait a second and check that threads are still there.
|
|
time.sleep(1)
|
|
|
|
stack_trace = session.request(
|
|
"stackTrace", {"threadId": thread_name_to_id["thread1"]}
|
|
)
|
|
assert "_thread1" in [frame["name"] for frame in stack_trace["stackFrames"]]
|
|
|
|
stack_trace = session.request(
|
|
"stackTrace", {"threadId": thread_name_to_id["thread2"]}
|
|
)
|
|
assert "_thread2" in [frame["name"] for frame in stack_trace["stackFrames"]]
|
|
|
|
session.request_continue()
|
|
|
|
else:
|
|
session.request("stepOut", {"threadId": stop.thread_id}, freeze=False)
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
sys.platform not in ["win32", "darwin"] and not sys.platform.startswith("linux"),
|
|
reason="Test not implemented for sys.platform=" + repr(sys.platform),
|
|
)
|
|
def test_debug_this_thread(pyfile, target, run):
|
|
@pyfile
|
|
def code_to_debug():
|
|
from debug_me import ptvsd
|
|
import sys
|
|
import threading
|
|
|
|
def foo(x):
|
|
ptvsd.debug_this_thread()
|
|
event.set() # @bp
|
|
return 0
|
|
|
|
event = threading.Event()
|
|
|
|
if sys.platform == "win32":
|
|
from ctypes import CFUNCTYPE, c_void_p, c_size_t, c_uint32, windll
|
|
|
|
thread_func_p = CFUNCTYPE(c_uint32, c_void_p)
|
|
thread_func = thread_func_p(
|
|
foo
|
|
) # must hold a reference to wrapper during the call
|
|
assert windll.kernel32.CreateThread(
|
|
c_void_p(0),
|
|
c_size_t(0),
|
|
thread_func,
|
|
c_void_p(0),
|
|
c_uint32(0),
|
|
c_void_p(0),
|
|
)
|
|
elif sys.platform == "darwin" or sys.platform.startswith("linux"):
|
|
from ctypes import CDLL, CFUNCTYPE, byref, c_void_p, c_ulong
|
|
from ctypes.util import find_library
|
|
|
|
libpthread = CDLL(find_library("libpthread"))
|
|
thread_func_p = CFUNCTYPE(c_void_p, c_void_p)
|
|
thread_func = thread_func_p(
|
|
foo
|
|
) # must hold a reference to wrapper during the call
|
|
assert not libpthread.pthread_create(
|
|
byref(c_ulong(0)), c_void_p(0), thread_func, c_void_p(0)
|
|
)
|
|
else:
|
|
pytest.fail(sys.platform)
|
|
|
|
event.wait()
|
|
|
|
with debug.Session() as session:
|
|
with run(session, target(code_to_debug)):
|
|
session.set_breakpoints(code_to_debug, [code_to_debug.lines["bp"]])
|
|
|
|
session.wait_for_stop()
|
|
session.request_continue()
|