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

Properly deal with handled/unhandled exceptions on top-level frames on a remote attach. Fixes #580, #581 #827

Merged
merged 2 commits into from
Sep 24, 2018
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
3 changes: 2 additions & 1 deletion ptvsd/_vendored/pydevd/.travis_install_jython_deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
set -ev

pip install pytest
pip install untangle
pip install untangle
pip install pathlib2
10 changes: 10 additions & 0 deletions ptvsd/_vendored/pydevd/.travis_install_python_deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ conda install --yes numpy ipython pytest cython psutil
if [ "$PYDEVD_PYTHON_VERSION" = "2.6" ]; then
conda install --yes pyqt=4
pip install pympler==0.5
pip install pathlib2
# Django 1.7 does not support Python 2.7
else
# pytest-xdist not available for python 2.6
Expand All @@ -17,6 +18,7 @@ fi
if [ "$PYDEVD_PYTHON_VERSION" = "2.7" ]; then
conda install --yes pyqt=4
pip install "django>=1.7,<1.8"
pip install pathlib2

fi

Expand All @@ -25,5 +27,13 @@ if [ "$PYDEVD_PYTHON_VERSION" = "3.5" ]; then
pip install "django>=1.7,<1.8"
fi

if [ "$PYDEVD_PYTHON_VERSION" = "3.6" ]; then
conda install --yes pyqt=5
fi

if [ "$PYDEVD_PYTHON_VERSION" = "3.7" ]; then
conda install --yes pyqt=5
fi

pip install untangle
pip install scapy==2.4.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
from opcode import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname, hasname, hasjrel, haslocal, \
hascompare, hasfree, cmp_op
import dis
import sys
from collections import namedtuple

try:
xrange
except NameError:
xrange = range


class TryExceptInfo(object):

def __init__(self, try_line, is_finally=False):
self.try_line = try_line
self.is_finally = is_finally
self.except_line = -1
self.except_bytecode_offset = -1
self.except_end_line = -1
self.except_end_bytecode_offset = -1
self.raise_lines_in_except = []

def is_line_in_try_block(self, line):
return self.try_line <= line <= self.except_line

def is_line_in_except_block(self, line):
return self.except_line <= line <= self.except_end_line

def __str__(self):
lst = [
'{try:',
str(self.try_line),
' except ',
str(self.except_line),
' end block ',
str(self.except_end_line),
]
if self.raise_lines_in_except:
lst.append(' raises: %s' % (', '.join(str(x) for x in self.raise_lines_in_except),))

lst.append('}')
return ''.join(lst)

__repr__ = __str__


def _get_line(op_offset_to_line, op_offset, firstlineno, search=False):
op_offset_original = op_offset
while op_offset >= 0:
ret = op_offset_to_line.get(op_offset)
if ret is not None:
return ret - firstlineno
if not search:
return ret
else:
op_offset -= 1
raise AssertionError('Unable to find line for offset: %s.Info: %s' % (
op_offset_original, op_offset_to_line))


def debug(s):
pass


_Instruction = namedtuple('_Instruction', 'opname, opcode, starts_line, argval, is_jump_target, offset')


def _iter_as_bytecode_as_instructions_py2(co):
code = co.co_code
op_offset_to_line = dict(dis.findlinestarts(co))
labels = set(dis.findlabels(code))
bytecode_len = len(code)
i = 0
extended_arg = 0
free = None

op_to_name = opname

while i < bytecode_len:
c = code[i]
op = ord(c)
is_jump_target = i in labels

curr_op_name = op_to_name[op]
initial_bytecode_offset = i

i = i + 1
if op < HAVE_ARGUMENT:
yield _Instruction(curr_op_name, op, _get_line(op_offset_to_line, initial_bytecode_offset, 0), None, is_jump_target, initial_bytecode_offset)

else:
oparg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg

extended_arg = 0
i = i + 2
if op == EXTENDED_ARG:
extended_arg = oparg * 65536

