blob: 8a3d00b46771d95a20d4cc4962bff013230a3dc9 [file] [log] [blame]
Eric Jeong59a56502022-01-31 19:00:37 +00001#!/usr/bin/env python3
2#
3# Copyright 2019, The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Helper util libraries for parsing logcat logs."""
18
19import asyncio
20import re
21from datetime import datetime
22from typing import Optional, Pattern
23
24# local import
25import lib.print_utils as print_utils
26
27def parse_logcat_datetime(timestamp: str) -> Optional[datetime]:
28 """Parses the timestamp of logcat.
29
30 Params:
31 timestamp: for example "2019-07-01 16:13:55.221".
32
33 Returns:
34 a datetime of timestamp with the year now.
35 """
36 try:
37 # Match the format of logcat. For example: "2019-07-01 16:13:55.221",
38 # because it doesn't have year, set current year to it.
39 timestamp = datetime.strptime(timestamp,
40 '%Y-%m-%d %H:%M:%S.%f')
41 return timestamp
42 except ValueError as ve:
43 print_utils.debug_print('Invalid line: ' + timestamp)
44 return None
45
46def _is_time_out(timeout: datetime, line: str) -> bool:
47 """Checks if the timestamp of this line exceeds the timeout.
48
49 Returns:
50 true if the timestamp exceeds the timeout.
51 """
52 # Get the timestampe string.
53 cur_timestamp_str = ' '.join(re.split(r'\s+', line)[0:2])
54 timestamp = parse_logcat_datetime(cur_timestamp_str)
55 if not timestamp:
56 return False
57
58 return timestamp > timeout
59
60async def _blocking_wait_for_logcat_pattern(timestamp: datetime,
61 pattern: Pattern,
62 timeout: datetime) -> Optional[str]:
63 # Show the year in the timestampe.
64 logcat_cmd = 'adb logcat -v UTC -v year -v threadtime -T'.split()
65 logcat_cmd.append(str(timestamp))
66 print_utils.debug_print('[LOGCAT]:' + ' '.join(logcat_cmd))
67
68 # Create subprocess
69 process = await asyncio.create_subprocess_exec(
70 *logcat_cmd,
71 # stdout must a pipe to be accessible as process.stdout
72 stdout=asyncio.subprocess.PIPE)
73
74 while (True):
75 # Read one line of output.
76 data = await process.stdout.readline()
77 line = data.decode('utf-8').rstrip()
78
79 # 2019-07-01 14:54:21.946 27365 27392 I ActivityTaskManager: Displayed
80 # com.android.settings/.Settings: +927ms
81 # TODO: Detect timeouts even when there is no logcat output.
82 if _is_time_out(timeout, line):
83 print_utils.debug_print('DID TIMEOUT BEFORE SEEING ANYTHING ('
84 'timeout={timeout} seconds << {pattern} '
85 '>>'.format(timeout=timeout, pattern=pattern))
86 return None
87
88 if pattern.match(line):
89 print_utils.debug_print(
90 'WE DID SEE PATTERN << "{}" >>.'.format(pattern))
91 return line
92
93def blocking_wait_for_logcat_pattern(timestamp: datetime,
94 pattern: Pattern,
95 timeout: datetime) -> Optional[str]:
96 """Selects the line that matches the pattern and within the timeout.
97
98 Returns:
99 the line that matches the pattern and within the timeout.
100 """
101 loop = asyncio.get_event_loop()
102 result = loop.run_until_complete(
103 _blocking_wait_for_logcat_pattern(timestamp, pattern, timeout))
104 return result