Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(460)

Side by Side Diff: build/android/pylib/linker/test_case.py

Issue 896503002: [Android] Add LogcatMonitor. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Base class for linker-specific test cases. 5 """Base class for linker-specific test cases.
6 6
7 The custom dynamic linker can only be tested through a custom test case 7 The custom dynamic linker can only be tested through a custom test case
8 for various technical reasons: 8 for various technical reasons:
9 9
10 - It's an 'invisible feature', i.e. it doesn't expose a new API or 10 - It's an 'invisible feature', i.e. it doesn't expose a new API or
(...skipping 24 matching lines...) Expand all
35 """ 35 """
36 # pylint: disable=R0201 36 # pylint: disable=R0201
37 37
38 import logging 38 import logging
39 import os 39 import os
40 import re 40 import re
41 import time 41 import time
42 42
43 from pylib import constants 43 from pylib import constants
44 from pylib.base import base_test_result 44 from pylib.base import base_test_result
45 from pylib.device import device_errors
45 from pylib.device import intent 46 from pylib.device import intent
46 47
47 48
48 ResultType = base_test_result.ResultType 49 ResultType = base_test_result.ResultType
49 50
50 _PACKAGE_NAME = 'org.chromium.chromium_linker_test_apk' 51 _PACKAGE_NAME = 'org.chromium.chromium_linker_test_apk'
51 _ACTIVITY_NAME = '.ChromiumLinkerTestActivity' 52 _ACTIVITY_NAME = '.ChromiumLinkerTestActivity'
52 _COMMAND_LINE_FILE = '/data/local/tmp/chromium-linker-test-command-line' 53 _COMMAND_LINE_FILE = '/data/local/tmp/chromium-linker-test-command-line'
53 54
54 # Path to the Linker.java source file. 55 # Path to the Linker.java source file.
55 _LINKER_JAVA_SOURCE_PATH = ( 56 _LINKER_JAVA_SOURCE_PATH = (
56 'base/android/java/src/org/chromium/base/library_loader/Linker.java') 57 'base/android/java/src/org/chromium/base/library_loader/Linker.java')
57 58
58 # A regular expression used to extract the browser shared RELRO configuration 59 # A regular expression used to extract the browser shared RELRO configuration
59 # from the Java source file above. 60 # from the Java source file above.
60 _RE_LINKER_BROWSER_CONFIG = re.compile( 61 _RE_LINKER_BROWSER_CONFIG = re.compile(
61 r'.*BROWSER_SHARED_RELRO_CONFIG\s+=\s+' + 62 r'.*BROWSER_SHARED_RELRO_CONFIG\s+=\s+' +
62 r'BROWSER_SHARED_RELRO_CONFIG_(\S+)\s*;.*', 63 r'BROWSER_SHARED_RELRO_CONFIG_(\S+)\s*;.*',
63 re.MULTILINE | re.DOTALL) 64 re.MULTILINE | re.DOTALL)
64 65
65 # Logcat filters used during each test. Only the 'chromium' one is really 66 # Logcat filters used during each test. Only the 'chromium' one is really
66 # needed, but the logs are added to the TestResult in case of error, and 67 # needed, but the logs are added to the TestResult in case of error, and
67 # it is handy to have the 'chromium_android_linker' ones as well when 68 # it is handy to have the 'chromium_android_linker' ones as well when
68 # troubleshooting. 69 # troubleshooting.
69 _LOGCAT_FILTERS = ['*:s', 'chromium:v', 'chromium_android_linker:v'] 70 _LOGCAT_FILTERS = ['*:s', 'chromium:v', 'chromium_android_linker:v']
70 #_LOGCAT_FILTERS = ['*:v'] ## DEBUG 71 #_LOGCAT_FILTERS = ['*:v'] ## DEBUG
71 72
72 # Regular expression used to match status lines in logcat. 73 # Regular expression used to match status lines in logcat.
73 re_status_line = re.compile(r'(BROWSER|RENDERER)_LINKER_TEST: (FAIL|SUCCESS)') 74 RE_BROWSER_STATUS_LINE = re.compile(r' BROWSER_LINKER_TEST: (FAIL|SUCCESS)$')
75 RE_RENDERER_STATUS_LINE = re.compile(r' RENDERER_LINKER_TEST: (FAIL|SUCCESS)$')
74 76
75 # Regular expression used to mach library load addresses in logcat. 77 # Regular expression used to mach library load addresses in logcat.
76 re_library_address = re.compile( 78 RE_LIBRARY_ADDRESS = re.compile(
77 r'(BROWSER|RENDERER)_LIBRARY_ADDRESS: (\S+) ([0-9A-Fa-f]+)') 79 r'(BROWSER|RENDERER)_LIBRARY_ADDRESS: (\S+) ([0-9A-Fa-f]+)')
78 80
79 81
80 def _GetBrowserSharedRelroConfig(): 82 def _GetBrowserSharedRelroConfig():
81 """Returns a string corresponding to the Linker's configuration of shared 83 """Returns a string corresponding to the Linker's configuration of shared
82 RELRO sections in the browser process. This parses the Java linker source 84 RELRO sections in the browser process. This parses the Java linker source
83 file to get the appropriate information. 85 file to get the appropriate information.
84 Return: 86 Return:
85 None in case of error (e.g. could not locate the source file). 87 None in case of error (e.g. could not locate the source file).
86 'NEVER' if the browser process shall never use shared RELROs. 88 'NEVER' if the browser process shall never use shared RELROs.
(...skipping 15 matching lines...) Expand all
102 return None 104 return None
103 105
104 if configs[0] not in ['NEVER', 'LOW_RAM_ONLY', 'ALWAYS']: 106 if configs[0] not in ['NEVER', 'LOW_RAM_ONLY', 'ALWAYS']:
105 logging.error('Unexpected browser config value: ' + configs[0]) 107 logging.error('Unexpected browser config value: ' + configs[0])
106 return None 108 return None
107 109
108 logging.info('Found linker browser shared RELRO config: ' + configs[0]) 110 logging.info('Found linker browser shared RELRO config: ' + configs[0])
109 return configs[0] 111 return configs[0]
110 112
111 113
112 def _WriteCommandLineFile(device, command_line, command_line_file):
113 """Create a command-line file on the device. This does not use FlagChanger
114 because its implementation assumes the device has 'su', and thus does
115 not work at all with production devices."""
116 device.RunShellCommand(
117 'echo "%s" > %s' % (command_line, command_line_file))
118
119
120 def _CheckLinkerTestStatus(logcat):
121 """Parse the content of |logcat| and checks for both a browser and
122 renderer status line.
123
124 Args:
125 logcat: A string to parse. Can include line separators.
126
127 Returns:
128 A tuple, result[0] is True if there is a complete match, then
129 result[1] and result[2] will be True or False to reflect the
130 test status for the browser and renderer processes, respectively.
131 """
132 browser_found = False
133 renderer_found = False
134 for m in re_status_line.finditer(logcat):
135 process_type, status = m.groups()
136 if process_type == 'BROWSER':
137 browser_found = True
138 browser_success = (status == 'SUCCESS')
139 elif process_type == 'RENDERER':
140 renderer_found = True
141 renderer_success = (status == 'SUCCESS')
142 else:
143 assert False, 'Invalid process type ' + process_type
144
145 if browser_found and renderer_found:
146 return (True, browser_success, renderer_success)
147
148 # Didn't find anything.
149 return (False, None, None)
150
151
152 def _StartActivityAndWaitForLinkerTestStatus(device, timeout): 114 def _StartActivityAndWaitForLinkerTestStatus(device, timeout):
153 """Force-start an activity and wait up to |timeout| seconds until the full 115 """Force-start an activity and wait up to |timeout| seconds until the full
154 linker test status lines appear in the logcat, recorded through |device|. 116 linker test status lines appear in the logcat, recorded through |device|.
155 Args: 117 Args:
156 device: A DeviceUtils instance. 118 device: A DeviceUtils instance.
157 timeout: Timeout in seconds 119 timeout: Timeout in seconds
158 Returns: 120 Returns:
159 A (status, logs) tuple, where status is a ResultType constant, and logs 121 A (status, logs) tuple, where status is a ResultType constant, and logs
160 if the final logcat output as a string. 122 if the final logcat output as a string.
161 """ 123 """
124
162 # 1. Start recording logcat with appropriate filters. 125 # 1. Start recording logcat with appropriate filters.
163 device.old_interface.StartRecordingLogcat( 126 with device.GetLogcatMonitor(filters=_LOGCAT_FILTERS) as logmon:
164 clear=True, filters=_LOGCAT_FILTERS)
165 127
166 try:
167 # 2. Force-start activity. 128 # 2. Force-start activity.
168 device.StartActivity( 129 device.StartActivity(
169 intent.Intent(package=_PACKAGE_NAME, activity=_ACTIVITY_NAME), 130 intent.Intent(package=_PACKAGE_NAME, activity=_ACTIVITY_NAME),
170 force_stop=True) 131 force_stop=True)
171 132
172 # 3. Wait up to |timeout| seconds until the test status is in the logcat. 133 # 3. Wait up to |timeout| seconds until the test status is in the logcat.
173 num_tries = 0 134 result = ResultType.PASS
174 max_tries = timeout 135 try:
175 found = False 136 browser_match = logmon.WaitFor(RE_BROWSER_STATUS_LINE, None,
176 while num_tries < max_tries: 137 timeout=timeout)
177 time.sleep(1) 138 logging.debug('Found browser match: %s', browser_match.group(0))
178 num_tries += 1 139 renderer_match = logmon.WaitFor(RE_RENDERER_STATUS_LINE, None,
179 found, browser_ok, renderer_ok = _CheckLinkerTestStatus( 140 timeout=timeout)
180 device.old_interface.GetCurrentRecordedLogcat()) 141 logging.debug('Found renderer match: %s', renderer_match.group(0))
181 if found: 142 if (browser_match.group(1) != 'SUCCESS'
182 break 143 or renderer_match.group(1) != 'SUCCESS'):
144 result = ResultType.FAIL
145 except device_errors.CommandTimeoutError:
146 result = ResultType.TIMEOUT
183 147
184 finally: 148 return result, '\n'.join(device.adb.Logcat(dump=True))
185 logs = device.old_interface.StopRecordingLogcat()
186
187 if num_tries >= max_tries:
188 return ResultType.TIMEOUT, logs
189
190 if browser_ok and renderer_ok:
191 return ResultType.PASS, logs
192
193 return ResultType.FAIL, logs
194 149
195 150
196 class LibraryLoadMap(dict): 151 class LibraryLoadMap(dict):
197 """A helper class to pretty-print a map of library names to load addresses.""" 152 """A helper class to pretty-print a map of library names to load addresses."""
198 def __str__(self): 153 def __str__(self):
199 items = ['\'%s\': 0x%x' % (name, address) for \ 154 items = ['\'%s\': 0x%x' % (name, address) for \
200 (name, address) in self.iteritems()] 155 (name, address) in self.iteritems()]
201 return '{%s}' % (', '.join(items)) 156 return '{%s}' % (', '.join(items))
202 157
203 def __repr__(self): 158 def __repr__(self):
(...skipping 15 matching lines...) Expand all
219 browser and renderer processes. 174 browser and renderer processes.
220 Args: 175 Args:
221 logs: A string containing logcat output. 176 logs: A string containing logcat output.
222 Returns: 177 Returns:
223 A tuple (browser_libs, renderer_libs), where each item is a map of 178 A tuple (browser_libs, renderer_libs), where each item is a map of
224 library names (strings) to library load addresses (ints), for the 179 library names (strings) to library load addresses (ints), for the
225 browser and renderer processes, respectively. 180 browser and renderer processes, respectively.
226 """ 181 """
227 browser_libs = LibraryLoadMap() 182 browser_libs = LibraryLoadMap()
228 renderer_libs = LibraryLoadMap() 183 renderer_libs = LibraryLoadMap()
229 for m in re_library_address.finditer(logs): 184 for m in RE_LIBRARY_ADDRESS.finditer(logs):
230 process_type, lib_name, lib_address = m.groups() 185 process_type, lib_name, lib_address = m.groups()
231 lib_address = int(lib_address, 16) 186 lib_address = int(lib_address, 16)
232 if process_type == 'BROWSER': 187 if process_type == 'BROWSER':
233 browser_libs[lib_name] = lib_address 188 browser_libs[lib_name] = lib_address
234 elif process_type == 'RENDERER': 189 elif process_type == 'RENDERER':
235 renderer_libs[lib_name] = lib_address 190 renderer_libs[lib_name] = lib_address
236 else: 191 else:
237 assert False, 'Invalid process type' 192 assert False, 'Invalid process type'
238 193
239 return browser_libs, renderer_libs 194 return browser_libs, renderer_libs
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 A base_test_result.TestRunResult() instance. 271 A base_test_result.TestRunResult() instance.
317 """ 272 """
318 margin = 8 273 margin = 8
319 print '[ %-*s ] %s' % (margin, 'RUN', self.tagged_name) 274 print '[ %-*s ] %s' % (margin, 'RUN', self.tagged_name)
320 logging.info('Running linker test: %s', self.tagged_name) 275 logging.info('Running linker test: %s', self.tagged_name)
321 276
322 # Create command-line file on device. 277 # Create command-line file on device.
323 command_line_flags = '' 278 command_line_flags = ''
324 if self.is_low_memory: 279 if self.is_low_memory:
325 command_line_flags = '--low-memory-device' 280 command_line_flags = '--low-memory-device'
326 _WriteCommandLineFile(device, command_line_flags, _COMMAND_LINE_FILE) 281 device.WriteFile(_COMMAND_LINE_FILE, command_line_flags)
327 282
328 # Run the test. 283 # Run the test.
329 status, logs = self._RunTest(device) 284 status, logs = self._RunTest(device)
330 285
331 result_text = 'OK' 286 result_text = 'OK'
332 if status == ResultType.FAIL: 287 if status == ResultType.FAIL:
333 result_text = 'FAILED' 288 result_text = 'FAILED'
334 elif status == ResultType.TIMEOUT: 289 elif status == ResultType.TIMEOUT:
335 result_text = 'TIMEOUT' 290 result_text = 'TIMEOUT'
336 print '[ %*s ] %s' % (margin, result_text, self.tagged_name) 291 print '[ %*s ] %s' % (margin, result_text, self.tagged_name)
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after
533 # Note that this behaviour doesn't seem to happen when starting an 488 # Note that this behaviour doesn't seem to happen when starting an
534 # application 'normally', i.e. when using the application launcher to 489 # application 'normally', i.e. when using the application launcher to
535 # start the activity. 490 # start the activity.
536 logging.info('Ignoring system\'s low randomization of browser libraries' + 491 logging.info('Ignoring system\'s low randomization of browser libraries' +
537 ' for regular devices') 492 ' for regular devices')
538 493
539 if not renderer_status: 494 if not renderer_status:
540 return ResultType.FAIL, renderer_logs 495 return ResultType.FAIL, renderer_logs
541 496
542 return ResultType.PASS, logs 497 return ResultType.PASS, logs
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698