| OLD | NEW |
| (Empty) |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 # pylint: disable=unused-argument | |
| 6 | |
| 7 import collections | |
| 8 import itertools | |
| 9 import logging | |
| 10 import subprocess | |
| 11 import tempfile | |
| 12 import time | |
| 13 import re | |
| 14 | |
| 15 from pylib.device import adb_wrapper | |
| 16 from pylib.device import decorators | |
| 17 from pylib.device import device_errors | |
| 18 | |
| 19 | |
| 20 class LogcatMonitor(object): | |
| 21 | |
| 22 _THREADTIME_RE_FORMAT = ( | |
| 23 r'(?P<date>\S*) +(?P<time>\S*) +(?P<proc_id>%s) +(?P<thread_id>%s) +' | |
| 24 r'(?P<log_level>%s) +(?P<component>%s) *: +(?P<message>%s)$') | |
| 25 | |
| 26 def __init__(self, adb, clear=True, filter_specs=None): | |
| 27 """Create a LogcatMonitor instance. | |
| 28 | |
| 29 Args: | |
| 30 adb: An instance of adb_wrapper.AdbWrapper. | |
| 31 clear: If True, clear the logcat when monitoring starts. | |
| 32 filter_specs: An optional list of '<tag>[:priority]' strings. | |
| 33 """ | |
| 34 if isinstance(adb, adb_wrapper.AdbWrapper): | |
| 35 self._adb = adb | |
| 36 else: | |
| 37 raise ValueError('Unsupported type passed for argument "device"') | |
| 38 self._clear = clear | |
| 39 self._filter_specs = filter_specs | |
| 40 self._logcat_out = None | |
| 41 self._logcat_out_file = None | |
| 42 self._logcat_proc = None | |
| 43 | |
| 44 @decorators.WithTimeoutAndRetriesDefaults(10, 0) | |
| 45 def WaitFor(self, success_regex, failure_regex=None, timeout=None, | |
| 46 retries=None): | |
| 47 """Wait for a matching logcat line or until a timeout occurs. | |
| 48 | |
| 49 This will attempt to match lines in the logcat against both |success_regex| | |
| 50 and |failure_regex| (if provided). Note that this calls re.search on each | |
| 51 logcat line, not re.match, so the provided regular expressions don't have | |
| 52 to match an entire line. | |
| 53 | |
| 54 Args: | |
| 55 success_regex: The regular expression to search for. | |
| 56 failure_regex: An optional regular expression that, if hit, causes this | |
| 57 to stop looking for a match. Can be None. | |
| 58 timeout: timeout in seconds | |
| 59 retries: number of retries | |
| 60 | |
| 61 Returns: | |
| 62 A match object if |success_regex| matches a part of a logcat line, or | |
| 63 None if |failure_regex| matches a part of a logcat line. | |
| 64 Raises: | |
| 65 CommandFailedError on logcat failure (NOT on a |failure_regex| match). | |
| 66 CommandTimeoutError if no logcat line matching either |success_regex| or | |
| 67 |failure_regex| is found in |timeout| seconds. | |
| 68 DeviceUnreachableError if the device becomes unreachable. | |
| 69 """ | |
| 70 if isinstance(success_regex, basestring): | |
| 71 success_regex = re.compile(success_regex) | |
| 72 if isinstance(failure_regex, basestring): | |
| 73 failure_regex = re.compile(failure_regex) | |
| 74 | |
| 75 logging.debug('Waiting %d seconds for "%s"', timeout, success_regex.pattern) | |
| 76 | |
| 77 # NOTE This will continue looping until: | |
| 78 # - success_regex matches a line, in which case the match object is | |
| 79 # returned. | |
| 80 # - failure_regex matches a line, in which case None is returned | |
| 81 # - the timeout is hit, in which case a CommandTimeoutError is raised. | |
| 82 for l in self._adb.Logcat(filter_specs=self._filter_specs): | |
| 83 m = success_regex.search(l) | |
| 84 if m: | |
| 85 return m | |
| 86 if failure_regex and failure_regex.search(l): | |
| 87 return None | |
| 88 | |
| 89 def FindAll(self, message_regex, proc_id=None, thread_id=None, log_level=None, | |
| 90 component=None): | |
| 91 """Finds all lines in the logcat that match the provided constraints. | |
| 92 | |
| 93 Args: | |
| 94 message_regex: The regular expression that the <message> section must | |
| 95 match. | |
| 96 proc_id: The process ID to match. If None, matches any process ID. | |
| 97 thread_id: The thread ID to match. If None, matches any thread ID. | |
| 98 log_level: The log level to match. If None, matches any log level. | |
| 99 component: The component to match. If None, matches any component. | |
| 100 | |
| 101 Yields: | |
| 102 A match object for each matching line in the logcat. The match object | |
| 103 will always contain, in addition to groups defined in |message_regex|, | |
| 104 the following named groups: 'date', 'time', 'proc_id', 'thread_id', | |
| 105 'log_level', 'component', and 'message'. | |
| 106 """ | |
| 107 if proc_id is None: | |
| 108 proc_id = r'\d+' | |
| 109 if thread_id is None: | |
| 110 thread_id = r'\d+' | |
| 111 if log_level is None: | |
| 112 log_level = r'[VDIWEF]' | |
| 113 if component is None: | |
| 114 component = r'[^\s:]+' | |
| 115 threadtime_re = re.compile( | |
| 116 type(self)._THREADTIME_RE_FORMAT % ( | |
| 117 proc_id, thread_id, log_level, component, message_regex)) | |
| 118 | |
| 119 for line in self._adb.Logcat(dump=True, logcat_format='threadtime'): | |
| 120 m = re.match(threadtime_re, line) | |
| 121 if m: | |
| 122 yield m | |
| 123 | |
| 124 def Start(self): | |
| 125 """Starts the logcat monitor. | |
| 126 | |
| 127 Clears the logcat if |clear| was set in |__init__|. | |
| 128 """ | |
| 129 if self._clear: | |
| 130 self._adb.Logcat(clear=True) | |
| 131 | |
| 132 def __enter__(self): | |
| 133 """Starts the logcat monitor.""" | |
| 134 self.Start() | |
| 135 return self | |
| 136 | |
| 137 def __exit__(self, exc_type, exc_val, exc_tb): | |
| 138 """Stops the logcat monitor.""" | |
| 139 pass | |
| OLD | NEW |