示例#1
0
    def test_wait_for_attach(self):
        addr = Address('localhost', PORT)
        filename = self.write_script(
            'spam.py',
            """
            import sys
            sys.path.insert(0, {!r})
            import ptvsd
            ptvsd.enable_attach({}, redirect_output=False)

            ptvsd.wait_for_attach()
            # <ready>
            # <wait>
            """.format(PROJECT_ROOT, tuple(addr)),
        )
        lockfile1 = self.workspace.lockfile()
        _, wait = set_release(filename, lockfile1, 'ready')
        lockfile2 = self.workspace.lockfile()
        done, _ = set_lock(filename, lockfile2, 'wait')

        adapter = DebugAdapter.start_embedded(addr, filename)
        with adapter:
            with DebugClient() as editor:
                session = editor.attach_socket(addr, adapter, timeout=1)
                # Ensure that it really does wait.
                with self.assertRaises(LockTimeoutError):
                    wait(timeout=0.5)

                lifecycle_handshake(session, 'attach')
                wait(timeout=1)
                done()
                adapter.wait()
示例#2
0
 def _set_lock(self, label=None, script=None):
     if script is None:
         if not os.path.exists(self.filename):
             script = self.SOURCE
     lockfile = self.workspace.lockfile()
     return set_lock(self.filename, lockfile, label, script)
示例#3
0
    def test_never_call_wait_for_attach(self):
        addr = Address('localhost', PORT)
        filename = self.write_script(
            'spam.py',
            """
            import sys
            import threading
            import time

            sys.path.insert(0, {!r})
            import ptvsd
            ptvsd.enable_attach({}, redirect_output=False)
            # <ready>
            print('== ready ==')

            # Allow tracing to be triggered.
            def wait():
                # <wait>
                pass
            t = threading.Thread(target=wait)
            t.start()
            for _ in range(100):  # 10 seconds
                print('-----')
                t.join(0.1)
                if not t.is_alive():
                    break
            t.join()

            print('== starting ==')
            # <bp>
            print('== done ==')
            """.format(PROJECT_ROOT, tuple(addr)),
        )
        lockfile1 = self.workspace.lockfile('ready.lock')
        _, wait = set_release(filename, lockfile1, 'ready')
        lockfile2 = self.workspace.lockfile('wait.log')
        done, script = set_lock(filename, lockfile2, 'wait')

        bp = find_line(script, 'bp')
        breakpoints = [{
            'source': {
                'path': filename
            },
            'breakpoints': [
                {
                    'line': bp
                },
            ],
        }]

        #DebugAdapter.VERBOSE = True
        #DebugClient.SESSION.VERBOSE = True
        adapter = DebugAdapter.start_embedded(
            addr,
            filename,
            srvtimeout=None,
        )
        with adapter:
            # Wait longer that WAIT_TIMEOUT, so that debugging isn't
            # immediately enabled in the script's thread.
            wait(timeout=3.0)

            with DebugClient() as editor:
                session = editor.attach_socket(addr, adapter, timeout=1)
                stopped = session.get_awaiter_for_event('stopped')
                with session.wait_for_event('thread') as result:
                    lifecycle_handshake(session,
                                        'attach',
                                        breakpoints=breakpoints,
                                        threads=True)
                event = result['msg']
                tid = event.body['threadId']

                stopped.wait(timeout=5.0)
                done()
                session.send_request('continue', threadId=tid)

                adapter.wait()
        out = str(adapter.output)

        self.assertIn('== ready ==', out)
        self.assertIn('== starting ==', out)
