Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Commit

Permalink
Fixes issue with multi-threaded application suspend and resume.
Browse files Browse the repository at this point in the history
  • Loading branch information
karthiknadig committed Feb 23, 2018
1 parent 81afa81 commit 9da283b
Showing 1 changed file with 73 additions and 26 deletions.
99 changes: 73 additions & 26 deletions ptvsd/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import _pydevd_bundle.pydevd_comm as pydevd_comm
import _pydevd_bundle.pydevd_extension_api as pydevd_extapi
import _pydevd_bundle.pydevd_extension_utils as pydevd_extutil
import _pydevd_bundle.pydevd_breakpoints as pydevd_bp
#from _pydevd_bundle.pydevd_comm import pydevd_log

import ptvsd.ipcjson as ipcjson
Expand All @@ -39,6 +40,12 @@
ptvsd_sys_exit_code = 0


# Setting the suspend policy for breakpoints here since pydevd does not expose this feature via CMD_SET_BREAK command
def set_bp_suspend_policy_to_all():
pydevd_bp.LineBreakpoint.suspend_policy = property(fget=lambda self: 'ALL', fset=lambda self, value: None)
set_bp_suspend_policy_to_all()


def unquote(s):
if s is None:
return None
Expand Down Expand Up @@ -115,7 +122,7 @@ def to_pydevd(self, vscode_id):
# TODO: docstring
return self._vscode_to_pydevd[vscode_id]

def to_vscode(self, pydevd_id, autogen=True):
def to_vscode(self, pydevd_id, autogen):
# TODO: docstring
try:
return self._pydevd_to_vscode[pydevd_id]
Expand Down Expand Up @@ -381,6 +388,10 @@ def __init__(self, socket, pydevd, logfile=None):
self.next_var_ref = 0
self.loop = futures.EventLoop()
self.exceptions_mgr = ExceptionsManager(self)
self.suspending_threads_for_bp = False
self.suspending_threads_for_bp_lock = threading.Lock()
self.suspended_thread_count = 0
self.vsc_breakpoint_tid = None

pydevd._vscprocessor = self
self._closed = False
Expand Down Expand Up @@ -544,7 +555,11 @@ def on_threads(self, request, args):

threads = []
for xthread in xthreads:
tid = self.thread_map.to_vscode(xthread['id'])
try:
tid = self.thread_map.to_vscode(xthread['id'], False)
except KeyError:
continue

try:
name = unquote(xthread['name'])
except KeyError:
Expand All @@ -557,13 +572,13 @@ def on_threads(self, request, args):
@async_handler
def on_stackTrace(self, request, args):
# TODO: docstring
tid = int(args['threadId'])
vsc_tid = int(args['threadId'])
startFrame = int(args.get('startFrame', 0))
levels = int(args.get('levels', 0))

tid = self.thread_map.to_pydevd(tid)
pyd_tid = self.thread_map.to_pydevd(vsc_tid)
with self.stack_traces_lock:
xframes = self.stack_traces[tid]
xframes = self.stack_traces[pyd_tid]
totalFrames = len(xframes)

if levels == 0:
Expand All @@ -577,8 +592,8 @@ def on_stackTrace(self, request, args):
if levels <= 0:
break
levels -= 1
key = (tid, int(xframe['id']))
fid = self.frame_map.to_vscode(key)
key = (pyd_tid, int(xframe['id']))
fid = self.frame_map.to_vscode(key, True)
name = unquote(xframe['name'])
file = unquote(xframe['file'])
line = int(xframe['line'])
Expand All @@ -599,7 +614,7 @@ def on_scopes(self, request, args):
vsc_fid = int(args['frameId'])
pyd_tid, pyd_fid = self.frame_map.to_pydevd(vsc_fid)
pyd_var = (pyd_tid, pyd_fid, 'FRAME')
vsc_var = self.var_map.to_vscode(pyd_var)
vsc_var = self.var_map.to_vscode(pyd_var, True)
scope = {
'name': 'Locals',
'expensive': False,
Expand Down Expand Up @@ -635,7 +650,7 @@ def on_variables(self, request, args):
}
if bool(xvar['isContainer']):
pyd_child = pyd_var + (var['name'],)
var['variablesReference'] = self.var_map.to_vscode(pyd_child)
var['variablesReference'] = self.var_map.to_vscode(pyd_child, True)
variables.append(var)

self.send_response(request, variables=variables)
Expand All @@ -649,7 +664,7 @@ def on_setVariable(self, request, args):
# being set, and variable name; but pydevd wants the ID
# (or rather path) of the variable itself.
pyd_var += (args['name'],)
vsc_var = self.var_map.to_vscode(pyd_var)
vsc_var = self.var_map.to_vscode(pyd_var, True)

