diff options
| author | Todd Fiala <todd.fiala@gmail.com> | 2014-07-11 22:50:13 +0000 |
|---|---|---|
| committer | Todd Fiala <todd.fiala@gmail.com> | 2014-07-11 22:50:13 +0000 |
| commit | 67041194b9b3ea424054cd0cf2f66fc14fa99cac (patch) | |
| tree | 6a19b139feec6b3f5b85044ccdeb982b40933cba /lldb | |
| parent | a39390699c2b1752840d9995325423997ff9799a (diff) | |
| download | bcm5719-llvm-67041194b9b3ea424054cd0cf2f66fc14fa99cac.tar.gz bcm5719-llvm-67041194b9b3ea424054cd0cf2f66fc14fa99cac.zip | |
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).
llvm-svn: 212854
Diffstat (limited to 'lldb')
| -rw-r--r-- | lldb/test/tools/lldb-gdbserver/TestStubNamedPipe.py | 87 | ||||
| -rw-r--r-- | lldb/test/tools/lldb-gdbserver/gdbremote_testcase.py | 3 | ||||
| -rw-r--r-- | lldb/tools/lldb-gdbserver/lldb-gdbserver.cpp | 212 |
3 files changed, 251 insertions, 51 deletions
diff --git a/lldb/test/tools/lldb-gdbserver/TestStubNamedPipe.py b/lldb/test/tools/lldb-gdbserver/TestStubNamedPipe.py new file mode 100644 index 00000000000..93b7241e7f8 --- /dev/null +++ b/lldb/test/tools/lldb-gdbserver/TestStubNamedPipe.py @@ -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() diff --git a/lldb/test/tools/lldb-gdbserver/gdbremote_testcase.py b/lldb/test/tools/lldb-gdbserver/gdbremote_testcase.py index 96e941b244e..6debf6494a6 100644 --- a/lldb/test/tools/lldb-gdbserver/gdbremote_testcase.py +++ b/lldb/test/tools/lldb-gdbserver/gdbremote_testcase.py @@ -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) diff --git a/lldb/tools/lldb-gdbserver/lldb-gdbserver.cpp b/lldb/tools/lldb-gdbserver/lldb-gdbserver.cpp index 1e9808c0148..51a5234f8aa 100644 --- a/lldb/tools/lldb-gdbserver/lldb-gdbserver.cpp +++ b/lldb/tools/lldb-gdbserver/lldb-gdbserver.cpp @@ -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 (GDBRemoteCommunicationServer &gdb_server, int argc, const char *c } } +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; + 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); + listening_portno = Args::StringToUInt32 (listening_port.c_str (), 0); + } + else { - std::string final_host_and_port; - std::string listening_host; - std::string listening_port; + fprintf (stderr, "failed to parse host and port from connection string '%s'\n", final_host_and_port.c_str ()); + display_usage (progname); + exit (1); + } - // 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); + // 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 ()); - const std::string::size_type colon_pos = final_host_and_port.find(':'); - if (colon_pos != std::string::npos) + // 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) { - listening_host = final_host_and_port.substr(0, colon_pos); - listening_port = final_host_and_port.substr(colon_pos + 1); - } - - std::string connect_url ("listen://"); - connect_url.append (final_host_and_port); + const uint16_t bound_port = s_listen_connection_up->GetBoundPort (10); - 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) - { - printf ("Connection established.\n"); - gdb_server.SetConnection (conn_ap.release()); + 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; + fprintf (stderr, "failed to join the listener thread\n"); + display_usage (progname); + exit (1); + } - 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; - } - } + // 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 (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; - if (error.Fail()) + 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 (); |

