mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
140 lines
4.8 KiB
Python
140 lines
4.8 KiB
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.
|
|
|
|
# pylint: disable=unused-argument
|
|
|
|
import collections
|
|
import itertools
|
|
import logging
|
|
import subprocess
|
|
import tempfile
|
|
import time
|
|
import re
|
|
|
|
from pylib.device import adb_wrapper
|
|
from pylib.device import decorators
|
|
from pylib.device import device_errors
|
|
|
|
|
|
class LogcatMonitor(object):
|
|
|
|
_THREADTIME_RE_FORMAT = (
|
|
r'(?P<date>\S*) +(?P<time>\S*) +(?P<proc_id>%s) +(?P<thread_id>%s) +'
|
|
r'(?P<log_level>%s) +(?P<component>%s) *: +(?P<message>%s)$')
|
|
|
|
def __init__(self, adb, clear=True, filter_specs=None):
|
|
"""Create a LogcatMonitor instance.
|
|
|
|
Args:
|
|
adb: An instance of adb_wrapper.AdbWrapper.
|
|
clear: If True, clear the logcat when monitoring starts.
|
|
filter_specs: An optional list of '<tag>[:priority]' strings.
|
|
"""
|
|
if isinstance(adb, adb_wrapper.AdbWrapper):
|
|
self._adb = adb
|
|
else:
|
|
raise ValueError('Unsupported type passed for argument "device"')
|
|
self._clear = clear
|
|
self._filter_specs = filter_specs
|
|
self._logcat_out = None
|
|
self._logcat_out_file = None
|
|
self._logcat_proc = None
|
|
|
|
@decorators.WithTimeoutAndRetriesDefaults(10, 0)
|
|
def WaitFor(self, success_regex, failure_regex=None, timeout=None,
|
|
retries=None):
|
|
"""Wait for a matching logcat line or until a timeout occurs.
|
|
|
|
This will attempt to match lines in the logcat against both |success_regex|
|
|
and |failure_regex| (if provided). Note that this calls re.search on each
|
|
logcat line, not re.match, so the provided regular expressions don't have
|
|
to match an entire line.
|
|
|
|
Args:
|
|
success_regex: The regular expression to search for.
|
|
failure_regex: An optional regular expression that, if hit, causes this
|
|
to stop looking for a match. Can be None.
|
|
timeout: timeout in seconds
|
|
retries: number of retries
|
|
|
|
Returns:
|
|
A match object if |success_regex| matches a part of a logcat line, or
|
|
None if |failure_regex| matches a part of a logcat line.
|
|
Raises:
|
|
CommandFailedError on logcat failure (NOT on a |failure_regex| match).
|
|
CommandTimeoutError if no logcat line matching either |success_regex| or
|
|
|failure_regex| is found in |timeout| seconds.
|
|
DeviceUnreachableError if the device becomes unreachable.
|
|
"""
|
|
if isinstance(success_regex, basestring):
|
|
success_regex = re.compile(success_regex)
|
|
if isinstance(failure_regex, basestring):
|
|
failure_regex = re.compile(failure_regex)
|
|
|
|
logging.debug('Waiting %d seconds for "%s"', timeout, success_regex.pattern)
|
|
|
|
# NOTE This will continue looping until:
|
|
# - success_regex matches a line, in which case the match object is
|
|
# returned.
|
|
# - failure_regex matches a line, in which case None is returned
|
|
# - the timeout is hit, in which case a CommandTimeoutError is raised.
|
|
for l in self._adb.Logcat(filter_specs=self._filter_specs):
|
|
m = success_regex.search(l)
|
|
if m:
|
|
return m
|
|
if failure_regex and failure_regex.search(l):
|
|
return None
|
|
|
|
def FindAll(self, message_regex, proc_id=None, thread_id=None, log_level=None,
|
|
component=None):
|
|
"""Finds all lines in the logcat that match the provided constraints.
|
|
|
|
Args:
|
|
message_regex: The regular expression that the <message> section must
|
|
match.
|
|
proc_id: The process ID to match. If None, matches any process ID.
|
|
thread_id: The thread ID to match. If None, matches any thread ID.
|
|
log_level: The log level to match. If None, matches any log level.
|
|
component: The component to match. If None, matches any component.
|
|
|
|
Yields:
|
|
A match object for each matching line in the logcat. The match object
|
|
will always contain, in addition to groups defined in |message_regex|,
|
|
the following named groups: 'date', 'time', 'proc_id', 'thread_id',
|
|
'log_level', 'component', and 'message'.
|
|
"""
|
|
if proc_id is None:
|
|
proc_id = r'\d+'
|
|
if thread_id is None:
|
|
thread_id = r'\d+'
|
|
if log_level is None:
|
|
log_level = r'[VDIWEF]'
|
|
if component is None:
|
|
component = r'[^\s:]+'
|
|
threadtime_re = re.compile(
|
|
type(self)._THREADTIME_RE_FORMAT % (
|
|
proc_id, thread_id, log_level, component, message_regex))
|
|
|
|
for line in self._adb.Logcat(dump=True, logcat_format='threadtime'):
|
|
m = re.match(threadtime_re, line)
|
|
if m:
|
|
yield m
|
|
|
|
def Start(self):
|
|
"""Starts the logcat monitor.
|
|
|
|
Clears the logcat if |clear| was set in |__init__|.
|
|
"""
|
|
if self._clear:
|
|
self._adb.Logcat(clear=True)
|
|
|
|
def __enter__(self):
|
|
"""Starts the logcat monitor."""
|
|
self.Start()
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
"""Stops the logcat monitor."""
|
|
pass
|