示例#4
0
    def test_attach_breakpoints(self):
        # See https://github.com/Microsoft/ptvsd/issues/448.
        addr = Address('localhost', 8888)
        filename = self.write_script('spam.py', """
            import sys
            sys.path.insert(0, {!r})
            import ptvsd

            addr = {}
            ptvsd.enable_attach(addr)
            print('== waiting for attach ==')
            # <waiting>
            ptvsd.wait_for_attach()
            # <attached>
            print('== attached! ==')
            # <bp 2>
            print('== done waiting ==')
            """.format(ROOT, tuple(addr)))
        lockfile1 = self.workspace.lockfile()
        done1, _ = set_lock(filename, lockfile1, 'waiting')
        lockfile2 = self.workspace.lockfile()
        done2, script = set_lock(filename, lockfile2, 'bp 2')

        bp1 = find_line(script, 'attached')
        bp2 = find_line(script, 'bp 2')
        breakpoints = [{
            'source': {'path': filename},
            'breakpoints': [
                {'line': bp1},
                {'line': bp2},
            ],
        }]

        options = {
            'pathMappings': [
                {
                    'localRoot': os.path.dirname(filename),
                    'remoteRoot': os.path.dirname(filename)
                },
                # This specific mapping is for Mac.
                # For some reason temp paths on Mac get prefixed with
                # `private` when returned from ptvsd.
                {
                    'localRoot': os.path.dirname(filename),
                    'remoteRoot': '/private' + os.path.dirname(filename)
                }
            ]
        }

        #DebugAdapter.VERBOSE = True
        adapter = DebugAdapter.start_embedded(addr, filename)
        with adapter:
            with DebugClient() as editor:
                session = editor.attach_socket(addr, adapter, timeout=5)

                with session.wait_for_event('thread') as result:
                    with session.wait_for_event('process'):
                        (req_init, req_attach, req_config,
                         reqs_bps, _, req_threads1,
                         ) = lifecycle_handshake(session, 'attach',
                                                 breakpoints=breakpoints,
                                                 options=options,
                                                 threads=True)
                        Awaitable.wait_all(req_init, req_attach, req_config)
                        req_bps, = reqs_bps  # There should only be one.
                event = result['msg']
                tid = event.body['threadId']

                # Grab the initial output.
                out1 = next(adapter.output)  # "waiting for attach"
                line = adapter.output.readline()
                while line:
                    out1 += line
                    line = adapter.output.readline()

                with session.wait_for_event('stopped'):
                    # Tell the script to proceed (at "# <waiting>").
                    # This leads to the first breakpoint.
                    done1()
                req_threads2, req_stacktrace1 = react_to_stopped(session, tid)
                out2 = str(adapter.output)  # ""

                # Tell the script to proceed (at "# <bp 2>").  This
                # leads to the second breakpoint.  At this point
                # execution is still stopped at the first breakpoint.
                done2()
                with session.wait_for_event('stopped'):
                    with session.wait_for_event('continued'):
                        req_continue1 = session.send_request(
                            'continue',
                            threadId=tid,
                        )
                        req_continue1.wait()
                req_threads3, req_stacktrace2 = react_to_stopped(session, tid)
                out3 = str(adapter.output)  # "attached!"

                with session.wait_for_event('continued'):
                    req_continue2 = session.send_request(
                        'continue',
                        threadId=tid,
                    )
                    req_continue2.wait()

                adapter.wait()
            out4 = str(adapter.output)  # "done waiting"

        # Output between enable_attach() and wait_for_attach() may
        # be sent at a relatively arbitrary time (or not at all).
        # So we ignore it by removing it from the message list.
        received = list(_strip_output_event(session.received,
                                            u'== waiting for attach =='))
        received = list(_strip_newline_output_events(received))
        # There's an ordering race with continue/continued that pops
        # up occasionally.  We work around that by manually fixing the
        # order.
        for pos, msg in _find_events(received, 'continued'):
            prev = received[pos-1]
            if prev.type != 'response' or prev.command != 'continue':
                received.pop(pos-1)
                received.insert(pos + 1, prev)
        # Sometimes the proc ends before the exited and terminated
        # events are received.
        received = list(_strip_exit(received))
        self.assert_contains(received, [
            self.new_version_event(session.received),
            self.new_response(req_init.req, **INITIALIZE_RESPONSE),
            self.new_event('initialized'),
            self.new_response(req_attach.req),
            self.new_event(
                'thread',
                threadId=tid,
                reason='started',
            ),
            self.new_response(req_threads1.req, **{
                'threads': [{
                    'id': 1,
                    'name': 'MainThread',
                }],
            }),
            self.new_response(req_bps.req, **{
                'breakpoints': [{
                    'id': 1,
                    'line': bp1,
                    'verified': True,
                }, {
                    'id': 2,
                    'line': bp2,
                    'verified': True,
                }],
            }),
            self.new_response(req_config.req),
            self.new_event('process', **{
                'isLocalProcess': True,
                'systemProcessId': adapter.pid,
                'startMethod': 'attach',
                'name': filename,
            }),
            self.new_event(
                'stopped',
                threadId=tid,
                reason='breakpoint',
                description=None,
                text=None,
            ),
            self.new_response(req_threads2.req, **{
                'threads': [{
                    'id': 1,
                    'name': 'MainThread',
                }],
            }),
            self.new_event(
                'module',
                module={
                    'id': 1,
                    'name': '__main__',
                    'path': filename,
                    'package': None,
                },
                reason='new',
            ),
            self.new_response(req_stacktrace1.req, **{
                'totalFrames': 1,
                'stackFrames': [{
                    'id': 1,
                    'name': '<module>',
                    'source': {
                        'path': filename,
                        'sourceReference': 0,
                    },
                    'line': bp1,
                    'column': 1,
                }],
            }),
            self.new_response(req_continue1.req, **{
                'allThreadsContinued': True
            }),
            self.new_event('continued', threadId=tid),
            self.new_event(
                'output',
                category='stdout',
                output='== attached! ==',
            ),
            self.new_event(
                'stopped',
                threadId=tid,
                reason='breakpoint',
                description=None,
                text=None,
            ),
            self.new_response(req_threads3.req, **{
                'threads': [{
                    'id': 1,
                    'name': 'MainThread',
                }],
            }),
            self.new_response(req_stacktrace2.req, **{
                'totalFrames': 1,
                'stackFrames': [{
                    'id': 2,  # TODO: Isn't this the same frame as before?
                    'name': '<module>',
                    'source': {
                        'path': filename,
                        'sourceReference': 0,
                    },
                    'line': bp2,
                    'column': 1,
                }],
            }),
            self.new_response(req_continue2.req, **{
                'allThreadsContinued': True
            }),
            self.new_event('continued', threadId=tid),
            self.new_event(
                'output',
                category='stdout',
                output='== done waiting ==',
            ),
            #self.new_event(
            #    'thread',
            #    threadId=tid,
            #    reason='exited',
            #),
            #self.new_event('exited', exitCode=0),
            #self.new_event('terminated'),
        ])
        # before attaching
        self.assertIn(b'waiting for attach', out1)
        self.assertNotIn(b'attached!', out1)
        # after attaching
        self.assertNotIn('attached!', out2)
        # after bp1 continue
        self.assertIn('attached!', out3)
        self.assertNotIn('done waiting', out3)
        # after bp2 continue
        self.assertIn('done waiting', out4)
