Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(225)

Side by Side Diff: build/android/pylib/device/logcat_monitor.py

Issue 896503002: [Android] Add LogcatMonitor. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 # Format: <DATE> <TIME> <PID> <TID> <LEVEL> <COMPONENT>: <MESSAGE>
23 _THREADTIME_RE_FORMAT = r'\S* +\S* +(%s) +(%s) +(%s) +(%s): +(%s)$'
24
25 def __init__(self, adb, clear=True, filters=None):
Sami 2015/02/03 14:24:22 Could you document these parameters?
jbudorick 2015/02/03 15:38:02 Done. Removed filters, since it's not used.
26 if isinstance(adb, adb_wrapper.AdbWrapper):
27 self._adb = adb
28 else:
29 raise ValueError('Unsupported type passed for argument "device"')
30 self._clear = clear
31 self._filters = filters
32 self._logcat_out = None
33 self._logcat_out_file = None
34 self._logcat_proc = None
35
36 @decorators.WithTimeoutAndRetriesDefaults(10, 0)
37 def WaitFor(self, success_regex, failure_regex, timeout=None, retries=None):
perezju 2015/02/03 09:18:44 maybe failure_regex=None should be a default?
jbudorick 2015/02/03 15:38:02 Done.
38 """Wait for a matching logcat line or until a timeout occurs.
39
40 This will attempt to match lines in the logcat against both |success_regex|
41 and |failure_regex| (if provided). Note that this calls re.search on each
42 logcat line, not re.match, so the provided regular expressions don't have
43 to match an entire line.
44
45 Args:
46 success_regex: The regular expression to search for.
47 failure_regex: An optional regular expression that, if hit, causes this
48 to stop looking for a match. Can be None.
49 timeout: timeout in seconds
50 retries: number of retries
51
52 Returns:
53 A match object if |success_regex| matches a part of a logcat line, or
54 None if |failure_regex| matches a part of a logcat line.
55 Raises:
56 CommandFailedError on logcat failure (NOT on a |failure_regex| match).
57 CommandTimeoutError if no logcat line matching either |success_regex| or
58 |failure_regex| is found in |timeout| seconds.
59 DeviceUnreachableError if the device becomes unreachable.
60 """
61 if isinstance(success_regex, basestring):
62 success_regex = re.compile(success_regex)
63 if isinstance(failure_regex, basestring):
64 failure_regex = re.compile(failure_regex)
65
66 logging.debug('Waiting %d seconds for "%s"', timeout, success_regex.pattern)
67
68 # NOTE This will continue looping until:
69 # - success_regex matches a line, in which case the match object is
70 # returned.
71 # - failure_regex matches a line, in which case None is returned
72 # - the timeout is hit, in which case a CommandTimeoutError is raised.
73 for l in self._adb.Logcat():
74 m = success_regex.search(l)
75 if m:
76 return m
77 if failure_regex and failure_regex.search(l):
78 return None
79
80 def FindAll(self, message_regex, proc_id=None, thread_id=None, log_level=None,
81 component=None):
82 """Finds all lines in the logcat that match the provided constraints.
83
84 Args:
85 message_regex: The regular expression that the <message> section must
86 match.
87 proc_id: The process ID to match. If None, matches any process ID.
88 thread_id: The thread ID to match. If None, matches any thread ID.
89 log_level: The log level to match. If None, matches any log level.
90 component: The component to match. If None, matches any component.
91
92 Returns:
93 An iterable containing objects with five attributes:
94 |proc_id|: the process ID
95 |thread_id|: the thread ID
96 |log_level|: the log level
97 |component|: the component
98 |message|: the logcat message
99 """
100 LogcatLine = collections.namedtuple(
101 'LogcatLine',
102 ['proc_id', 'thread_id', 'log_level', 'component', 'message'])
103
104 if proc_id is None:
105 proc_id = r'\d+'
106 if thread_id is None:
107 thread_id = r'\d+'
108 if log_level is None:
109 log_level = r'[VDIWEF]'
110 if component is None:
111 component = r'[^\s:]+'
112 threadtime_re = re.compile(
113 type(self)._THREADTIME_RE_FORMAT % (
114 proc_id, thread_id, log_level, component, message_regex))
115
116 regexed_lines = (
117 re.match(threadtime_re, l)
118 for l in self._adb.Logcat(dump=True, logcat_format='threadtime'))
119 only_matches = (m for m in regexed_lines if m)
120 return (LogcatLine(*m.group(1, 2, 3, 4, 5)) for m in only_matches)
121
122 def Start(self):
123 """Starts the logcat monitor."""
Sami 2015/02/03 14:24:22 It might be good to mention that this is the point
jbudorick 2015/02/03 15:38:01 Done.
124 if self._clear:
125 self._adb.Logcat(clear=True)
126
127 def Stop(self):
Sami 2015/02/03 14:24:22 Do we need to keep this around?
jbudorick 2015/02/03 15:38:02 No. I had thought it was aesthetically cleaner for
128 """Stops the logcat monitor."""
129 pass
130
131 def __enter__(self):
132 """Starts the logcat monitor."""
133 self.Start()
134 return self
135
136 def __exit__(self, exc_type, exc_val, exc_tb):
137 """Stops the logcat monitor."""
138 self.Stop()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698