Index: lldb/trunk/test/lldbtest.py =================================================================== --- lldb/trunk/test/lldbtest.py +++ lldb/trunk/test/lldbtest.py @@ -269,10 +269,14 @@ if self._proc.poll() == None: self._proc.terminate() + def poll(self): + return self._proc.poll() + class _RemoteProcess(_BaseProcess): - def __init__(self): + def __init__(self, install_remote): self._pid = None + self._install_remote = install_remote @property def pid(self): @@ -280,14 +284,18 @@ def launch(self, executable, args): remote_work_dir = lldb.remote_platform.GetWorkingDirectory() - src_path = executable - dst_path = os.path.join(remote_work_dir, os.path.basename(executable)) - dst_file_spec = lldb.SBFileSpec(dst_path, False) - err = lldb.remote_platform.Install(lldb.SBFileSpec(src_path, True), - dst_file_spec) - if err.Fail(): - raise Exception("remote_platform.Install('%s', '%s') failed: %s" % (src_path, dst_path, err)) + if self._install_remote: + src_path = executable + dst_path = os.path.join(remote_work_dir, os.path.basename(executable)) + + dst_file_spec = lldb.SBFileSpec(dst_path, False) + err = lldb.remote_platform.Install(lldb.SBFileSpec(src_path, True), dst_file_spec) + if err.Fail(): + raise Exception("remote_platform.Install('%s', '%s') failed: %s" % (src_path, dst_path, err)) + else: + dst_path = executable + dst_file_spec = lldb.SBFileSpec(executable, False) launch_info = lldb.SBLaunchInfo(args) launch_info.SetExecutableFile(dst_file_spec, True) @@ -303,9 +311,7 @@ self._pid = launch_info.GetProcessID() def terminate(self): - err = lldb.remote_platform.Kill(self._pid) - if err.Fail(): - raise Exception("remote_platform.Kill(%d) failed: %s" % (self._pid, err)) + lldb.remote_platform.Kill(self._pid) # From 2.7's subprocess.check_output() convenience function. # Return a tuple (stdoutdata, stderrdata). @@ -1047,7 +1053,7 @@ if os.path.exists("/proc/" + str(pid)): os.kill(pid, signal.SIGTERM) - def spawnSubprocess(self, executable, args=[]): + def spawnSubprocess(self, executable, args=[], install_remote=True): """ Creates a subprocess.Popen object with the specified executable and arguments, saves it in self.subprocesses, and returns the object. NOTE: if using this function, ensure you also call: @@ -1056,7 +1062,7 @@ otherwise the test suite will leak processes. """ - proc = _RemoteProcess() if lldb.remote_platform else _LocalProcess(self.TraceOn()) + proc = _RemoteProcess(install_remote) if lldb.remote_platform else _LocalProcess(self.TraceOn()) proc.launch(executable, args) self.subprocesses.append(proc) return proc Index: lldb/trunk/test/tools/lldb-server/TestGdbRemoteKill.py =================================================================== --- lldb/trunk/test/tools/lldb-server/TestGdbRemoteKill.py +++ lldb/trunk/test/tools/lldb-server/TestGdbRemoteKill.py @@ -24,9 +24,10 @@ # Wait a moment for completed and now-detached inferior process to clear. time.sleep(1) - # Process should be dead now. Reap results. - poll_result = procs["inferior"].poll() - self.assertIsNotNone(poll_result) + if not lldb.remote_platform: + # Process should be dead now. Reap results. + poll_result = procs["inferior"].poll() + self.assertIsNotNone(poll_result) # Where possible, verify at the system level that the process is not running. self.assertFalse(lldbgdbserverutils.process_is_running(procs["inferior"].pid, False)) Index: lldb/trunk/test/tools/lldb-server/TestLldbGdbServer.py =================================================================== --- lldb/trunk/test/tools/lldb-server/TestLldbGdbServer.py +++ lldb/trunk/test/tools/lldb-server/TestLldbGdbServer.py @@ -89,12 +89,24 @@ self.init_llgs_test() self.list_threads_in_stop_reply_supported() + def install_and_create_launch_args(self): + exe_path = os.path.abspath('a.out') + if not lldb.remote_platform: + return [exe_path] + remote_work_dir = lldb.remote_platform.GetWorkingDirectory() + remote_path = os.path.join(remote_work_dir, os.path.basename(exe_path)) + remote_file_spec = lldb.SBFileSpec(remote_path, False) + err = lldb.remote_platform.Install(lldb.SBFileSpec(exe_path, True), remote_file_spec) + if err.Fail(): + raise Exception("remote_platform.Install('%s', '%s') failed: %s" % (exe_path, remote_path, err)) + return [remote_path] + def start_inferior(self): + launch_args = self.install_and_create_launch_args() + server = self.connect_to_debug_monitor() self.assertIsNotNone(server) - # build launch args - launch_args = [os.path.abspath('a.out')] self.add_no_ack_remote_stream() self.test_sequence.add_log_lines( ["read packet: %s" % lldbgdbserverutils.build_gdbremote_A_packet(launch_args), @@ -117,12 +129,11 @@ self.start_inferior() def inferior_exit_0(self): + launch_args = self.install_and_create_launch_args() + server = self.connect_to_debug_monitor() self.assertIsNotNone(server) - # build launch args - launch_args = [os.path.abspath('a.out')] - self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.test_sequence.add_log_lines( @@ -147,13 +158,15 @@ self.inferior_exit_0() def inferior_exit_42(self): + launch_args = self.install_and_create_launch_args() + server = self.connect_to_debug_monitor() self.assertIsNotNone(server) RETVAL = 42 # build launch args - launch_args = [os.path.abspath('a.out'), "retval:%d" % RETVAL] + launch_args += ["retval:%d" % RETVAL] self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) @@ -179,12 +192,11 @@ self.inferior_exit_42() def c_packet_works(self): + launch_args = self.install_and_create_launch_args() + server = self.connect_to_debug_monitor() self.assertIsNotNone(server) - # build launch args - launch_args = [os.path.abspath('a.out')] - self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.test_sequence.add_log_lines( @@ -209,11 +221,13 @@ self.c_packet_works() def inferior_print_exit(self): + launch_args = self.install_and_create_launch_args() + server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # build launch args - launch_args = [os.path.abspath('a.out'), "hello, world"] + launch_args += ["hello, world"] self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) @@ -241,11 +255,13 @@ self.inferior_print_exit() def first_launch_stop_reply_thread_matches_first_qC(self): + launch_args = self.install_and_create_launch_args() + server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # build launch args - launch_args = [os.path.abspath('a.out'), "hello, world"] + launch_args += ["hello, world"] self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) @@ -282,9 +298,10 @@ # Wait a moment for completed and now-detached inferior process to clear. time.sleep(1) - # Process should be dead now. Reap results. - poll_result = procs["inferior"].poll() - self.assertIsNotNone(poll_result) + if not lldb.remote_platform: + # Process should be dead now. Reap results. + poll_result = procs["inferior"].poll() + self.assertIsNotNone(poll_result) # Where possible, verify at the system level that the process is not running. self.assertFalse(lldbgdbserverutils.process_is_running(procs["inferior"].pid, False)) @@ -306,12 +323,11 @@ self.attach_commandline_continue_app_exits() def qRegisterInfo_returns_one_valid_result(self): + launch_args = self.install_and_create_launch_args() + server = self.connect_to_debug_monitor() self.assertIsNotNone(server) - # Build launch args - launch_args = [os.path.abspath('a.out')] - # Build the expected protocol stream self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) @@ -343,12 +359,11 @@ self.qRegisterInfo_returns_one_valid_result() def qRegisterInfo_returns_all_valid_results(self): + launch_args = self.install_and_create_launch_args() + server = self.connect_to_debug_monitor() self.assertIsNotNone(server) - # Build launch args. - launch_args = [os.path.abspath('a.out')] - # Build the expected protocol stream. self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) @@ -377,12 +392,11 @@ self.qRegisterInfo_returns_all_valid_results() def qRegisterInfo_contains_required_generics(self): + launch_args = self.install_and_create_launch_args() + server = self.connect_to_debug_monitor() self.assertIsNotNone(server) - # Build launch args - launch_args = [os.path.abspath('a.out')] - # Build the expected protocol stream self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) @@ -425,12 +439,11 @@ self.qRegisterInfo_contains_required_generics() def qRegisterInfo_contains_at_least_one_register_set(self): + launch_args = self.install_and_create_launch_args() + server = self.connect_to_debug_monitor() self.assertIsNotNone(server) - # Build launch args - launch_args = [os.path.abspath('a.out')] - # Build the expected protocol stream self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) @@ -462,12 +475,11 @@ self.qRegisterInfo_contains_at_least_one_register_set() def qRegisterInfo_contains_avx_registers_on_linux_x86_64(self): + launch_args = self.install_and_create_launch_args() + server = self.connect_to_debug_monitor() self.assertIsNotNone(server) - # Build launch args - launch_args = [os.path.abspath('a.out')] - # Build the expected protocol stream self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) Index: lldb/trunk/test/tools/lldb-server/commandline/TestStubReverseConnect.py =================================================================== --- lldb/trunk/test/tools/lldb-server/commandline/TestStubReverseConnect.py +++ lldb/trunk/test/tools/lldb-server/commandline/TestStubReverseConnect.py @@ -5,6 +5,7 @@ sys.path.append(os.path.join(os.path.dirname(__file__), "..")) import gdbremote_testcase +import lldbgdbserverutils import re import select import socket @@ -43,7 +44,7 @@ def reverse_connect_works(self): # Indicate stub startup should do a reverse connect. - appended_stub_args = " --reverse-connect" + appended_stub_args = ["--reverse-connect"] if self.debug_monitor_extra_args: self.debug_monitor_extra_args += appended_stub_args else: @@ -55,7 +56,7 @@ # Start the stub. server = self.launch_debug_monitor(logfile=sys.stdout) self.assertIsNotNone(server) - self.assertTrue(server.isalive()) + self.assertTrue(lldbgdbserverutils.process_is_running(server.pid, True)) # Listen for the stub's connection to us. (stub_socket, address) = self.listener_socket.accept() Index: lldb/trunk/test/tools/lldb-server/commandline/TestStubSetSID.py =================================================================== --- lldb/trunk/test/tools/lldb-server/commandline/TestStubSetSID.py +++ lldb/trunk/test/tools/lldb-server/commandline/TestStubSetSID.py @@ -7,29 +7,22 @@ sys.path.append(os.path.join(os.path.dirname(__file__), "..")) import gdbremote_testcase +import lldbgdbserverutils import os import select import tempfile import time from lldbtest import * - -def get_common_stub_args(): - return [] if 'darwin' in sys.platform else ['g'] - - class TestStubSetSIDTestCase(gdbremote_testcase.GdbRemoteTestCaseBase): def get_stub_sid(self, extra_stub_args=None): # Launch debugserver if extra_stub_args: - self.debug_monitor_extra_args = extra_stub_args - else: - self.debug_monitor_extra_args = "" + self.debug_monitor_extra_args += extra_stub_args server = self.launch_debug_monitor() self.assertIsNotNone(server) - self.assertTrue(server.isalive()) - server.expect("(debugserver|lldb-server)", timeout=10) + self.assertTrue(lldbgdbserverutils.process_is_running(server.pid, True)) # Get the process id for the stub. return os.getsid(server.pid) @@ -39,11 +32,11 @@ self.assertEquals(stub_sid, os.getsid(0)) def sid_is_different_with_setsid(self): - stub_sid = self.get_stub_sid(" %s --setsid" % ' '.join(get_common_stub_args())) + stub_sid = self.get_stub_sid(["--setsid"]) self.assertNotEquals(stub_sid, os.getsid(0)) def sid_is_different_with_S(self): - stub_sid = self.get_stub_sid(" %s -S" % ' '.join(get_common_stub_args())) + stub_sid = self.get_stub_sid(["-S"]) self.assertNotEquals(stub_sid, os.getsid(0)) @debugserver_test Index: lldb/trunk/test/tools/lldb-server/gdbremote_testcase.py =================================================================== --- lldb/trunk/test/tools/lldb-server/gdbremote_testcase.py +++ lldb/trunk/test/tools/lldb-server/gdbremote_testcase.py @@ -134,12 +134,31 @@ return stub_port def init_llgs_test(self, use_named_pipe=True): - self.debug_monitor_exe = get_lldb_server_exe() - if not self.debug_monitor_exe: - self.skipTest("lldb-server exe not found") - dname = os.path.join(os.environ["LLDB_TEST"], - os.environ["LLDB_SESSION_DIRNAME"]) - self.debug_monitor_extra_args = " gdbserver -c 'log enable -T -f {}/process-{}.log lldb break process thread' -c 'log enable -T -f {}/packets-{}.log gdb-remote packets'".format(dname, self.id(), dname, self.id()) + if lldb.remote_platform: + # Remote platforms don't support named pipe based port negotiation + use_named_pipe = False + + platform = self.dbg.GetSelectedPlatform() + + shell_command = lldb.SBPlatformShellCommand("echo $PPID") + err = platform.Run(shell_command) + if err.Fail(): + raise Exception("remote_platform.RunShellCommand('echo $PPID') failed: %s" % err) + pid = shell_command.GetOutput().strip() + + shell_command = lldb.SBPlatformShellCommand("readlink /proc/%s/exe" % pid) + err = platform.Run(shell_command) + if err.Fail(): + raise Exception("remote_platform.RunShellCommand('readlink /proc/%d/exe') failed: %s" % (pid, err)) + self.debug_monitor_exe = shell_command.GetOutput().strip() + dname = self.dbg.GetSelectedPlatform().GetWorkingDirectory() + else: + self.debug_monitor_exe = get_lldb_server_exe() + if not self.debug_monitor_exe: + self.skipTest("lldb-server exe not found") + dname = os.path.join(os.environ["LLDB_TEST"], os.environ["LLDB_SESSION_DIRNAME"]) + + self.debug_monitor_extra_args = ["gdbserver", "-c", "log enable -T -f {}/process-{}.log lldb break process thread".format(dname, self.id()), "-c", "log enable -T -f {}/packets-{}.log gdb-remote packets".format(dname, self.id())] if use_named_pipe: (self.named_pipe_path, self.named_pipe, self.named_pipe_fd) = self.create_named_pipe() @@ -147,7 +166,7 @@ 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) + self.debug_monitor_extra_args = ["--log-file=/tmp/packets-{}.log".format(self._testMethodName), "--log-flags=0x800000"] if use_named_pipe: (self.named_pipe_path, self.named_pipe, self.named_pipe_fd) = self.create_named_pipe() # The debugserver stub has a race on handling the 'k' command, so it sends an X09 right away, then sends the real X notification @@ -173,6 +192,14 @@ self.addTearDownHook(shutdown_socket) + triple = self.dbg.GetSelectedPlatform().GetTriple() + if re.match(".*-.*-.*-android", triple): + subprocess.call(["adb", "forward", "tcp:%d" % self.port, "tcp:%d" % self.port]) + def remove_port_forward(): + subprocess.call(["adb", "forward", "--remove", "tcp:%d" % self.port]) + + self.addTearDownHook(remove_port_forward) + connect_info = (self.stub_hostname, self.port) # print "connecting to stub on {}:{}".format(connect_info[0], connect_info[1]) sock.connect(connect_info) @@ -188,32 +215,32 @@ def set_inferior_startup_attach_manually(self): self._inferior_startup = self._STARTUP_ATTACH_MANUALLY - def get_debug_monitor_command_line(self, attach_pid=None): - commandline = "{}{} localhost:{}".format(self.debug_monitor_exe, self.debug_monitor_extra_args, self.port) + def get_debug_monitor_command_line_args(self, attach_pid=None): + commandline_args = self.debug_monitor_extra_args + ["localhost:{}".format(self.port)] if attach_pid: - commandline += " --attach=%d" % attach_pid + commandline_args += ["--attach=%d" % attach_pid] if self.named_pipe_path: - commandline += " --named-pipe %s" % self.named_pipe_path - return commandline + commandline_args += ["--named-pipe", self.named_pipe_path] + return commandline_args + + def run_platform_command(self, cmd): + platform = self.dbg.GetSelectedPlatform() + shell_command = lldb.SBPlatformShellCommand(cmd) + err = platform.Run(shell_command) + return (err, shell_command.GetOutput()) 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) + commandline_args = self.get_debug_monitor_command_line_args(attach_pid=attach_pid) # Start the server. - server = pexpect.spawn(commandline, logfile=logfile) + server = self.spawnSubprocess(self.debug_monitor_exe, commandline_args, install_remote=False) + self.addTearDownHook(self.cleanupSubprocesses) self.assertIsNotNone(server) - server.expect(r"(debugserver|lldb-server)", 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(): - server.logfile_read = sys.stdout return server @@ -225,7 +252,7 @@ def shutdown_debug_monitor(): try: - server.close() + server.terminate() except: logger.warning("failed to close pexpect server for debug monitor: {}; ignoring".format(sys.exc_info()[0])) self.addTearDownHook(shutdown_debug_monitor) @@ -245,34 +272,25 @@ while attempts < MAX_ATTEMPTS: server = self.launch_debug_monitor(attach_pid=attach_pid) - # Wait until we receive the server ready message before continuing. - port_good = True - try: - server.expect_exact('Listening to port {} for a connection from localhost'.format(self.port)) - except: - port_good = False - server.close() - - if port_good: - # Schedule debug monitor to be shut down during teardown. - logger = self.logger - 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) - - # Create a socket to talk to the server + # Schedule debug monitor to be shut down during teardown. + logger = self.logger + def shutdown_debug_monitor(): try: - self.sock = self.create_socket() - return server - except socket.error as serr: - # We're only trying to handle connection refused. - if serr.errno != errno.ECONNREFUSED: - raise serr - # We should close the server here to be safe. - server.close() + server.terminate() + except: + logger.warning("failed to terminate server for debug monitor: {}; ignoring".format(sys.exc_info()[0])) + self.addTearDownHook(shutdown_debug_monitor) + + # Create a socket to talk to the server + try: + self.sock = self.create_socket() + return server + except socket.error as serr: + # We're only trying to handle connection refused. + if serr.errno != errno.ECONNREFUSED: + raise serr + # We should close the server here to be safe. + server.terminate() # Increment attempts. print("connect to debug monitor on port %d failed, attempt #%d of %d" % (self.port, attempts + 1, MAX_ATTEMPTS)) @@ -286,20 +304,20 @@ raise Exception("failed to create a socket to the launched debug monitor after %d tries" % attempts) - def launch_process_for_attach(self,inferior_args=None, sleep_seconds=3, exe_path=None): + def launch_process_for_attach(self, inferior_args=None, sleep_seconds=3, exe_path=None): # We're going to start a child process that the debug monitor stub can later attach to. # This process needs to be started so that it just hangs around for a while. We'll # have it sleep. if not exe_path: exe_path = os.path.abspath("a.out") - args = [exe_path] + args = [] if inferior_args: args.extend(inferior_args) if sleep_seconds: args.append("sleep:%d" % sleep_seconds) - return subprocess.Popen(args) + return self.spawnSubprocess(exe_path, args) def prep_debug_monitor_and_inferior(self, inferior_args=None, inferior_sleep_seconds=3, inferior_exe_path=None): """Prep the debug monitor, the inferior, and the expected packet stream. @@ -333,18 +351,28 @@ # In this case, we want the stub to attach via the command line, so set the command line attach pid here. attach_pid = inferior.pid - # Launch the debug monitor stub, attaching to the inferior. - server = self.connect_to_debug_monitor(attach_pid=attach_pid) - self.assertIsNotNone(server) - if self._inferior_startup == self._STARTUP_LAUNCH: # Build launch args if not inferior_exe_path: inferior_exe_path = os.path.abspath("a.out") + + if lldb.remote_platform: + remote_work_dir = lldb.remote_platform.GetWorkingDirectory() + remote_path = os.path.join(remote_work_dir, os.path.basename(inferior_exe_path)) + remote_file_spec = lldb.SBFileSpec(remote_path, False) + err = lldb.remote_platform.Install(lldb.SBFileSpec(inferior_exe_path, True), remote_file_spec) + if err.Fail(): + raise Exception("remote_platform.Install('%s', '%s') failed: %s" % (inferior_exe_path, remote_path, err)) + inferior_exe_path = remote_path + launch_args = [inferior_exe_path] if inferior_args: launch_args.extend(inferior_args) + # Launch the debug monitor stub, attaching to the inferior. + server = self.connect_to_debug_monitor(attach_pid=attach_pid) + self.assertIsNotNone(server) + # Build the expected protocol stream self.add_no_ack_remote_stream() if self._inferior_startup == self._STARTUP_LAUNCH: Index: lldb/trunk/test/tools/lldb-server/lldbgdbserverutils.py =================================================================== --- lldb/trunk/test/tools/lldb-server/lldbgdbserverutils.py +++ lldb/trunk/test/tools/lldb-server/lldbgdbserverutils.py @@ -9,6 +9,7 @@ import socket_packet_pump import subprocess import time +from lldbtest import * def _get_debug_monitor_from_lldb(lldb_exe, debug_monitor_basename): """Return the debug monitor exe path given the lldb exe path. @@ -808,12 +809,16 @@ If we don't know how to check running process ids on the given OS: return the value provided by the unknown_value arg. """ - if type(pid) != int: - raise Exception("pid must be of type int") + if type(pid) not in [int, long]: + raise Exception("pid must be of type int (actual type: %s)" % str(type(pid))) process_ids = [] - if platform.system() in ['Darwin', 'Linux', 'FreeBSD', 'NetBSD']: + if lldb.remote_platform: + # Don't know how to get list of running process IDs on a remote + # platform + return unknown_value + elif platform.system() in ['Darwin', 'Linux', 'FreeBSD', 'NetBSD']: # Build the list of running process ids output = subprocess.check_output("ps ax | awk '{ print $1; }'", shell=True) text_process_ids = output.split('\n')[1:]