if op in hasconst:
yield _Instruction(curr_op_name, op, _get_line(op_offset_to_line, initial_bytecode_offset, 0), co.co_consts[oparg], is_jump_target, initial_bytecode_offset)
elif op in hasname:
yield _Instruction(curr_op_name, op, _get_line(op_offset_to_line, initial_bytecode_offset, 0), co.co_names[oparg], is_jump_target, initial_bytecode_offset)
elif op in hasjrel:
argval = i + oparg
yield _Instruction(curr_op_name, op, _get_line(op_offset_to_line, initial_bytecode_offset, 0), argval, is_jump_target, initial_bytecode_offset)
elif op in haslocal:
yield _Instruction(curr_op_name, op, _get_line(op_offset_to_line, initial_bytecode_offset, 0), co.co_varnames[oparg], is_jump_target, initial_bytecode_offset)
elif op in hascompare:
yield _Instruction(curr_op_name, op, _get_line(op_offset_to_line, initial_bytecode_offset, 0), cmp_op[oparg], is_jump_target, initial_bytecode_offset)
elif op in hasfree:
if free is None:
free = co.co_cellvars + co.co_freevars
yield _Instruction(curr_op_name, op, _get_line(op_offset_to_line, initial_bytecode_offset, 0), free[oparg], is_jump_target, initial_bytecode_offset)
else:
yield _Instruction(curr_op_name, op, _get_line(op_offset_to_line, initial_bytecode_offset, 0), oparg, is_jump_target, initial_bytecode_offset)


def collect_try_except_info(co, use_func_first_line=False):
if not hasattr(co, 'co_lnotab'):
return []

if use_func_first_line:
firstlineno = co.co_firstlineno
else:
firstlineno = 0

try_except_info_lst = []
stack_in_setup = []

if sys.version_info[0] < 3:
iter_in = _iter_as_bytecode_as_instructions_py2(co)
else:
iter_in = dis.Bytecode(co)
iter_in = list(iter_in)

op_offset_to_line = dict(dis.findlinestarts(co))
bytecode_to_instruction = {}
for instruction in iter_in:
bytecode_to_instruction[instruction.offset] = instruction

if iter_in:
for instruction in iter_in:
curr_op_name = instruction.opname

if curr_op_name == 'SETUP_EXCEPT':
try_except_info = TryExceptInfo(
_get_line(op_offset_to_line, instruction.offset, firstlineno, search=True))
try_except_info.except_bytecode_offset = instruction.argval
try_except_info.except_line = _get_line(
op_offset_to_line,
try_except_info.except_bytecode_offset,
firstlineno,
)

stack_in_setup.append(try_except_info)

elif curr_op_name == 'SETUP_FINALLY':
# We need to collect try..finally blocks too to make sure that
# the stack_in_setup we're using to collect info is correct.
try_except_info = TryExceptInfo(
_get_line(op_offset_to_line, instruction.offset, firstlineno, search=True), is_finally=True)
stack_in_setup.append(try_except_info)

elif curr_op_name == 'RAISE_VARARGS':
# We want to know about reraises and returns inside of except blocks (unfortunately
# a raise appears to the debugger as a return, so, we may need to differentiate).
if instruction.argval == 0:
for info in stack_in_setup:
info.raise_lines_in_except.append(
_get_line(op_offset_to_line, instruction.offset, firstlineno, search=True))

elif curr_op_name == 'END_FINALLY': # The except block also ends with 'END_FINALLY'.
stack_in_setup[-1].except_end_bytecode_offset = instruction.offset
stack_in_setup[-1].except_end_line = _get_line(op_offset_to_line, instruction.offset, firstlineno, search=True)
if not stack_in_setup[-1].is_finally:
# Don't add try..finally blocks.
try_except_info_lst.append(stack_in_setup[-1])
del stack_in_setup[-1]

while stack_in_setup:
# On Py3 the END_FINALLY may not be there (so, the end of the function is also the end
# of the stack).
stack_in_setup[-1].except_end_bytecode_offset = instruction.offset
stack_in_setup[-1].except_end_line = _get_line(op_offset_to_line, instruction.offset, firstlineno, search=True)
if not stack_in_setup[-1].is_finally:
# Don't add try..finally blocks.
try_except_info_lst.append(stack_in_setup[-1])
del stack_in_setup[-1]

return try_except_info_lst
1 change: 1 addition & 0 deletions ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ def unquote(s):

'200': 'CMD_REDIRECT_OUTPUT',
'201': 'CMD_GET_NEXT_STATEMENT_TARGETS',
'202': 'CMD_SET_PROJECT_ROOTS',

'501': 'CMD_VERSION',
'502': 'CMD_RETURN',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
'pydevd_additional_thread_info.py': PYDEV_FILE,
'pydevd_additional_thread_info_regular.py': PYDEV_FILE,
'pydevd_breakpoints.py': PYDEV_FILE,
'pydevd_collect_try_except_info.py': PYDEV_FILE,
'pydevd_comm.py': PYDEV_FILE,
'pydevd_command_line_handling.py': PYDEV_FILE,
'pydevd_concurrency_logger.py': PYDEV_FILE,
Expand Down
Loading