[Lldb-commits] [lldb] r212854 - Added llgs --named-pipe support and program_name-version_number printout support.
Todd Fiala
todd.fiala at gmail.com
Fri Jul 11 15:50:13 PDT 2014
Author: tfiala
Date: Fri Jul 11 17:50:13 2014
New Revision: 212854
URL: http://llvm.org/viewvc/llvm-project?rev=212854&view=rev
Log:
Added llgs --named-pipe support and program_name-version_number printout support.
Added a unit test to test debugserver and llgs compliance on --named-pipe support.
Modified llgs to implement --named-pipe support. (Note: need to revisit with
new generic pipe support).
Added:
lldb/trunk/test/tools/lldb-gdbserver/TestStubNamedPipe.py
Modified:
lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py
lldb/trunk/tools/lldb-gdbserver/lldb-gdbserver.cpp
Added: lldb/trunk/test/tools/lldb-gdbserver/TestStubNamedPipe.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/tools/lldb-gdbserver/TestStubNamedPipe.py?rev=212854&view=auto
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/TestStubNamedPipe.py (added)
+++ lldb/trunk/test/tools/lldb-gdbserver/TestStubNamedPipe.py Fri Jul 11 17:50:13 2014
@@ -0,0 +1,87 @@
+import unittest2
+
+import gdbremote_testcase
+import os
+import os.path
+import select
+import tempfile
+import time
+from lldbtest import *
+
+class TestStubNamedPipe(gdbremote_testcase.GdbRemoteTestCaseBase):
+ def create_named_pipe(self):
+ temp_dir = tempfile.mkdtemp()
+ named_pipe_path = os.path.join(temp_dir, "stub_port_number")
+ try:
+ os.mkfifo(named_pipe_path)
+ except OSError, e:
+ # print "Failed to create named pipe: %s" % e
+ raise e
+ return named_pipe_path
+
+ def get_port_from_named_pipe(self):
+ # Set port to 0
+ self.port = 0
+
+ # Don't turn on any kind of logging
+ self.debug_monitor_extra_args = ""
+
+ # Create the named pipe that we're reading on.
+ self.named_pipe_path = self.create_named_pipe()
+ self.assertIsNotNone(self.named_pipe_path)
+ # print "using named pipe:{}".format(self.named_pipe_path)
+ try:
+ # print "launching server..."
+ server = self.launch_debug_monitor()
+ # print "server launched..."
+ self.assertIsNotNone(server)
+ self.assertTrue(server.isalive())
+ server.expect("(debugserver|lldb-gdbserver)", timeout=10)
+
+ # print "about to open named pipe..."
+ # Open the read side of the pipe in non-blocking mode. This will return right away, ready or not.
+ fd = os.open(self.named_pipe_path, os.O_RDONLY | os.O_NONBLOCK)
+ named_pipe = os.fdopen(fd, "r")
+ self.assertIsNotNone(named_pipe)
+
+ # print "waiting on content from the named pipe..."
+ # Wait for something to read with a max timeout.
+ (ready_readers, _, _) = select.select([fd], [], [], 5)
+ self.assertIsNotNone(ready_readers, "write side of pipe has not written anything - stub isn't writing to pipe.")
+ self.assertNotEqual(len(ready_readers), 0, "write side of pipe has not written anything - stub isn't writing to pipe.")
+
+ try:
+ # Read the port from the named pipe.
+ stub_port_raw = named_pipe.read()
+ self.assertIsNotNone(stub_port_raw)
+ self.assertNotEqual(len(stub_port_raw), 0, "no content to read on pipe")
+
+ # Trim null byte, convert to int.
+ stub_port_raw = stub_port_raw[:-1]
+ stub_port = int(stub_port_raw)
+ self.assertTrue(stub_port > 0)
+ finally:
+ named_pipe.close()
+ # print "stub is listening on port: {} (from text '{}')".format(stub_port, stub_port_raw)
+ finally:
+ temp_dir = os.path.dirname(self.named_pipe_path)
+ try:
+ os.remove(self.named_pipe_path)
+ except:
+ # Not required.
+ None
+ os.rmdir(temp_dir)
+
+ @debugserver_test
+ def test_get_port_from_named_pipe_debugserver(self):
+ self.init_debugserver_test()
+ self.set_inferior_startup_launch()
+ self.get_port_from_named_pipe()
+
+ @llgs_test
+ @dwarf_test
+ # @unittest2.expectedFailure()
+ def test_get_port_from_named_pipe_llgs(self):
+ self.init_llgs_test()
+ self.set_inferior_startup_launch()
+ self.get_port_from_named_pipe()
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=212854&r1=212853&r2=212854&view=diff
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py (original)
+++ lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py Fri Jul 11 17:50:13 2014
@@ -50,6 +50,7 @@ class GdbRemoteTestCaseBase(TestBase):
self.test_sequence = GdbRemoteTestSequence(self.logger)
self.set_inferior_startup_launch()
self.port = self.get_next_port()
+ self.named_pipe_path = None
def get_next_port(self):
return 12000 + random.randint(0,3999)
@@ -102,6 +103,8 @@ class GdbRemoteTestCaseBase(TestBase):
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
# Start the server.
server = pexpect.spawn(commandline)
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=212854&r1=212853&r2=212854&view=diff
==============================================================================
--- lldb/trunk/tools/lldb-gdbserver/lldb-gdbserver.cpp (original)
+++ lldb/trunk/tools/lldb-gdbserver/lldb-gdbserver.cpp Fri Jul 11 17:50:13 2014
@@ -34,9 +34,26 @@
#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h"
#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+#ifndef LLGS_PROGRAM_NAME
+#define LLGS_PROGRAM_NAME "lldb-gdbserver"
+#endif
+
+#ifndef LLGS_VERSION_STR
+#define LLGS_VERSION_STR "local_build"
+#endif
+
using namespace lldb;
using namespace lldb_private;
+// lldb-gdbserver state
+
+namespace
+{
+ static lldb::tid_t s_listen_thread = LLDB_INVALID_HOST_THREAD;
+ static std::unique_ptr<ConnectionFileDescriptor> s_listen_connection_up;
+ static std::string s_listen_url;
+}
+
//----------------------------------------------------------------------
// option descriptors for getopt_long_only()
//----------------------------------------------------------------------
@@ -53,6 +70,7 @@ static struct option g_long_options[] =
{ "log-file", required_argument, NULL, 'l' },
{ "log-flags", required_argument, NULL, 'f' },
{ "attach", required_argument, NULL, 'a' },
+ { "named-pipe", required_argument, NULL, 'P' },
{ NULL, 0, NULL, 0 }
};
@@ -254,82 +272,165 @@ handle_launch (GDBRemoteCommunicationSer
}
}
+static lldb::thread_result_t
+ListenThread (lldb::thread_arg_t /* arg */)
+{
+ Error error;
+
+ if (s_listen_connection_up)
+ {
+ // Do the listen on another thread so we can continue on...
+ if (s_listen_connection_up->Connect(s_listen_url.c_str(), &error) != eConnectionStatusSuccess)
+ s_listen_connection_up.reset();
+ }
+ return nullptr;
+}
+
+static Error
+StartListenThread (const char *hostname, uint16_t port)
+{
+ Error error;
+ if (IS_VALID_LLDB_HOST_THREAD(s_listen_thread))
+ {
+ error.SetErrorString("listen thread already running");
+ }
+ else
+ {
+ char listen_url[512];
+ if (hostname && hostname[0])
+ snprintf(listen_url, sizeof(listen_url), "listen://%s:%i", hostname, port);
+ else
+ snprintf(listen_url, sizeof(listen_url), "listen://%i", port);
+
+ s_listen_url = listen_url;
+ s_listen_connection_up.reset (new ConnectionFileDescriptor ());
+ s_listen_thread = Host::ThreadCreate (listen_url, ListenThread, nullptr, &error);
+ }
+ return error;
+}
+
+static bool
+JoinListenThread ()
+{
+ if (IS_VALID_LLDB_HOST_THREAD(s_listen_thread))
+ {
+ Host::ThreadJoin(s_listen_thread, nullptr, nullptr);
+ s_listen_thread = LLDB_INVALID_HOST_THREAD;
+ }
+ return true;
+}
+
void
-start_listener (GDBRemoteCommunicationServer &gdb_server, const char *const host_and_port, const char *const progname)
+start_listener (GDBRemoteCommunicationServer &gdb_server, 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])
{
- std::unique_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor());
- if (conn_ap.get())
- {
- std::string final_host_and_port;
- std::string listening_host;
- std::string listening_port;
-
- // 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] == ':')
- final_host_and_port.append ("localhost");
- final_host_and_port.append (host_and_port);
+ std::string final_host_and_port;
+ std::string listening_host;
+ std::string listening_port;
+ uint32_t listening_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] == ':')
+ final_host_and_port.append ("localhost");
+ final_host_and_port.append (host_and_port);
- 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);
- }
+ 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);
+ }
+ else
+ {
+ fprintf (stderr, "failed to parse host and port from connection string '%s'\n", final_host_and_port.c_str ());
+ display_usage (progname);
+ exit (1);
+ }
- std::string connect_url ("listen://");
- connect_url.append (final_host_and_port);
+ // 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 ());
- printf ("Listening to port %s for a connection from %s...\n", listening_port.c_str (), listening_host.c_str ());
- if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess)
+ // 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)
+ {
+ // FIXME use new generic named pipe support.
+ int fd = ::open(named_pipe_path, O_WRONLY);
+ if (fd > -1)
{
- printf ("Connection established.\n");
- gdb_server.SetConnection (conn_ap.release());
+ 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 connect to '%s': %s\n", final_host_and_port.c_str (), error.AsCString ());
- display_usage (progname);
- exit (1);
+ fprintf (stderr, "failed to open named pipe '%s' for writing\n", named_pipe_path);
}
}
- if (gdb_server.IsConnected())
+ // Join the listener thread.
+ if (!JoinListenThread ())
{
- // After we connected, we need to get an initial ack from...
- if (gdb_server.HandshakeWithClient(&error))
- {
- // We'll use a half a second timeout interval so that an exit conditions can
- // be checked that often.
- const uint32_t TIMEOUT_USEC = 500000;
-
- bool interrupt = false;
- bool done = false;
- while (!interrupt && !done && (g_sighup_received_count < 1))
- {
- const GDBRemoteCommunication::PacketResult result = gdb_server.GetPacketAndSendResponse (TIMEOUT_USEC, error, interrupt, done);
- if ((result != GDBRemoteCommunication::PacketResult::Success) &&
- (result != GDBRemoteCommunication::PacketResult::ErrorReplyTimeout))
- {
- // We're bailing out - we only support successful handling and timeouts.
- fprintf(stderr, "leaving packet loop due to PacketResult %d\n", result);
- break;
- }
- }
+ 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);
+ }
+ }
- if (error.Fail())
+ if (gdb_server.IsConnected())
+ {
+ // After we connected, we need to get an initial ack from...
+ if (gdb_server.HandshakeWithClient(&error))
+ {
+ // We'll use a half a second timeout interval so that an exit conditions can
+ // be checked that often.
+ const uint32_t TIMEOUT_USEC = 500000;
+
+ bool interrupt = false;
+ bool done = false;
+ while (!interrupt && !done && (g_sighup_received_count < 1))
+ {
+ const GDBRemoteCommunication::PacketResult result = gdb_server.GetPacketAndSendResponse (TIMEOUT_USEC, error, interrupt, done);
+ if ((result != GDBRemoteCommunication::PacketResult::Success) &&
+ (result != GDBRemoteCommunication::PacketResult::ErrorReplyTimeout))
{
- fprintf(stderr, "error: %s\n", error.AsCString());
+ // We're bailing out - we only support successful handling and timeouts.
+ fprintf(stderr, "leaving packet loop due to PacketResult %d\n", result);
+ break;
}
}
- else
+
+ if (error.Fail())
{
- fprintf(stderr, "error: handshake with client failed\n");
+ fprintf(stderr, "error: %s\n", error.AsCString());
}
}
+ else
+ {
+ fprintf(stderr, "error: handshake with client failed\n");
+ }
}
else
{
@@ -357,6 +458,7 @@ main (int argc, char *argv[])
int ch;
std::string platform_name;
std::string attach_target;
+ std::string named_pipe_path;
initialize_lldb_gdbserver ();
@@ -433,6 +535,11 @@ main (int argc, char *argv[])
platform_name = optarg;
break;
+ case 'P': // named pipe
+ if (optarg && optarg[0])
+ named_pipe_path = optarg;
+ break;
+
case 'a': // attach {pid|process_name}
if (optarg && optarg[0])
attach_target = optarg;
@@ -490,7 +597,10 @@ main (int argc, char *argv[])
else if (argc > 0)
handle_launch (gdb_server, argc, argv);
- start_listener (gdb_server, host_and_port, progname);
+ // 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 ());
terminate_lldb_gdbserver ();
More information about the lldb-commits
mailing list