示例#5
0
    def test_detach_clear_and_resume(self):
        addr = Address('localhost', 8888)
        filename = self.write_script('spam.py', """
            import sys
            sys.path.insert(0, {!r})
            import ptvsd

            addr = {}
            ptvsd.enable_attach(addr)
            ptvsd.wait_for_attach()

            # <before>
            print('==before==')

            # <after>
            print('==after==')

            # <done>
            """.format(ROOT, tuple(addr)))
        lockfile2 = self.workspace.lockfile()
        done1, _ = set_lock(filename, lockfile2, 'before')
        lockfile3 = self.workspace.lockfile()
        _, wait2 = set_release(filename, lockfile3, 'done')
        lockfile4 = self.workspace.lockfile()
        done2, script = set_lock(filename, lockfile4, 'done')

        bp1 = find_line(script, 'before')
        bp2 = find_line(script, 'after')

        #DebugAdapter.VERBOSE = True
        adapter = DebugAdapter.start_embedded(addr, filename)
        with adapter:
            with DebugClient() as editor:
                session1 = editor.attach_socket(addr, adapter, timeout=5)
                with session1.wait_for_event('thread') as result:
                    with session1.wait_for_event('process'):
                        (req_init1, req_attach1, req_config1,
                         _, _, req_threads1,
                         ) = lifecycle_handshake(session1, 'attach',
                                                 threads=True)
                event = result['msg']
                tid1 = event.body['threadId']

                stopped_event = session1.get_awaiter_for_event('stopped')
                req_bps = session1.send_request(
                    'setBreakpoints',
                    source={'path': filename},
                    breakpoints=[
                        {'line': bp1},
                        {'line': bp2},
                    ],
                )
                req_bps.wait()

                done1()
                stopped_event.wait()

                req_threads2 = session1.send_request('threads')
                req_stacktrace1 = session1.send_request(
                    'stackTrace',
                    threadId=tid1,
                )
                out1 = str(adapter.output)

                # Detach with execution stopped and 1 breakpoint left.
                req_disconnect = session1.send_request('disconnect')
                Awaitable.wait_all(req_threads2, req_stacktrace1, req_disconnect) # noqa
                editor.detach(adapter)
                try:
                    wait2()
                except LockTimeoutError:
                    self.fail('execution never resumed upon detach '
                              'or breakpoints never cleared')
                out2 = str(adapter.output)
                import time
                time.sleep(2)
                session2 = editor.attach_socket(addr, adapter, timeout=5)
                #session2.VERBOSE = True
                with session2.wait_for_event('thread') as result:
                    with session2.wait_for_event('process'):
                        (req_init2, req_attach2, req_config2,
                         _, _, req_threads3,
                         ) = lifecycle_handshake(session2, 'attach',
                                                 threads=True)
                event = result['msg']
                tid2 = event.body['threadId']

                done2()
                adapter.wait()
            out3 = str(adapter.output)

        received = list(_strip_newline_output_events(session1.received))
        self.assert_contains(received, [
            self.new_version_event(session1.received),
            self.new_response(req_init1.req, **INITIALIZE_RESPONSE),
            self.new_event('initialized'),
            self.new_response(req_attach1.req),
            self.new_event(
                'thread',
                threadId=tid1,
                reason='started',
            ),
            self.new_response(req_threads1.req, **{
                'threads': [{
                    'id': 1,
                    'name': 'MainThread',
                }],
            }),
            self.new_response(req_config1.req),
            self.new_event('process', **{
                'isLocalProcess': True,
                'systemProcessId': adapter.pid,
                'startMethod': 'attach',
                'name': filename,
            }),
            self.new_response(req_bps.req, **{
                'breakpoints': [{
                    'id': 1,
                    'line': bp1,
                    'verified': True,
                }, {
                    'id': 2,
                    'line': bp2,
                    'verified': True,
                }],
            }),
            self.new_event(
                'stopped',
                threadId=tid1,
                reason='breakpoint',
                description=None,
                text=None,
            ),
            self.new_response(req_threads2.req, **{
                'threads': [{
                    'id': 1,
                    'name': 'MainThread',
                }],
            }),
            self.new_response(req_disconnect.req),
        ])
        self.messages.reset_all()
        received = list(_strip_newline_output_events(session2.received))
        # Sometimes the proc ends before the exited and terminated
        # events are received.
        received = list(_strip_exit(received))
        self.assert_contains(received, [
            self.new_version_event(session2.received),
            self.new_response(req_init2.req, **INITIALIZE_RESPONSE),
            self.new_event('initialized'),
            self.new_response(req_attach2.req),
            self.new_event(
                'thread',
                threadId=tid2,
                reason='started',
            ),
            self.new_response(req_threads3.req, **{
                'threads': [{
                    'id': 1,
                    'name': 'MainThread',
                }],
            }),
            self.new_response(req_config2.req),
            self.new_event('process', **{
                'isLocalProcess': True,
                'systemProcessId': adapter.pid,
                'startMethod': 'attach',
                'name': filename,
            }),
            #self.new_event(
            #    'thread',
            #    threadId=tid2,
            #    reason='exited',
            #),
            #self.new_event('exited', exitCode=0),
            #self.new_event('terminated'),
        ])
        # at breakpoint
        self.assertEqual(out1, '')
        # after detaching
        self.assertIn('==before==', out2)
        self.assertIn('==after==', out2)
        # after reattach
        self.assertEqual(out3, out2)