# Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #
# Directory containing ART tests within an ART APEX (if the package includes # any). ART test executables are installed in `bin/art/<arch>`. Segregating # tests by architecture is useful on devices supporting more than one # architecture, as it permits testing all of them using a single ART APEX # package.
ART_TEST_DIR = 'bin/art'
# Test if a given variable is set to a string "true". def isEnvTrue(var): return var in os.environ and os.environ[var] == 'true'
def get(self, path):
apex_dir, name = os.path.split(path) ifnot apex_dir:
apex_dir = '.'
apex_map = self.read_dir(apex_dir) return apex_map[name] if name in apex_map elseNone
def read_dir(self, apex_dir): if apex_dir in self._folder_cache: return self._folder_cache[apex_dir]
apex_map = {}
dirname = os.path.join(self._apex, apex_dir) if os.path.exists(dirname): for basename in os.listdir(dirname):
filepath = os.path.join(dirname, basename)
is_dir = os.path.isdir(filepath)
is_exec = os.access(filepath, os.X_OK)
is_symlink = os.path.islink(filepath) if is_symlink: # Report the length of the symlink's target's path as file size, like `ls`.
size = len(os.readlink(filepath)) else:
size = os.path.getsize(filepath)
apex_map[basename] = FSObject(basename, is_dir, is_exec, is_symlink, size)
self._folder_cache[apex_dir] = apex_map return apex_map
# DO NOT USE DIRECTLY! This is an "abstract" base class. class Checker: def __init__(self, provider):
self._provider = provider
self._errors = 0
self._expected_file_globs = set()
def is_file(self, path):
fs_object = self._provider.get(path) if fs_object isNone: returnFalse, 'Could not find %s' if fs_object.is_dir: returnFalse, '%s is a directory' if fs_object.is_symlink: returnFalse, '%s is a symlink' returnTrue, ''
def is_dir(self, path):
fs_object = self._provider.get(path) if fs_object isNone: returnFalse, 'Could not find %s' ifnot fs_object.is_dir: returnFalse, '%s is not a directory' returnTrue, ''
def check_executable(self, filename):
path = 'bin/%s' % filename ifnot self.check_file(path): return ifnot self._provider.get(path).is_exec:
self.fail('%s is not executable', path)
def check_executable_symlink(self, filename):
path = 'bin/%s' % filename
fs_object = self._provider.get(path) if fs_object isNone:
self.fail('Could not find %s', path) return if fs_object.is_dir:
self.fail('%s is a directory', path) return ifnot fs_object.is_symlink:
self.fail('%s is not a symlink', path)
self._expected_file_globs.add(path)
def arch_dirs_for_path(self, path, multilib=None): # Look for target-specific subdirectories for the given directory path. # This is needed because the list of build targets is not propagated # to this script. # # TODO(b/123602136): Pass build target information to this script and fix # all places where this function in used (or similar workarounds).
dirs = [] for archs_per_bitness in self.possible_archs_per_bitness(multilib):
found_dir = False for arch in archs_per_bitness:
dir = '%s/%s' % (path, arch)
found, _ = self.is_dir(dir) if found:
found_dir = True
dirs.append(dir) # At least one arch directory per bitness must exist. ifnot found_dir:
self.fail('Arch directories missing in %s - expected at least one of %s',
path, ', '.join(archs_per_bitness)) return dirs
def check_art_test_executable(self, filename, multilib=None): for dir in self.arch_dirs_for_path(ART_TEST_DIR, multilib):
test_path = '%s/%s' % (dir, filename)
self._expected_file_globs.add(test_path)
file_obj = self._provider.get(test_path) ifnot file_obj:
self.fail('ART test binary missing: %s', test_path) elifnot file_obj.is_exec:
self.fail('%s is not executable', test_path)
def check_art_test_data(self, filename): for dir in self.arch_dirs_for_path(ART_TEST_DIR): ifnot self.check_file('%s/%s' % (dir, filename)): return
def check_optional_art_test_executable(self, filename): for archs_per_bitness in self.possible_archs_per_bitness(): for arch in archs_per_bitness:
self.ignore_path('%s/%s/%s' % (ART_TEST_DIR, arch, filename))
def check_no_superfluous_files(self): def recurse(dir_path):
paths = [] for name, fsobj in sorted(self._provider.read_dir(dir_path).items(), key=lambda p: p[0]): if name in ('.', '..'): continue
path = os.path.join(dir_path, name)
paths.append(path) if fsobj.is_dir:
recurse(path) for path_glob in self._expected_file_globs:
paths = [path for path in paths ifnot fnmatch.fnmatch(path, path_glob)] for unexpected_path in paths:
fs_object = self._provider.get(unexpected_path)
self.fail('Unexpected %s: %s', fs_object.type_descr(), unexpected_path)
recurse('')
# Just here for docs purposes, even if it isn't good Python style.
def check_native_library(self, basename): # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve # the precision of this test?
self.check_file('lib/%s.so' % basename)
def check_native_library(self, basename): # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve # the precision of this test?
self.check_file('lib64/%s.so' % basename)
def check_native_library(self, basename): # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve # the precision of this test?
self.check_file('lib/%s.so' % basename)
self.check_file('lib64/%s.so' % basename)
# Check flagging files that don't get added in builds on master-art. # TODO(b/345713436): Make flags work on master-art.
self._checker.check_optional_file('etc/aconfig_flags.pb')
self._checker.check_optional_file('etc/flag.info')
self._checker.check_optional_file('etc/flag.map')
self._checker.check_optional_file('etc/flag.val')
self._checker.check_optional_file('etc/package.map')
# Check exported libraries for ART.
self._checker.check_native_library('libdexfile')
self._checker.check_native_library('libnativebridge')
self._checker.check_native_library('libnativehelper')
self._checker.check_native_library('libnativeloader')
# Check internal libraries for ART.
self._checker.check_native_library('libadbconnection')
self._checker.check_native_library('libart')
self._checker.check_native_library('libart-disassembler')
self._checker.check_native_library('libartbase')
self._checker.check_native_library('libartpalette')
self._checker.check_native_library('libdt_fd_forward')
self._checker.check_native_library('libopenjdkjvm')
self._checker.check_native_library('libopenjdkjvmti')
self._checker.check_native_library('libperfetto_hprof')
self._checker.check_native_library('libprofile')
self._checker.check_native_library('libsigchain')
self._checker.check_prefer64_library('libartservice')
self._checker.check_prefer64_library('libarttools') # Only on ARM/ARM64
self._checker.check_optional_native_library('libart-simulator-container')
# Check internal Java libraries for ART.
self._checker.check_java_library('service-art')
self._checker.check_file('javalib/service-art.jar.prof')
# Check exported native libraries for Managed Core Library.
self._checker.check_native_library('libandroidio')
# Check Java libraries for Managed Core Library.
self._checker.check_java_library('apache-xml')
self._checker.check_java_library('bouncycastle')
self._checker.check_java_library('core-libart')
self._checker.check_java_library('core-oj')
self._checker.check_java_library('okhttp') if isEnvTrue('EMMA_INSTRUMENT_FRAMEWORK'): # In coverage builds jacoco is added to the list of ART apex jars.
self._checker.check_java_library('jacocoagent')
# Check internal native library dependencies. # # Any internal dependency not listed here will cause a failure in # NoSuperfluousFilesChecker. Internal dependencies are generally just # implementation details, but in the release package we want to track them # because a) they add to the package size and the RAM usage (in particular # if the library is also present in /system or another APEX and hence might # get loaded twice through linker namespace separation), and b) we need to # catch invalid dependencies on /system or other APEXes that should go # through an exported library with stubs (b/128708192 tracks implementing a # better approach for that).
self._checker.check_native_library('libbase')
self._checker.check_native_library('libc++')
self._checker.check_native_library('libdt_socket')
self._checker.check_native_library('libexpat')
self._checker.check_native_library('libjdwp')
self._checker.check_native_library('liblz4')
self._checker.check_native_library('liblzma')
self._checker.check_native_library('libnpt')
self._checker.check_native_library('libunwindstack')
# Allow extra dependencies that appear in ASAN builds.
self._checker.check_optional_native_library('libclang_rt.asan*')
self._checker.check_optional_native_library('libclang_rt.hwasan*')
self._checker.check_optional_native_library('libclang_rt.ubsan*')
class DebugChecker: def __init__(self, checker):
self._checker = checker
def __str__(self): return'Debug Checker'
def run(self): # Check binaries for ART.
self._checker.check_executable('dexanalyze')
self._checker.check_symlinked_multilib_executable('imgdiag')
# Check exported libraries for ART.
self._checker.check_native_library('libdexfiled')
# Check internal libraries for ART.
self._checker.check_native_library('libadbconnectiond')
self._checker.check_native_library('libartbased')
self._checker.check_native_library('libartd')
self._checker.check_native_library('libartd-disassembler')
self._checker.check_native_library('libopenjdkjvmd')
self._checker.check_native_library('libopenjdkjvmtid')
self._checker.check_native_library('libperfetto_hprofd')
self._checker.check_native_library('libprofiled')
self._checker.check_prefer64_library('libartserviced') # Only on ARM/ARM64
self._checker.check_optional_native_library('libartd-simulator-container')
self._checker.check_optional_native_library('libartd-simulator')
# Check internal libraries for Managed Core Library.
self._checker.check_native_library('libopenjdkd')
# Check internal native library dependencies. # # Like in the release package, we check that we don't get other dependencies # besides those listed here. In this case the concern is not bloat, but # rather that we don't get behavioural differences between user (release) # and userdebug/eng builds, which could happen if the debug package has # duplicate library instances where releases don't. In other words, it's # uncontroversial to add debug-only dependencies, as long as they don't make # assumptions on having a single global state (ideally they should have # double_loadable:true, cf. go/double_loadable). Also, like in the release # package we need to look out for dependencies that should go through # exported library stubs (until b/128708192 is fixed). # # (There are currently no debug-only native libraries.)
class TestingChecker: def __init__(self, checker):
self._checker = checker
def __str__(self): return'Testing Checker'
def run(self): # Check test directories.
self._checker.check_dir(ART_TEST_DIR) for arch_dir in self._checker.arch_dirs_for_path(ART_TEST_DIR):
self._checker.check_dir(arch_dir)
# Some libraries are in odd location (libarttest(d) and libtiagent(d)). # We intend to remove the whole testing apex, so just ignore those for now.
self._checker.ignore_path('lib*/com.android.art')
self._checker.ignore_path('lib*/com.android.art/lib*')
# Check ART test tools.
self._checker.check_executable('signal_dumper')
@staticmethod def get_vertical(has_next_list):
string = '' for v in has_next_list:
string += '%s ' % ('│'if v else' ') return string
@staticmethod def get_last_vertical(last): return'└── 'if last else'├── '
def print_tree(self):
def print_tree_rec(path):
apex_map = self._provider.read_dir(path) if apex_map isNone: return
apex_map = dict(apex_map) if'.'in apex_map: del apex_map['.'] if'..'in apex_map: del apex_map['..']
key_list = list(sorted(apex_map.keys())) for i, key in enumerate(key_list):
prev = self.get_vertical(self._has_next_list)
last = self.get_last_vertical(i == len(key_list) - 1)
val = apex_map[key] if self._print_size: if val.size < 0:
print('%s%s[ n/a ] %s' % (prev, last, val.name)) else:
print('%s%s[%11d] %s' % (prev, last, val.size, val.name)) else:
print('%s%s%s' % (prev, last, val.name)) if val.is_dir:
self._has_next_list.append(i < len(key_list) - 1)
val_path = os.path.join(path, val.name)
print_tree_rec(val_path)
self._has_next_list.pop()
print_tree_rec('')
# Note: do not sys.exit early, for __del__ cleanup. def art_apex_test_main(test_args): if test_args.list and test_args.tree:
logging.error('Both of --list and --tree set') return1 if test_args.size andnot (test_args.list or test_args.tree):
logging.error('--size set but neither --list nor --tree set') return1 ifnot test_args.flattened andnot test_args.tmpdir:
logging.error('Need a tmpdir.') return1 ifnot test_args.flattened: ifnot test_args.deapexer:
logging.error('Need deapexer.') return1 ifnot test_args.debugfs:
logging.error('Need debugfs.') return1 ifnot test_args.fsckerofs:
logging.error('Need fsck.erofs.') return1
if test_args.flavor == FLAVOR_AUTO:
logging.warning('--flavor=auto, trying to autodetect. This may be incorrect!') # The order of flavors in the list below matters, as the release tag (empty string) will # match any package name. for flavor in [ FLAVOR_DEBUG, FLAVOR_TESTING, FLAVOR_RELEASE ]:
flavor_tag = flavor # Special handling for the release flavor, whose name is no longer part of the Release ART # APEX file name (`com.android.art.capex` / `com.android.art`). if flavor == FLAVOR_RELEASE:
flavor_tag = ''
flavor_pattern = '*.%s*' % flavor_tag if fnmatch.fnmatch(test_args.apex, flavor_pattern):
test_args.flavor = flavor
logging.warning(' Detected %s flavor', flavor) break if test_args.flavor == FLAVOR_AUTO:
logging.error(' Could not detect APEX flavor, neither %s, %s nor %s for \'%s\'',
FLAVOR_RELEASE, FLAVOR_DEBUG, FLAVOR_TESTING, test_args.apex) return1
apex_dir = test_args.apex ifnot test_args.flattened: # Extract the apex. It would be nice to use the output from "deapexer list" # to avoid this work, but it doesn't provide info about executable bits.
apex_dir = extract_apex(test_args.apex, test_args.deapexer, test_args.debugfs,
test_args.fsckerofs, test_args.tmpdir)
apex_provider = TargetApexProvider(apex_dir)
if test_args.tree:
Tree(apex_provider, test_args.apex, test_args.size).print_tree() return0 if test_args.list:
List(apex_provider, test_args.size).print_list() return0
checkers = [] if test_args.bitness == BITNESS_AUTO:
logging.warning('--bitness=auto, trying to autodetect. This may be incorrect!')
has_32 = apex_provider.get('lib') isnotNone
has_64 = apex_provider.get('lib64') isnotNone if has_32 and has_64:
logging.warning(' Detected multilib')
test_args.bitness = BITNESS_MULTILIB elif has_32:
logging.warning(' Detected 32-only')
test_args.bitness = BITNESS_32 elif has_64:
logging.warning(' Detected 64-only')
test_args.bitness = BITNESS_64 else:
logging.error(' Could not detect bitness, neither lib nor lib64 contained.')
List(apex_provider).print_list() return1
checkers.append(ReleaseChecker(base_checker)) if test_args.flavor == FLAVOR_DEBUG or test_args.flavor == FLAVOR_TESTING:
checkers.append(DebugChecker(base_checker)) if test_args.flavor == FLAVOR_TESTING:
checkers.append(TestingChecker(base_checker))
# This checker must be last.
checkers.append(NoSuperfluousFilesChecker(base_checker))
failed = False for checker in checkers:
logging.info('%s...', checker)
checker.run() if base_checker.error_count() > 0:
logging.error('%s FAILED', checker)
failed = True else:
logging.info('%s SUCCEEDED', checker)
base_checker.reset_errors()
# TODO: Add support for flattened APEX packages.
configs = [
{'name': 'com.android.art.capex', 'flavor': FLAVOR_RELEASE},
{'name': 'com.android.art.debug.capex', 'flavor': FLAVOR_DEBUG}, # Note: The Testing ART APEX is not a Compressed APEX.
{'name': 'com.android.art.testing.apex', 'flavor': FLAVOR_TESTING},
]
parser.add_argument('--tmpdir', help='Directory for temp files')
parser.add_argument('--deapexer', help='Path to deapexer')
parser.add_argument('--debugfs', help='Path to debugfs')
parser.add_argument('--fsckerofs', help='Path to fsck.erofs')
parser.add_argument('--bitness', help='Bitness to check', choices=BITNESS_ALL,
default=BITNESS_AUTO)
if len(sys.argv) == 1:
art_apex_test_default(parser) else:
args = parser.parse_args()
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.