Files
clang-p2996/lldb/test/API/tools/lldb-server/commandline/TestGdbRemoteConnection.py
Pavel Labath 8cc49bec2e [lldb] Use reverse connection method for lldb-server tests
This fixes an flakyness is all gdb-remote tests. These tests have been
(mildly) flaky since we started using "localhost" instead of 127.0.0.1
in the test suite. The reason is that lldb-server needs to create two
sockets (v4 and v6) to listen for localhost connections. The algorithm
it uses first tries to select a random port (bind(localhost:0)) for the
first address, and then bind the same port for the second one.

The creating of the second socket can fail as there's no guarantee that
port will be available -- it seems that the (linux) kernel tries to
choose an unused port for the first socket (I've had to create thousands
of sockets to reproduce this reliably), but this can apparantly fail
when the system is under load (and our test suite creates a _lot_ of
sockets).

The socket creationg operation is considered successful if it creates at
least one socket is created, but the test harness has no way of knowing
which one it is, so it can end up connecting to the wrong address.

I'm not aware of a way to atomically create two sockets bound to the
same port. One way to fix this would be to make lldb-server report the
address is it listening on instead of just the port. However, this would
be a breaking change and it's not clear to me that's worth it (the
algorithm works pretty well under normal circumstances).

Instead, this patch sidesteps that problem by using "reverse"
connections. This way, the test harness is responsible for creating the
listening socket so it can pass the address that it has managed to open.
It also results in much simpler code overall.

To preserve test coverage for the named pipe method, I've moved the
relevant code to a dedicated test. To avoid original problem, this test
passes raw addresses (as obtained by getaddrinfo(localhost)) instead of
"localhost".

Differential Revision: https://reviews.llvm.org/D90313
2020-10-29 13:49:51 +01:00

90 lines
2.8 KiB
Python

from __future__ import print_function
import gdbremote_testcase
import select
import socket
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
class TestGdbRemoteConnection(gdbremote_testcase.GdbRemoteTestCaseBase):
mydir = TestBase.compute_mydir(__file__)
@debugserver_test
# <rdar://problem/34539270> lldb-server tests not updated to work on ios etc yet
@skipIfDarwinEmbedded
def test_reverse_connect_debugserver(self):
self.init_debugserver_test()
self._reverse_connect()
@llgs_test
@skipIfRemote # reverse connect is not a supported use case for now
def test_reverse_connect_llgs(self):
self.init_llgs_test()
self._reverse_connect()
def _reverse_connect(self):
# Reverse connect is the default connection method.
self.connect_to_debug_monitor()
# Verify we can do the handshake. If that works, we'll call it good.
self.do_handshake(self.sock)
@debugserver_test
@skipIfRemote
def test_named_pipe_debugserver(self):
self.init_debugserver_test()
self._named_pipe()
@llgs_test
@skipIfRemote
@skipIfWindows
def test_named_pipe_llgs(self):
self.init_llgs_test()
self._named_pipe()
def _named_pipe(self):
family, type, proto, _, addr = socket.getaddrinfo(
self.stub_hostname, 0, proto=socket.IPPROTO_TCP)[0]
self.sock = socket.socket(family, type, proto)
self.sock.settimeout(self.DEFAULT_TIMEOUT)
self.addTearDownHook(lambda: self.sock.close())
named_pipe_path = self.getBuildArtifact("stub_port_number")
# Create the named pipe.
os.mkfifo(named_pipe_path)
# Open the read side of the pipe in non-blocking mode. This will
# return right away, ready or not.
named_pipe_fd = os.open(named_pipe_path, os.O_RDONLY | os.O_NONBLOCK)
self.addTearDownHook(lambda: os.close(named_pipe_fd))
args = self.debug_monitor_extra_args
if lldb.remote_platform:
args += ["*:0"]
else:
args += ["localhost:0"]
args += ["--named-pipe", named_pipe_path]
server = self.spawnSubprocess(
self.debug_monitor_exe,
args,
install_remote=False)
(ready_readers, _, _) = select.select(
[named_pipe_fd], [], [], self.DEFAULT_TIMEOUT)
self.assertIsNotNone(
ready_readers,
"write side of pipe has not written anything - stub isn't writing to pipe.")
port = os.read(named_pipe_fd, 10)
# Trim null byte, convert to int
addr = (addr[0], int(port[:-1]))
self.sock.connect(addr)
# Verify we can do the handshake. If that works, we'll call it good.
self.do_handshake(self.sock)