cmd_args = [str(s) for s in pyd_var] + [args['value']]
_, _, resp_args = yield self.pydevd_request(
Expand Down Expand Up @@ -683,7 +698,7 @@ def on_evaluate(self, request, args):
xvar = xml.var

pyd_var = (pyd_tid, pyd_fid, 'EXPRESSION', expr)
vsc_var = self.var_map.to_vscode(pyd_var)
vsc_var = self.var_map.to_vscode(pyd_var, True)
response = {
'type': unquote(xvar['type']),
'result': unquote(xvar['value']),
Expand All @@ -707,9 +722,13 @@ def on_pause(self, request, args):
@async_handler
def on_continue(self, request, args):
# TODO: docstring
tid = self.thread_map.to_pydevd(int(args['threadId']))
self.pydevd_notify(pydevd_comm.CMD_THREAD_RUN, tid)
self.send_response(request)
with self.suspending_threads_for_bp_lock:
for pyd_tid in self.thread_map.pydevd_ids():
self.pydevd_notify(pydevd_comm.CMD_THREAD_RUN, pyd_tid)
self.suspending_threads_for_bp = False
self.suspended_thread_count = 0
self.vsc_breakpoint_tid = None
self.send_response(request, allThreadsContinued=True)

@async_handler
def on_next(self, request, args):
Expand Down Expand Up @@ -799,19 +818,20 @@ def on_exceptionInfo(self, request, args):
def on_pydevd_thread_create(self, seq, args):
# TODO: docstring
xml = untangle.parse(args).xml
tid = self.thread_map.to_vscode(xml.thread['id'])
try:
name = unquote(xml.thread['name'])
except KeyError:
name = None
if not self.is_debugger_internal_thread(name):
pyd_tid = xml.thread['id']
tid = self.thread_map.to_vscode(pyd_tid, True)
self.send_event('thread', reason='started', threadId=tid)

@pydevd_events.handler(pydevd_comm.CMD_THREAD_KILL)
def on_pydevd_thread_kill(self, seq, args):
# TODO: docstring
try:
tid = self.thread_map.to_vscode(args, autogen=False)
tid = self.thread_map.to_vscode(args, False)
except KeyError:
pass
else:
Expand All @@ -821,7 +841,7 @@ def on_pydevd_thread_kill(self, seq, args):
def on_pydevd_thread_suspend(self, seq, args):
# TODO: docstring
xml = untangle.parse(args).xml
tid = xml.thread['id']
pyd_tid = xml.thread['id']
reason = int(xml.thread['stop_reason'])
STEP_REASONS = {
pydevd_comm.CMD_STEP_INTO,
Expand All @@ -832,24 +852,40 @@ def on_pydevd_thread_suspend(self, seq, args):
pydevd_comm.CMD_STEP_CAUGHT_EXCEPTION,
pydevd_comm.CMD_ADD_EXCEPTION_BREAK
}
with self.stack_traces_lock:
self.stack_traces[pyd_tid] = xml.thread.frame
try:
tid = self.thread_map.to_vscode(pyd_tid, False)
with self.suspending_threads_for_bp_lock:
self.suspended_thread_count += 1
except KeyError:
return

if reason in STEP_REASONS:
reason = 'step'
self.send_event('stopped', reason='step', threadId=tid)
elif reason in EXCEPTION_REASONS:
reason = 'exception'
self.send_event('stopped', reason='exception', threadId=tid)
elif reason == pydevd_comm.CMD_SET_BREAK:
reason = 'breakpoint'
self.vsc_breakpoint_tid = tid
else:
reason = 'pause'
with self.stack_traces_lock:
self.stack_traces[tid] = xml.thread.frame
tid = self.thread_map.to_vscode(tid)
self.send_event('stopped', reason=reason, threadId=tid)
with self.suspending_threads_for_bp_lock:
if not self.suspending_threads_for_bp:
self.send_event('stopped', reason='pause', threadId=tid)

with self.suspending_threads_for_bp_lock:
if self.suspending_threads_for_bp and \
self.vsc_breakpoint_tid and \
self.suspended_thread_count == len(self.thread_map.vscode_ids()):
self.send_event('stopped', reason='breakpoint', threadId=self.vsc_breakpoint_tid, allThreadsStopped=True)

@pydevd_events.handler(pydevd_comm.CMD_THREAD_RUN)
def on_pydevd_thread_run(self, seq, args):
# TODO: docstring
pyd_tid, reason = args.split('\t')
vsc_tid = self.thread_map.to_vscode(pyd_tid)
try:
vsc_tid = self.thread_map.to_vscode(pyd_tid, False)
except KeyError:
return

# Stack trace, and all frames and variables for this thread
# are now invalid; clear their IDs.
Expand All @@ -866,6 +902,17 @@ def on_pydevd_thread_run(self, seq, args):

self.send_event('continued', threadId=vsc_tid)

@pydevd_events.handler(pydevd_comm.CMD_THREADS_SUSPENDING)
def on_pydevd_threads_suspending(self, seq, args):
# TODO: docstring
with self.suspending_threads_for_bp_lock:
self.suspending_threads_for_bp = True

@pydevd_events.handler(pydevd_comm.CMD_THREADS_SUSPENDED)
def on_pydevd_threads_suspended(self, seq, args):
# TODO: docstring
pass

@pydevd_events.handler(pydevd_comm.CMD_SEND_CURR_EXCEPTION_TRACE)
def on_pydevd_send_curr_exception_trace(self, seq, args):
# TODO: docstring
Expand Down

0 comments on commit 9da283b

Please sign in to comment.