1be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik# Copyright 2015 The Chromium Authors. All rights reserved. 2be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik# Use of this source code is governed by a BSD-style license that can be 3be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik# found in the LICENSE file. 4be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 5be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik# pylint: disable=unused-argument 6be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 7be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikimport errno 8be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikimport logging 9be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikimport os 10be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikimport re 11be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikimport shutil 12be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikimport tempfile 13be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikimport threading 14be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikimport time 15be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 16be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikfrom devil.android import decorators 17be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikfrom devil.android import device_errors 18be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikfrom devil.android.sdk import adb_wrapper 19be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikfrom devil.utils import reraiser_thread 20be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 21eeb03f366d148f92337cfd7577087ade44ab9285John Recklogger = logging.getLogger(__name__) 22eeb03f366d148f92337cfd7577087ade44ab9285John Reck 23be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 24be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikclass LogcatMonitor(object): 25be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 26b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang _RECORD_ITER_TIMEOUT = 0.2 27d0ebf633155e2d637d289933ef7dbc5d86f73881Chris Craik _RECORD_THREAD_JOIN_WAIT = 5.0 28be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik _WAIT_TIME = 0.2 29b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang THREADTIME_RE_FORMAT = ( 30be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik r'(?P<date>\S*) +(?P<time>\S*) +(?P<proc_id>%s) +(?P<thread_id>%s) +' 31be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik r'(?P<log_level>%s) +(?P<component>%s) *: +(?P<message>%s)$') 32be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 33b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def __init__(self, adb, clear=True, filter_specs=None, output_file=None, 34b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang transform_func=None): 35be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """Create a LogcatMonitor instance. 36be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 37be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik Args: 38be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik adb: An instance of adb_wrapper.AdbWrapper. 39be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik clear: If True, clear the logcat when monitoring starts. 40be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik filter_specs: An optional list of '<tag>[:priority]' strings. 41be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik output_file: File path to save recorded logcat. 42b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang transform_func: An optional unary callable that takes and returns 43b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang a list of lines, possibly transforming them in the process. 44be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """ 45be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if isinstance(adb, adb_wrapper.AdbWrapper): 46be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._adb = adb 47be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik else: 48be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik raise ValueError('Unsupported type passed for argument "device"') 49be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._clear = clear 50be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._filter_specs = filter_specs 51be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._output_file = output_file 52be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_file = None 53be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_file_lock = threading.Lock() 54be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_thread = None 55be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._stop_recording_event = threading.Event() 56b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self._transform_func = transform_func 57be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 58be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik @property 59be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik def output_file(self): 60be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik return self._output_file 61be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 62be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik @decorators.WithTimeoutAndRetriesDefaults(10, 0) 63be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik def WaitFor(self, success_regex, failure_regex=None, timeout=None, 64be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik retries=None): 65be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """Wait for a matching logcat line or until a timeout occurs. 66be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 67be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik This will attempt to match lines in the logcat against both |success_regex| 68be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik and |failure_regex| (if provided). Note that this calls re.search on each 69be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik logcat line, not re.match, so the provided regular expressions don't have 70be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik to match an entire line. 71be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 72be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik Args: 73be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik success_regex: The regular expression to search for. 74be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik failure_regex: An optional regular expression that, if hit, causes this 75be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik to stop looking for a match. Can be None. 76be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik timeout: timeout in seconds 77be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik retries: number of retries 78be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 79be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik Returns: 80be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik A match object if |success_regex| matches a part of a logcat line, or 81be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik None if |failure_regex| matches a part of a logcat line. 82be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik Raises: 83be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik CommandFailedError on logcat failure (NOT on a |failure_regex| match). 84be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik CommandTimeoutError if no logcat line matching either |success_regex| or 85be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik |failure_regex| is found in |timeout| seconds. 86be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik DeviceUnreachableError if the device becomes unreachable. 87be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik LogcatMonitorCommandError when calling |WaitFor| while not recording 88be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik logcat. 89be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """ 90be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if self._record_thread is None: 91be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik raise LogcatMonitorCommandError( 92be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 'Must be recording logcat when calling |WaitFor|', 93be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik device_serial=str(self._adb)) 94be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if isinstance(success_regex, basestring): 95be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik success_regex = re.compile(success_regex) 96be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if isinstance(failure_regex, basestring): 97be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik failure_regex = re.compile(failure_regex) 98be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 99eeb03f366d148f92337cfd7577087ade44ab9285John Reck logger.debug('Waiting %d seconds for "%s"', timeout, success_regex.pattern) 100be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 101be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik # NOTE This will continue looping until: 102be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik # - success_regex matches a line, in which case the match object is 103be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik # returned. 104be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik # - failure_regex matches a line, in which case None is returned 105be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik # - the timeout is hit, in which case a CommandTimeoutError is raised. 106be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik with open(self._record_file.name, 'r') as f: 107be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik while True: 108be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik line = f.readline() 109be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if line: 110be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik m = success_regex.search(line) 111be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if m: 112be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik return m 113be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if failure_regex and failure_regex.search(line): 114be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik return None 115be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik else: 116be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik time.sleep(self._WAIT_TIME) 117be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 118be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik def FindAll(self, message_regex, proc_id=None, thread_id=None, log_level=None, 119be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik component=None): 120be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """Finds all lines in the logcat that match the provided constraints. 121be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 122be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik Args: 123be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik message_regex: The regular expression that the <message> section must 124be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik match. 125be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik proc_id: The process ID to match. If None, matches any process ID. 126be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik thread_id: The thread ID to match. If None, matches any thread ID. 127be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik log_level: The log level to match. If None, matches any log level. 128be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik component: The component to match. If None, matches any component. 129be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 130be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik Raises: 131be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik LogcatMonitorCommandError when calling |FindAll| before recording logcat. 132be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 133be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik Yields: 134be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik A match object for each matching line in the logcat. The match object 135be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik will always contain, in addition to groups defined in |message_regex|, 136be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik the following named groups: 'date', 'time', 'proc_id', 'thread_id', 137be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 'log_level', 'component', and 'message'. 138be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """ 139be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if self._record_file is None: 140be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik raise LogcatMonitorCommandError( 141be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 'Must have recorded or be recording a logcat to call |FindAll|', 142be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik device_serial=str(self._adb)) 143be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if proc_id is None: 144be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik proc_id = r'\d+' 145be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if thread_id is None: 146be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik thread_id = r'\d+' 147be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if log_level is None: 148be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik log_level = r'[VDIWEF]' 149be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if component is None: 150be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik component = r'[^\s:]+' 151be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik # pylint: disable=protected-access 152be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik threadtime_re = re.compile( 153b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang type(self).THREADTIME_RE_FORMAT % ( 154be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik proc_id, thread_id, log_level, component, message_regex)) 155be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 156be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik with open(self._record_file.name, 'r') as f: 157be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik for line in f: 158be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik m = re.match(threadtime_re, line) 159be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if m: 160be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik yield m 161be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 162be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik def _StartRecording(self): 163be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """Starts recording logcat to file. 164be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 165be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik Function spawns a thread that records logcat to file and will not die 166be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik until |StopRecording| is called. 167be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """ 168be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik def record_to_file(): 169be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik # Write the log with line buffering so the consumer sees each individual 170be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik # line. 171be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik for data in self._adb.Logcat(filter_specs=self._filter_specs, 172d0ebf633155e2d637d289933ef7dbc5d86f73881Chris Craik logcat_format='threadtime', 173d0ebf633155e2d637d289933ef7dbc5d86f73881Chris Craik iter_timeout=self._RECORD_ITER_TIMEOUT): 174d0ebf633155e2d637d289933ef7dbc5d86f73881Chris Craik if self._stop_recording_event.isSet(): 175d0ebf633155e2d637d289933ef7dbc5d86f73881Chris Craik return 176d0ebf633155e2d637d289933ef7dbc5d86f73881Chris Craik 177d0ebf633155e2d637d289933ef7dbc5d86f73881Chris Craik if data is None: 178d0ebf633155e2d637d289933ef7dbc5d86f73881Chris Craik # Logcat can yield None if the iter_timeout is hit. 179d0ebf633155e2d637d289933ef7dbc5d86f73881Chris Craik continue 180d0ebf633155e2d637d289933ef7dbc5d86f73881Chris Craik 181be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik with self._record_file_lock: 182be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if self._record_file and not self._record_file.closed: 183b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang if self._transform_func: 184b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang data = '\n'.join(self._transform_func([data])) 185be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_file.write(data + '\n') 186be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 187be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._stop_recording_event.clear() 188be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if not self._record_thread: 189be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_thread = reraiser_thread.ReraiserThread(record_to_file) 190be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_thread.start() 191be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 192be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik def _StopRecording(self): 193be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """Finish recording logcat.""" 194be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if self._record_thread: 195be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._stop_recording_event.set() 196be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_thread.join(timeout=self._RECORD_THREAD_JOIN_WAIT) 197be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_thread.ReraiseIfException() 198be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_thread = None 199be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 200be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik def Start(self): 201be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """Starts the logcat monitor. 202be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 203be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik Clears the logcat if |clear| was set in |__init__|. 204be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """ 205be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if self._clear: 206be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._adb.Logcat(clear=True) 207be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if not self._record_file: 208be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_file = tempfile.NamedTemporaryFile(mode='a', bufsize=1) 209be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._StartRecording() 210be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 211be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik def Stop(self): 212be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """Stops the logcat monitor. 213be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 214be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik Stops recording the logcat. Copies currently recorded logcat to 215be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik |self._output_file|. 216be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """ 217be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._StopRecording() 218be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik with self._record_file_lock: 219be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if self._record_file and self._output_file: 220be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik try: 221be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik os.makedirs(os.path.dirname(self._output_file)) 222be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik except OSError as e: 223be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if e.errno != errno.EEXIST: 224be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik raise 225be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik shutil.copy(self._record_file.name, self._output_file) 226be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 227be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik def Close(self): 228be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """Closes logcat recording file. 229be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 230be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik Should be called when finished using the logcat monitor. 231be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """ 232be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik with self._record_file_lock: 233be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if self._record_file: 234be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_file.close() 235be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_file = None 236be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 237b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def close(self): 238b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang """An alias for Close. 239b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 240b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang Allows LogcatMonitors to be used with contextlib.closing. 241b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang """ 242b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self.Close() 243b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 244be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik def __enter__(self): 245be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """Starts the logcat monitor.""" 246be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self.Start() 247be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik return self 248be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 249be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik def __exit__(self, exc_type, exc_val, exc_tb): 250be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """Stops the logcat monitor.""" 251be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self.Stop() 252be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 253be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik def __del__(self): 254be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """Closes logcat recording file in case |Close| was never called.""" 255be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik with self._record_file_lock: 256be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik if self._record_file: 257eeb03f366d148f92337cfd7577087ade44ab9285John Reck logger.warning( 258be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 'Need to call |Close| on the logcat monitor when done!') 259be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik self._record_file.close() 260be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 261a23c9e9f6fc22fe5611def685e1984062b13b560Chris Craik @property 262a23c9e9f6fc22fe5611def685e1984062b13b560Chris Craik def adb(self): 263a23c9e9f6fc22fe5611def685e1984062b13b560Chris Craik return self._adb 264a23c9e9f6fc22fe5611def685e1984062b13b560Chris Craik 265be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik 266be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craikclass LogcatMonitorCommandError(device_errors.CommandFailedError): 267be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik """Exception for errors with logcat monitor commands.""" 268be1f909aea58dd8b153538c9fa19cb0bf50bdb17Chris Craik pass 269