aboutsummaryrefslogtreecommitdiff
path: root/third_party/catapult/devil/devil/android/logcat_monitor.py
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/catapult/devil/devil/android/logcat_monitor.py')
-rw-r--r--third_party/catapult/devil/devil/android/logcat_monitor.py255
1 files changed, 0 insertions, 255 deletions
diff --git a/third_party/catapult/devil/devil/android/logcat_monitor.py b/third_party/catapult/devil/devil/android/logcat_monitor.py
deleted file mode 100644
index 0aece87..0000000
--- a/third_party/catapult/devil/devil/android/logcat_monitor.py
+++ /dev/null
@@ -1,255 +0,0 @@
-# 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 errno
-import logging
-import os
-import re
-import shutil
-import tempfile
-import threading
-import time
-
-from devil.android import decorators
-from devil.android import device_errors
-from devil.android.sdk import adb_wrapper
-from devil.utils import reraiser_thread
-
-logger = logging.getLogger(__name__)
-
-
-class LogcatMonitor(object):
-
- _RECORD_ITER_TIMEOUT = 2.0
- _RECORD_THREAD_JOIN_WAIT = 5.0
- _WAIT_TIME = 0.2
- _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, output_file=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.
- output_file: File path to save recorded logcat.
- """
- 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._output_file = output_file
- self._record_file = None
- self._record_file_lock = threading.Lock()
- self._record_thread = None
- self._stop_recording_event = threading.Event()
-
- @property
- def output_file(self):
- return self._output_file
-
- @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.
- LogcatMonitorCommandError when calling |WaitFor| while not recording
- logcat.
- """
- if self._record_thread is None:
- raise LogcatMonitorCommandError(
- 'Must be recording logcat when calling |WaitFor|',
- device_serial=str(self._adb))
- if isinstance(success_regex, basestring):
- success_regex = re.compile(success_regex)
- if isinstance(failure_regex, basestring):
- failure_regex = re.compile(failure_regex)
-
- logger.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.
- with open(self._record_file.name, 'r') as f:
- while True:
- line = f.readline()
- if line:
- m = success_regex.search(line)
- if m:
- return m
- if failure_regex and failure_regex.search(line):
- return None
- else:
- time.sleep(self._WAIT_TIME)
-
- 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.
-
- Raises:
- LogcatMonitorCommandError when calling |FindAll| before recording logcat.
-
- 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 self._record_file is None:
- raise LogcatMonitorCommandError(
- 'Must have recorded or be recording a logcat to call |FindAll|',
- device_serial=str(self._adb))
- 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:]+'
- # pylint: disable=protected-access
- threadtime_re = re.compile(
- type(self)._THREADTIME_RE_FORMAT % (
- proc_id, thread_id, log_level, component, message_regex))
-
- with open(self._record_file.name, 'r') as f:
- for line in f:
- m = re.match(threadtime_re, line)
- if m:
- yield m
-
- def _StartRecording(self):
- """Starts recording logcat to file.
-
- Function spawns a thread that records logcat to file and will not die
- until |StopRecording| is called.
- """
- def record_to_file():
- # Write the log with line buffering so the consumer sees each individual
- # line.
- for data in self._adb.Logcat(filter_specs=self._filter_specs,
- logcat_format='threadtime',
- iter_timeout=self._RECORD_ITER_TIMEOUT):
- if self._stop_recording_event.isSet():
- return
-
- if data is None:
- # Logcat can yield None if the iter_timeout is hit.
- continue
-
- with self._record_file_lock:
- if self._record_file and not self._record_file.closed:
- self._record_file.write(data + '\n')
-
- self._stop_recording_event.clear()
- if not self._record_thread:
- self._record_thread = reraiser_thread.ReraiserThread(record_to_file)
- self._record_thread.start()
-
- def _StopRecording(self):
- """Finish recording logcat."""
- if self._record_thread:
- self._stop_recording_event.set()
- self._record_thread.join(timeout=self._RECORD_THREAD_JOIN_WAIT)
- self._record_thread.ReraiseIfException()
- self._record_thread = None
-
- def Start(self):
- """Starts the logcat monitor.
-
- Clears the logcat if |clear| was set in |__init__|.
- """
- if self._clear:
- self._adb.Logcat(clear=True)
- if not self._record_file:
- self._record_file = tempfile.NamedTemporaryFile(mode='a', bufsize=1)
- self._StartRecording()
-
- def Stop(self):
- """Stops the logcat monitor.
-
- Stops recording the logcat. Copies currently recorded logcat to
- |self._output_file|.
- """
- self._StopRecording()
- with self._record_file_lock:
- if self._record_file and self._output_file:
- try:
- os.makedirs(os.path.dirname(self._output_file))
- except OSError as e:
- if e.errno != errno.EEXIST:
- raise
- shutil.copy(self._record_file.name, self._output_file)
-
- def Close(self):
- """Closes logcat recording file.
-
- Should be called when finished using the logcat monitor.
- """
- with self._record_file_lock:
- if self._record_file:
- self._record_file.close()
- self._record_file = None
-
- def __enter__(self):
- """Starts the logcat monitor."""
- self.Start()
- return self
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- """Stops the logcat monitor."""
- self.Stop()
-
- def __del__(self):
- """Closes logcat recording file in case |Close| was never called."""
- with self._record_file_lock:
- if self._record_file:
- logger.warning(
- 'Need to call |Close| on the logcat monitor when done!')
- self._record_file.close()
-
- @property
- def adb(self):
- return self._adb
-
-
-class LogcatMonitorCommandError(device_errors.CommandFailedError):
- """Exception for errors with logcat monitor commands."""
- pass