mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Fix for server attach break into debugger (#746)
* Fix server attach break into debugger issue * More fixes and tweaks * Added launch module test for break_into_debugger * Tweak allowing break_into_debugger * Add more tests * Switch to using CMD_GET_EXCEPTION_DETAILS (#738) * Switch to using CMD_GET_EXCEPTION_DETAILS * Fix exception tests * Fix breakpoint tests * Fix ThreadSuspendEventTests * Fix ExceptionInfoTests * Fix linter issue * Some tweaks * Fix server attach break into debugger issue * More fixes and tweaks * Added launch module test for break_into_debugger * Fix linter issues
This commit is contained in:
parent
b706081f23
commit
917ac6b019
12 changed files with 250 additions and 32 deletions
|
|
@ -395,3 +395,17 @@ def get_line_for_traceback(file_path, line_no):
|
|||
return f.readlines()[line_no - 1]
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
_enable_debug_break = False
|
||||
|
||||
|
||||
def _allow_debug_break(enabled=True):
|
||||
"""Enable breaking into debugger feature.
|
||||
"""
|
||||
global _enable_debug_break
|
||||
_enable_debug_break = enabled
|
||||
|
||||
|
||||
def _is_debug_break_allowed():
|
||||
return _enable_debug_break
|
||||
|
|
|
|||
|
|
@ -2,19 +2,16 @@
|
|||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
# TODO: Why import run_module & run_file?
|
||||
from ptvsd._local import run_module, run_file # noqa
|
||||
from ptvsd._remote import (
|
||||
enable_attach as ptvsd_enable_attach, _pydevd_settrace,
|
||||
)
|
||||
from ptvsd.wrapper import debugger_attached, is_launch
|
||||
from ptvsd.wrapper import debugger_attached
|
||||
|
||||
WAIT_TIMEOUT = 1.0
|
||||
|
||||
DEFAULT_HOST = '0.0.0.0'
|
||||
DEFAULT_PORT = 5678
|
||||
|
||||
_enabled = False
|
||||
_debug_current_thread = None
|
||||
_pending_threads = set()
|
||||
|
||||
|
|
@ -60,10 +57,8 @@ def enable_attach(address=(DEFAULT_HOST, DEFAULT_PORT), redirect_output=True):
|
|||
attached. Any threads that are already running before this function is
|
||||
called will not be visible.
|
||||
"""
|
||||
global _enabled
|
||||
if _enabled:
|
||||
if is_attached():
|
||||
return
|
||||
_enabled = True
|
||||
debugger_attached.clear()
|
||||
|
||||
# Ensure port is int
|
||||
|
|
@ -88,12 +83,9 @@ def break_into_debugger():
|
|||
"""If a remote debugger is attached, pauses execution of all threads,
|
||||
and breaks into the debugger with current thread as active.
|
||||
"""
|
||||
if is_launch():
|
||||
if not is_attached():
|
||||
return
|
||||
else:
|
||||
if not is_attached() or not _enabled:
|
||||
return
|
||||
if not is_attached():
|
||||
return
|
||||
|
||||
import sys
|
||||
_pydevd_settrace(
|
||||
suspend=True,
|
||||
|
|
|
|||
|
|
@ -44,19 +44,12 @@ from ptvsd.safe_repr import SafeRepr # noqa
|
|||
from ptvsd.version import __version__ # noqa
|
||||
from ptvsd.socket import TimeoutError # noqa
|
||||
|
||||
|
||||
WAIT_FOR_THREAD_FINISH_TIMEOUT = 1 # seconds
|
||||
|
||||
|
||||
debug = _util.debug
|
||||
debugger_attached = threading.Event()
|
||||
|
||||
_debugger_start_reason = None
|
||||
|
||||
|
||||
def is_launch():
|
||||
return _debugger_start_reason == 'launch'
|
||||
|
||||
#def ipcjson_trace(s):
|
||||
# print(s)
|
||||
#ipcjson._TRACE = ipcjson_trace
|
||||
|
|
@ -1090,9 +1083,6 @@ class VSCLifecycleMsgProcessor(VSCodeMessageProcessorBase):
|
|||
def on_configurationDone(self, request, args):
|
||||
# TODO: docstring
|
||||
debugger_attached.set()
|
||||
if self.start_reason == 'launch':
|
||||
global _debugger_start_reason
|
||||
_debugger_start_reason = 'launch'
|
||||
self.send_response(request)
|
||||
self._process_debug_options(self.debug_options)
|
||||
self._handle_configurationDone(args)
|
||||
|
|
@ -1100,9 +1090,6 @@ class VSCLifecycleMsgProcessor(VSCodeMessageProcessorBase):
|
|||
|
||||
def on_disconnect(self, request, args):
|
||||
debugger_attached.clear()
|
||||
if self.start_reason == 'launch':
|
||||
global _debugger_start_reason
|
||||
_debugger_start_reason = None
|
||||
self._restart_debugger = args.get('restart', False)
|
||||
|
||||
# TODO: docstring
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
import ptvsd
|
||||
|
||||
|
||||
def main():
|
||||
print('one')
|
||||
ptvsd.break_into_debugger()
|
||||
print('two')
|
||||
|
||||
|
||||
main()
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import sys
|
||||
import ptvsd
|
||||
import os
|
||||
import time
|
||||
|
||||
ptvsd.enable_attach((sys.argv[1], sys.argv[2]))
|
||||
|
||||
|
||||
loopy = False
|
||||
if os.getenv('PTVSD_WAIT_FOR_ATTACH', None) is not None:
|
||||
print('waiting for attach')
|
||||
ptvsd.wait_for_attach()
|
||||
elif os.getenv('PTVSD_IS_ATTACHED', None) is not None:
|
||||
print('checking is attached')
|
||||
while not ptvsd.is_attached():
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
loopy = True
|
||||
|
||||
|
||||
def main():
|
||||
if loopy:
|
||||
count = 0
|
||||
while count < 50:
|
||||
print('one')
|
||||
ptvsd.break_into_debugger()
|
||||
time.sleep(0.1)
|
||||
print('two')
|
||||
count += 1
|
||||
else:
|
||||
print('one')
|
||||
ptvsd.break_into_debugger()
|
||||
print('two')
|
||||
|
||||
|
||||
main()
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import ptvsd
|
||||
|
||||
|
||||
def main():
|
||||
print('one')
|
||||
ptvsd.break_into_debugger()
|
||||
print('two')
|
||||
|
||||
|
||||
main()
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import sys
|
||||
import ptvsd
|
||||
import os
|
||||
import time
|
||||
|
||||
ptvsd.enable_attach((sys.argv[1], sys.argv[2]))
|
||||
|
||||
|
||||
def main():
|
||||
count = 0
|
||||
while count < 50:
|
||||
if os.getenv('PTVSD_WAIT_FOR_ATTACH', None) is not None:
|
||||
print('waiting for attach')
|
||||
ptvsd.wait_for_attach()
|
||||
elif os.getenv('PTVSD_IS_ATTACHED', None) is not None:
|
||||
print('checking is attached')
|
||||
while not ptvsd.is_attached():
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
pass
|
||||
print('one')
|
||||
ptvsd.break_into_debugger()
|
||||
time.sleep(0.5)
|
||||
print('two')
|
||||
count += 1
|
||||
|
||||
|
||||
main()
|
||||
|
|
@ -94,6 +94,51 @@ class BreakIntoDebuggerTests(LifecycleTestsBase):
|
|||
self.new_event('terminated'),
|
||||
])
|
||||
|
||||
|
||||
class LaunchFileBreakIntoDebuggerTests(BreakIntoDebuggerTests):
|
||||
def test_launch_and_break(self):
|
||||
filename = TEST_FILES.resolve('launch_test.py')
|
||||
cwd = os.path.dirname(filename)
|
||||
debug_info = DebugInfo(filename=filename, cwd=cwd)
|
||||
self.run_test_attach_or_launch(debug_info)
|
||||
|
||||
|
||||
class LaunchModuleBreakIntoDebuggerTests(BreakIntoDebuggerTests):
|
||||
def test_launch_and_break(self):
|
||||
module_name = 'mypkg_launch'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.parent.root
|
||||
self.run_test_attach_or_launch(
|
||||
DebugInfo(modulename=module_name, env=env, cwd=cwd))
|
||||
|
||||
|
||||
class ServerAttachBreakIntoDebuggerTests(BreakIntoDebuggerTests):
|
||||
def test_attach_and_break(self):
|
||||
filename = TEST_FILES.resolve('launch_test.py')
|
||||
cwd = os.path.dirname(filename)
|
||||
debug_info = DebugInfo(
|
||||
filename=filename,
|
||||
cwd=cwd,
|
||||
starttype='attach',
|
||||
)
|
||||
self.run_test_attach_or_launch(debug_info)
|
||||
|
||||
|
||||
class ServerAttachModuleBreakIntoDebuggerTests(BreakIntoDebuggerTests):
|
||||
def test_attach_and_break(self):
|
||||
module_name = 'mypkg_launch'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.root
|
||||
debug_info = DebugInfo(
|
||||
modulename=module_name,
|
||||
cwd=cwd,
|
||||
env=env,
|
||||
starttype='attach',
|
||||
)
|
||||
self.run_test_attach_or_launch(debug_info)
|
||||
|
||||
|
||||
class PTVSDAttachBreakIntoDebuggerTests(BreakIntoDebuggerTests):
|
||||
def test_attach_enable_wait_and_break(self):
|
||||
# Uses enable_attach followed by wait_for_attach
|
||||
# before calling break_into_debugger
|
||||
|
|
@ -138,12 +183,6 @@ class BreakIntoDebuggerTests(LifecycleTestsBase):
|
|||
)
|
||||
self.run_test_attach_or_launch(debug_info, end_loop=True)
|
||||
|
||||
def test_launch(self):
|
||||
filename = TEST_FILES.resolve('launch_test.py')
|
||||
cwd = os.path.dirname(filename)
|
||||
debug_info = DebugInfo(filename=filename, cwd=cwd)
|
||||
self.run_test_attach_or_launch(debug_info)
|
||||
|
||||
def test_reattach_enable_wait_and_break(self):
|
||||
# Uses enable_attach followed by wait_for_attach
|
||||
# before calling break_into_debugger
|
||||
|
|
@ -187,3 +226,105 @@ class BreakIntoDebuggerTests(LifecycleTestsBase):
|
|||
attachtype='import',
|
||||
)
|
||||
self.run_test_reattach(debug_info)
|
||||
|
||||
|
||||
class PTVSDAttachModuleBreakIntoDebuggerTests(BreakIntoDebuggerTests):
|
||||
def test_attach_enable_wait_and_break(self):
|
||||
# Uses enable_attach followed by wait_for_attach
|
||||
# before calling break_into_debugger
|
||||
module_name = 'mypkg_attach'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
env['PTVSD_WAIT_FOR_ATTACH'] = 'True'
|
||||
cwd = TEST_FILES.root
|
||||
debug_info = DebugInfo(
|
||||
modulename=module_name,
|
||||
env=env,
|
||||
cwd=cwd,
|
||||
argv=['localhost', str(PORT)],
|
||||
starttype='attach',
|
||||
attachtype='import',
|
||||
)
|
||||
self.run_test_attach_or_launch(debug_info)
|
||||
|
||||
def test_attach_enable_check_and_break(self):
|
||||
# Uses enable_attach followed by a loop that checks if the
|
||||
# debugger is attached before calling break_into_debugger
|
||||
module_name = 'mypkg_attach'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
env['PTVSD_IS_ATTACHED'] = 'True'
|
||||
cwd = TEST_FILES.root
|
||||
debug_info = DebugInfo(
|
||||
modulename=module_name,
|
||||
env=env,
|
||||
cwd=cwd,
|
||||
argv=['localhost', str(PORT)],
|
||||
starttype='attach',
|
||||
attachtype='import',
|
||||
)
|
||||
self.run_test_attach_or_launch(debug_info)
|
||||
|
||||
def test_attach_enable_and_break(self):
|
||||
# Uses enable_attach followed by break_into_debugger
|
||||
# not is_attached check or wait_for_debugger
|
||||
module_name = 'mypkg_attach'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.root
|
||||
debug_info = DebugInfo(
|
||||
modulename=module_name,
|
||||
env=env,
|
||||
cwd=cwd,
|
||||
argv=['localhost', str(PORT)],
|
||||
starttype='attach',
|
||||
attachtype='import',
|
||||
)
|
||||
self.run_test_attach_or_launch(debug_info, end_loop=True)
|
||||
|
||||
def test_reattach_enable_wait_and_break(self):
|
||||
# Uses enable_attach followed by wait_for_attach
|
||||
# before calling break_into_debugger
|
||||
module_name = 'mypkg_reattach'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
env['PTVSD_WAIT_FOR_ATTACH'] = 'True'
|
||||
cwd = TEST_FILES.root
|
||||
debug_info = DebugInfo(
|
||||
modulename=module_name,
|
||||
env=env,
|
||||
cwd=cwd,
|
||||
argv=['localhost', str(PORT)],
|
||||
starttype='attach',
|
||||
attachtype='import',
|
||||
)
|
||||
self.run_test_reattach(debug_info)
|
||||
|
||||
def test_reattach_enable_check_and_break(self):
|
||||
# Uses enable_attach followed by a loop that checks if the
|
||||
# debugger is attached before calling break_into_debugger
|
||||
module_name = 'mypkg_reattach'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
env['PTVSD_IS_ATTACHED'] = 'True'
|
||||
cwd = TEST_FILES.root
|
||||
debug_info = DebugInfo(
|
||||
modulename=module_name,
|
||||
env=env,
|
||||
cwd=cwd,
|
||||
argv=['localhost', str(PORT)],
|
||||
starttype='attach',
|
||||
attachtype='import',
|
||||
)
|
||||
self.run_test_reattach(debug_info)
|
||||
|
||||
def test_reattach_enable_and_break(self):
|
||||
# Uses enable_attach followed by break_into_debugger
|
||||
# not is_attached check or wait_for_debugger
|
||||
module_name = 'mypkg_reattach'
|
||||
env = TEST_FILES.env_with_py_path()
|
||||
cwd = TEST_FILES.root
|
||||
debug_info = DebugInfo(
|
||||
modulename=module_name,
|
||||
env=env,
|
||||
cwd=cwd,
|
||||
argv=['localhost', str(PORT)],
|
||||
starttype='attach',
|
||||
attachtype='import',
|
||||
)
|
||||
self.run_test_reattach(debug_info)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue