diff options
4 files changed, 131 insertions, 99 deletions
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index 719ba027519..8cda558fa5a 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -36,8 +36,8 @@ #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/NativeRegisterContext.h" -#include "../../../Host/common/NativeProcessProtocol.h" -#include "../../../Host/common/NativeThreadProtocol.h" +#include "Host/common/NativeProcessProtocol.h" +#include "Host/common/NativeThreadProtocol.h" // Project includes #include "Utility/StringExtractorGDBRemote.h" @@ -3041,24 +3041,38 @@ GDBRemoteCommunicationServer::Handle_qfThreadInfo (StringExtractorGDBRemote &pac if (!IsGdbServer()) return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_qfThreadInfo() unimplemented"); + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + // Fail if we don't have a current process. if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - return SendErrorResponse (68); + { + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s() no process (%s), returning OK", __FUNCTION__, m_debugged_process_sp ? "invalid process id" : "null m_debugged_process_sp"); + return SendOKResponse (); + } StreamGDBRemote response; response.PutChar ('m'); + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s() starting thread iteration", __FUNCTION__); + NativeThreadProtocolSP thread_sp; uint32_t thread_index; for (thread_index = 0, thread_sp = m_debugged_process_sp->GetThreadAtIndex (thread_index); thread_sp; ++thread_index, thread_sp = m_debugged_process_sp->GetThreadAtIndex (thread_index)) { + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s() iterated thread %" PRIu32 "(%s, tid=0x%" PRIx64 ")", __FUNCTION__, thread_index, thread_sp ? "is not null" : "null", thread_sp ? thread_sp->GetID () : LLDB_INVALID_THREAD_ID); if (thread_index > 0) response.PutChar(','); response.Printf ("%" PRIx64, thread_sp->GetID ()); } + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s() finished thread iteration", __FUNCTION__); + return SendPacketNoLock(response.GetData(), response.GetSize()); } diff --git a/lldb/test/tools/lldb-gdbserver/commandline/TestStubNamedPipe.py b/lldb/test/tools/lldb-gdbserver/commandline/TestStubNamedPipe.py deleted file mode 100644 index af7f22bcf44..00000000000 --- a/lldb/test/tools/lldb-gdbserver/commandline/TestStubNamedPipe.py +++ /dev/null @@ -1,90 +0,0 @@ -import unittest2 - -# 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 os -import select -import tempfile -import time -from lldbtest import * - -class TestStubNamedPipeTestCase(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 - 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 6debf6494a6..fe23b5a5ea5 100644 --- a/lldb/test/tools/lldb-gdbserver/gdbremote_testcase.py +++ b/lldb/test/tools/lldb-gdbserver/gdbremote_testcase.py @@ -3,7 +3,8 @@ Base class for gdb-remote test cases. """ import errno -import unittest2 +import os +import os.path import pexpect import platform import random @@ -13,11 +14,12 @@ import signal import socket import subprocess import sys +import tempfile import time +import unittest2 from lldbtest import * from lldbgdbserverutils import * import logging -import os.path class GdbRemoteTestCaseBase(TestBase): @@ -51,6 +53,8 @@ class GdbRemoteTestCaseBase(TestBase): self.set_inferior_startup_launch() self.port = self.get_next_port() self.named_pipe_path = None + self.named_pipe = None + self.named_pipe_fd = None def get_next_port(self): return 12000 + random.randint(0,3999) @@ -58,17 +62,86 @@ class GdbRemoteTestCaseBase(TestBase): def reset_test_sequence(self): self.test_sequence = GdbRemoteTestSequence(self.logger) - def init_llgs_test(self): + def create_named_pipe(self): + # Create a temp dir and name for a pipe. + temp_dir = tempfile.mkdtemp() + named_pipe_path = os.path.join(temp_dir, "stub_port_number") + + # Create the named pipe. + os.mkfifo(named_pipe_path) + + # Open the read side of the pipe in non-blocking mode. This will return right away, ready or not. + named_pipe_fd = os.open(named_pipe_path, os.O_RDONLY | os.O_NONBLOCK) + + # Create the file for the named pipe. Note this will follow semantics of + # a non-blocking read side of a named pipe, which has different semantics + # than a named pipe opened for read in non-blocking mode. + named_pipe = os.fdopen(named_pipe_fd, "r") + self.assertIsNotNone(named_pipe) + + def shutdown_named_pipe(): + # Close the pipe. + try: + named_pipe.close() + except: + print "failed to close named pipe" + None + + # Delete the pipe. + try: + os.remove(named_pipe_path) + except: + print "failed to delete named pipe: {}".format(named_pipe_path) + None + + # Delete the temp directory. + try: + os.rmdir(temp_dir) + except: + print "failed to delete temp dir: {}, directory contents: '{}'".format(temp_dir, os.listdir(temp_dir)) + None + + # Add the shutdown hook to clean up the named pipe. + self.addTearDownHook(shutdown_named_pipe) + + # Clear the port so the stub selects a port number. + self.port = 0 + + return (named_pipe_path, named_pipe, named_pipe_fd) + + def get_stub_port_from_named_socket(self, read_timeout_seconds=5): + # Wait for something to read with a max timeout. + (ready_readers, _, _) = select.select([self.named_pipe_fd], [], [], read_timeout_seconds) + 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.") + + # Read the port from the named pipe. + stub_port_raw = self.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) + + return stub_port + + def init_llgs_test(self, use_named_pipe=True): self.debug_monitor_exe = get_lldb_gdbserver_exe() if not self.debug_monitor_exe: self.skipTest("lldb_gdbserver exe not found") self.debug_monitor_extra_args = " -c 'log enable -T -f process-{}.log lldb break process thread' -c 'log enable -T -f packets-{}.log gdb-remote packets'".format(self.id(), self.id(), self.id()) + if use_named_pipe: + (self.named_pipe_path, self.named_pipe, self.named_pipe_fd) = self.create_named_pipe() - def init_debugserver_test(self): + def init_debugserver_test(self, use_named_pipe=True): self.debug_monitor_exe = get_debugserver_exe() if not self.debug_monitor_exe: self.skipTest("debugserver exe not found") self.debug_monitor_extra_args = " --log-file=/tmp/packets-{}.log --log-flags=0x800000".format(self._testMethodName) + if use_named_pipe: + (self.named_pipe_path, self.named_pipe, self.named_pipe_fd) = self.create_named_pipe() def create_socket(self): sock = socket.socket() @@ -89,7 +162,10 @@ class GdbRemoteTestCaseBase(TestBase): self.addTearDownHook(shutdown_socket) - sock.connect(('localhost', self.port)) + connect_info = ("localhost", self.port) + # print "connecting to stub on {}:{}".format(connect_info[0], connect_info[1]) + sock.connect(connect_info) + return sock def set_inferior_startup_launch(self): @@ -108,6 +184,13 @@ class GdbRemoteTestCaseBase(TestBase): # Start the server. server = pexpect.spawn(commandline) + self.assertIsNotNone(server) + server.expect(r"(debugserver|lldb-gdbserver)", timeout=10) + + # If we're receiving the stub's listening port from the named pipe, do that here. + if self.named_pipe: + self.port = self.get_stub_port_from_named_socket() + # print "debug server listening on {}".format(self.port) # Turn on logging for what the child sends back. if self.TraceOn(): @@ -116,6 +199,27 @@ class GdbRemoteTestCaseBase(TestBase): return server def connect_to_debug_monitor(self, attach_pid=None): + if self.named_pipe: + # Create the stub. + server = self.launch_debug_monitor(attach_pid=attach_pid) + self.assertIsNotNone(server) + + def shutdown_debug_monitor(): + try: + server.close() + except: + logger.warning("failed to close pexpect server for debug monitor: {}; ignoring".format(sys.exc_info()[0])) + self.addTearDownHook(shutdown_debug_monitor) + + # Schedule debug monitor to be shut down during teardown. + logger = self.logger + + # Attach to the stub and return a socket opened to it. + self.sock = self.create_socket() + return server + + # We're using a random port algorithm to try not to collide with other ports, + # and retry a max # times. attempts = 0 MAX_ATTEMPTS = 20 diff --git a/lldb/test/tools/lldb-gdbserver/lldbgdbserverutils.py b/lldb/test/tools/lldb-gdbserver/lldbgdbserverutils.py index 87194f8a90f..d89107e449e 100644 --- a/lldb/test/tools/lldb-gdbserver/lldbgdbserverutils.py +++ b/lldb/test/tools/lldb-gdbserver/lldbgdbserverutils.py @@ -195,7 +195,11 @@ def expect_lldb_gdbserver_replay( # This is an entry to send to the remote debug monitor. send_packet = sequence_entry.get_send_packet() if logger: - logger.info("sending packet to remote: %s" % send_packet) + if len(send_packet) == 1 and send_packet[0] == chr(3): + packet_desc = "^C" + else: + packet_desc = send_packet + logger.info("sending packet to remote: {}".format(packet_desc)) sock.sendall(send_packet) else: # This is an entry expecting to receive content from the remote debug monitor. |