Skip to content

Commit

Permalink
Fix microsoft#1510: [sys.monitoring] Deadlocks when breakpoints are hit
Browse files Browse the repository at this point in the history
  • Loading branch information
Pavel Minaev committed Feb 1, 2024
1 parent 8ce0f35 commit 2569672
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 20 deletions.
22 changes: 12 additions & 10 deletions src/debugpy/server/tracing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ class Thread:
DAP "thread" event with "reason":"started".
"""

is_traced: bool
"""
Whether this thread is traced. Threads are normally traced, but API clients
can exclude a specific thread from tracing.
"""

_last_id = 0
_all: ClassVar[Dict[int, "Thread"]] = {}

Expand All @@ -48,6 +54,7 @@ def __init__(self, python_thread):
"""
self.python_thread = python_thread
self.is_known_to_adapter = False
self.is_traced = True

with _cvar:
# Thread IDs are serialized as JSON numbers in DAP, which are handled as 64-bit
Expand All @@ -71,14 +78,6 @@ def __getstate__(self):
"name": self.name,
}

@property
def is_debugpy_thread(self):
return getattr(self.python_thread, "is_debugpy_thread", False)

@property
def is_traced(self):
return not self.is_debugpy_thread

@property
def name(self):
return self.python_thread.name
Expand All @@ -88,10 +87,13 @@ def from_python_thread(self, python_thread: threading.Thread = None) -> "Thread"
"""
Returns the DAP Thread object corresponding to the given Python thread, or for
the current Python thread if None, creating it and reporting it to adapter if
necessary.
necessary. If the current thread is internal debugpy thread, returns None.
"""

if python_thread is None:
python_thread = threading.current_thread()
if getattr(python_thread, "is_debugpy_thread", False):
return None
with _cvar:
for thread in self._all.values():
if thread.python_thread is python_thread:
Expand All @@ -118,7 +120,7 @@ def enumerate(self) -> List["Thread"]:
thread
for python_thread in threading.enumerate()
for thread in [Thread.from_python_thread(python_thread)]
if thread.is_traced
if thread is not None and thread.is_traced
]

def make_known_to_adapter(self):
Expand Down
20 changes: 10 additions & 10 deletions src/debugpy/server/tracing/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ def _stop(

def _trace_line(self, code: CodeType, line_number: int):
thread = self.Thread.from_python_thread()
if not thread.is_traced:
if thread is None or not thread.is_traced:
return self.monitoring.DISABLE

self.log.debug(f"sys.monitoring event: LINE({line_number}, {code})")
Expand Down Expand Up @@ -347,67 +347,67 @@ def _trace_line(self, code: CodeType, line_number: int):

def _trace_py_start(self, code: CodeType, ip: int):
thread = self.Thread.from_python_thread()
if not thread.is_traced:
if thread is None or not thread.is_traced:
return self.monitoring.DISABLE
self.log.debug(f"sys.monitoring event: PY_START({code}, {ip})")

def _trace_py_resume(self, code: CodeType, ip: int):
thread = self.Thread.from_python_thread()
if not thread.is_traced:
if thread is None or not thread.is_traced:
return self.monitoring.DISABLE
self.log.debug(f"sys.monitoring event: PY_RESUME({code}, {ip})")

def _trace_py_return(self, code: CodeType, ip: int, retval: object):
thread = self.Thread.from_python_thread()
if not thread.is_traced:
if thread is None or not thread.is_traced:
return self.monitoring.DISABLE
self.log.debug(f"sys.monitoring event: PY_RETURN({code}, {ip})")
# TODO: capture returned value to report it when client requests locals.
pass

def _trace_py_yield(self, code: CodeType, ip: int, retval: object):
thread = self.Thread.from_python_thread()
if not thread.is_traced:
if thread is None or not thread.is_traced:
return self.monitoring.DISABLE
self.log.debug(f"sys.monitoring event: PY_YIELD({code}, {ip})")
# TODO: capture yielded value to report it when client requests locals.
pass

def _trace_py_throw(self, code: CodeType, ip: int, exc: BaseException):
thread = self.Thread.from_python_thread()
if not thread.is_traced:
if thread is None or not thread.is_traced:
return
self.log.debug(
f"sys.monitoring event: PY_THROW({code}, {ip}, {type(exc).__qualname__})"
)

def _trace_py_unwind(self, code: CodeType, ip: int, exc: BaseException):
thread = self.Thread.from_python_thread()
if not thread.is_traced:
if thread is None or not thread.is_traced:
return
self.log.debug(
f"sys.monitoring event: PY_UNWIND({code}, {ip}, {type(exc).__qualname__})"
)

def _trace_raise(self, code: CodeType, ip: int, exc: BaseException):
thread = self.Thread.from_python_thread()
if not thread.is_traced:
if thread is None or not thread.is_traced:
return
self.log.debug(
f"sys.monitoring event: RAISE({code}, {ip}, {type(exc).__qualname__})"
)

def _trace_reraise(self, code: CodeType, ip: int, exc: BaseException):
thread = self.Thread.from_python_thread()
if not thread.is_traced:
if thread is None or not thread.is_traced:
return
self.log.debug(
f"sys.monitoring event: RERAISE({code}, {ip}, {type(exc).__qualname__})"
)

def _trace_exception_handled(self, code: CodeType, ip: int, exc: BaseException):
thread = self.Thread.from_python_thread()
if not thread.is_traced:
if thread is None or not thread.is_traced:
return
self.log.debug(
f"sys.monitoring event: EXCEPTION_HANDLED({code}, {ip}, {type(exc).__qualname__})"
Expand Down

0 comments on commit 2569672

Please sign in to comment.