media/webrtc/trunk/build/android/adb_logcat_monitor.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/media/webrtc/trunk/build/android/adb_logcat_monitor.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,155 @@
     1.4 +#!/usr/bin/env python
     1.5 +#
     1.6 +# Copyright (c) 2012 The Chromium Authors. All rights reserved.
     1.7 +# Use of this source code is governed by a BSD-style license that can be
     1.8 +# found in the LICENSE file.
     1.9 +
    1.10 +"""Saves logcats from all connected devices.
    1.11 +
    1.12 +Usage: adb_logcat_monitor.py <base_dir> [<adb_binary_path>]
    1.13 +
    1.14 +This script will repeatedly poll adb for new devices and save logcats
    1.15 +inside the <base_dir> directory, which it attempts to create.  The
    1.16 +script will run until killed by an external signal.  To test, run the
    1.17 +script in a shell and <Ctrl>-C it after a while.  It should be
    1.18 +resilient across phone disconnects and reconnects and start the logcat
    1.19 +early enough to not miss anything.
    1.20 +"""
    1.21 +
    1.22 +import logging
    1.23 +import os
    1.24 +import re
    1.25 +import shutil
    1.26 +import signal
    1.27 +import subprocess
    1.28 +import sys
    1.29 +import time
    1.30 +
    1.31 +# Map from device_id -> (process, logcat_num)
    1.32 +devices = {}
    1.33 +
    1.34 +
    1.35 +class TimeoutException(Exception):
    1.36 +  """Exception used to signal a timeout."""
    1.37 +  pass
    1.38 +
    1.39 +
    1.40 +class SigtermError(Exception):
    1.41 +  """Exception used to catch a sigterm."""
    1.42 +  pass
    1.43 +
    1.44 +
    1.45 +def StartLogcatIfNecessary(device_id, adb_cmd, base_dir):
    1.46 +  """Spawns a adb logcat process if one is not currently running."""
    1.47 +  process, logcat_num = devices[device_id]
    1.48 +  if process:
    1.49 +    if process.poll() is None:
    1.50 +      # Logcat process is still happily running
    1.51 +      return
    1.52 +    else:
    1.53 +      logging.info('Logcat for device %s has died', device_id)
    1.54 +      error_filter = re.compile('- waiting for device -')
    1.55 +      for line in process.stderr:
    1.56 +        if not error_filter.match(line):
    1.57 +          logging.error(device_id + ':   ' + line)
    1.58 +
    1.59 +  logging.info('Starting logcat %d for device %s', logcat_num,
    1.60 +               device_id)
    1.61 +  logcat_filename = 'logcat_%s_%03d' % (device_id, logcat_num)
    1.62 +  logcat_file = open(os.path.join(base_dir, logcat_filename), 'w')
    1.63 +  process = subprocess.Popen([adb_cmd, '-s', device_id,
    1.64 +                              'logcat', '-v', 'threadtime'],
    1.65 +                             stdout=logcat_file,
    1.66 +                             stderr=subprocess.PIPE)
    1.67 +  devices[device_id] = (process, logcat_num + 1)
    1.68 +
    1.69 +
    1.70 +def GetAttachedDevices(adb_cmd):
    1.71 +  """Gets the device list from adb.
    1.72 +
    1.73 +  We use an alarm in this function to avoid deadlocking from an external
    1.74 +  dependency.
    1.75 +
    1.76 +  Args:
    1.77 +    adb_cmd: binary to run adb
    1.78 +
    1.79 +  Returns:
    1.80 +    list of devices or an empty list on timeout
    1.81 +  """
    1.82 +  signal.alarm(2)
    1.83 +  try:
    1.84 +    out, err = subprocess.Popen([adb_cmd, 'devices'],
    1.85 +                                stdout=subprocess.PIPE,
    1.86 +                                stderr=subprocess.PIPE).communicate()
    1.87 +    if err:
    1.88 +      logging.warning('adb device error %s', err.strip())
    1.89 +    return re.findall('^(\w+)\tdevice$', out, re.MULTILINE)
    1.90 +  except TimeoutException:
    1.91 +    logging.warning('"adb devices" command timed out')
    1.92 +    return []
    1.93 +  except (IOError, OSError):
    1.94 +    logging.exception('Exception from "adb devices"')
    1.95 +    return []
    1.96 +  finally:
    1.97 +    signal.alarm(0)
    1.98 +
    1.99 +
   1.100 +def main(base_dir, adb_cmd='adb'):
   1.101 +  """Monitor adb forever.  Expects a SIGINT (Ctrl-C) to kill."""
   1.102 +  # We create the directory to ensure 'run once' semantics
   1.103 +  if os.path.exists(base_dir):
   1.104 +    print 'adb_logcat_monitor: %s already exists? Cleaning' % base_dir
   1.105 +    shutil.rmtree(base_dir, ignore_errors=True)
   1.106 +
   1.107 +  os.makedirs(base_dir)
   1.108 +  logging.basicConfig(filename=os.path.join(base_dir, 'eventlog'),
   1.109 +                      level=logging.INFO,
   1.110 +                      format='%(asctime)-2s %(levelname)-8s %(message)s')
   1.111 +
   1.112 +  # Set up the alarm for calling 'adb devices'. This is to ensure
   1.113 +  # our script doesn't get stuck waiting for a process response
   1.114 +  def TimeoutHandler(_, unused_frame):
   1.115 +    raise TimeoutException()
   1.116 +  signal.signal(signal.SIGALRM, TimeoutHandler)
   1.117 +
   1.118 +  # Handle SIGTERMs to ensure clean shutdown
   1.119 +  def SigtermHandler(_, unused_frame):
   1.120 +    raise SigtermError()
   1.121 +  signal.signal(signal.SIGTERM, SigtermHandler)
   1.122 +
   1.123 +  logging.info('Started with pid %d', os.getpid())
   1.124 +  pid_file_path = os.path.join(base_dir, 'LOGCAT_MONITOR_PID')
   1.125 +
   1.126 +  try:
   1.127 +    with open(pid_file_path, 'w') as f:
   1.128 +      f.write(str(os.getpid()))
   1.129 +    while True:
   1.130 +      for device_id in GetAttachedDevices(adb_cmd):
   1.131 +        if not device_id in devices:
   1.132 +          devices[device_id] = (None, 0)
   1.133 +
   1.134 +      for device in devices:
   1.135 +        # This will spawn logcat watchers for any device ever detected
   1.136 +        StartLogcatIfNecessary(device, adb_cmd, base_dir)
   1.137 +
   1.138 +      time.sleep(5)
   1.139 +  except SigtermError:
   1.140 +    logging.info('Received SIGTERM, shutting down')
   1.141 +  except:
   1.142 +    logging.exception('Unexpected exception in main.')
   1.143 +  finally:
   1.144 +    for process, _ in devices.itervalues():
   1.145 +      if process:
   1.146 +        try:
   1.147 +          process.terminate()
   1.148 +        except OSError:
   1.149 +          pass
   1.150 +    os.remove(pid_file_path)
   1.151 +
   1.152 +
   1.153 +if __name__ == '__main__':
   1.154 +  if 2 <= len(sys.argv) <= 3:
   1.155 +    print 'adb_logcat_monitor: Initializing'
   1.156 +    sys.exit(main(*sys.argv[1:3]))
   1.157 +
   1.158 +  print 'Usage: %s <base_dir> [<adb_binary_path>]' % sys.argv[0]

mercurial