OLD | NEW |
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 15 matching lines...) Expand all Loading... |
26 | 26 |
27 Host-driven tests have also been tried, but since they're really | 27 Host-driven tests have also been tried, but since they're really |
28 sub-classes of instrumentation tests, they didn't work well either. | 28 sub-classes of instrumentation tests, they didn't work well either. |
29 | 29 |
30 To build and run the linker tests, do the following: | 30 To build and run the linker tests, do the following: |
31 | 31 |
32 ninja -C out/Debug chromium_linker_test_apk | 32 ninja -C out/Debug chromium_linker_test_apk |
33 build/android/test_runner.py linker | 33 build/android/test_runner.py linker |
34 | 34 |
35 """ | 35 """ |
36 # pylint: disable=R0201 | |
37 | 36 |
38 import logging | 37 import logging |
39 import os | 38 import os |
40 import re | 39 import re |
| 40 import StringIO |
| 41 import subprocess |
| 42 import tempfile |
41 import time | 43 import time |
42 | 44 |
43 from pylib import constants | 45 from pylib import constants |
44 from pylib import android_commands | 46 from pylib import android_commands |
| 47 from pylib import flag_changer |
45 from pylib.base import base_test_result | 48 from pylib.base import base_test_result |
46 | 49 |
47 | |
48 ResultType = base_test_result.ResultType | 50 ResultType = base_test_result.ResultType |
49 | 51 |
50 _PACKAGE_NAME = 'org.chromium.chromium_linker_test_apk' | 52 _PACKAGE_NAME='org.chromium.chromium_linker_test_apk' |
51 _ACTIVITY_NAME = '.ChromiumLinkerTestActivity' | 53 _ACTIVITY_NAME='.ChromiumLinkerTestActivity' |
52 _COMMAND_LINE_FILE = '/data/local/tmp/chromium-linker-test-command-line' | 54 _COMMAND_LINE_FILE='/data/local/tmp/chromium-linker-test-command-line' |
53 | 55 |
54 # Path to the Linker.java source file. | 56 # Path to the Linker.java source file. |
55 _LINKER_JAVA_SOURCE_PATH = ( | 57 _LINKER_JAVA_SOURCE_PATH = \ |
56 'base/android/java/src/org/chromium/base/library_loader/Linker.java') | 58 'base/android/java/src/org/chromium/base/library_loader/Linker.java' |
57 | 59 |
58 # A regular expression used to extract the browser shared RELRO configuration | 60 # A regular expression used to extract the browser shared RELRO configuration |
59 # from the Java source file above. | 61 # from the Java source file above. |
60 _RE_LINKER_BROWSER_CONFIG = re.compile( | 62 _RE_LINKER_BROWSER_CONFIG = \ |
61 r'.*BROWSER_SHARED_RELRO_CONFIG\s+=\s+' + | 63 re.compile(r'.*BROWSER_SHARED_RELRO_CONFIG\s+=\s+' + \ |
62 'BROWSER_SHARED_RELRO_CONFIG_(\S+)\s*;.*', | 64 'BROWSER_SHARED_RELRO_CONFIG_(\S+)\s*;.*', |
63 re.MULTILINE | re.DOTALL) | 65 re.MULTILINE | re.DOTALL) |
64 | 66 |
65 # Logcat filters used during each test. Only the 'chromium' one is really | 67 # 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 | 68 # 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 | 69 # it is handy to have the 'chromium_android_linker' ones as well when |
68 # troubleshooting. | 70 # troubleshooting. |
69 _LOGCAT_FILTERS = [ '*:s', 'chromium:v', 'chromium_android_linker:v' ] | 71 _LOGCAT_FILTERS = [ '*:s', 'chromium:v', 'chromium_android_linker:v' ] |
70 #_LOGCAT_FILTERS = [ '*:v' ] ## DEBUG | 72 #_LOGCAT_FILTERS = [ '*:v' ] ## DEBUG |
71 | 73 |
72 # Regular expression used to match status lines in logcat. | 74 # Regular expression used to match status lines in logcat. |
73 re_status_line = re.compile(r'(BROWSER|RENDERER)_LINKER_TEST: (FAIL|SUCCESS)') | 75 re_status_line = re.compile(r'(BROWSER|RENDERER)_LINKER_TEST: (FAIL|SUCCESS)') |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
141 else: | 143 else: |
142 assert False, 'Invalid process type ' + process_type | 144 assert False, 'Invalid process type ' + process_type |
143 | 145 |
144 if browser_found and renderer_found: | 146 if browser_found and renderer_found: |
145 return (True, browser_success, renderer_success) | 147 return (True, browser_success, renderer_success) |
146 | 148 |
147 # Didn't find anything. | 149 # Didn't find anything. |
148 return (False, None, None) | 150 return (False, None, None) |
149 | 151 |
150 | 152 |
151 def _WaitForLinkerTestStatus(_adb, _timeout): | 153 def _WaitForLinkerTestStatus(adb, timeout): |
152 """Wait up to |timeout| seconds until the full linker test status lines appear | 154 """Wait up to |timeout| seconds until the full linker test status lines appear |
153 in the logcat being recorded with |adb|. | 155 in the logcat being recorded with |adb|. |
154 Args: | 156 Args: |
155 adb: An AndroidCommands instance. This assumes adb.StartRecordingLogcat() | 157 adb: An AndroidCommands instance. This assumes adb.StartRecordingLogcat() |
156 was called previously. | 158 was called previously. |
157 timeout: Timeout in seconds. | 159 timeout: Timeout in seconds. |
158 Returns: | 160 Returns: |
159 ResultType.TIMEOUT in case of timeout, ResulType.PASS if both status lines | 161 ResultType.TIMEOUT in case of timeout, ResulType.PASS if both status lines |
160 report 'SUCCESS', or ResulType.FAIL otherwise. | 162 report 'SUCCESS', or ResulType.FAIL otherwise. |
161 """ | 163 """ |
162 pass | |
163 | 164 |
164 | 165 |
165 def _StartActivityAndWaitForLinkerTestStatus(adb, timeout): | 166 def _StartActivityAndWaitForLinkerTestStatus(adb, timeout): |
166 """Force-start an activity and wait up to |timeout| seconds until the full | 167 """Force-start an activity and wait up to |timeout| seconds until the full |
167 linker test status lines appear in the logcat, recorded through |adb|. | 168 linker test status lines appear in the logcat, recorded through |adb|. |
168 Args: | 169 Args: |
169 adb: An AndroidCommands instance. | 170 adb: An AndroidCommands instance. |
170 timeout: Timeout in seconds | 171 timeout: Timeout in seconds |
171 Returns: | 172 Returns: |
172 A (status, logs) tuple, where status is a ResultType constant, and logs | 173 A (status, logs) tuple, where status is a ResultType constant, and logs |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 for lib_map in lib_map_list: | 269 for lib_map in lib_map_list: |
269 for lib_name, lib_address in lib_map.iteritems(): | 270 for lib_name, lib_address in lib_map.iteritems(): |
270 if lib_name not in lib_addr_map: | 271 if lib_name not in lib_addr_map: |
271 lib_addr_map[lib_name] = AddressList() | 272 lib_addr_map[lib_name] = AddressList() |
272 lib_addr_map[lib_name].append(lib_address) | 273 lib_addr_map[lib_name].append(lib_address) |
273 | 274 |
274 logging.info('%s library load map: %s', process_type, lib_addr_map) | 275 logging.info('%s library load map: %s', process_type, lib_addr_map) |
275 | 276 |
276 # For each library, check the randomness of its load addresses. | 277 # For each library, check the randomness of its load addresses. |
277 bad_libs = {} | 278 bad_libs = {} |
| 279 success = True |
278 for lib_name, lib_address_list in lib_addr_map.iteritems(): | 280 for lib_name, lib_address_list in lib_addr_map.iteritems(): |
279 # If all addresses are different, skip to next item. | 281 # If all addresses are different, skip to next item. |
280 lib_address_set = set(lib_address_list) | 282 lib_address_set = set(lib_address_list) |
281 # Consider that if there is more than one pair of identical addresses in | 283 # Consider that if there is more than one pair of identical addresses in |
282 # the list, then randomization is broken. | 284 # the list, then randomization is broken. |
283 if len(lib_address_set) < len(lib_address_list) - 1: | 285 if len(lib_address_set) < len(lib_address_list) - 1: |
284 bad_libs[lib_name] = lib_address_list | 286 bad_libs[lib_name] = lib_address_list |
285 | 287 |
286 | 288 |
287 if bad_libs: | 289 if bad_libs: |
288 return False, '%s libraries failed randomization: %s' % \ | 290 return False, '%s libraries failed randomization: %s' % \ |
289 (process_type, bad_libs) | 291 (process_type, bad_libs) |
290 | 292 |
291 return True, '%s libraries properly randomized: %s' % \ | 293 return True, '%s libraries properly randomized: %s' % \ |
292 (process_type, lib_addr_map) | 294 (process_type, lib_addr_map) |
293 | 295 |
294 | 296 |
295 class LinkerTestCaseBase(object): | 297 class LinkerTestCaseBase(object): |
296 """Base class for linker test cases.""" | 298 """Base class for linker test cases.""" |
297 | 299 |
298 def __init__(self, is_low_memory=False): | 300 def __init__(self, is_low_memory=False): |
299 """Create a test case. | 301 """Create a test case. |
300 Args: | 302 Args: |
301 is_low_memory: True to simulate a low-memory device, False otherwise. | 303 is_low_memory: True to simulate a low-memory device, False otherwise. |
302 """ | 304 """ |
303 self.is_low_memory = is_low_memory | 305 self.is_low_memory = is_low_memory |
304 if is_low_memory: | 306 if is_low_memory: |
305 test_suffix = 'ForLowMemoryDevice' | 307 test_suffix = 'ForLowMemoryDevice' |
306 else: | 308 else: |
307 test_suffix = 'ForRegularDevice' | 309 test_suffix = 'ForRegularDevice' |
308 class_name = self.__class__.__name__ | 310 class_name = self.__class__.__name__ |
309 self.qualified_name = '%s.%s' % (class_name, test_suffix) | 311 self.qualified_name = '%s.%s' % (class_name, test_suffix) |
310 self.tagged_name = self.qualified_name | 312 self.tagged_name = self.qualified_name |
311 | 313 |
312 def _RunTest(self, _adb): | 314 def _RunTest(self, adb): |
313 """Run the test, must be overriden. | 315 """Run the test, must be overriden. |
314 Args: | 316 Args: |
315 _adb: An AndroidCommands instance to the device. | 317 adb: An AndroidCommands instance to the device. |
316 Returns: | 318 Returns: |
317 A (status, log) tuple, where <status> is a ResultType constant, and <log> | 319 A (status, log) tuple, where <status> is a ResultType constant, and <log> |
318 is the logcat output captured during the test in case of error, or None | 320 is the logcat output captured during the test in case of error, or None |
319 in case of success. | 321 in case of success. |
320 """ | 322 """ |
321 return ResultType.FAIL, 'Unimplemented _RunTest() method!' | 323 return ResultType.FAIL, 'Unimplemented _RunTest() method!' |
322 | 324 |
323 def Run(self, device): | 325 def Run(self, device): |
324 """Run the test on a given device. | 326 """Run the test on a given device. |
325 Args: | 327 Args: |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
490 In theory, two successive runs could (very rarely) use the same load | 492 In theory, two successive runs could (very rarely) use the same load |
491 address, so loop 5 times and compare the values there. It is assumed | 493 address, so loop 5 times and compare the values there. It is assumed |
492 that if there are more than one pair of identical addresses, then the | 494 that if there are more than one pair of identical addresses, then the |
493 load addresses are not random enough for this test. | 495 load addresses are not random enough for this test. |
494 """ | 496 """ |
495 def _RunTest(self, adb): | 497 def _RunTest(self, adb): |
496 max_loops = 5 | 498 max_loops = 5 |
497 browser_lib_map_list = [] | 499 browser_lib_map_list = [] |
498 renderer_lib_map_list = [] | 500 renderer_lib_map_list = [] |
499 logs_list = [] | 501 logs_list = [] |
500 for _loop in range(max_loops): | 502 for loop in range(max_loops): |
501 # Start the activity. | 503 # Start the activity. |
502 result, logs = _StartActivityAndWaitForLinkerTestStatus(adb, timeout=30) | 504 result, logs = _StartActivityAndWaitForLinkerTestStatus(adb, timeout=30) |
503 if result == ResultType.TIMEOUT: | 505 if result == ResultType.TIMEOUT: |
504 # Something bad happened. Return immediately. | 506 # Something bad happened. Return immediately. |
505 return result, logs | 507 return result, logs |
506 | 508 |
507 # Collect library addresses. | 509 # Collect library addresses. |
508 browser_libs, renderer_libs = _ExtractLibraryLoadAddressesFromLogcat(logs) | 510 browser_libs, renderer_libs = _ExtractLibraryLoadAddressesFromLogcat(logs) |
509 browser_lib_map_list.append(browser_libs) | 511 browser_lib_map_list.append(browser_libs) |
510 renderer_lib_map_list.append(renderer_libs) | 512 renderer_lib_map_list.append(renderer_libs) |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
545 # Note that this behaviour doesn't seem to happen when starting an | 547 # Note that this behaviour doesn't seem to happen when starting an |
546 # application 'normally', i.e. when using the application launcher to | 548 # application 'normally', i.e. when using the application launcher to |
547 # start the activity. | 549 # start the activity. |
548 logging.info('Ignoring system\'s low randomization of browser libraries' + | 550 logging.info('Ignoring system\'s low randomization of browser libraries' + |
549 ' for regular devices') | 551 ' for regular devices') |
550 | 552 |
551 if not renderer_status: | 553 if not renderer_status: |
552 return ResultType.FAIL, renderer_logs | 554 return ResultType.FAIL, renderer_logs |
553 | 555 |
554 return ResultType.PASS, logs | 556 return ResultType.PASS, logs |
OLD | NEW |