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

Commit

Permalink
Turning suspend all policy and using get stack trace command (#731)
Browse files Browse the repository at this point in the history
* Continue resumes all threads

* Enable suspend policy ALL

* stacktraces and active exceptions are no longer stored

* stacktraces and active exceptions are no longer stored

* Fix set breakpoints tests

* Fix exception info

* Fix breakpoint tests

* Fix exception breakpoint tests

* Fix syntax

* Fix exception info

* Fix syntax

* Fix Stack trace tests

* Fix for 2.7

* Minor refactor

* Fix ContinueTests

* Fix ExceptionInfoTests

* Enable more tests

* Remove tests that are now no-ops

* Minor tweaks

* Fix test_attach_breakpoints

* typo

* More tweaks

* Send error response for unknown threads

* Use assert_contains in continue tests

* Add some TODOs
  • Loading branch information
karthiknadig authored Aug 7, 2018
1 parent bd1d1aa commit ed565b2
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 324 deletions.
172 changes: 84 additions & 88 deletions ptvsd/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,16 +563,12 @@ def append(self, var):
if var_name.startswith('__'):
if var_name.endswith('__'):
self.dunder.append(var)
#print('Apended dunder: %s' % var_name)
else:
self.double_underscore.append(var)
#print('Apended double under: %s' % var_name)
elif var_name.startswith('_'):
self.single_underscore.append(var)
#print('Apended single under: %s' % var_name)
else:
self.variables.append(var)
#print('Apended variable: %s' % var_name)

def get_sorted_variables(self):
def get_sort_key(o):
Expand All @@ -581,7 +577,6 @@ def get_sort_key(o):
self.single_underscore.sort(key=get_sort_key)
self.double_underscore.sort(key=get_sort_key)
self.dunder.sort(key=get_sort_key)
#print('sorted')
return self.variables + self.single_underscore + self.double_underscore + self.dunder # noqa


Expand Down Expand Up @@ -1218,10 +1213,6 @@ def __init__(self, socket, pydevd_notify, pydevd_request,
# debugger state
self.is_process_created = False
self.is_process_created_lock = threading.Lock()
self.stack_traces = {}
self.stack_traces_lock = threading.Lock()
self.active_exceptions = {}
self.active_exceptions_lock = threading.Lock()
self.thread_map = IDMap()
self.frame_map = IDMap()
self.var_map = IDMap()
Expand Down Expand Up @@ -1487,8 +1478,9 @@ def _clear_breakpoints(self):
# TODO: Wait until the last request has been handled?

def _resume_all_threads(self):
for tid in self.stack_traces:
self.pydevd_notify(pydevd_comm.CMD_THREAD_RUN, tid)
# TODO: Replace this with resume all command after #732 is fixed
for pyd_tid in self.thread_map.pydevd_ids():
self.pydevd_notify(pydevd_comm.CMD_THREAD_RUN, pyd_tid)

def send_process_event(self, start_method):
# TODO: docstring
Expand Down Expand Up @@ -1588,16 +1580,22 @@ def on_stackTrace(self, request, args):
levels = int(args.get('levels', 0))
fmt = args.get('format', {})

pyd_tid = self.thread_map.to_pydevd(vsc_tid)
with self.stack_traces_lock:
try:
xframes = self.stack_traces[pyd_tid]
except KeyError:
# This means the stack was requested before the
# thread was suspended
xframes = []
totalFrames = len(xframes)
try:
pyd_tid = self.thread_map.to_pydevd(vsc_tid)
except KeyError:
# Unknown thread, nothing much we cna do about it here
self.send_error_response(request)
return

try:
cmd = pydevd_comm.CMD_GET_THREAD_STACK
_, _, resp_args = yield self.pydevd_request(cmd, pyd_tid)
xml = self.parse_xml_response(resp_args)
xframes = list(xml.thread.frame)
except Exception:
xframes = []

totalFrames = len(xframes)
if levels == 0:
levels = totalFrames

Expand Down Expand Up @@ -1641,8 +1639,9 @@ def on_stackTrace(self, request, args):

user_frames = []
for frame in stackFrames:
if not self.internals_filter.is_internal_path(
frame['source']['path']):
path = frame['source']['path']
if not self.internals_filter.is_internal_path(path) and \
self._should_debug(path):
user_frames.append(frame)

totalFrames = len(user_frames)
Expand Down Expand Up @@ -1988,16 +1987,19 @@ def on_pause(self, request, args):
return

# Always suspend all threads.
# TODO: Replace this with suspend all command after #732 is fixed
for pyd_tid in self.thread_map.pydevd_ids():
self.pydevd_notify(pydevd_comm.CMD_THREAD_SUSPEND, pyd_tid)
self.send_response(request)

@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)
# Always continue all threads.
# TODO: Replace this with resume all command after #732 is fixed
for pyd_tid in self.thread_map.pydevd_ids():
self.pydevd_notify(pydevd_comm.CMD_THREAD_RUN, pyd_tid)
self.send_response(request, allThreadsContinued=True)

@async_handler
def on_next(self, request, args):
Expand Down Expand Up @@ -2081,7 +2083,7 @@ def on_setBreakpoints(self, request, args):
self.bp_map.remove(pyd_bpid, vsc_bpid)

cmd = pydevd_comm.CMD_SET_BREAK
msgfmt = '{}\t{}\t{}\t{}\tNone\t{}\t{}\t{}\t{}'
msgfmt = '{}\t{}\t{}\t{}\tNone\t{}\t{}\t{}\t{}\tALL'
if needs_unicode(path):
msgfmt = unicode(msgfmt) # noqa
for src_bp in src_bps:
Expand Down Expand Up @@ -2147,13 +2149,59 @@ def on_setExceptionBreakpoints(self, request, args):
def on_exceptionInfo(self, request, args):
# TODO: docstring
pyd_tid = self.thread_map.to_pydevd(args['threadId'])
with self.active_exceptions_lock:
try:
exc = self.active_exceptions[pyd_tid]
except KeyError:
exc = ExceptionInfo('BaseException',
'exception: no description',
None, None)

# For exception cases both raise and uncaught, pydevd adds a
# __exception__ object to the top most frame. Extracting the
# exception name and description from that frame gives accurate
# exception information. Get exception info from frame
try:
cmd = pydevd_comm.CMD_GET_THREAD_STACK
_, _, resp_args = yield self.pydevd_request(cmd, pyd_tid)
xml = self.parse_xml_response(resp_args)
xframes = list(xml.thread.frame)

xframe = xframes[0]
pyd_fid = xframe['id']

cmdargs = '{}\t{}\tFRAME\t__exception__'.format(pyd_tid, pyd_fid)
cmdid = pydevd_comm.CMD_GET_VARIABLE
_, _, resp_args = yield self.pydevd_request(cmdid, cmdargs)
xml = self.parse_xml_response(resp_args)

name = unquote(xml.var[1]['type'])
description = unquote(xml.var[1]['value'])

frame_data = []
for f in xframes:
file_path = unquote(f['file'])
if not self.internals_filter.is_internal_path(file_path) and \
self._should_debug(file_path):
line_no = int(f['line'])
func_name = unquote(f['name'])
if _util.is_py34():
# NOTE: In 3.4.* format_list requires the text
# to be passed in the tuple list.
line_text = _util.get_line_for_traceback(file_path,
line_no)
frame_data.append((file_path, line_no,
func_name, line_text))
else:
frame_data.append((file_path, line_no,
func_name, None))

stack = ''.join(traceback.format_list(frame_data))

source = unquote(xframe['file'])
if self.internals_filter.is_internal_path(source) or \
not self._should_debug(source):
source = None
except Exception:
name = 'BaseException'
description = 'exception: no description'
stack = None
source = None

exc = ExceptionInfo(name, description, stack, source)
self.send_response(
request,
exceptionId=exc.name,
Expand Down Expand Up @@ -2318,9 +2366,6 @@ def on_pydevd_thread_suspend(self, seq, args):
autogen = self.start_reason == 'attach'
vsc_tid = self.thread_map.to_vscode(pyd_tid, autogen=autogen)

with self.stack_traces_lock:
self.stack_traces[pyd_tid] = list(xml.thread.frame)

description = None
text = None
if reason in STEP_REASONS:
Expand All @@ -2332,52 +2377,20 @@ def on_pydevd_thread_suspend(self, seq, args):
else:
reason = 'pause'

# For exception cases both raise and uncaught, pydevd adds a
# __exception__ object to the top most frame. Extracting the
# exception name and description from that frame gives accurate
# exception information.
if reason == 'exception':
# Get exception info from frame
try:
pyd_fid = xframe['id']
cmdargs = '{}\t{}\tFRAME\t__exception__'.format(pyd_tid,
pyd_fid)
cmdid = pydevd_comm.CMD_GET_VARIABLE
_, _, resp_args = yield self.pydevd_request(cmdid, cmdargs)
xml = self.parse_xml_response(resp_args)

text = unquote(xml.var[1]['type'])
description = unquote(xml.var[1]['value'])
frame_data = []
for f in xframes:
file_path = unquote(f['file'])
if not self.internals_filter.is_internal_path(file_path):
line_no = int(f['line'])
func_name = unquote(f['name'])
if _util.is_py34():
# NOTE: In 3.4.* format_list requires the text
# to be passed in the tuple list.
line_text = _util.get_line_for_traceback(file_path,
line_no)
frame_data.append((file_path, line_no,
func_name, line_text))
else:
frame_data.append((file_path, line_no,
func_name, None))
stack = ''.join(traceback.format_list(frame_data))
source = unquote(xframe['file'])
if self.internals_filter.is_internal_path(source):
source = None
except Exception:
text = 'BaseException'
description = 'exception: no description'
stack = None
source = None

with self.active_exceptions_lock:
self.active_exceptions[pyd_tid] = ExceptionInfo(text,
description,
stack,
source)

self.send_event(
'stopped',
Expand All @@ -2392,20 +2405,8 @@ def on_pydevd_thread_run(self, seq, args):
pyd_tid, _ = args.split('\t')
pyd_tid = pyd_tid.strip()

# Stack trace, active exception, all frames, and variables for
# All frames, and variables for
# this thread are now invalid; clear their IDs.
with self.stack_traces_lock:
try:
del self.stack_traces[pyd_tid]
except KeyError:
pass

with self.active_exceptions_lock:
try:
del self.active_exceptions[pyd_tid]
except KeyError:
pass

for pyd_fid, vsc_fid in self.frame_map.pairs():
if pyd_fid[0] == pyd_tid:
self.frame_map.remove(pyd_fid, vsc_fid)
Expand All @@ -2429,12 +2430,7 @@ def on_pydevd_send_curr_exception_trace(self, seq, args):
@pydevd_events.handler(pydevd_comm.CMD_SEND_CURR_EXCEPTION_TRACE_PROCEEDED)
def on_pydevd_send_curr_exception_trace_proceeded(self, seq, args):
# TODO: docstring
pyd_tid = args.strip()
with self.active_exceptions_lock:
try:
del self.active_exceptions[pyd_tid]
except KeyError:
pass
pass

@pydevd_events.handler(pydevd_comm.CMD_WRITE_TO_CONSOLE)
def on_pydevd_cmd_write_to_console2(self, seq, args):
Expand Down
15 changes: 10 additions & 5 deletions tests/highlevel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
CMD_THREAD_CREATE,
CMD_GET_VARIABLE,
CMD_SET_PROJECT_ROOTS,
CMD_GET_THREAD_STACK,
)

from ptvsd._util import new_hidden_thread
Expand Down Expand Up @@ -833,6 +834,8 @@ def pause(self, threadname, *stack):
self._pydevd.send_pause_event(thread, *stack)
if self._vsc._hidden:
self._vsc.msgs.next_event()
payload = self.debugger_msgs.format_frames(thread.id, 'pause', *stack)
self.set_debugger_response(CMD_GET_THREAD_STACK, payload)
self.send_request('stackTrace', {'threadId': tid})
self.send_request('scopes', {'frameId': 1})
return tid, thread
Expand Down Expand Up @@ -909,8 +912,9 @@ def assert_vsc_failure(self, received, expected, req):
self.assertEqual(received[:-1], expected)

failure = received[-1] if len(received) > 0 else []
expected = self.vsc.protocol.parse(
self.fix.vsc_msgs.new_failure(req, failure.message))
if failure:
expected = self.vsc.protocol.parse(
self.fix.vsc_msgs.new_failure(req, failure.message))
self.assertEqual(failure, expected)

def assert_received(self, daemon, expected):
Expand All @@ -919,10 +923,11 @@ def assert_received(self, daemon, expected):
expected = list(daemon.protocol.parse_each(expected))
self.assertEqual(received, expected)

def assert_contains(self, received, expected):
def assert_contains(self, received, expected, parser='vsc'):
parser = self.vsc.protocol if parser == 'vsc' else parser
from tests.helpers.message import assert_contains_messages
received = list(self.vsc.protocol.parse_each(received))
expected = list(self.vsc.protocol.parse_each(expected))
received = list(parser.parse_each(received))
expected = list(parser.parse_each(expected))
assert_contains_messages(received, expected)

def assert_received_unordered_payload(self, daemon, expected):
Expand Down
Loading

0 comments on commit ed565b2

Please sign in to comment.