[Lldb-commits] [lldb] r214031 - llgs: add --reverse-connect support.
Todd Fiala
todd.fiala at gmail.com
Sat Jul 26 13:39:17 PDT 2014
Author: tfiala
Date: Sat Jul 26 15:39:17 2014
New Revision: 214031
URL: http://llvm.org/viewvc/llvm-project?rev=214031&view=rev
Log:
llgs: add --reverse-connect support.
Also includes --reverse-connect tests for llgs and debugserver.
Added:
lldb/trunk/test/tools/lldb-gdbserver/commandline/TestStubReverseConnect.py
Modified:
lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py
lldb/trunk/test/tools/lldb-gdbserver/lldbgdbserverutils.py
lldb/trunk/tools/lldb-gdbserver/lldb-gdbserver.cpp
Added: lldb/trunk/test/tools/lldb-gdbserver/commandline/TestStubReverseConnect.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/tools/lldb-gdbserver/commandline/TestStubReverseConnect.py?rev=214031&view=auto
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/commandline/TestStubReverseConnect.py (added)
+++ lldb/trunk/test/tools/lldb-gdbserver/commandline/TestStubReverseConnect.py Sat Jul 26 15:39:17 2014
@@ -0,0 +1,86 @@
+# Add the directory above ours to the python library path since we
+# will import from there.
+import os.path
+import sys
+sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
+
+import gdbremote_testcase
+import re
+import select
+import socket
+import time
+from lldbtest import *
+
+class TestStubReverseConnect(gdbremote_testcase.GdbRemoteTestCaseBase):
+ _DEFAULT_TIMEOUT = 20
+
+ def setUp(self):
+ # Set up the test.
+ gdbremote_testcase.GdbRemoteTestCaseBase.setUp(self)
+
+ # Create a listener on a local port.
+ self.listener_socket = self.create_listener_socket()
+ self.assertIsNotNone(self.listener_socket)
+ self.listener_port = self.listener_socket.getsockname()[1]
+
+ def create_listener_socket(self, timeout_seconds=_DEFAULT_TIMEOUT):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.assertIsNotNone(sock)
+
+ sock.settimeout(timeout_seconds)
+ sock.bind(("127.0.0.1",0))
+ sock.listen(1)
+
+ def tear_down_listener():
+ try:
+ sock.shutdown(socket.SHUT_RDWR)
+ except:
+ # ignore
+ None
+
+ self.addTearDownHook(tear_down_listener)
+ return sock
+
+ def reverse_connect_works(self):
+ # Indicate stub startup should do a reverse connect.
+ appended_stub_args = " --reverse-connect"
+ if self.debug_monitor_extra_args:
+ self.debug_monitor_extra_args += appended_stub_args
+ else:
+ self.debug_monitor_extra_args = appended_stub_args
+
+ self.stub_hostname = "127.0.0.1"
+ self.port = self.listener_port
+
+ # Start the stub.
+ server = self.launch_debug_monitor(logfile=sys.stdout)
+ self.assertIsNotNone(server)
+ self.assertTrue(server.isalive())
+
+ # Listen for the stub's connection to us.
+ (stub_socket, address) = self.listener_socket.accept()
+ self.assertIsNotNone(stub_socket)
+ self.assertIsNotNone(address)
+ print "connected to stub {} on {}".format(address, stub_socket.getsockname())
+
+ # Verify we can do the handshake. If that works, we'll call it good.
+ self.do_handshake(stub_socket, timeout_seconds=self._DEFAULT_TIMEOUT)
+
+ # Clean up.
+ stub_socket.shutdown(socket.SHUT_RDWR)
+
+ @debugserver_test
+ def test_reverse_connect_works_debugserver(self):
+ self.init_debugserver_test(use_named_pipe=False)
+ self.set_inferior_startup_launch()
+ self.reverse_connect_works()
+
+ @llgs_test
+ def test_reverse_connect_works_llgs(self):
+ self.init_llgs_test(use_named_pipe=False)
+ self.set_inferior_startup_launch()
+ self.reverse_connect_works()
+
+
+if __name__ == '__main__':
+ unittest2.main()
Modified: lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py?rev=214031&r1=214030&r2=214031&view=diff
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py (original)
+++ lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py Sat Jul 26 15:39:17 2014
@@ -8,6 +8,7 @@ import os.path
import platform
import random
import re
+import select
import sets
import signal
import socket
@@ -55,6 +56,7 @@ class GdbRemoteTestCaseBase(TestBase):
self.named_pipe = None
self.named_pipe_fd = None
self.stub_sends_two_stop_notifications_on_kill = False
+ self.stub_hostname = "localhost"
def get_next_port(self):
return 12000 + random.randint(0,3999)
@@ -165,7 +167,7 @@ class GdbRemoteTestCaseBase(TestBase):
self.addTearDownHook(shutdown_socket)
- connect_info = ("localhost", self.port)
+ connect_info = (self.stub_hostname, self.port)
# print "connecting to stub on {}:{}".format(connect_info[0], connect_info[1])
sock.connect(connect_info)
@@ -177,17 +179,21 @@ class GdbRemoteTestCaseBase(TestBase):
def set_inferior_startup_attach(self):
self._inferior_startup = self._STARTUP_ATTACH
- def launch_debug_monitor(self, attach_pid=None):
- # Create the command line.
- import pexpect
+ def get_debug_monitor_command_line(self, attach_pid=None):
commandline = "{}{} localhost:{}".format(self.debug_monitor_exe, self.debug_monitor_extra_args, self.port)
if attach_pid:
commandline += " --attach=%d" % attach_pid
if self.named_pipe_path:
commandline += " --named-pipe %s" % self.named_pipe_path
+ return commandline
+
+ def launch_debug_monitor(self, attach_pid=None, logfile=None):
+ # Create the command line.
+ import pexpect
+ commandline = self.get_debug_monitor_command_line(attach_pid=attach_pid)
# Start the server.
- server = pexpect.spawn(commandline)
+ server = pexpect.spawn(commandline, logfile=logfile)
self.assertIsNotNone(server)
server.expect(r"(debugserver|lldb-gdbserver)", timeout=10)
@@ -332,6 +338,45 @@ class GdbRemoteTestCaseBase(TestBase):
return {"inferior":inferior, "server":server}
+ def expect_socket_recv(self, sock, expected_content_regex, timeout_seconds):
+ response = ""
+ timeout_time = time.time() + timeout_seconds
+
+ while not expected_content_regex.match(response) and time.time() < timeout_time:
+ can_read, _, _ = select.select([sock], [], [], timeout_seconds)
+ if can_read and sock in can_read:
+ recv_bytes = sock.recv(4096)
+ if recv_bytes:
+ response += recv_bytes
+
+ self.assertTrue(expected_content_regex.match(response))
+
+ def expect_socket_send(self, sock, content, timeout_seconds):
+ request_bytes_remaining = content
+ timeout_time = time.time() + timeout_seconds
+
+ while len(request_bytes_remaining) > 0 and time.time() < timeout_time:
+ _, can_write, _ = select.select([], [sock], [], timeout_seconds)
+ if can_write and sock in can_write:
+ written_byte_count = sock.send(request_bytes_remaining)
+ request_bytes_remaining = request_bytes_remaining[written_byte_count:]
+ self.assertEquals(len(request_bytes_remaining), 0)
+
+ def do_handshake(self, stub_socket, timeout_seconds=5):
+ # Write the ack.
+ self.expect_socket_send(stub_socket, "+", timeout_seconds)
+
+ # Send the start no ack mode packet.
+ NO_ACK_MODE_REQUEST = "$QStartNoAckMode#b0"
+ bytes_sent = stub_socket.send(NO_ACK_MODE_REQUEST)
+ self.assertEquals(bytes_sent, len(NO_ACK_MODE_REQUEST))
+
+ # Receive the ack and "OK"
+ self.expect_socket_recv(stub_socket, re.compile(r"^\+\$OK#[0-9a-fA-F]{2}$"), timeout_seconds)
+
+ # Send the final ack.
+ self.expect_socket_send(stub_socket, "+", timeout_seconds)
+
def add_no_ack_remote_stream(self):
self.test_sequence.add_log_lines(
["read packet: +",
Modified: lldb/trunk/test/tools/lldb-gdbserver/lldbgdbserverutils.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/tools/lldb-gdbserver/lldbgdbserverutils.py?rev=214031&r1=214030&r2=214031&view=diff
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/lldbgdbserverutils.py (original)
+++ lldb/trunk/test/tools/lldb-gdbserver/lldbgdbserverutils.py Sat Jul 26 15:39:17 2014
@@ -6,7 +6,6 @@ import os.path
import platform
import Queue
import re
-import select
import socket_packet_pump
import subprocess
import time
Modified: lldb/trunk/tools/lldb-gdbserver/lldb-gdbserver.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/lldb-gdbserver/lldb-gdbserver.cpp?rev=214031&r1=214030&r2=214031&view=diff
==============================================================================
--- lldb/trunk/tools/lldb-gdbserver/lldb-gdbserver.cpp (original)
+++ lldb/trunk/tools/lldb-gdbserver/lldb-gdbserver.cpp Sat Jul 26 15:39:17 2014
@@ -53,9 +53,9 @@ using namespace lldb_private;
namespace
{
- static lldb::thread_t s_listen_thread = LLDB_INVALID_HOST_THREAD;
- static std::unique_ptr<ConnectionFileDescriptor> s_listen_connection_up;
- static std::string s_listen_url;
+ lldb::thread_t s_listen_thread = LLDB_INVALID_HOST_THREAD;
+ std::unique_ptr<ConnectionFileDescriptor> s_listen_connection_up;
+ std::string s_listen_url;
}
//----------------------------------------------------------------------
@@ -76,6 +76,7 @@ static struct option g_long_options[] =
{ "attach", required_argument, NULL, 'a' },
{ "named-pipe", required_argument, NULL, 'P' },
{ "native-regs", no_argument, NULL, 'r' }, // Specify to use the native registers instead of the gdb defaults for the architecture. NOTE: this is a do-nothing arg as it's behavior is default now. FIXME remove call from lldb-platform.
+ { "reverse-connect", no_argument, NULL, 'R' }, // Specifies that llgs attaches to the client address:port rather than llgs listening for a connection from address on port.
{ "setsid", no_argument, NULL, 'S' }, // Call setsid() to make llgs run in its own session.
{ NULL, 0, NULL, 0 }
};
@@ -322,16 +323,17 @@ JoinListenThread ()
}
void
-start_listener (GDBRemoteCommunicationServer &gdb_server, const char *const host_and_port, const char *const progname, const char *const named_pipe_path)
+ConnectToRemote (GDBRemoteCommunicationServer &gdb_server, bool reverse_connect, const char *const host_and_port, const char *const progname, const char *const named_pipe_path)
{
Error error;
if (host_and_port && host_and_port[0])
{
+ // Parse out host and port.
std::string final_host_and_port;
- std::string listening_host;
- std::string listening_port;
- uint32_t listening_portno = 0;
+ std::string connection_host;
+ std::string connection_port;
+ uint32_t connection_portno = 0;
// If host_and_port starts with ':', default the host to be "localhost" and expect the remainder to be the port.
if (host_and_port[0] == ':')
@@ -341,9 +343,9 @@ start_listener (GDBRemoteCommunicationSe
const std::string::size_type colon_pos = final_host_and_port.find (':');
if (colon_pos != std::string::npos)
{
- listening_host = final_host_and_port.substr (0, colon_pos);
- listening_port = final_host_and_port.substr (colon_pos + 1);
- listening_portno = Args::StringToUInt32 (listening_port.c_str (), 0);
+ connection_host = final_host_and_port.substr (0, colon_pos);
+ connection_port = final_host_and_port.substr (colon_pos + 1);
+ connection_portno = Args::StringToUInt32 (connection_port.c_str (), 0);
}
else
{
@@ -352,51 +354,89 @@ start_listener (GDBRemoteCommunicationSe
exit (1);
}
- // Start the listener on a new thread. We need to do this so we can resolve the
- // bound listener port.
- StartListenThread(listening_host.c_str (), static_cast<uint16_t> (listening_portno));
- printf ("Listening to port %s for a connection from %s...\n", listening_port.c_str (), listening_host.c_str ());
-
- // If we have a named pipe to write the port number back to, do that now.
- if (named_pipe_path && named_pipe_path[0] && listening_portno == 0)
+ if (reverse_connect)
{
- // FIXME use new generic named pipe support.
- int fd = ::open(named_pipe_path, O_WRONLY);
- if (fd > -1)
- {
- const uint16_t bound_port = s_listen_connection_up->GetBoundPort (10);
-
- char port_str[64];
- const ssize_t port_str_len = ::snprintf (port_str, sizeof(port_str), "%u", bound_port);
- // Write the port number as a C string with the NULL terminator.
- ::write (fd, port_str, port_str_len + 1);
- close (fd);
- }
- else
+ // llgs will connect to the gdb-remote client.
+
+ // Ensure we have a port number for the connection.
+ if (connection_portno == 0)
{
- fprintf (stderr, "failed to open named pipe '%s' for writing\n", named_pipe_path);
+ fprintf (stderr, "error: port number must be specified on when using reverse connect");
+ exit (1);
}
- }
- // Join the listener thread.
- if (!JoinListenThread ())
- {
- fprintf (stderr, "failed to join the listener thread\n");
- display_usage (progname);
- exit (1);
- }
+ // Build the connection string.
+ char connection_url[512];
+ snprintf(connection_url, sizeof(connection_url), "connect://%s", final_host_and_port.c_str ());
+
+ // Create the connection.
+ std::unique_ptr<ConnectionFileDescriptor> connection_up (new ConnectionFileDescriptor ());
+ connection_up.reset (new ConnectionFileDescriptor ());
+ auto connection_result = connection_up->Connect (connection_url, &error);
+ if (connection_result != eConnectionStatusSuccess)
+ {
+ fprintf (stderr, "error: failed to connect to client at '%s' (connection status: %d)", connection_url, static_cast<int> (connection_result));
+ exit (-1);
+ }
+ if (error.Fail ())
+ {
+ fprintf (stderr, "error: failed to connect to client at '%s': %s", connection_url, error.AsCString ());
+ exit (-1);
+ }
- // Ensure we connected.
- if (s_listen_connection_up)
- {
+ // We're connected.
printf ("Connection established.\n");
- gdb_server.SetConnection (s_listen_connection_up.release());
+ gdb_server.SetConnection (connection_up.release());
}
else
{
- fprintf (stderr, "failed to connect to '%s': %s\n", final_host_and_port.c_str (), error.AsCString ());
- display_usage (progname);
- exit (1);
+ // llgs will listen for connections on the given port from the given address.
+ // Start the listener on a new thread. We need to do this so we can resolve the
+ // bound listener port.
+ StartListenThread(connection_host.c_str (), static_cast<uint16_t> (connection_portno));
+ printf ("Listening to port %s for a connection from %s...\n", connection_port.c_str (), connection_host.c_str ());
+
+ // If we have a named pipe to write the port number back to, do that now.
+ if (named_pipe_path && named_pipe_path[0] && connection_portno == 0)
+ {
+ // FIXME use new generic named pipe support.
+ int fd = ::open(named_pipe_path, O_WRONLY);
+ if (fd > -1)
+ {
+ const uint16_t bound_port = s_listen_connection_up->GetBoundPort (10);
+
+ char port_str[64];
+ const ssize_t port_str_len = ::snprintf (port_str, sizeof(port_str), "%u", bound_port);
+ // Write the port number as a C string with the NULL terminator.
+ ::write (fd, port_str, port_str_len + 1);
+ close (fd);
+ }
+ else
+ {
+ fprintf (stderr, "failed to open named pipe '%s' for writing\n", named_pipe_path);
+ }
+ }
+
+ // Join the listener thread.
+ if (!JoinListenThread ())
+ {
+ fprintf (stderr, "failed to join the listener thread\n");
+ display_usage (progname);
+ exit (1);
+ }
+
+ // Ensure we connected.
+ if (s_listen_connection_up)
+ {
+ printf ("Connection established.\n");
+ gdb_server.SetConnection (s_listen_connection_up.release());
+ }
+ else
+ {
+ fprintf (stderr, "failed to connect to '%s': %s\n", final_host_and_port.c_str (), error.AsCString ());
+ display_usage (progname);
+ exit (1);
+ }
}
}
@@ -411,7 +451,7 @@ start_listener (GDBRemoteCommunicationSe
bool interrupt = false;
bool done = false;
- while (!interrupt && !done && (g_sighup_received_count < 1))
+ while (!interrupt && !done && (g_sighup_received_count < 2))
{
const GDBRemoteCommunication::PacketResult result = gdb_server.GetPacketAndSendResponse (TIMEOUT_USEC, error, interrupt, done);
if ((result != GDBRemoteCommunication::PacketResult::Success) &&
@@ -462,6 +502,7 @@ main (int argc, char *argv[])
std::string platform_name;
std::string attach_target;
std::string named_pipe_path;
+ bool reverse_connect = false;
initialize_lldb_gdbserver ();
@@ -547,6 +588,10 @@ main (int argc, char *argv[])
// Do nothing, native regs is the default these days
break;
+ case 'R':
+ reverse_connect = true;
+ break;
+
#ifndef _WIN32
case 'S':
// Put llgs into a new session. Terminals group processes
@@ -631,7 +676,7 @@ main (int argc, char *argv[])
// Print version info.
printf("%s-%s", LLGS_PROGRAM_NAME, LLGS_VERSION_STR);
- start_listener (gdb_server, host_and_port, progname, named_pipe_path.c_str ());
+ ConnectToRemote (gdb_server, reverse_connect, host_and_port, progname, named_pipe_path.c_str ());
terminate_lldb_gdbserver ();
More information about the lldb-commits
mailing list