David Spickett 98e87f76d0 [lldb] Error when there are no ports to launch a gdbserver on
Previously if you did:
$ lldb-server platform --server <...> --min-gdbserver-port 12346
--max-gdbserver-port 12347
(meaning only use port 12346 for gdbservers)

Then tried to launch two gdbservers on the same connection,
the second one would return port 65535. Which is a real port
number but it actually means lldb-server didn't find one it was
allowed to use.

send packet: $qLaunchGDBServer;<...>
read packet: $pid:1919;port:12346;#c0
<...>
send packet: $qLaunchGDBServer;<...>
read packet: $pid:1927;port:65535;#c7

This situation should be an error even if port 65535 does happen
to be available on the current machine.

To fix this make PortMap it's own class within
GDBRemoteCommunicationServerPlatform.

This almost the same as the old typedef but for
GetNextAvailablePort() returning an llvm::Expected.
This means we have to handle not finding a port,
by returning an error packet.

Also add unit tests for this new PortMap class.

Reviewed By: labath

Differential Revision: https://reviews.llvm.org/D91634
2020-11-30 10:19:14 +00:00

116 lines
3.8 KiB
C++

//===-- PortMapTest.cpp ---------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h"
using namespace lldb_private::process_gdb_remote;
TEST(PortMapTest, Constructors) {
// Default construct to empty map
GDBRemoteCommunicationServerPlatform::PortMap p1;
ASSERT_TRUE(p1.empty());
// Empty means no restrictions, return 0 and and bind to get a port
llvm::Expected<uint16_t> available_port = p1.GetNextAvailablePort();
ASSERT_THAT_EXPECTED(available_port, llvm::HasValue(0));
// Adding any port makes it not empty
p1.AllowPort(1);
ASSERT_FALSE(p1.empty());
// So we will return the added port this time
available_port = p1.GetNextAvailablePort();
ASSERT_THAT_EXPECTED(available_port, llvm::HasValue(1));
// Construct from a range of ports
GDBRemoteCommunicationServerPlatform::PortMap p2(1, 4);
ASSERT_FALSE(p2.empty());
// Use up all the ports
for (uint16_t expected = 1; expected < 4; ++expected) {
available_port = p2.GetNextAvailablePort();
ASSERT_THAT_EXPECTED(available_port, llvm::HasValue(expected));
p2.AssociatePortWithProcess(*available_port, 1);
}
// Now we fail since we're not an empty port map but all ports are used
available_port = p2.GetNextAvailablePort();
ASSERT_THAT_EXPECTED(available_port, llvm::Failed());
}
TEST(PortMapTest, FreePort) {
GDBRemoteCommunicationServerPlatform::PortMap p(1, 4);
// Use up all the ports
for (uint16_t port = 1; port < 4; ++port) {
p.AssociatePortWithProcess(port, 1);
}
llvm::Expected<uint16_t> available_port = p.GetNextAvailablePort();
ASSERT_THAT_EXPECTED(available_port, llvm::Failed());
// Can't free a port that isn't in the map
ASSERT_FALSE(p.FreePort(0));
ASSERT_FALSE(p.FreePort(4));
// After freeing a port it becomes available
ASSERT_TRUE(p.FreePort(2));
available_port = p.GetNextAvailablePort();
ASSERT_THAT_EXPECTED(available_port, llvm::HasValue(2));
}
TEST(PortMapTest, FreePortForProcess) {
GDBRemoteCommunicationServerPlatform::PortMap p;
p.AllowPort(1);
p.AllowPort(2);
ASSERT_TRUE(p.AssociatePortWithProcess(1, 11));
ASSERT_TRUE(p.AssociatePortWithProcess(2, 22));
// All ports have been used
llvm::Expected<uint16_t> available_port = p.GetNextAvailablePort();
ASSERT_THAT_EXPECTED(available_port, llvm::Failed());
// Can't free a port for a process that doesn't have any
ASSERT_FALSE(p.FreePortForProcess(33));
// You can move a used port to a new pid
ASSERT_TRUE(p.AssociatePortWithProcess(1, 99));
ASSERT_TRUE(p.FreePortForProcess(22));
available_port = p.GetNextAvailablePort();
ASSERT_THAT_EXPECTED(available_port, llvm::Succeeded());
ASSERT_EQ(2, *available_port);
// proces 22 no longer has a port
ASSERT_FALSE(p.FreePortForProcess(22));
}
TEST(PortMapTest, AllowPort) {
GDBRemoteCommunicationServerPlatform::PortMap p;
// Allow port 1 and tie it to process 11
p.AllowPort(1);
ASSERT_TRUE(p.AssociatePortWithProcess(1, 11));
// Allowing it a second time shouldn't change existing mapping
p.AllowPort(1);
llvm::Expected<uint16_t> available_port = p.GetNextAvailablePort();
ASSERT_THAT_EXPECTED(available_port, llvm::Failed());
// A new port is marked as free when allowed
p.AllowPort(2);
available_port = p.GetNextAvailablePort();
ASSERT_THAT_EXPECTED(available_port, llvm::HasValue(2));
// 11 should still be tied to port 1
ASSERT_TRUE(p.FreePortForProcess(11));
}