Add Apps.AppListSearchQueryLength UMA histogram.
[chromium-blink-merge.git] / build / android / pylib / device / logcat_monitor.py
blob2eebc2dbbbe70065d324a97603939b9305f81a1c
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.
5 # pylint: disable=unused-argument
7 import collections
8 import itertools
9 import logging
10 import subprocess
11 import tempfile
12 import time
13 import re
15 from pylib.device import adb_wrapper
16 from pylib.device import decorators
17 from pylib.device import device_errors
20 class LogcatMonitor(object):
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)$')
26 def __init__(self, adb, clear=True, filter_specs=None):
27 """Create a LogcatMonitor instance.
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
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.
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.
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
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)
75 logging.debug('Waiting %d seconds for "%s"', timeout, success_regex.pattern)
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
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.
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.
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'.
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))
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
124 def Start(self):
125 """Starts the logcat monitor.
127 Clears the logcat if |clear| was set in |__init__|.
129 if self._clear:
130 self._adb.Logcat(clear=True)
132 def __enter__(self):
133 """Starts the logcat monitor."""
134 self.Start()
135 return self
137 def __exit__(self, exc_type, exc_val, exc_tb):
138 """Stops the logcat monitor."""
139 pass