Repository: microsoft/ptvsd

Issue:
Remote debugging failed with warning "pydev debugger: warning: try to add breakpoint to file that does not exist"
@xiaofeng0123 commented on [Tue Apr 02 2019](https://github.com/Microsoft/vscode-python/issues/5094)

Visual Studio Code - Insiders: 1.33.0-insider
python-insider: 2019.4.6365-alpha
ptvsd version: 4.2.6 

Repro step:
1. Create a python file named remote_debug.py with below code:
     import ptvsd
     ptvsd.enable_attach(('IP of unbuntu',5678))
     ptvsd.wait_for_attach()
     for i in range(1,10,2):
            print(i)
     print('demo done')
2. Copy this file to ubuntu which should include ptvsd installed with same version as current machine
3. Execute python file in ubuntu:
    python3 remote_debug.py
4. In VSC, set DEBUG to "Python:Remote Attach", and set the path mappings and host value
![image](https://user-images.githubusercontent.com/41426540/55449113-df6b9500-55fc-11e9-9dd7-24e2622864e2.png)
5. Set breakpoint on current machine and press F5 to start debugging

Actual Result:
Fail to hit breakpoint with warning "pydev debugger: warning: trying to add breakpoint to file that does not exist..."
![image](https://user-images.githubusercontent.com/41426540/55449381-e3e47d80-55fd-11e9-97a0-b09e6ec03d9d.png)

Expect Result:
It should hit breakpoint







Discussion:
@xiaofeng0123 From the paths it looks like the remote root might not be correct. 
Can you try with following path mapping?
```
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}",
                    "remoteRoot": "."
                }
            ],
```
If that does not work, can you provide more details about your project layout local and remote.
@karthiknadig Could be a dup of https://github.com/Microsoft/ptvsd/issues/1297
Problems with Path mappings
I have the exact same problem. Tried using ptvsd **3.0.0, 4.2.6** and **4.2.7**. After extensive troubleshooting trying out all manner off different values for the two path mappings I can report that it seems as if ptvsd just concatenates both path values.

I am running a Flask app in a docker container. I am exposing port 8000 for the Flask app as well as port 5678 for ptvsd. My app resides in /app inside the docker container and on my host machine in a different path altogether. 

My launch.json entry looks like:

``` 
       {
            "name": "Python: Docker Remote Attach",
            "type": "python",
            "request": "attach",
            "port": 5678,
            "host": "walaa",            
            "pathMappings": [
                {                    
                    "remoteRoot": "",
                    "localRoot": "${workspaceFolder}"                    
                }
            ]

        },
```

If I run my container I can start the `Python: Docker Remote Attach` config without any issues. The moment I add a breakpoint using the IDE the following error occurs:

`pydev debugger: warning: trying to add breakpoint to file that does not exist: /app/c:/Users/adria/Projects/voila/src/Blueprints/location_bp.py (will have no effect)`

I have also noticed that no matter what I set the `localRoot` and the `remoteRoot` values to the error message remains the same as if it totally ignores the values I set them to and using the default values only.

For instance, if I change my launch.json entry to the following:

```
        {
            "name": "Python: Docker Remote Attach",
            "type": "python",
            "request": "attach",
            "port": 5678,
            "host": "walaa",            
            "pathMappings": [
                {                    
                    "remoteRoot": "/somethingelse",
                    "localRoot": "${workspaceFolder}"                    
                }
            ]

        },
```

One would expect the error message to at least reflect the new remoteRoot value of `/somethingelse` but sadly this is not the case, instead the error message remains the same.

`pydev debugger: warning: trying to add breakpoint to file that does not exist: /app/c:/Users/adria/Projects/voila/src/Blueprints/location_bp.py (will have no effect)`

I have tried removing the pathMappings collection entirely, changing the *Root values to absolute paths, making both ".", making one "." and the other absolute, making both blank, one blank and the other either absolute or "." and all combinations of  the aforementioned without any luck. Those values are happily ignored.

I have no idea if the VS Code extension is sending the incorrect information to the remote server or if the remote server's ptvsd is incorrectly interpreting the values it receives.

Any help will be greatly appreciated.
I'll take a look at this.
I've been able to reproduce this.

The problem I found is that if the `localRoot` set in the path mapping does not match the case of the file with the breakpoint exactly, things go awry.

This means that if the path mapping `localRoot` is set to something as the following on windows:

`"localRoot": "c:\my\folder"`

and later a breakpoint is sent as:

`"C:\my\folder\my_file.py"` (notice the different case on the drive letter)

then there will be no translation applied and when the debugger goes on to make that path absolute it'll end up concatenating the result with the working directory generating something as:

`/home/cwd/C:/my/folder/my_file.py`

The workaround for this would be making sure that the `localRoot` casing matches and use that value as the entry.

i.e.: if it fails with `/home/cwd/C:/my/folder/my_file.py`, set the `localRoot` to `C:/my/folder` and if it fails with `/home/cwd/c:/my/folder/my_file.py`, set it to `c:/my/folder` (notice drive letter case).

-- it seems that the `${workspaceFolder}` doesn't always match the case of the paths with breakpoints sent to the remote later on, so, it can't be always used.

The fix I'll make so that this doesn't happen anymore is taking into account that the client is on Windows and then normalizing the cases of the paths sent from the client and in the path mapping (I don't know why on some cases VSCode sends the case for the `${workspaceFolder}` different from the case of the breakpoints later on, but we should have enough information to work around it).
Thank you so much for taking the time to look into this. I have made the changes that you have suggested and it works like a charm!

Thanks again.

Resolving patch:
diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_json_debug_options.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_json_debug_options.py
--- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_json_debug_options.py
+++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_json_debug_options.py
@@ -97,9 +97,6 @@ def _parse_debug_options(opts):
         except KeyError:
             continue
 
-    if 'CLIENT_OS_TYPE' not in options:
-        options['CLIENT_OS_TYPE'] = 'WINDOWS' if platform.system() == 'Windows' else 'UNIX'  # noqa
-
     return options
 
 
diff --git a/src/ptvsd/_vendored/pydevd/pydevd_file_utils.py b/src/ptvsd/_vendored/pydevd/pydevd_file_utils.py
--- a/src/ptvsd/_vendored/pydevd/pydevd_file_utils.py
+++ b/src/ptvsd/_vendored/pydevd/pydevd_file_utils.py
@@ -44,15 +44,17 @@
 from _pydevd_bundle.pydevd_constants import IS_PY2, IS_PY3K, DebugInfoHolder, IS_WINDOWS, IS_JYTHON
 from _pydev_bundle._pydev_filesystem_encoding import getfilesystemencoding
 from _pydevd_bundle.pydevd_comm_constants import file_system_encoding, filesystem_encoding_is_utf8
+from _pydev_bundle.pydev_log import error_once
 
 import json
 import os.path
 import sys
 import traceback
 import itertools
+import ntpath
 from functools import partial
 
-_os_normcase = os.path.normcase
+_nt_os_normcase = ntpath.normcase
 basename = os.path.basename
 exists = os.path.exists
 join = os.path.join
@@ -96,11 +98,11 @@
         import ctypes
         from ctypes.wintypes import MAX_PATH, LPCWSTR, LPWSTR, DWORD
 
-        GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
+        GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW  # noqa
         GetLongPathName.argtypes = [LPCWSTR, LPWSTR, DWORD]
         GetLongPathName.restype = DWORD
 
-        GetShortPathName = ctypes.windll.kernel32.GetShortPathNameW
+        GetShortPathName = ctypes.windll.kernel32.GetShortPathNameW  # noqa
         GetShortPathName.argtypes = [LPCWSTR, LPWSTR, DWORD]
         GetShortPathName.restype = DWORD
 
@@ -152,38 +154,45 @@ def _get_path_with_real_case(filename):
 elif IS_JYTHON and IS_WINDOWS:
 
     def get_path_with_real_case(filename):
-        from java.io import File
+        from java.io import File  # noqa
         f = File(filename)
         ret = f.getCanonicalPath()
         if IS_PY2 and not isinstance(ret, str):
             return ret.encode(getfilesystemencoding())
         return ret
 
-if IS_WINDOWS:
+if IS_JYTHON:
 
-    if IS_JYTHON:
+    def _normcase_windows(filename):
+        return filename.lower()
 
-        def normcase(filename):
-            return filename.lower()
+else:
 
-    else:
+    def _normcase_windows(filename):
+        # `normcase` doesn't lower case on Python 2 for non-English locale, so we should do it manually.
+        if '~' in filename:
+            filename = convert_to_long_pathname(filename)
 
-        def normcase(filename):
-            # `normcase` doesn't lower case on Python 2 for non-English locale, but Java
-            # side does it, so we should do it manually.
-            if '~' in filename:
-                filename = convert_to_long_pathname(filename)
+        filename = _nt_os_normcase(filename)
+        return filename.lower()
 
-            filename = _os_normcase(filename)
-            return filename.lower()
 
-else:
+def _normcase_linux(filename):
+    return filename  # no-op
+
 
-    def normcase(filename):
-        return filename  # no-op
+if IS_WINDOWS:
+    normcase = _normcase_windows
+
+else:
+    normcase = _normcase_linux
 
 _ide_os = 'WINDOWS' if IS_WINDOWS else 'UNIX'
 
+_normcase_from_client = normcase
+
+DEBUG_CLIENT_SERVER_TRANSLATION = os.environ.get('DEBUG_PYDEVD_PATHS_TRANSLATION', 'False').lower() in ('1', 'true')
+
 
 def set_ide_os(os):
     '''
@@ -195,32 +204,46 @@ def set_ide_os(os):
         'UNIX' or 'WINDOWS'
     '''
     global _ide_os
+    global _normcase_from_client
     prev = _ide_os
     if os == 'WIN':  # Apparently PyCharm uses 'WIN' (https://github.com/fabioz/PyDev.Debugger/issues/116)
         os = 'WINDOWS'
 
     assert os in ('WINDOWS', 'UNIX')
 
+    if DEBUG_CLIENT_SERVER_TRANSLATION:
+        print('pydev debugger: client OS: %s' % (os,))
+
+    _normcase_from_client = normcase
+    if os == 'WINDOWS':
+
+        # Client in Windows and server in Unix, we need to normalize the case.
+        if not IS_WINDOWS:
+            _normcase_from_client = _normcase_windows
+
+    else:
+        # Client in Unix and server in Windows, we can't normalize the case.
+        if IS_WINDOWS:
+            _normcase_from_client = _normcase_linux
+
     if prev != os:
         _ide_os = os
         # We need to (re)setup how the client <-> server translation works to provide proper separators.
         setup_client_server_paths(_last_client_server_paths_set)
 
 
-DEBUG_CLIENT_SERVER_TRANSLATION = os.environ.get('DEBUG_PYDEVD_PATHS_TRANSLATION', 'False').lower() in ('1', 'true')
-
 # Caches filled as requested during the debug session.
 NORM_PATHS_CONTAINER = {}
 NORM_PATHS_AND_BASE_CONTAINER = {}
 
 
 def _NormFile(filename):
-    abs_path, real_path = _NormPaths(filename)
+    _abs_path, real_path = _NormPaths(filename)
     return real_path
 
 
 def _AbsFile(filename):
-    abs_path, real_path = _NormPaths(filename)
+    abs_path, _real_path = _NormPaths(filename)
     return abs_path
 
 
@@ -455,16 +478,16 @@ def setup_client_server_paths(paths):
 
     for i, (path0, path1) in enumerate(paths_from_eclipse_to_python[:]):
         if IS_PY2:
-            if isinstance(path0, unicode):
+            if isinstance(path0, unicode):  # noqa
                 path0 = path0.encode(sys.getfilesystemencoding())
-            if isinstance(path1, unicode):
+            if isinstance(path1, unicode):  # noqa
                 path1 = path1.encode(sys.getfilesystemencoding())
 
         path0 = _fix_path(path0, eclipse_sep)
         path1 = _fix_path(path1, python_sep)
         initial_paths[i] = (path0, path1)
 
-        paths_from_eclipse_to_python[i] = (normcase(path0), normcase(path1))
+        paths_from_eclipse_to_python[i] = (_normcase_from_client(path0), normcase(path1))
 
     if not paths_from_eclipse_to_python:
         # no translation step needed (just inline the calls)
@@ -484,24 +507,37 @@ def _norm_file_to_server(filename, cache=norm_filename_to_server_container):
                 filename = filename.replace(python_sep, eclipse_sep)
 
             # used to translate a path from the client to the debug server
-            translated = normcase(filename)
+            translated = filename
+            translated_normalized = _normcase_from_client(filename)
             for eclipse_prefix, server_prefix in paths_from_eclipse_to_python:
-                if translated.startswith(eclipse_prefix):
+                if translated_normalized.startswith(eclipse_prefix):
+                    found_translation = True
                     if DEBUG_CLIENT_SERVER_TRANSLATION:
-                        sys.stderr.write('pydev debugger: replacing to server: %s\n' % (translated,))
-                    translated = translated.replace(eclipse_prefix, server_prefix)
+                        sys.stderr.write('pydev debugger: replacing to server: %s\n' % (filename,))
+                    translated = server_prefix + filename[len(eclipse_prefix):]
                     if DEBUG_CLIENT_SERVER_TRANSLATION:
                         sys.stderr.write('pydev debugger: sent to server: %s\n' % (translated,))
                     break
             else:
-                if DEBUG_CLIENT_SERVER_TRANSLATION:
-                    sys.stderr.write('pydev debugger: to server: unable to find matching prefix for: %s in %s\n' % \
-                        (translated, [x[0] for x in paths_from_eclipse_to_python]))
+                found_translation = False
 
             # Note that when going to the server, we do the replace first and only later do the norm file.
             if eclipse_sep != python_sep:
                 translated = translated.replace(eclipse_sep, python_sep)
-            translated = _NormFile(translated)
+
+            if found_translation:
+                translated = _NormFile(translated)
+            else:
+                if not os.path.exists(translated):
+                    if not translated.startswith('<'):
+                        # This is a configuration error, so, write it always so
+                        # that the user can fix it.
+                        error_once('pydev debugger: unable to find translation for: "%s" in [%s] (please revise your path mappings).\n' %
+                            (filename, ', '.join(['"%s"' % (x[0],) for x in paths_from_eclipse_to_python])))
+                else:
+                    # It's possible that we had some round trip (say, we sent /usr/lib and received
+                    # it back, so, having no translation is ok too).
+                    translated = _NormFile(translated)
 
             cache[filename] = translated
             return translated
diff --git a/src/ptvsd/wrapper.py b/src/ptvsd/wrapper.py
--- a/src/ptvsd/wrapper.py
+++ b/src/ptvsd/wrapper.py
@@ -9,6 +9,7 @@
 import errno
 import json
 import os
+import re
 import platform
 import pydevd_file_utils
 import site
@@ -687,9 +688,6 @@ def _parse_debug_options(opts):
         except KeyError:
             continue
 
-    if 'CLIENT_OS_TYPE' not in options:
-        options['CLIENT_OS_TYPE'] = 'WINDOWS' if platform.system() == 'Windows' else 'UNIX'  # noqa
-
     return options
 
 ########################
@@ -1101,6 +1099,8 @@ def __init__(
         self._pydevd_request = pydevd_request
         self._notify_debugger_ready = notify_debugger_ready
 
+        self._client_os_type = 'WINDOWS' if platform.system() == 'Windows' else 'UNIX'
+
         self.loop = None
         self.event_loop_thread = None
 
@@ -1334,16 +1334,36 @@ def _initialize_path_maps(self, args):
 
     def _send_cmd_version_command(self):
         cmd = pydevd_comm.CMD_VERSION
-        default_os_type = 'WINDOWS' if platform.system() == 'Windows' else 'UNIX'
-        client_os_type = self.debug_options.get('CLIENT_OS_TYPE', default_os_type)
-        os_id = client_os_type
-        msg = '1.1\t{}\tID'.format(os_id)
+
+        msg = '1.1\t{}\tID'.format(self._client_os_type)
         return self.pydevd_request(cmd, msg)
 
     @async_handler
     def _handle_launch_or_attach(self, request, args):
         self._path_mappings_received = True
 
+        client_os_type = self.debug_options.get('CLIENT_OS_TYPE', '').upper().strip()
+        if client_os_type and client_os_type not in ('WINDOWS', 'UNIX'):
+            ptvsd.log.warn('Invalid CLIENT_OS_TYPE passed: %s (must be either "WINDOWS" or "UNIX").' % (client_os_type,))
+            client_os_type = ''
+
+        if not client_os_type:
+            for pathMapping in args.get('pathMappings', []):
+                localRoot = pathMapping.get('localRoot', '')
+                if localRoot:
+                    if localRoot.startswith('/'):
+                        client_os_type = 'UNIX'
+                        break
+
+                    if re.match('^([a-zA-Z]):', localRoot):  # Match drive letter
+                        client_os_type = 'WINDOWS'
+                        break
+
+        if not client_os_type:
+            client_os_type = 'WINDOWS' if platform.system() == 'Windows' else 'UNIX'
+
+        self._client_os_type = client_os_type
+
         self.pydevd_request(pydevd_comm.CMD_SET_PROTOCOL, 'json')
         yield self._send_cmd_version_command()
 


Test patch:
diff --git a/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_large_exception_stack.py b/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_large_exception_stack.py
--- a/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_large_exception_stack.py
+++ b/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_large_exception_stack.py
@@ -1,11 +1,16 @@
 def method1(n):
     if n <= 0:
         raise IndexError('foo')
-    method2(n-1)
+    method2(n - 1)
+
 
 def method2(n):
-    method1(n-1)
+    method1(n - 1)
+
 
 if __name__ == '__main__':
-    method1(100)
-    print('TEST SUCEEDED!')
\ No newline at end of file
+    try:
+        method1(100)
+    except:
+        pass  # Don't let it print the exception (just deal with caught exceptions).
+    print('TEST SUCEEDED!')
diff --git a/src/ptvsd/_vendored/pydevd/tests_python/test_convert_utilities.py b/src/ptvsd/_vendored/pydevd/tests_python/test_convert_utilities.py
--- a/src/ptvsd/_vendored/pydevd/tests_python/test_convert_utilities.py
+++ b/src/ptvsd/_vendored/pydevd/tests_python/test_convert_utilities.py
@@ -186,6 +186,9 @@ def check(obtained, expected):
 
                 pydevd_file_utils.setup_client_server_paths(PATHS_FROM_ECLIPSE_TO_PYTHON)
                 assert pydevd_file_utils.norm_file_to_server('c:\\foo\\my') == '/báéíóúr/my'
+                assert pydevd_file_utils.norm_file_to_server('C:\\foo\\my') == '/báéíóúr/my'
+                assert pydevd_file_utils.norm_file_to_server('C:\\foo\\MY') == '/báéíóúr/MY'
+                assert pydevd_file_utils.norm_file_to_server('C:\\foo\\MY\\') == '/báéíóúr/MY'
                 assert pydevd_file_utils.norm_file_to_server('c:\\foo\\my\\file.py') == '/báéíóúr/my/file.py'
                 assert pydevd_file_utils.norm_file_to_server('c:\\foo\\my\\other\\file.py') == '/báéíóúr/my/other/file.py'
                 assert pydevd_file_utils.norm_file_to_server('c:/foo/my') == '/báéíóúr/my'
@@ -202,6 +205,12 @@ def check(obtained, expected):
                 assert pydevd_file_utils.norm_file_to_server('\\usr\\bin') == '/usr/bin'
                 assert pydevd_file_utils.norm_file_to_server('\\usr\\bin\\') == '/usr/bin'
 
+                # When we have a client file and there'd be no translation, and making it absolute would
+                # do something as '$cwd/$file_received' (i.e.: $cwd/c:/another in the case below),
+                # warn the user that it's not correct and the path that should be translated instead
+                # and don't make it absolute.
+                assert pydevd_file_utils.norm_file_to_server('c:\\another') == 'c:/another'
+
             # Client and server on unix
             pydevd_file_utils.set_ide_os('UNIX')
             in_eclipse = '/foo'
diff --git a/src/ptvsd/_vendored/pydevd/tests_python/test_debugger.py b/src/ptvsd/_vendored/pydevd/tests_python/test_debugger.py
--- a/src/ptvsd/_vendored/pydevd/tests_python/test_debugger.py
+++ b/src/ptvsd/_vendored/pydevd/tests_python/test_debugger.py
@@ -21,6 +21,7 @@
 from _pydevd_bundle.pydevd_constants import IS_WINDOWS
 from _pydevd_bundle.pydevd_comm_constants import CMD_RELOAD_CODE
 import json
+import pydevd_file_utils
 try:
     from urllib import unquote
 except ImportError:
@@ -1858,7 +1859,14 @@ def _ignore_stderr_line(line):
         writer.finished_ok = True
 
 
-def test_path_translation(case_setup):
+def _path_equals(path1, path2):
+    path1 = pydevd_file_utils.normcase(path1)
+    path2 = pydevd_file_utils.normcase(path2)
+    return path1 == path2
+
+
+@pytest.mark.parametrize('mixed_case', [True, False] if sys.platform == 'win32' else [False])
+def test_path_translation(case_setup, mixed_case):
 
     def get_file_in_client(writer):
         # Instead of using: test_python/_debugger_case_path_translation.py
@@ -1873,9 +1881,13 @@ def get_environ(writer):
         env["PYTHONIOENCODING"] = 'utf-8'
 
         assert writer.TEST_FILE.endswith('_debugger_case_path_translation.py')
+        file_in_client = get_file_in_client(writer)
+        if mixed_case:
+            new_file_in_client = ''.join([file_in_client[i].upper() if i % 2 == 0 else file_in_client[i].lower() for i in range(len(file_in_client))])
+            assert _path_equals(file_in_client, new_file_in_client)
         env["PATHS_FROM_ECLIPSE_TO_PYTHON"] = json.dumps([
             (
-                os.path.dirname(get_file_in_client(writer)),
+                os.path.dirname(file_in_client),
                 os.path.dirname(writer.TEST_FILE)
             )
         ])
diff --git a/tests/func/test_path_mapping.py b/tests/func/test_path_mapping.py
--- a/tests/func/test_path_mapping.py
+++ b/tests/func/test_path_mapping.py
@@ -10,6 +10,53 @@
 from tests.helpers.session import DebugSession
 from tests.helpers.timeline import Event
 from tests.helpers.pathutils import get_test_root
+from tests.helpers import get_marked_line_numbers
+import pytest
+import sys
+
+
+@pytest.mark.skipif(sys.platform == 'win32', reason='Linux/Mac only test.')
+@pytest.mark.parametrize('invalid_os_type', [True])
+def test_client_ide_from_path_mapping_linux_backend(pyfile, tmpdir, run_as, start_method, invalid_os_type):
+    '''
+    Test simulating that the backend is on Linux and the client is on Windows
+    (automatically detect it from the path mapping).
+    '''
+
+    @pyfile
+    def code_to_debug():
+        from dbgimporter import import_and_enable_debugger
+        import_and_enable_debugger()
+        import backchannel
+        import pydevd_file_utils
+        backchannel.write_json({'ide_os': pydevd_file_utils._ide_os})
+        print('done')  # @break_here
+
+    with DebugSession() as session:
+        session.initialize(
+            target=(run_as, code_to_debug),
+            start_method=start_method,
+            ignore_unobserved=[Event('continued')],
+            use_backchannel=True,
+            path_mappings=[{
+                'localRoot': 'C:\\TEMP\\src',
+                'remoteRoot': os.path.dirname(code_to_debug),
+            }],
+        )
+        if invalid_os_type:
+            session.debug_options.append('CLIENT_OS_TYPE=INVALID')
+        bp_line = get_marked_line_numbers(code_to_debug)['break_here']
+        session.set_breakpoints('c:\\temp\\src\\' + os.path.basename(code_to_debug), [bp_line])
+        session.start_debugging()
+        hit = session.wait_for_thread_stopped('breakpoint')
+        frames = hit.stacktrace.body['stackFrames']
+        assert frames[0]['source']['path'] == 'C:\\TEMP\\src\\' + os.path.basename(code_to_debug)
+
+        json_read = session.read_json()
+        assert json_read == {'ide_os': 'WINDOWS'}
+
+        session.send_request('continue').wait_for_response(freeze=False)
+        session.wait_for_exit()
 
 
 def test_with_dot_remote_root(pyfile, tmpdir, run_as, start_method):