diff --git a/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py b/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py index 874952608..ba0b3cf2c 100644 --- a/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py +++ b/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py @@ -346,7 +346,7 @@ def _stop_trace(self): disable_tracing = False if disable_tracing: - pydevd_tracing.SetTrace(None) # no debugging on this thread + pydevd_tracing.SetTrace(None, apply_to_pydevd_thread=True) # no debugging on this thread #======================================================================================================================= diff --git a/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_trace_dispatch_regular.py b/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_trace_dispatch_regular.py index dbff716ff..1644be5af 100644 --- a/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_trace_dispatch_regular.py +++ b/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_trace_dispatch_regular.py @@ -26,7 +26,6 @@ # ELSE # ENDIF - # Cache where we should keep that we completely skipped entering some context. # It needs to be invalidated when: # - Breakpoints are changed @@ -79,6 +78,7 @@ def trace_dispatch(py_db, frame, event, arg): thread = threadingCurrentThread() if getattr(thread, 'pydev_do_not_trace', None): + SetTrace(None, apply_to_pydevd_thread=True) return None try: diff --git a/ptvsd/_vendored/pydevd/pydevd.py b/ptvsd/_vendored/pydevd/pydevd.py index 06493d55e..cda12559d 100644 --- a/ptvsd/_vendored/pydevd/pydevd.py +++ b/ptvsd/_vendored/pydevd/pydevd.py @@ -696,8 +696,14 @@ def add_break_on_exception( def set_suspend(self, thread, stop_reason): - thread.additional_info.suspend_type = PYTHON_SUSPEND - thread.additional_info.pydev_state = STATE_SUSPEND + info = thread.additional_info + info.suspend_type = PYTHON_SUSPEND + info.pydev_state = STATE_SUSPEND + if info.pydev_step_cmd == -1: + # If the step command is not specified, set it to step into + # to make sure it'll break as soon as possible. + info.pydev_step_cmd = CMD_STEP_INTO + thread.stop_reason = stop_reason # If conditional breakpoint raises any exception during evaluation send details to Java @@ -1187,7 +1193,8 @@ def write(self, s): if IS_PY2: # Need s in bytes if isinstance(s, unicode): - s = s.encode('utf-8', errors='replace') + # Note: python 2.6 does not accept the "errors" keyword. + s = s.encode('utf-8', 'replace') else: # Need s in str if isinstance(s, bytes): @@ -1230,6 +1237,7 @@ def settrace( trace_only_current_thread=False, overwrite_prev_trace=False, patch_multiprocessing=False, + stop_at_frame=None, ): '''Sets the tracing function with the pydev debug function and initializes needed facilities. @@ -1253,6 +1261,9 @@ def settrace( @param patch_multiprocessing: if True we'll patch the functions which create new processes so that launched processes are debugged. + + @param stop_at_frame: if passed it'll stop at the given frame, otherwise it'll stop in the function which + called this method. ''' _set_trace_lock.acquire() try: @@ -1265,6 +1276,7 @@ def settrace( trace_only_current_thread, overwrite_prev_trace, patch_multiprocessing, + stop_at_frame, ) finally: _set_trace_lock.release() @@ -1282,6 +1294,7 @@ def _locked_settrace( trace_only_current_thread, overwrite_prev_trace, patch_multiprocessing, + stop_at_frame, ): if patch_multiprocessing: try: @@ -1370,16 +1383,11 @@ def _locked_settrace( PyDBCommandThread(debugger).start() CheckOutputThread(debugger).start() - #Suspend as the last thing after all tracing is in place. - if suspend: - debugger.set_suspend(t, CMD_THREAD_SUSPEND) - - else: # ok, we're already in debug mode, with all set, so, let's just set the break debugger = get_global_debugger() - debugger.set_trace_for_frame_and_parents(get_frame(), False) + debugger.set_trace_for_frame_and_parents(get_frame(), also_add_to_passed_frame=False, overwrite_prev_trace=True) t = threadingCurrentThread() try: @@ -1394,8 +1402,17 @@ def _locked_settrace( # Trace future threads? debugger.patch_threads() - - if suspend: + # Suspend as the last thing after all tracing is in place. + if suspend: + if stop_at_frame is not None: + # If the step was set we have to go to run state and + # set the proper frame for it to stop. + additional_info.pydev_state = STATE_RUN + additional_info.pydev_step_cmd = CMD_STEP_OVER + additional_info.pydev_step_stop = stop_at_frame + additional_info.suspend_type = PYTHON_SUSPEND + else: + # Ask to break as soon as possible. debugger.set_suspend(t, CMD_THREAD_SUSPEND) diff --git a/ptvsd/_vendored/pydevd/pydevd_tracing.py b/ptvsd/_vendored/pydevd/pydevd_tracing.py index 86176a86a..a4621bf65 100644 --- a/ptvsd/_vendored/pydevd/pydevd_tracing.py +++ b/ptvsd/_vendored/pydevd/pydevd_tracing.py @@ -69,7 +69,7 @@ def _internal_set_trace(tracing_func): TracingFunctionHolder._original_tracing(tracing_func) -def SetTrace(tracing_func, frame_eval_func=None, dummy_tracing_func=None): +def SetTrace(tracing_func, frame_eval_func=None, dummy_tracing_func=None, apply_to_pydevd_thread=False): if tracing_func is not None and frame_eval_func is not None: # There is no need to set tracing function if frame evaluation is available frame_eval_func() @@ -82,8 +82,9 @@ def SetTrace(tracing_func, frame_eval_func=None, dummy_tracing_func=None): current_thread = threading.currentThread() do_not_trace_before = getattr(current_thread, 'pydev_do_not_trace', None) - if do_not_trace_before: - return + if not apply_to_pydevd_thread: + if do_not_trace_before: + return try: TracingFunctionHolder._lock.acquire() current_thread.pydev_do_not_trace = True # avoid settrace reentering diff --git a/ptvsd/_vendored/pydevd/tests_python/_debugger_case_settrace.py b/ptvsd/_vendored/pydevd/tests_python/_debugger_case_settrace.py new file mode 100644 index 000000000..6090089a9 --- /dev/null +++ b/ptvsd/_vendored/pydevd/tests_python/_debugger_case_settrace.py @@ -0,0 +1,26 @@ +def ask_for_stop(use_back): + import pydevd + if use_back: + pydevd.settrace(stop_at_frame=sys._getframe().f_back) + else: + pydevd.settrace() + print('Will stop here if use_back==False.') + + +def outer_method(): + ask_for_stop(True) + print('will stop here.') + ask_for_stop(False) + + +if __name__ == '__main__': + import os + import sys + root_dirname = os.path.dirname(os.path.dirname(__file__)) + + if root_dirname not in sys.path: + sys.path.append(root_dirname) + + outer_method() + print('TEST SUCEEDED!') + diff --git a/ptvsd/_vendored/pydevd/tests_python/test_debugger.py b/ptvsd/_vendored/pydevd/tests_python/test_debugger.py index c49b1b4bc..079295437 100644 --- a/ptvsd/_vendored/pydevd/tests_python/test_debugger.py +++ b/ptvsd/_vendored/pydevd/tests_python/test_debugger.py @@ -1490,6 +1490,28 @@ def run(self): self.finished_ok = True +#======================================================================================================================= +# WriterCaseSetTrace +#====================================================================================================================== +class WriterCaseSetTrace(debugger_unittest.AbstractWriterThread): + + TEST_FILE = debugger_unittest._get_debugger_test_file('_debugger_case_settrace.py') + + def run(self): + self.start_socket() + + self.write_make_initial_run() + + thread_id, frame_id, line = self.wait_for_breakpoint_hit('108', True) + assert line == 12, 'Expected return to be in line 12, was: %s' % line + self.write_run_thread(thread_id) + + thread_id, frame_id, line = self.wait_for_breakpoint_hit('105', True) + assert line == 7, 'Expected return to be in line 7, was: %s' % line + self.write_run_thread(thread_id) + + self.finished_ok = True + #======================================================================================================================= # WriterThreadCaseRedirectOutput #====================================================================================================================== @@ -1817,6 +1839,9 @@ def test_case_handled_exceptions2(self): def test_case_handled_exceptions3(self): self.check_case(WriterThreadCaseHandledExceptions3) + def test_case_settrace(self): + self.check_case(WriterCaseSetTrace) + def test_redirect_output(self): self.check_case(WriterThreadCaseRedirectOutput) diff --git a/ptvsd/attach_server.py b/ptvsd/attach_server.py index 735990f14..3bc85c47e 100644 --- a/ptvsd/attach_server.py +++ b/ptvsd/attach_server.py @@ -5,22 +5,14 @@ import threading import pydevd -# TODO: Why import these? -from _pydevd_bundle.pydevd_custom_frames import ( # noqa - CustomFramesContainer, custom_frames_container_init, -) -from _pydevd_bundle.pydevd_additional_thread_info import ( - PyDBAdditionalThreadInfo, -) from _pydevd_bundle.pydevd_comm import ( - get_global_debugger, CMD_THREAD_SUSPEND, + get_global_debugger, ) # 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 - DEFAULT_HOST = '0.0.0.0' DEFAULT_PORT = 5678 @@ -73,7 +65,6 @@ def enable_attach(address=(DEFAULT_HOST, DEFAULT_PORT), redirect_output=True): _attached.clear() ptvsd_enable_attach(address, redirect_output, on_attach=_attached.set) - # TODO: Add disable_attach()? @@ -90,11 +81,9 @@ def break_into_debugger(): if not _attached.isSet() or debugger is None: return - thread = pydevd.threadingCurrentThread() - try: - additional_info = thread.additional_info - except AttributeError: - additional_info = PyDBAdditionalThreadInfo() - thread.additional_info = additional_info - - debugger.set_suspend(thread, CMD_THREAD_SUSPEND) + import sys + pydevd.settrace( + suspend=True, + trace_only_current_thread=True, + patch_multiprocessing=False, + stop_at_frame=sys._getframe().f_back)