[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