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

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

Issue 23717023: Android: Add chrome-specific dynamic linker. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update content_tests.gypi Created 7 years, 2 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
« no previous file with comments | « build/android/pylib/linker/setup.py ('k') | build/java_apk.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 36
37 import logging 37 import logging
38 import os 38 import os
39 import re 39 import re
40 import StringIO 40 import StringIO
41 import subprocess 41 import subprocess
42 import tempfile 42 import tempfile
43 import time 43 import time
44 44
45 from pylib import constants
45 from pylib import android_commands 46 from pylib import android_commands
46 from pylib import flag_changer 47 from pylib import flag_changer
47 from pylib.base import base_test_result 48 from pylib.base import base_test_result
48 49
49 ResultType = base_test_result.ResultType 50 ResultType = base_test_result.ResultType
50 51
51 _PACKAGE_NAME='org.chromium.content_linker_test_apk' 52 _PACKAGE_NAME='org.chromium.content_linker_test_apk'
52 _ACTIVITY_NAME='.ContentLinkerTestActivity' 53 _ACTIVITY_NAME='.ContentLinkerTestActivity'
53 _COMMAND_LINE_FILE='/data/local/tmp/content-linker-test-command-line' 54 _COMMAND_LINE_FILE='/data/local/tmp/content-linker-test-command-line'
54 55
56 # Path to the Linker.java source file.
57 _LINKER_JAVA_SOURCE_PATH = \
58 'content/public/android/java/src/org/chromium/content/app/Linker.java'
59
60 # A regular expression used to extract the browser shared RELRO configuration
61 # from the Java source file above.
62 _RE_LINKER_BROWSER_CONFIG = \
63 re.compile(r'.*BROWSER_SHARED_RELRO_CONFIG\s+=\s+' + \
64 'BROWSER_SHARED_RELRO_CONFIG_(\S+)\s*;.*',
65 re.MULTILINE | re.DOTALL)
66
55 # 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
56 # 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
57 # it is handy to have the 'content_android_linker' ones as well when 69 # it is handy to have the 'content_android_linker' ones as well when
58 # troubleshooting. 70 # troubleshooting.
59 _LOGCAT_FILTERS = [ '*:s', 'chromium:v', 'content_android_linker:v' ] 71 _LOGCAT_FILTERS = [ '*:s', 'chromium:v', 'content_android_linker:v' ]
60 #_LOGCAT_FILTERS = [ '*:v' ] ## DEBUG 72 #_LOGCAT_FILTERS = [ '*:v' ] ## DEBUG
61 73
62 # Regular expression used to match status lines in logcat. 74 # Regular expression used to match status lines in logcat.
63 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)')
64 76
65 # Regular expression used to mach library load addresses in logcat. 77 # Regular expression used to mach library load addresses in logcat.
66 re_library_address = re.compile( 78 re_library_address = re.compile(
67 r'(BROWSER|RENDERER)_LIBRARY_ADDRESS: (\S+) ([0-9A-Fa-f]+)') 79 r'(BROWSER|RENDERER)_LIBRARY_ADDRESS: (\S+) ([0-9A-Fa-f]+)')
68 80
69 81
82 def _GetBrowserSharedRelroConfig():
83 """Returns a string corresponding to the Linker's configuration of shared
84 RELRO sections in the browser process. This parses the Java linker source
85 file to get the appropriate information.
86 Return:
87 None in case of error (e.g. could not locate the source file).
88 'NEVER' if the browser process shall never use shared RELROs.
89 'LOW_RAM_ONLY' if if uses it only on low-end devices.
90 'ALWAYS' if it always uses a shared RELRO.
91 """
92 source_path = \
93 os.path.join(constants.DIR_SOURCE_ROOT, _LINKER_JAVA_SOURCE_PATH)
94 if not os.path.exists(source_path):
95 logging.error('Could not find linker source file: ' + source_path)
96 return None
97
98 with open(source_path) as f:
99 configs = _RE_LINKER_BROWSER_CONFIG.findall(f.read())
100 if not configs:
101 logging.error(
102 'Can\'t find browser shared RELRO configuration value in ' + \
103 source_path)
104 return None
105
106 if configs[0] not in ['NEVER', 'LOW_RAM_ONLY', 'ALWAYS']:
107 logging.error('Unexpected browser config value: ' + configs[0])
108 return None
109
110 logging.info('Found linker browser shared RELRO config: ' + configs[0])
111 return configs[0]
112
113
70 def _WriteCommandLineFile(adb, command_line, command_line_file): 114 def _WriteCommandLineFile(adb, command_line, command_line_file):
71 """Create a command-line file on the device. This does not use FlagChanger 115 """Create a command-line file on the device. This does not use FlagChanger
72 because its implementation assumes the device has 'su', and thus does 116 because its implementation assumes the device has 'su', and thus does
73 not work at all with production devices.""" 117 not work at all with production devices."""
74 adb.RunShellCommand('echo "%s" > %s' % (command_line, command_line_file)) 118 adb.RunShellCommand('echo "%s" > %s' % (command_line, command_line_file))
75 119
76 120
77 def _CheckLinkerTestStatus(logcat): 121 def _CheckLinkerTestStatus(logcat):
78 """Parse the content of |logcat| and checks for both a browser and 122 """Parse the content of |logcat| and checks for both a browser and
79 renderer status line. 123 renderer status line.
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after
394 memory_boundary = 0x40000000 438 memory_boundary = 0x40000000
395 bad_libs = [] 439 bad_libs = []
396 for lib_name, lib_address in renderer_libs.iteritems(): 440 for lib_name, lib_address in renderer_libs.iteritems():
397 if lib_address >= memory_boundary: 441 if lib_address >= memory_boundary:
398 bad_libs.append((lib_name, lib_address)) 442 bad_libs.append((lib_name, lib_address))
399 443
400 if bad_libs: 444 if bad_libs:
401 logging.error('Renderer libraries loaded at high addresses: %s', bad_libs) 445 logging.error('Renderer libraries loaded at high addresses: %s', bad_libs)
402 return ResultType.FAIL, logs 446 return ResultType.FAIL, logs
403 447
404 if self.is_low_memory: 448 browser_config = _GetBrowserSharedRelroConfig()
405 # For low-memory devices, the libraries must all be loaded at the same 449 if not browser_config:
406 # addresses. This also implicitly checks that the browser libraries are at 450 return ResultType.FAIL, 'Bad linker source configuration'
407 # low addresses. 451
452 if browser_config == 'ALWAYS' or \
453 (browser_config == 'LOW_RAM_ONLY' and self.is_low_memory):
454 # The libraries must all be loaded at the same addresses. This also
455 # implicitly checks that the browser libraries are at low addresses.
408 addr_mismatches = [] 456 addr_mismatches = []
409 for lib_name, lib_address in browser_libs.iteritems(): 457 for lib_name, lib_address in browser_libs.iteritems():
410 lib_address2 = renderer_libs[lib_name] 458 lib_address2 = renderer_libs[lib_name]
411 if lib_address != lib_address2: 459 if lib_address != lib_address2:
412 addr_mismatches.append((lib_name, lib_address, lib_address2)) 460 addr_mismatches.append((lib_name, lib_address, lib_address2))
413 461
414 if addr_mismatches: 462 if addr_mismatches:
415 logging.error('Library load address mismatches: %s', 463 logging.error('Library load address mismatches: %s',
416 addr_mismatches) 464 addr_mismatches)
417 return ResultType.FAIL, logs 465 return ResultType.FAIL, logs
418 466
419 # For regular devices, check that libraries are loaded at 'high-addresses'. 467 # Otherwise, check that libraries are loaded at 'high-addresses'.
420 # Note that for low-memory devices, the previous checks ensure that they 468 # Note that for low-memory devices, the previous checks ensure that they
421 # were loaded at low-addresses. 469 # were loaded at low-addresses.
422 if not self.is_low_memory: 470 else:
423 bad_libs = [] 471 bad_libs = []
424 for lib_name, lib_address in browser_libs.iteritems(): 472 for lib_name, lib_address in browser_libs.iteritems():
425 if lib_address < memory_boundary: 473 if lib_address < memory_boundary:
426 bad_libs.append((lib_name, lib_address)) 474 bad_libs.append((lib_name, lib_address))
427 475
428 if bad_libs: 476 if bad_libs:
429 logging.error('Browser libraries loaded at low addresses: %s', bad_libs) 477 logging.error('Browser libraries loaded at low addresses: %s', bad_libs)
430 return ResultType.FAIL, logs 478 return ResultType.FAIL, logs
431 479
432 # Everything's ok. 480 # Everything's ok.
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
466 514
467 # Check randomization in the browser libraries. 515 # Check randomization in the browser libraries.
468 logs = '\n'.join(logs_list) 516 logs = '\n'.join(logs_list)
469 517
470 browser_status, browser_logs = _CheckLoadAddressRandomization( 518 browser_status, browser_logs = _CheckLoadAddressRandomization(
471 browser_lib_map_list, 'Browser') 519 browser_lib_map_list, 'Browser')
472 520
473 renderer_status, renderer_logs = _CheckLoadAddressRandomization( 521 renderer_status, renderer_logs = _CheckLoadAddressRandomization(
474 renderer_lib_map_list, 'Renderer') 522 renderer_lib_map_list, 'Renderer')
475 523
524 browser_config = _GetBrowserSharedRelroConfig()
525 if not browser_config:
526 return ResultType.FAIL, 'Bad linker source configuration'
527
476 if not browser_status: 528 if not browser_status:
477 if self.is_low_memory: 529 if browser_config == 'ALWAYS' or \
478 return ResultType.FAIL, browser_logs 530 (browser_config == 'LOW_RAM_ONLY' and self.is_low_memory):
531 return ResultType.FAIL, browser_logs
479 532
480 # IMPORTANT NOTE: The system's ASLR implementation seems to be very poor 533 # IMPORTANT NOTE: The system's ASLR implementation seems to be very poor
481 # when starting an activity process in a loop with "adb shell am start". 534 # when starting an activity process in a loop with "adb shell am start".
482 # 535 #
483 # When simulating a regular device, loading libraries in the browser 536 # When simulating a regular device, loading libraries in the browser
484 # process uses a simple mmap(NULL, ...) to let the kernel device where to 537 # process uses a simple mmap(NULL, ...) to let the kernel device where to
485 # load the file (this is similar to what System.loadLibrary() does). 538 # load the file (this is similar to what System.loadLibrary() does).
486 # 539 #
487 # Unfortunately, at least in the context of this test, doing so while 540 # Unfortunately, at least in the context of this test, doing so while
488 # restarting the activity with the activity manager very, very, often 541 # restarting the activity with the activity manager very, very, often
489 # results in the system using the same load address for all 5 runs, or 542 # results in the system using the same load address for all 5 runs, or
490 # sometimes only 4 out of 5. 543 # sometimes only 4 out of 5.
491 # 544 #
492 # This has been tested experimentally on both Android 4.1.2 and 4.3. 545 # This has been tested experimentally on both Android 4.1.2 and 4.3.
493 # 546 #
494 # 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
495 # application 'normally', i.e. when using the application launcher to 548 # application 'normally', i.e. when using the application launcher to
496 # start the activity. 549 # start the activity.
497 logging.info('Ignoring system\'s low randomization of browser libraries' + 550 logging.info('Ignoring system\'s low randomization of browser libraries' +
498 ' for regular devices') 551 ' for regular devices')
499 552
500 if not renderer_status: 553 if not renderer_status:
501 return ResultType.FAIL, renderer_logs 554 return ResultType.FAIL, renderer_logs
502 555
503 return ResultType.PASS, logs 556 return ResultType.PASS, logs
557
558
559 class LinkerLowMemoryThresholdTest(LinkerTestCaseBase):
560 """This test checks that the definitions for the low-memory device physical
561 RAM threshold are identical in the base/ and linker sources. Because these
562 two components should absolutely not depend on each other, it's difficult
563 to perform this check correctly at runtime inside the linker test binary
564 without introducing hairy dependency issues in the build, or complicated
565 plumbing at runtime.
566
567 To work-around this, this test looks directly into the sources for a
568 definition of the same constant that should look like:
569
570 #define ANDROID_LOW_MEMORY_DEVICE_THRESHOLD_MB <number>
571
572 And will check that the values for <number> are identical in all of
573 them."""
574
575 # A regular expression used to find the definition of the threshold in all
576 # sources:
577 _RE_THRESHOLD_DEFINITION = re.compile(
578 r'^\s*#\s*define\s+ANDROID_LOW_MEMORY_DEVICE_THRESHOLD_MB\s+(\d+)\s*$',
579 re.MULTILINE)
580
581 # The list of source files, relative to DIR_SOURCE_ROOT, which must contain
582 # a line that matches the re above.
583 _SOURCES_LIST = [
584 'base/android/sys_utils.cc',
585 'content/common/android/linker/linker_jni.cc' ]
586
587 def _RunTest(self, adb):
588 failure = False
589 values = []
590 # First, collect all the values in all input sources.
591 re = LinkerLowMemoryThresholdTest._RE_THRESHOLD_DEFINITION
592 for source in LinkerLowMemoryThresholdTest._SOURCES_LIST:
593 source_path = os.path.join(constants.DIR_SOURCE_ROOT, source);
594 if not os.path.exists(source_path):
595 logging.error('Missing source file: ' + source_path)
596 failure = True
597 continue
598 with open(source_path) as f:
599 source_text = f.read()
600 # For some reason, re.match() never works here.
601 source_values = re.findall(source_text)
602 if not source_values:
603 logging.error('Missing low-memory threshold definition in ' + \
604 source_path)
605 logging.error('Source:\n%s\n' % source_text)
606 failure = True
607 continue
608 values += source_values
609
610 # Second, check that they are all the same.
611 if not failure:
612 for value in values[1:]:
613 if value != values[0]:
614 logging.error('Value mismatch: ' + repr(values))
615 failure = True
616
617 if failure:
618 return ResultType.FAIL, 'Incorrect low-end memory threshold definitions!'
619
620 return ResultType.PASS, ''
OLDNEW
« no previous file with comments | « build/android/pylib/linker/setup.py ('k') | build/java_apk.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698