diff options
-rwxr-xr-x | build/android/buildbot/bb_device_status_check.py | 88 | ||||
-rw-r--r-- | build/android/devil/__init__.py | 3 | ||||
-rw-r--r-- | build/android/devil/utils/__init__.py | 3 | ||||
-rwxr-xr-x | build/android/devil/utils/reset_usb.py | 163 | ||||
-rw-r--r-- | build/android/pylib/device/adb_wrapper.py | 13 |
5 files changed, 200 insertions, 70 deletions
diff --git a/build/android/buildbot/bb_device_status_check.py b/build/android/buildbot/bb_device_status_check.py index 917c51e..7098154 100755 --- a/build/android/buildbot/bb_device_status_check.py +++ b/build/android/buildbot/bb_device_status_check.py @@ -26,7 +26,8 @@ sys.path.append(os.path.join(os.path.dirname(__file__), 'common')) import perf_tests_results_helper # pylint: disable=F0401 -sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +from devil.utils import reset_usb from pylib import constants from pylib.cmd_helper import GetCmdOutput from pylib.device import adb_wrapper @@ -197,40 +198,6 @@ def SendEmail(from_address, to_addresses, cc_addresses, subject, msg): logging.exception('Failed to send alert email.') -def RestartUsb(): - if not os.path.isfile('/usr/bin/restart_usb'): - logging.error('Could not restart usb. ''/usr/bin/restart_usb not ' - 'installed on host (see BUG=305769).') - return False - - lsusb_proc = bb_utils.SpawnCmd(['lsusb'], stdout=subprocess.PIPE) - lsusb_output, _ = lsusb_proc.communicate() - if lsusb_proc.returncode: - logging.error('Could not get list of USB ports (i.e. lsusb).') - return lsusb_proc.returncode - - usb_devices = [re.findall(r'Bus (\d\d\d) Device (\d\d\d)', lsusb_line)[0] - for lsusb_line in lsusb_output.strip().split('\n')] - - all_restarted = True - # Walk USB devices from leaves up (i.e reverse sorted) restarting the - # connection. If a parent node (e.g. usb hub) is restarted before the - # devices connected to it, the (bus, dev) for the hub can change, making the - # output we have wrong. This way we restart the devices before the hub. - for (bus, dev) in reversed(sorted(usb_devices)): - # Can not restart root usb connections - if dev != '001': - return_code = bb_utils.RunCmd(['/usr/bin/restart_usb', bus, dev]) - if return_code: - logging.error('Error restarting USB device /dev/bus/usb/%s/%s', - bus, dev) - all_restarted = False - else: - logging.info('Restarted USB device /dev/bus/usb/%s/%s', bus, dev) - - return all_restarted - - def KillAllAdb(): def GetAllAdb(): for p in psutil.process_iter(): @@ -266,7 +233,8 @@ def main(): parser.add_option('--device-status-dashboard', action='store_true', help='Output device status data for dashboard.') parser.add_option('--restart-usb', action='store_true', - help='Restart USB ports before running device check.') + help='DEPRECATED. ' + 'This script now always tries to reset USB.') parser.add_option('--json-output', help='Output JSON information into a specified file.') parser.add_option('-v', '--verbose', action='count', default=1, @@ -281,39 +249,27 @@ def main(): # Remove the last build's "bad devices" before checking device statuses. device_blacklist.ResetBlacklist() + KillAllAdb() + reset_usb.reset_all_usb() + try: - expected_devices = device_list.GetPersistentDeviceList( - os.path.join(options.out_dir, device_list.LAST_DEVICES_FILENAME)) + expected_devices = set(device_list.GetPersistentDeviceList( + os.path.join(options.out_dir, device_list.LAST_DEVICES_FILENAME))) except IOError: - expected_devices = [] + expected_devices = set() devices = device_utils.DeviceUtils.HealthyDevices() - device_serials = [d.adb.GetDeviceSerial() for d in devices] - # Only restart usb if devices are missing. - if set(expected_devices) != set(device_serials): - logging.warning('expected_devices: %s', expected_devices) - logging.warning('devices: %s', device_serials) - KillAllAdb() - retries = 5 - usb_restarted = True - if options.restart_usb: - if not RestartUsb(): - usb_restarted = False - bb_annotations.PrintWarning() - logging.error('USB reset stage failed, ' - 'wait for any device to come back.') - while retries: - logging.info('retry adb devices...') - time.sleep(1) - devices = device_utils.DeviceUtils.HealthyDevices() - device_serials = [d.adb.GetDeviceSerial() for d in devices] - if set(expected_devices) == set(device_serials): - # All devices are online, keep going. - break - if not usb_restarted and devices: - # The USB wasn't restarted, but there's at least one device online. - # No point in trying to wait for all devices. - break - retries -= 1 + device_serials = set(d.adb.GetDeviceSerial() for d in devices) + + missing_devices = expected_devices.difference(device_serials) + new_devices = device_serials.difference(expected_devices) + + if missing_devices or new_devices: + logging.warning('expected_devices:') + for d in sorted(expected_devices): + logging.warning(' %s', d) + logging.warning('devices:') + for d in sorted(device_serials): + logging.warning(' %s', d) types, builds, batteries, errors, devices_ok, json_data = ( [], [], [], [], [], []) diff --git a/build/android/devil/__init__.py b/build/android/devil/__init__.py new file mode 100644 index 0000000..50b23df --- /dev/null +++ b/build/android/devil/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. diff --git a/build/android/devil/utils/__init__.py b/build/android/devil/utils/__init__.py new file mode 100644 index 0000000..50b23df --- /dev/null +++ b/build/android/devil/utils/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. diff --git a/build/android/devil/utils/reset_usb.py b/build/android/devil/utils/reset_usb.py new file mode 100755 index 0000000..97facc2 --- /dev/null +++ b/build/android/devil/utils/reset_usb.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import fcntl +import logging +import re +import sys + +from pylib import cmd_helper +from pylib.device import adb_wrapper +from pylib.device import device_errors +from pylib.utils import run_tests_helper + +_INDENTATION_RE = re.compile(r'^( *)') +_LSUSB_BUS_DEVICE_RE = re.compile(r'^Bus (\d{3}) Device (\d{3}):') +_LSUSB_ENTRY_RE = re.compile(r'^ *([^ ]+) +([^ ]+) *([^ ].*)?$') +_LSUSB_GROUP_RE = re.compile(r'^ *([^ ]+.*):$') + +_USBDEVFS_RESET = ord('U') << 8 | 20 + + +def reset_usb(bus, device): + """Reset the USB device with the given bus and device.""" + usb_file_path = '/dev/bus/usb/%03d/%03d' % (bus, device) + with open(usb_file_path, 'w') as usb_file: + logging.debug('fcntl.ioctl(%s, %d)', usb_file_path, _USBDEVFS_RESET) + fcntl.ioctl(usb_file, _USBDEVFS_RESET) + + +def reset_android_usb(serial): + """Reset the USB device for the given Android device.""" + lsusb_info = lsusb() + + bus = None + device = None + for device_info in lsusb_info: + device_serial = _get_lsusb_serial(device) + if device_serial == serial: + bus = int(device_info.get('bus')) + device = int(device_info.get('device')) + + if bus and device: + reset_usb(bus, device) + else: + raise device_errors.DeviceUnreachableError( + 'Unable to determine bus or device for device %s' % serial) + + +def reset_all_usb(): + """Reset all non-root USB devices.""" + lsusb_info = lsusb() + for device_info in lsusb_info: + if int(device_info.get('device')) != 1: + bus = int(device_info.get('bus')) + device = int(device_info.get('device')) + try: + reset_usb(bus, device) + serial = _get_lsusb_serial(device_info) + if serial: + logging.info('Reset USB device (bus: %03d, device: %03d, serial: %s)', + bus, device, serial) + else: + logging.info('Reset USB device (bus: %03d, device: %03d)', + bus, device) + except IOError: + logging.error( + 'Failed to reset USB device (bus: %03d, device: %03d)', + bus, device) + + +def lsusb(): + """Call lsusb and return the parsed output.""" + lsusb_raw_output = cmd_helper.GetCmdOutput(['lsusb', '-v']) + device = None + devices = [] + depth_stack = [] + for line in lsusb_raw_output.splitlines(): + if not line: + if device: + devices.append(device) + device = None + continue + + if not device: + m = _LSUSB_BUS_DEVICE_RE.match(line) + if m: + device = { + 'bus': m.group(1), + 'device': m.group(2) + } + depth_stack = [device] + continue + + indent_match = _INDENTATION_RE.match(line) + if not indent_match: + continue + + depth = 1 + len(indent_match.group(1)) / 2 + if depth > len(depth_stack): + logging.error('lsusb parsing error: unexpected indentation: "%s"', line) + continue + + while depth < len(depth_stack): + depth_stack.pop() + + cur = depth_stack[-1] + + m = _LSUSB_GROUP_RE.match(line) + if m: + new_group = {} + cur[m.group(1)] = new_group + depth_stack.append(new_group) + continue + + m = _LSUSB_ENTRY_RE.match(line) + if m: + new_entry = { + '_value': m.group(2), + '_desc': m.group(3), + } + cur[m.group(1)] = new_entry + depth_stack.append(new_entry) + continue + + logging.error('lsusb parsing error: unrecognized line: "%s"', line) + + if device: + devices.append(device) + + return devices + + +def _get_lsusb_serial(device): + return device.get('Device Descriptor', {}).get('iSerial', {}).get('_desc') + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-v', '--verbose', action='count') + parser.add_argument('-s', '--serial') + parser.add_argument('--bus', type=int) + parser.add_argument('--device', type=int) + args = parser.parse_args() + + run_tests_helper.SetLogLevel(args.verbose) + + if args.serial: + reset_android_usb(args.serial) + elif args.bus and args.device: + reset_usb(args.bus, args.device) + else: + parser.error('Unable to determine target. ' + 'Specify --serial or BOTH --bus and --device.') + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) + diff --git a/build/android/pylib/device/adb_wrapper.py b/build/android/pylib/device/adb_wrapper.py index e897326..9ecb6bc 100644 --- a/build/android/pylib/device/adb_wrapper.py +++ b/build/android/pylib/device/adb_wrapper.py @@ -170,23 +170,28 @@ class AdbWrapper(object): return cls.Devices(timeout=timeout, retries=retries) @classmethod - def Devices(cls, is_ready=True, timeout=_DEFAULT_TIMEOUT, + def Devices(cls, is_ready=True, long_list=False, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): """Get the list of active attached devices. Args: is_ready: Whether the devices should be limited to only those that are ready for use. + long_list: Whether to use the long listing format. timeout: (optional) Timeout per try in seconds. retries: (optional) Number of retries to attempt. Yields: AdbWrapper instances. """ - output = cls._RunAdbCmd(['devices'], timeout=timeout, retries=retries) - lines = (line.split() for line in output.splitlines()) + cmd = ['devices'] + if long_list: + cmd.append('-l') + output = cls._RunAdbCmd(cmd, timeout=timeout, retries=retries) + lines = (line.split() for line in output.splitlines()[1:]) return [AdbWrapper(line[0]) for line in lines - if len(line) == 2 and (not is_ready or line[1] == _READY_STATE)] + if ((long_list or len(line) == 2) + and (not is_ready or line[1] == _READY_STATE))] def GetDeviceSerial(self): """Gets the device serial number associated with this object. |