| #!/usr/bin/env python3 | |
| # | |
| # Copyright 2019, The Android Open Source Project | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| """Helper util libraries for parsing logcat logs.""" | |
| import asyncio | |
| import re | |
| from datetime import datetime | |
| from typing import Optional, Pattern | |
| # local import | |
| import lib.print_utils as print_utils | |
| def parse_logcat_datetime(timestamp: str) -> Optional[datetime]: | |
| """Parses the timestamp of logcat. | |
| Params: | |
| timestamp: for example "2019-07-01 16:13:55.221". | |
| Returns: | |
| a datetime of timestamp with the year now. | |
| """ | |
| try: | |
| # Match the format of logcat. For example: "2019-07-01 16:13:55.221", | |
| # because it doesn't have year, set current year to it. | |
| timestamp = datetime.strptime(timestamp, | |
| '%Y-%m-%d %H:%M:%S.%f') | |
| return timestamp | |
| except ValueError as ve: | |
| print_utils.debug_print('Invalid line: ' + timestamp) | |
| return None | |
| def _is_time_out(timeout: datetime, line: str) -> bool: | |
| """Checks if the timestamp of this line exceeds the timeout. | |
| Returns: | |
| true if the timestamp exceeds the timeout. | |
| """ | |
| # Get the timestampe string. | |
| cur_timestamp_str = ' '.join(re.split(r'\s+', line)[0:2]) | |
| timestamp = parse_logcat_datetime(cur_timestamp_str) | |
| if not timestamp: | |
| return False | |
| return timestamp > timeout | |
| async def _blocking_wait_for_logcat_pattern(timestamp: datetime, | |
| pattern: Pattern, | |
| timeout: datetime) -> Optional[str]: | |
| # Show the year in the timestampe. | |
| logcat_cmd = 'adb logcat -v UTC -v year -v threadtime -T'.split() | |
| logcat_cmd.append(str(timestamp)) | |
| print_utils.debug_print('[LOGCAT]:' + ' '.join(logcat_cmd)) | |
| # Create subprocess | |
| process = await asyncio.create_subprocess_exec( | |
| *logcat_cmd, | |
| # stdout must a pipe to be accessible as process.stdout | |
| stdout=asyncio.subprocess.PIPE) | |
| while (True): | |
| # Read one line of output. | |
| data = await process.stdout.readline() | |
| line = data.decode('utf-8').rstrip() | |
| # 2019-07-01 14:54:21.946 27365 27392 I ActivityTaskManager: Displayed | |
| # com.android.settings/.Settings: +927ms | |
| # TODO: Detect timeouts even when there is no logcat output. | |
| if _is_time_out(timeout, line): | |
| print_utils.debug_print('DID TIMEOUT BEFORE SEEING ANYTHING (' | |
| 'timeout={timeout} seconds << {pattern} ' | |
| '>>'.format(timeout=timeout, pattern=pattern)) | |
| return None | |
| if pattern.match(line): | |
| print_utils.debug_print( | |
| 'WE DID SEE PATTERN << "{}" >>.'.format(pattern)) | |
| return line | |
| def blocking_wait_for_logcat_pattern(timestamp: datetime, | |
| pattern: Pattern, | |
| timeout: datetime) -> Optional[str]: | |
| """Selects the line that matches the pattern and within the timeout. | |
| Returns: | |
| the line that matches the pattern and within the timeout. | |
| """ | |
| loop = asyncio.get_event_loop() | |
| result = loop.run_until_complete( | |
| _blocking_wait_for_logcat_pattern(timestamp, pattern, timeout)) | |
| return result |