From 3fd43b5bf4850f187989d944ac39cd6b6fb5ed59 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Wed, 28 Oct 2015 11:56:13 -0700 Subject: [PATCH 1/5] Allow selection of adb command. This is useful for ndk-gdb.py on Windows. Change-Id: I2a2b6b50ae00bcfc530c555f06c945b4f53f9999 --- python-packages/adb/device.py | 47 +++++++++++------------ python-packages/gdbrunner/__init__.py | 54 +++++++++++++++------------ 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/python-packages/adb/device.py b/python-packages/adb/device.py index ceb263d69..fad920b3d 100644 --- a/python-packages/adb/device.py +++ b/python-packages/adb/device.py @@ -48,11 +48,11 @@ class ShellError(RuntimeError): self.exit_code = exit_code -def get_devices(): +def get_devices(adb_path='adb'): with open(os.devnull, 'wb') as devnull: - subprocess.check_call(['adb', 'start-server'], stdout=devnull, + subprocess.check_call([adb_path, 'start-server'], stdout=devnull, stderr=devnull) - out = subprocess.check_output(['adb', 'devices']).splitlines() + out = subprocess.check_output([adb_path, 'devices']).splitlines() # The first line of `adb devices` just says "List of attached devices", so # skip that. @@ -68,21 +68,21 @@ def get_devices(): return devices -def _get_unique_device(product=None): - devices = get_devices() +def _get_unique_device(product=None, adb_path='adb'): + devices = get_devices(adb_path=adb_path) if len(devices) != 1: raise NoUniqueDeviceError() - return AndroidDevice(devices[0], product) + return AndroidDevice(devices[0], product, adb_path) -def _get_device_by_serial(serial, product=None): - for device in get_devices(): +def _get_device_by_serial(serial, product=None, adb_path='adb'): + for device in get_devices(adb_path=adb_path): if device == serial: - return AndroidDevice(serial, product) + return AndroidDevice(serial, product, adb_path) raise DeviceNotFoundError(serial) -def get_device(serial=None, product=None): +def get_device(serial=None, product=None, adb_path='adb'): """Get a uniquely identified AndroidDevice if one is available. Raises: @@ -104,29 +104,29 @@ def get_device(serial=None, product=None): 3) The single device connnected to the system. """ if serial is not None: - return _get_device_by_serial(serial, product) + return _get_device_by_serial(serial, product, adb_path) android_serial = os.getenv('ANDROID_SERIAL') if android_serial is not None: - return _get_device_by_serial(android_serial, product) + return _get_device_by_serial(android_serial, product, adb_path) - return _get_unique_device(product) + return _get_unique_device(product, adb_path=adb_path) -def _get_device_by_type(flag): +def _get_device_by_type(flag, adb_path): with open(os.devnull, 'wb') as devnull: - subprocess.check_call(['adb', 'start-server'], stdout=devnull, + subprocess.check_call([adb_path, 'start-server'], stdout=devnull, stderr=devnull) try: - serial = subprocess.check_output(['adb', flag, 'get-serialno']).strip() + serial = subprocess.check_output([adb_path, flag, 'get-serialno']).strip() except subprocess.CalledProcessError: raise RuntimeError('adb unexpectedly returned nonzero') if serial == 'unknown': raise NoUniqueDeviceError() - return _get_device_by_serial(serial) + return _get_device_by_serial(serial, adb_path=adb_path) -def get_usb_device(): +def get_usb_device(adb_path='adb'): """Get the unique USB-connected AndroidDevice if it is available. Raises: @@ -136,10 +136,10 @@ def get_usb_device(): Returns: An AndroidDevice associated with the unique USB-connected device. """ - return _get_device_by_type('-d') + return _get_device_by_type('-d', adb_path=adb_path) -def get_emulator_device(): +def get_emulator_device(adb_path='adb'): """Get the unique emulator AndroidDevice if it is available. Raises: @@ -149,7 +149,7 @@ def get_emulator_device(): Returns: An AndroidDevice associated with the unique running emulator. """ - return _get_device_by_type('-e') + return _get_device_by_type('-e', adb_path=adb_path) @contextlib.contextmanager @@ -248,10 +248,11 @@ class AndroidDevice(object): # Feature name strings. SHELL_PROTOCOL_FEATURE = 'shell_v2' - def __init__(self, serial, product=None): + def __init__(self, serial, product=None, adb_path='adb'): self.serial = serial self.product = product - self.adb_cmd = ['adb'] + self.adb_cmd = [adb_path] + if self.serial is not None: self.adb_cmd.extend(['-s', serial]) if self.product is not None: diff --git a/python-packages/gdbrunner/__init__.py b/python-packages/gdbrunner/__init__.py index 379409e4e..b01ef6b91 100644 --- a/python-packages/gdbrunner/__init__.py +++ b/python-packages/gdbrunner/__init__.py @@ -21,49 +21,57 @@ import argparse import atexit import os import subprocess +import sys import tempfile class ArgumentParser(argparse.ArgumentParser): """ArgumentParser subclass that provides adb device selection.""" - class DeviceAction(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - if option_string is None: - raise RuntimeError("DeviceAction called without option_string") - elif option_string == "-a": - # Handled in parse_args - return - elif option_string == "-d": - namespace.device = adb.get_usb_device() - elif option_string == "-e": - namespace.device = adb.get_emulator_device() - elif option_string == "-s": - namespace.device = adb.get_device(values[0]) - else: - raise RuntimeError("Unexpected flag {}".format(option_string)) - def __init__(self): super(ArgumentParser, self).__init__() + self.add_argument( + "--adb", dest="adb_path", + help="Use specific adb command") + group = self.add_argument_group(title="device selection") group = group.add_mutually_exclusive_group() group.add_argument( - "-a", nargs=0, action=self.DeviceAction, + "-a", action="store_const", dest="device", const="-a", help="directs commands to all interfaces") group.add_argument( - "-d", nargs=0, action=self.DeviceAction, + "-d", action="store_const", dest="device", const="-d", help="directs commands to the only connected USB device") group.add_argument( - "-e", nargs=0, action=self.DeviceAction, + "-e", action="store_const", dest="device", const="-e", help="directs commands to the only connected emulator") group.add_argument( - "-s", nargs=1, metavar="SERIAL", action=self.DeviceAction, + "-s", metavar="SERIAL", action="store", dest="serial", help="directs commands to device/emulator with the given serial") def parse_args(self, args=None, namespace=None): result = super(ArgumentParser, self).parse_args(args, namespace) - # Default to -a behavior if no flags are given. - if "device" not in result: - result.device = adb.get_device() + + adb_path = result.adb_path or "adb" + + # Try to run the specified adb command + try: + subprocess.check_output([adb_path, "version"], + stderr=subprocess.STDOUT) + except (OSError, subprocess.CalledProcessError): + msg = "ERROR: Unable to run adb executable (tried '{}')." + if not result.adb_path: + msg += "\n Try specifying its location with --adb." + sys.exit(msg.format(adb_path)) + + if result.device == "-a": + result.device = adb.get_device(adb_path=adb_path) + elif result.device == "-d": + result.device = adb.get_usb_device(adb_path=adb_path) + elif result.device == "-e": + result.device = adb.get_emulator_device(adb_path=adb_path) + else: + result.device = adb.get_device(result.serial, adb_path=adb_path) + return result From 44b84a8e030c26280fbc92c2c9e67750147294e1 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Wed, 28 Oct 2015 11:57:37 -0700 Subject: [PATCH 2/5] Don't throw when we fail to find a device. Instead, set it to None and let the caller handle things. Change-Id: Ic4f27fdb52ebd6bc5b4048e4e688ec7bd2509bfa --- python-packages/gdbrunner/__init__.py | 20 ++++++++++++-------- scripts/gdbclient.py | 4 ++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/python-packages/gdbrunner/__init__.py b/python-packages/gdbrunner/__init__.py index b01ef6b91..bd2b72dd1 100644 --- a/python-packages/gdbrunner/__init__.py +++ b/python-packages/gdbrunner/__init__.py @@ -63,14 +63,18 @@ class ArgumentParser(argparse.ArgumentParser): msg += "\n Try specifying its location with --adb." sys.exit(msg.format(adb_path)) - if result.device == "-a": - result.device = adb.get_device(adb_path=adb_path) - elif result.device == "-d": - result.device = adb.get_usb_device(adb_path=adb_path) - elif result.device == "-e": - result.device = adb.get_emulator_device(adb_path=adb_path) - else: - result.device = adb.get_device(result.serial, adb_path=adb_path) + try: + if result.device == "-a": + result.device = adb.get_device(adb_path=adb_path) + elif result.device == "-d": + result.device = adb.get_usb_device(adb_path=adb_path) + elif result.device == "-e": + result.device = adb.get_emulator_device(adb_path=adb_path) + else: + result.device = adb.get_device(result.serial, adb_path=adb_path) + except (adb.DeviceNotFoundError, adb.NoUniqueDeviceError, RuntimeError): + # Don't error out if we can't find a device. + result.device = None return result diff --git a/scripts/gdbclient.py b/scripts/gdbclient.py index 4993ad590..4fb6c7e71 100755 --- a/scripts/gdbclient.py +++ b/scripts/gdbclient.py @@ -210,6 +210,10 @@ end def main(): args = parse_args() device = args.device + + if device is None: + sys.exit("ERROR: Failed to find device.") + props = device.get_props() root = os.environ["ANDROID_BUILD_TOP"] From aacbc2318d15557671a664508eaa736baf260243 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Tue, 27 Oct 2015 16:45:55 -0700 Subject: [PATCH 3/5] Add get_pids helper to gdbrunner. Change-Id: Id685bf740bde382c62a02fa4d6f03717bae91f50 --- python-packages/gdbrunner/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python-packages/gdbrunner/__init__.py b/python-packages/gdbrunner/__init__.py index bd2b72dd1..564df0429 100644 --- a/python-packages/gdbrunner/__init__.py +++ b/python-packages/gdbrunner/__init__.py @@ -133,6 +133,11 @@ def get_processes(device): return processes +def get_pids(device, process_name): + processes = get_processes(device) + return processes.get(process_name, []) + + def start_gdbserver(device, gdbserver_local_path, gdbserver_remote_path, target_pid, run_cmd, debug_socket, port, user=None): """Start gdbserver in the background and forward necessary ports. From 2c7e952f1cf1a7f28c99d8834d9b08dce920a65e Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Tue, 27 Oct 2015 16:46:15 -0700 Subject: [PATCH 4/5] Allow gdbrunner.start_gdbserver to not upload gdbserver. Change-Id: I59239c3989ccd91755407e65b0d2f8183482564c --- python-packages/gdbrunner/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python-packages/gdbrunner/__init__.py b/python-packages/gdbrunner/__init__.py index 564df0429..5ece37547 100644 --- a/python-packages/gdbrunner/__init__.py +++ b/python-packages/gdbrunner/__init__.py @@ -144,7 +144,7 @@ def start_gdbserver(device, gdbserver_local_path, gdbserver_remote_path, Args: device: ADB device to start gdbserver on. - gdbserver_local_path: Host path to push gdbserver from. + gdbserver_local_path: Host path to push gdbserver from, can be None. gdbserver_remote_path: Device path to push gdbserver to. target_pid: PID of device process to attach to. run_cmd: Command to run on the device. @@ -159,7 +159,8 @@ def start_gdbserver(device, gdbserver_local_path, gdbserver_remote_path, assert target_pid is None or run_cmd is None # Push gdbserver to the target. - device.push(gdbserver_local_path, gdbserver_remote_path) + if gdbserver_local_path is not None: + device.push(gdbserver_local_path, gdbserver_remote_path) # Run gdbserver. gdbserver_cmd = [gdbserver_remote_path, "--once", From acc0ba81cc0eea91562220d9f34e267909926305 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Wed, 28 Oct 2015 15:13:16 -0700 Subject: [PATCH 5/5] Add a gdb_flags argument to gdbrunner.start_gdb. Change-Id: I35aae1290f97a33c55e9bddd2b986a7c3e93d1df --- python-packages/gdbrunner/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python-packages/gdbrunner/__init__.py b/python-packages/gdbrunner/__init__.py index 5ece37547..493a4e71f 100644 --- a/python-packages/gdbrunner/__init__.py +++ b/python-packages/gdbrunner/__init__.py @@ -282,18 +282,19 @@ def get_binary_arch(binary_file): raise RuntimeError("unknown architecture: 0x{:x}".format(e_machine)) -def start_gdb(gdb_path, gdb_commands): +def start_gdb(gdb_path, gdb_commands, gdb_flags=None): """Start gdb in the background and block until it finishes. Args: gdb_path: Path of the gdb binary. gdb_commands: Contents of GDB script to run. + gdb_flags: List of flags to append to gdb command. """ with tempfile.NamedTemporaryFile() as gdb_script: gdb_script.write(gdb_commands) gdb_script.flush() - gdb_args = [gdb_path, "-x", gdb_script.name] + gdb_args = [gdb_path, "-x", gdb_script.name] + (gdb_flags or []) gdb_process = subprocess.Popen(gdb_args) while gdb_process.returncode is None: try: