android: Add 4 new linker tests.
This extracts the build/android/ changes for Patch Set 32 of https://codereview.chromium.org/23717023/ This adds 4 new linker tests to check the library load addresses and their proper randomization when simulating low-memory and regular devices. Since there are now 6 linker tests, also add support for -f / --gtest_filter options to filter the tests to run, e.g. to run all low-memory device tests, one can use: build/android/test_runner.py linker --gtest_filter='*LowMemory*' BUG=287739 R=bulach@chromium.org, frankf@chromium.org, yfriedman@chromium.org Review URL: https://codereview.chromium.org/26251003 git-svn-id: http://src.chromium.org/svn/trunk/src/build@227418 4ff67af0-8c30-449e-8e8b-ad334ec8d88c
This commit is contained in:
Родитель
4fa9932145
Коммит
d8db73faa7
|
@ -4,7 +4,6 @@
|
|||
|
||||
"""Setup for linker tests."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
|
@ -12,6 +11,12 @@ import types
|
|||
import test_case
|
||||
import test_runner
|
||||
|
||||
from pylib import constants
|
||||
|
||||
sys.path.insert(0,
|
||||
os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib',
|
||||
'common'))
|
||||
import unittest_util
|
||||
|
||||
def Setup(options, devices):
|
||||
"""Creates a list of test cases and a runner factory.
|
||||
|
@ -19,12 +24,20 @@ def Setup(options, devices):
|
|||
Returns:
|
||||
A tuple of (TestRunnerFactory, tests).
|
||||
"""
|
||||
test_cases = [
|
||||
test_case.LinkerLibraryAddressTest,
|
||||
test_case.LinkerSharedRelroTest,
|
||||
test_case.LinkerRandomizationTest ]
|
||||
|
||||
all_tests = [
|
||||
test_case.LinkerTestCase('ForRegularDevice',
|
||||
is_low_memory=False),
|
||||
test_case.LinkerTestCase('ForLowMemoryDevice',
|
||||
is_low_memory=True) ]
|
||||
low_memory_modes = [False, True]
|
||||
all_tests = [t(is_low_memory=m) for t in test_cases for m in low_memory_modes]
|
||||
|
||||
if options.test_filter:
|
||||
all_test_names = [ test.qualified_name for test in all_tests ]
|
||||
filtered_test_names = unittest_util.FilterTestNames(all_test_names,
|
||||
options.test_filter)
|
||||
all_tests = [t for t in all_tests \
|
||||
if t.qualified_name in filtered_test_names]
|
||||
|
||||
def TestRunnerFactory(device, shard_index):
|
||||
return test_runner.LinkerTestRunner(
|
||||
|
|
|
@ -32,26 +32,6 @@
|
|||
ninja -C out/Debug content_linker_test_apk
|
||||
build/android/test_runner.py linker
|
||||
|
||||
The core of the checks performed here are pretty simple:
|
||||
|
||||
- Clear the logcat and start recording with an appropriate set of filters.
|
||||
- Create the command-line appropriate for the test-case.
|
||||
- Start the activity (always forcing a cold start).
|
||||
- Every second, look at the current content of the filtered logcat lines
|
||||
and look for instances of the following:
|
||||
|
||||
BROWSER_LINKER_TEST: <status>
|
||||
RENDERER_LINKER_TEST: <status>
|
||||
|
||||
where <status> can be either FAIL or SUCCESS. These lines can appear
|
||||
in any order in the logcat. Once both browser and renderer status are
|
||||
found, stop the loop. Otherwise timeout after 30 seconds.
|
||||
|
||||
Note that there can be other lines beginning with BROWSER_LINKER_TEST:
|
||||
and RENDERER_LINKER_TEST:, but are not followed by a <status> code.
|
||||
|
||||
- The test case passes if the <status> for both the browser and renderer
|
||||
process are SUCCESS. Otherwise its a fail.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
@ -66,6 +46,7 @@ from pylib import android_commands
|
|||
from pylib import flag_changer
|
||||
from pylib.base import base_test_result
|
||||
|
||||
ResultType = base_test_result.ResultType
|
||||
|
||||
_PACKAGE_NAME='org.chromium.content_linker_test_apk'
|
||||
_ACTIVITY_NAME='.ContentLinkerTestActivity'
|
||||
|
@ -76,10 +57,23 @@ _COMMAND_LINE_FILE='/data/local/tmp/content-linker-test-command-line'
|
|||
# it is handy to have the 'content_android_linker' ones as well when
|
||||
# troubleshooting.
|
||||
_LOGCAT_FILTERS = [ '*:s', 'chromium:v', 'content_android_linker:v' ]
|
||||
#_LOGCAT_FILTERS = [ '*:v' ] ## DEBUG
|
||||
|
||||
# Regular expression used to match status lines in logcat.
|
||||
re_status_line = re.compile(r'(BROWSER|RENDERER)_LINKER_TEST: (FAIL|SUCCESS)')
|
||||
|
||||
# Regular expression used to mach library load addresses in logcat.
|
||||
re_library_address = re.compile(
|
||||
r'(BROWSER|RENDERER)_LIBRARY_ADDRESS: (\S+) ([0-9A-Fa-f]+)')
|
||||
|
||||
|
||||
def _WriteCommandLineFile(adb, command_line, command_line_file):
|
||||
"""Create a command-line file on the device. This does not use FlagChanger
|
||||
because its implementation assumes the device has 'su', and thus does
|
||||
not work at all with production devices."""
|
||||
adb.RunShellCommand('echo "%s" > %s' % (command_line, command_line_file))
|
||||
|
||||
|
||||
def _CheckLinkerTestStatus(logcat):
|
||||
"""Parse the content of |logcat| and checks for both a browser and
|
||||
renderer status line.
|
||||
|
@ -112,53 +106,42 @@ def _CheckLinkerTestStatus(logcat):
|
|||
return (False, None, None)
|
||||
|
||||
|
||||
def _CreateCommandLineFileOnDevice(adb, flags):
|
||||
changer = flag_changer.FlagChanger(adb, _COMMAND_LINE_FILE)
|
||||
changer.Set(flags)
|
||||
|
||||
|
||||
class LinkerTestCase(object):
|
||||
"""Base class for linker test cases."""
|
||||
|
||||
def __init__(self, test_name, is_low_memory=False):
|
||||
"""Create a test case initialized to run |test_name|.
|
||||
|
||||
def _WaitForLinkerTestStatus(adb, timeout):
|
||||
"""Wait up to |timeout| seconds until the full linker test status lines appear
|
||||
in the logcat being recorded with |adb|.
|
||||
Args:
|
||||
test_name: The name of the method to run as the test.
|
||||
is_low_memory: True to simulate a low-memory device, False otherwise.
|
||||
adb: An AndroidCommands instance. This assumes adb.StartRecordingLogcat()
|
||||
was called previously.
|
||||
timeout: Timeout in seconds.
|
||||
Returns:
|
||||
ResultType.TIMEOUT in case of timeout, ResulType.PASS if both status lines
|
||||
report 'SUCCESS', or ResulType.FAIL otherwise.
|
||||
"""
|
||||
self.test_name = test_name
|
||||
class_name = self.__class__.__name__
|
||||
self.qualified_name = '%s.%s' % (class_name, self.test_name)
|
||||
self.tagged_name = self.qualified_name
|
||||
self.is_low_memory = is_low_memory
|
||||
|
||||
def Run(self, device):
|
||||
margin = 8
|
||||
print '[ %-*s ] %s' % (margin, 'RUN', self.tagged_name)
|
||||
logging.info('Running linker test: %s', self.tagged_name)
|
||||
adb = android_commands.AndroidCommands(device)
|
||||
|
||||
# 1. Write command-line file with appropriate options.
|
||||
command_line_flags = []
|
||||
if self.is_low_memory:
|
||||
command_line_flags.append('--low-memory-device')
|
||||
_CreateCommandLineFileOnDevice(adb, command_line_flags)
|
||||
|
||||
# 2. Start recording logcat with appropriate filters.
|
||||
def _StartActivityAndWaitForLinkerTestStatus(adb, timeout):
|
||||
"""Force-start an activity and wait up to |timeout| seconds until the full
|
||||
linker test status lines appear in the logcat, recorded through |adb|.
|
||||
Args:
|
||||
adb: An AndroidCommands instance.
|
||||
timeout: Timeout in seconds
|
||||
Returns:
|
||||
A (status, logs) tuple, where status is a ResultType constant, and logs
|
||||
if the final logcat output as a string.
|
||||
"""
|
||||
# 1. Start recording logcat with appropriate filters.
|
||||
adb.StartRecordingLogcat(clear=True, filters=_LOGCAT_FILTERS)
|
||||
|
||||
try:
|
||||
# 3. Force-start activity.
|
||||
# 2. Force-start activity.
|
||||
adb.StartActivity(package=_PACKAGE_NAME,
|
||||
activity=_ACTIVITY_NAME,
|
||||
force_stop=True)
|
||||
|
||||
# 4. Wait up to 30 seconds until the linker test status is in the logcat.
|
||||
max_tries = 30
|
||||
# 3. Wait up to |timeout| seconds until the test status is in the logcat.
|
||||
num_tries = 0
|
||||
max_tries = timeout
|
||||
found = False
|
||||
logcat = None
|
||||
while num_tries < max_tries:
|
||||
time.sleep(1)
|
||||
num_tries += 1
|
||||
|
@ -168,37 +151,166 @@ class LinkerTestCase(object):
|
|||
break
|
||||
|
||||
finally:
|
||||
# Ensure the ADB polling process is always killed when
|
||||
# the script is interrupted by the user with Ctrl-C.
|
||||
logs = adb.StopRecordingLogcat()
|
||||
|
||||
results = base_test_result.TestRunResults()
|
||||
|
||||
if num_tries >= max_tries:
|
||||
# Timeout
|
||||
print '[ %*s ] %s' % (margin, 'TIMEOUT', self.tagged_name)
|
||||
results.AddResult(
|
||||
base_test_result.BaseTestResult(
|
||||
self.test_name,
|
||||
base_test_result.ResultType.TIMEOUT,
|
||||
logs))
|
||||
elif browser_ok and renderer_ok:
|
||||
# Passed
|
||||
logging.info(
|
||||
'Logcat start ---------------------------------\n%s' +
|
||||
'Logcat end -----------------------------------', logs)
|
||||
print '[ %*s ] %s' % (margin, 'OK', self.tagged_name)
|
||||
results.AddResult(
|
||||
base_test_result.BaseTestResult(
|
||||
self.test_name,
|
||||
base_test_result.ResultType.PASS))
|
||||
return ResultType.TIMEOUT, logs
|
||||
|
||||
if browser_ok and renderer_ok:
|
||||
return ResultType.PASS, logs
|
||||
|
||||
return ResultType.FAIL, logs
|
||||
|
||||
|
||||
class LibraryLoadMap(dict):
|
||||
"""A helper class to pretty-print a map of library names to load addresses."""
|
||||
def __str__(self):
|
||||
items = ['\'%s\': 0x%x' % (name, address) for \
|
||||
(name, address) in self.iteritems()]
|
||||
return '{%s}' % (', '.join(items))
|
||||
|
||||
def __repr__(self):
|
||||
return 'LibraryLoadMap(%s)' % self.__str__()
|
||||
|
||||
|
||||
class AddressList(list):
|
||||
"""A helper class to pretty-print a list of load addresses."""
|
||||
def __str__(self):
|
||||
items = ['0x%x' % address for address in self]
|
||||
return '[%s]' % (', '.join(items))
|
||||
|
||||
def __repr__(self):
|
||||
return 'AddressList(%s)' % self.__str__()
|
||||
|
||||
|
||||
def _ExtractLibraryLoadAddressesFromLogcat(logs):
|
||||
"""Extract the names and addresses of shared libraries loaded in the
|
||||
browser and renderer processes.
|
||||
Args:
|
||||
logs: A string containing logcat output.
|
||||
Returns:
|
||||
A tuple (browser_libs, renderer_libs), where each item is a map of
|
||||
library names (strings) to library load addresses (ints), for the
|
||||
browser and renderer processes, respectively.
|
||||
"""
|
||||
browser_libs = LibraryLoadMap()
|
||||
renderer_libs = LibraryLoadMap()
|
||||
for m in re_library_address.finditer(logs):
|
||||
process_type, lib_name, lib_address = m.groups()
|
||||
lib_address = int(lib_address, 16)
|
||||
if process_type == 'BROWSER':
|
||||
browser_libs[lib_name] = lib_address
|
||||
elif process_type == 'RENDERER':
|
||||
renderer_libs[lib_name] = lib_address
|
||||
else:
|
||||
print '[ %*s ] %s' % (margin, 'FAILED', self.tagged_name)
|
||||
# Failed
|
||||
assert False, 'Invalid process type'
|
||||
|
||||
return browser_libs, renderer_libs
|
||||
|
||||
|
||||
def _CheckLoadAddressRandomization(lib_map_list, process_type):
|
||||
"""Check that a map of library load addresses is random enough.
|
||||
Args:
|
||||
lib_map_list: a list of dictionaries that map library names (string)
|
||||
to load addresses (int). Each item in the list corresponds to a
|
||||
different run / process start.
|
||||
process_type: a string describing the process type.
|
||||
Returns:
|
||||
(status, logs) tuple, where <status> is True iff the load addresses are
|
||||
randomized, False otherwise, and <logs> is a string containing an error
|
||||
message detailing the libraries that are not randomized properly.
|
||||
"""
|
||||
# Collect, for each library, its list of load addresses.
|
||||
lib_addr_map = {}
|
||||
for lib_map in lib_map_list:
|
||||
for lib_name, lib_address in lib_map.iteritems():
|
||||
if lib_name not in lib_addr_map:
|
||||
lib_addr_map[lib_name] = AddressList()
|
||||
lib_addr_map[lib_name].append(lib_address)
|
||||
|
||||
logging.info('%s library load map: %s', process_type, lib_addr_map)
|
||||
|
||||
# For each library, check the randomness of its load addresses.
|
||||
bad_libs = {}
|
||||
success = True
|
||||
for lib_name, lib_address_list in lib_addr_map.iteritems():
|
||||
# If all addresses are different, skip to next item.
|
||||
lib_address_set = set(lib_address_list)
|
||||
# Consider that if there is more than one pair of identical addresses in
|
||||
# the list, then randomization is broken.
|
||||
if len(lib_address_set) < len(lib_address_list) - 1:
|
||||
bad_libs[lib_name] = lib_address_list
|
||||
|
||||
|
||||
if bad_libs:
|
||||
return False, '%s libraries failed randomization: %s' % \
|
||||
(process_type, bad_libs)
|
||||
|
||||
return True, '%s libraries properly randomized: %s' % \
|
||||
(process_type, lib_addr_map)
|
||||
|
||||
|
||||
class LinkerTestCaseBase(object):
|
||||
"""Base class for linker test cases."""
|
||||
|
||||
def __init__(self, is_low_memory=False):
|
||||
"""Create a test case.
|
||||
Args:
|
||||
is_low_memory: True to simulate a low-memory device, False otherwise.
|
||||
"""
|
||||
self.is_low_memory = is_low_memory
|
||||
if is_low_memory:
|
||||
test_suffix = 'ForLowMemoryDevice'
|
||||
else:
|
||||
test_suffix = 'ForRegularDevice'
|
||||
class_name = self.__class__.__name__
|
||||
self.qualified_name = '%s.%s' % (class_name, test_suffix)
|
||||
self.tagged_name = self.qualified_name
|
||||
|
||||
def _RunTest(self, adb):
|
||||
"""Run the test, must be overriden.
|
||||
Args:
|
||||
adb: An AndroidCommands instance to the device.
|
||||
Returns:
|
||||
A (status, log) tuple, where <status> is a ResultType constant, and <log>
|
||||
is the logcat output captured during the test in case of error, or None
|
||||
in case of success.
|
||||
"""
|
||||
return ResultType.FAIL, 'Unimplemented _RunTest() method!'
|
||||
|
||||
def Run(self, device):
|
||||
"""Run the test on a given device.
|
||||
Args:
|
||||
device: Name of target device where to run the test.
|
||||
Returns:
|
||||
A base_test_result.TestRunResult() instance.
|
||||
"""
|
||||
margin = 8
|
||||
print '[ %-*s ] %s' % (margin, 'RUN', self.tagged_name)
|
||||
logging.info('Running linker test: %s', self.tagged_name)
|
||||
adb = android_commands.AndroidCommands(device)
|
||||
|
||||
# Create command-line file on device.
|
||||
command_line_flags = ''
|
||||
if self.is_low_memory:
|
||||
command_line_flags = '--low-memory-device'
|
||||
_WriteCommandLineFile(adb, command_line_flags, _COMMAND_LINE_FILE)
|
||||
|
||||
# Run the test.
|
||||
status, logs = self._RunTest(adb)
|
||||
|
||||
result_text = 'OK'
|
||||
if status == ResultType.FAIL:
|
||||
result_text = 'FAILED'
|
||||
elif status == ResultType.TIMEOUT:
|
||||
result_text = 'TIMEOUT'
|
||||
print '[ %*s ] %s' % (margin, result_text, self.tagged_name)
|
||||
|
||||
results = base_test_result.TestRunResults()
|
||||
results.AddResult(
|
||||
base_test_result.BaseTestResult(
|
||||
self.test_name,
|
||||
base_test_result.ResultType.FAIL,
|
||||
self.tagged_name,
|
||||
status,
|
||||
logs))
|
||||
|
||||
return results
|
||||
|
@ -208,3 +320,184 @@ class LinkerTestCase(object):
|
|||
|
||||
def __repr__(self):
|
||||
return self.tagged_name
|
||||
|
||||
|
||||
class LinkerSharedRelroTest(LinkerTestCaseBase):
|
||||
"""A linker test case to check the status of shared RELRO sections.
|
||||
|
||||
The core of the checks performed here are pretty simple:
|
||||
|
||||
- Clear the logcat and start recording with an appropriate set of filters.
|
||||
- Create the command-line appropriate for the test-case.
|
||||
- Start the activity (always forcing a cold start).
|
||||
- Every second, look at the current content of the filtered logcat lines
|
||||
and look for instances of the following:
|
||||
|
||||
BROWSER_LINKER_TEST: <status>
|
||||
RENDERER_LINKER_TEST: <status>
|
||||
|
||||
where <status> can be either FAIL or SUCCESS. These lines can appear
|
||||
in any order in the logcat. Once both browser and renderer status are
|
||||
found, stop the loop. Otherwise timeout after 30 seconds.
|
||||
|
||||
Note that there can be other lines beginning with BROWSER_LINKER_TEST:
|
||||
and RENDERER_LINKER_TEST:, but are not followed by a <status> code.
|
||||
|
||||
- The test case passes if the <status> for both the browser and renderer
|
||||
process are SUCCESS. Otherwise its a fail.
|
||||
"""
|
||||
def _RunTest(self, adb):
|
||||
# Wait up to 30 seconds until the linker test status is in the logcat.
|
||||
return _StartActivityAndWaitForLinkerTestStatus(adb, timeout=30)
|
||||
|
||||
|
||||
class LinkerLibraryAddressTest(LinkerTestCaseBase):
|
||||
"""A test case that verifies library load addresses.
|
||||
|
||||
The point of this check is to ensure that the libraries are loaded
|
||||
according to the following rules:
|
||||
|
||||
- For low-memory devices, they should always be loaded at the same address
|
||||
in both browser and renderer processes, both below 0x4000_0000.
|
||||
|
||||
- For regular devices, the browser process should load libraries above
|
||||
0x4000_0000, and renderer ones below it.
|
||||
"""
|
||||
def _RunTest(self, adb):
|
||||
result, logs = _StartActivityAndWaitForLinkerTestStatus(adb, timeout=30)
|
||||
|
||||
# Return immediately in case of timeout.
|
||||
if result == ResultType.TIMEOUT:
|
||||
return result, logs
|
||||
|
||||
# Collect the library load addresses in the browser and renderer processes.
|
||||
browser_libs, renderer_libs = _ExtractLibraryLoadAddressesFromLogcat(logs)
|
||||
|
||||
logging.info('Browser libraries: %s', browser_libs)
|
||||
logging.info('Renderer libraries: %s', renderer_libs)
|
||||
|
||||
# Check that the same libraries are loaded into both processes:
|
||||
browser_set = set(browser_libs.keys())
|
||||
renderer_set = set(renderer_libs.keys())
|
||||
if browser_set != renderer_set:
|
||||
logging.error('Library set mistmach browser=%s renderer=%s',
|
||||
browser_libs.keys(), renderer_libs.keys())
|
||||
return ResultType.FAIL, logs
|
||||
|
||||
# And that there are not empty.
|
||||
if not browser_set:
|
||||
logging.error('No libraries loaded in any process!')
|
||||
return ResultType.FAIL, logs
|
||||
|
||||
# Check that the renderer libraries are loaded at 'low-addresses'. i.e.
|
||||
# below 0x4000_0000, for every kind of device.
|
||||
memory_boundary = 0x40000000
|
||||
bad_libs = []
|
||||
for lib_name, lib_address in renderer_libs.iteritems():
|
||||
if lib_address >= memory_boundary:
|
||||
bad_libs.append((lib_name, lib_address))
|
||||
|
||||
if bad_libs:
|
||||
logging.error('Renderer libraries loaded at high addresses: %s', bad_libs)
|
||||
return ResultType.FAIL, logs
|
||||
|
||||
if self.is_low_memory:
|
||||
# For low-memory devices, the libraries must all be loaded at the same
|
||||
# addresses. This also implicitly checks that the browser libraries are at
|
||||
# low addresses.
|
||||
addr_mismatches = []
|
||||
for lib_name, lib_address in browser_libs.iteritems():
|
||||
lib_address2 = renderer_libs[lib_name]
|
||||
if lib_address != lib_address2:
|
||||
addr_mismatches.append((lib_name, lib_address, lib_address2))
|
||||
|
||||
if addr_mismatches:
|
||||
logging.error('Library load address mismatches: %s',
|
||||
addr_mismatches)
|
||||
return ResultType.FAIL, logs
|
||||
|
||||
# For regular devices, check that libraries are loaded at 'high-addresses'.
|
||||
# Note that for low-memory devices, the previous checks ensure that they
|
||||
# were loaded at low-addresses.
|
||||
if not self.is_low_memory:
|
||||
bad_libs = []
|
||||
for lib_name, lib_address in browser_libs.iteritems():
|
||||
if lib_address < memory_boundary:
|
||||
bad_libs.append((lib_name, lib_address))
|
||||
|
||||
if bad_libs:
|
||||
logging.error('Browser libraries loaded at low addresses: %s', bad_libs)
|
||||
return ResultType.FAIL, logs
|
||||
|
||||
# Everything's ok.
|
||||
return ResultType.PASS, logs
|
||||
|
||||
|
||||
class LinkerRandomizationTest(LinkerTestCaseBase):
|
||||
"""A linker test case to check that library load address randomization works
|
||||
properly between successive starts of the test program/activity.
|
||||
|
||||
This starts the activity several time (each time forcing a new process
|
||||
creation) and compares the load addresses of the libraries in them to
|
||||
detect that they have changed.
|
||||
|
||||
In theory, two successive runs could (very rarely) use the same load
|
||||
address, so loop 5 times and compare the values there. It is assumed
|
||||
that if there are more than one pair of identical addresses, then the
|
||||
load addresses are not random enough for this test.
|
||||
"""
|
||||
def _RunTest(self, adb):
|
||||
max_loops = 5
|
||||
browser_lib_map_list = []
|
||||
renderer_lib_map_list = []
|
||||
logs_list = []
|
||||
for loop in range(max_loops):
|
||||
# Start the activity.
|
||||
result, logs = _StartActivityAndWaitForLinkerTestStatus(adb, timeout=30)
|
||||
if result == ResultType.TIMEOUT:
|
||||
# Something bad happened. Return immediately.
|
||||
return result, logs
|
||||
|
||||
# Collect library addresses.
|
||||
browser_libs, renderer_libs = _ExtractLibraryLoadAddressesFromLogcat(logs)
|
||||
browser_lib_map_list.append(browser_libs)
|
||||
renderer_lib_map_list.append(renderer_libs)
|
||||
logs_list.append(logs)
|
||||
|
||||
# Check randomization in the browser libraries.
|
||||
logs = '\n'.join(logs_list)
|
||||
|
||||
browser_status, browser_logs = _CheckLoadAddressRandomization(
|
||||
browser_lib_map_list, 'Browser')
|
||||
|
||||
renderer_status, renderer_logs = _CheckLoadAddressRandomization(
|
||||
renderer_lib_map_list, 'Renderer')
|
||||
|
||||
if not browser_status:
|
||||
if self.is_low_memory:
|
||||
return ResultType.FAIL, browser_logs
|
||||
|
||||
# IMPORTANT NOTE: The system's ASLR implementation seems to be very poor
|
||||
# when starting an activity process in a loop with "adb shell am start".
|
||||
#
|
||||
# When simulating a regular device, loading libraries in the browser
|
||||
# process uses a simple mmap(NULL, ...) to let the kernel device where to
|
||||
# load the file (this is similar to what System.loadLibrary() does).
|
||||
#
|
||||
# Unfortunately, at least in the context of this test, doing so while
|
||||
# restarting the activity with the activity manager very, very, often
|
||||
# results in the system using the same load address for all 5 runs, or
|
||||
# sometimes only 4 out of 5.
|
||||
#
|
||||
# This has been tested experimentally on both Android 4.1.2 and 4.3.
|
||||
#
|
||||
# Note that this behaviour doesn't seem to happen when starting an
|
||||
# application 'normally', i.e. when using the application launcher to
|
||||
# start the activity.
|
||||
logging.info('Ignoring system\'s low randomization of browser libraries' +
|
||||
' for regular devices')
|
||||
|
||||
if not renderer_status:
|
||||
return ResultType.FAIL, renderer_logs
|
||||
|
||||
return ResultType.PASS, logs
|
||||
|
|
|
@ -80,14 +80,14 @@ class LinkerTestRunner(base_test_runner.BaseTestRunner):
|
|||
"""Sets up and runs a test case.
|
||||
|
||||
Args:
|
||||
test: An object which is ostensibly a subclass of LinkerTestCase.
|
||||
test: An object which is ostensibly a subclass of LinkerTestCaseBase.
|
||||
|
||||
Returns:
|
||||
A TestRunResults object which contains the result produced by the test
|
||||
and, in the case of a failure, the test that should be retried.
|
||||
"""
|
||||
|
||||
assert isinstance(test, test_case.LinkerTestCase)
|
||||
assert isinstance(test, test_case.LinkerTestCaseBase)
|
||||
|
||||
try:
|
||||
results = test.Run(self.device)
|
||||
|
|
|
@ -121,6 +121,8 @@ def AddLinkerTestOptions(option_parser):
|
|||
option_parser.commands_dict = {}
|
||||
option_parser.example = '%prog linker'
|
||||
|
||||
option_parser.add_option('-f', '--gtest-filter', dest='test_filter',
|
||||
help='googletest-style filter string.')
|
||||
AddCommonOptions(option_parser)
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче