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

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, timeout=timeout)
176 while num_tries < max_tries: 137 logging.debug('Found browser match: %s', browser_match.group(0))
177 time.sleep(1) 138 renderer_match = logmon.WaitFor(_RE_RENDERER_STATUS_LINE,
178 num_tries += 1 139 timeout=timeout)
179 found, browser_ok, renderer_ok = _CheckLinkerTestStatus( 140 logging.debug('Found renderer match: %s', renderer_match.group(0))
180 device.old_interface.GetCurrentRecordedLogcat()) 141 if (browser_match.group(1) != 'SUCCESS'
181 if found: 142 or renderer_match.group(1) != 'SUCCESS'):
182 break 143 result = ResultType.FAIL
144 except device_errors.CommandTimeoutError:
145 result = ResultType.TIMEOUT
183 146
184 finally: 147 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 148
195 149
196 class LibraryLoadMap(dict): 150 class LibraryLoadMap(dict):
197 """A helper class to pretty-print a map of library names to load addresses.""" 151 """A helper class to pretty-print a map of library names to load addresses."""
198 def __str__(self): 152 def __str__(self):
199 items = ['\'%s\': 0x%x' % (name, address) for \ 153 items = ['\'%s\': 0x%x' % (name, address) for \
200 (name, address) in self.iteritems()] 154 (name, address) in self.iteritems()]
201 return '{%s}' % (', '.join(items)) 155 return '{%s}' % (', '.join(items))
202 156
203 def __repr__(self): 157 def __repr__(self):
(...skipping 15 matching lines...) Expand all
219 browser and renderer processes. 173 browser and renderer processes.
220 Args: 174 Args:
221 logs: A string containing logcat output. 175 logs: A string containing logcat output.
222 Returns: 176 Returns:
223 A tuple (browser_libs, renderer_libs), where each item is a map of 177 A tuple (browser_libs, renderer_libs), where each item is a map of
224 library names (strings) to library load addresses (ints), for the 178 library names (strings) to library load addresses (ints), for the
225 browser and renderer processes, respectively. 179 browser and renderer processes, respectively.
226 """ 180 """
227 browser_libs = LibraryLoadMap() 181 browser_libs = LibraryLoadMap()
228 renderer_libs = LibraryLoadMap() 182 renderer_libs = LibraryLoadMap()
229 for m in re_library_address.finditer(logs): 183 for m in _RE_LIBRARY_ADDRESS.finditer(logs):
230 process_type, lib_name, lib_address = m.groups() 184 process_type, lib_name, lib_address = m.groups()
231 lib_address = int(lib_address, 16) 185 lib_address = int(lib_address, 16)
232 if process_type == 'BROWSER': 186 if process_type == 'BROWSER':
233 browser_libs[lib_name] = lib_address 187 browser_libs[lib_name] = lib_address
234 elif process_type == 'RENDERER': 188 elif process_type == 'RENDERER':
235 renderer_libs[lib_name] = lib_address 189 renderer_libs[lib_name] = lib_address
236 else: 190 else:
237 assert False, 'Invalid process type' 191 assert False, 'Invalid process type'
238 192
239 return browser_libs, renderer_libs 193 return browser_libs, renderer_libs
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 A base_test_result.TestRunResult() instance. 270 A base_test_result.TestRunResult() instance.
317 """ 271 """
318 margin = 8 272 margin = 8
319 print '[ %-*s ] %s' % (margin, 'RUN', self.tagged_name) 273 print '[ %-*s ] %s' % (margin, 'RUN', self.tagged_name)
320 logging.info('Running linker test: %s', self.tagged_name) 274 logging.info('Running linker test: %s', self.tagged_name)
321 275
322 # Create command-line file on device. 276 # Create command-line file on device.
323 command_line_flags = '' 277 command_line_flags = ''
324 if self.is_low_memory: 278 if self.is_low_memory:
325 command_line_flags = '--low-memory-device' 279 command_line_flags = '--low-memory-device'
326 _WriteCommandLineFile(device, command_line_flags, _COMMAND_LINE_FILE) 280 device.WriteFile(_COMMAND_LINE_FILE, command_line_flags)
327 281
328 # Run the test. 282 # Run the test.
329 status, logs = self._RunTest(device) 283 status, logs = self._RunTest(device)
330 284
331 result_text = 'OK' 285 result_text = 'OK'
332 if status == ResultType.FAIL: 286 if status == ResultType.FAIL:
333 result_text = 'FAILED' 287 result_text = 'FAILED'
334 elif status == ResultType.TIMEOUT: 288 elif status == ResultType.TIMEOUT:
335 result_text = 'TIMEOUT' 289 result_text = 'TIMEOUT'
336 print '[ %*s ] %s' % (margin, result_text, self.tagged_name) 290 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 487 # Note that this behaviour doesn't seem to happen when starting an
534 # application 'normally', i.e. when using the application launcher to 488 # application 'normally', i.e. when using the application launcher to
535 # start the activity. 489 # start the activity.
536 logging.info('Ignoring system\'s low randomization of browser libraries' + 490 logging.info('Ignoring system\'s low randomization of browser libraries' +
537 ' for regular devices') 491 ' for regular devices')
538 492
539 if not renderer_status: 493 if not renderer_status:
540 return ResultType.FAIL, renderer_logs 494 return ResultType.FAIL, renderer_logs
541 495
542 return ResultType.PASS, logs 496 return ResultType.PASS, logs
OLDNEW
« no previous file with comments | « build/android/pylib/instrumentation/test_runner.py ('k') | tools/profile_chrome/chrome_controller.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698