From 917ac6b0195e293fd97cce83b5ee830a9e348e35 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Mon, 13 Aug 2018 14:10:23 -0700 Subject: [PATCH] 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 --- ptvsd/_util.py | 14 ++ ptvsd/attach_server.py | 18 +-- ptvsd/wrapper.py | 13 -- .../mypkg/__init__.py | 0 .../mypkg/__main__.py | 10 ++ .../mypkg_attach/__init__.py | 0 .../mypkg_attach/__main__.py | 36 +++++ .../mypkg_launch/__init__.py | 0 .../mypkg_launch/__main__.py | 10 ++ .../mypkg_reattach/__init__.py | 0 .../mypkg_reattach/__main__.py | 28 ++++ .../system_tests/test_break_into_debugger.py | 153 +++++++++++++++++- 12 files changed, 250 insertions(+), 32 deletions(-) create mode 100644 tests/resources/system_tests/test_break_into_debugger/mypkg/__init__.py create mode 100644 tests/resources/system_tests/test_break_into_debugger/mypkg/__main__.py create mode 100644 tests/resources/system_tests/test_break_into_debugger/mypkg_attach/__init__.py create mode 100644 tests/resources/system_tests/test_break_into_debugger/mypkg_attach/__main__.py create mode 100644 tests/resources/system_tests/test_break_into_debugger/mypkg_launch/__init__.py create mode 100644 tests/resources/system_tests/test_break_into_debugger/mypkg_launch/__main__.py create mode 100644 tests/resources/system_tests/test_break_into_debugger/mypkg_reattach/__init__.py create mode 100644 tests/resources/system_tests/test_break_into_debugger/mypkg_reattach/__main__.py diff --git a/ptvsd/_util.py b/ptvsd/_util.py index fbaf39b5..d44af8dd 100644 --- a/ptvsd/_util.py +++ b/ptvsd/_util.py @@ -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 diff --git a/ptvsd/attach_server.py b/ptvsd/attach_server.py index 27dbdf7f..44b5065a 100644 --- a/ptvsd/attach_server.py +++ b/ptvsd/attach_server.py @@ -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, diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index f434f443..95611b5d 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -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 diff --git a/tests/resources/system_tests/test_break_into_debugger/mypkg/__init__.py b/tests/resources/system_tests/test_break_into_debugger/mypkg/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/resources/system_tests/test_break_into_debugger/mypkg/__main__.py b/tests/resources/system_tests/test_break_into_debugger/mypkg/__main__.py new file mode 100644 index 00000000..341c6dff --- /dev/null +++ b/tests/resources/system_tests/test_break_into_debugger/mypkg/__main__.py @@ -0,0 +1,10 @@ +import ptvsd + + +def main(): + print('one') + ptvsd.break_into_debugger() + print('two') + + +main() diff --git a/tests/resources/system_tests/test_break_into_debugger/mypkg_attach/__init__.py b/tests/resources/system_tests/test_break_into_debugger/mypkg_attach/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/resources/system_tests/test_break_into_debugger/mypkg_attach/__main__.py b/tests/resources/system_tests/test_break_into_debugger/mypkg_attach/__main__.py new file mode 100644 index 00000000..e013ee91 --- /dev/null +++ b/tests/resources/system_tests/test_break_into_debugger/mypkg_attach/__main__.py @@ -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() diff --git a/tests/resources/system_tests/test_break_into_debugger/mypkg_launch/__init__.py b/tests/resources/system_tests/test_break_into_debugger/mypkg_launch/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/resources/system_tests/test_break_into_debugger/mypkg_launch/__main__.py b/tests/resources/system_tests/test_break_into_debugger/mypkg_launch/__main__.py new file mode 100644 index 00000000..341c6dff --- /dev/null +++ b/tests/resources/system_tests/test_break_into_debugger/mypkg_launch/__main__.py @@ -0,0 +1,10 @@ +import ptvsd + + +def main(): + print('one') + ptvsd.break_into_debugger() + print('two') + + +main() diff --git a/tests/resources/system_tests/test_break_into_debugger/mypkg_reattach/__init__.py b/tests/resources/system_tests/test_break_into_debugger/mypkg_reattach/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/resources/system_tests/test_break_into_debugger/mypkg_reattach/__main__.py b/tests/resources/system_tests/test_break_into_debugger/mypkg_reattach/__main__.py new file mode 100644 index 00000000..088a6a29 --- /dev/null +++ b/tests/resources/system_tests/test_break_into_debugger/mypkg_reattach/__main__.py @@ -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() diff --git a/tests/system_tests/test_break_into_debugger.py b/tests/system_tests/test_break_into_debugger.py index 97929483..9751cd31 100644 --- a/tests/system_tests/test_break_into_debugger.py +++ b/tests/system_tests/test_break_into_debugger.py @@ -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)