1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4# Copyright (C) 2019 The Android Open Source Project 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19import argparse 20import fnmatch 21import logging 22import os 23import os.path 24import shutil 25import subprocess 26import sys 27import zipfile 28 29logging.basicConfig(format='%(message)s') 30 31# Flavors of ART APEX package. 32FLAVOR_RELEASE = 'release' 33FLAVOR_DEBUG = 'debug' 34FLAVOR_TESTING = 'testing' 35FLAVOR_AUTO = 'auto' 36FLAVORS_ALL = [FLAVOR_RELEASE, FLAVOR_DEBUG, FLAVOR_TESTING, FLAVOR_AUTO] 37 38# Bitness options for APEX package 39BITNESS_32 = '32' 40BITNESS_64 = '64' 41BITNESS_MULTILIB = 'multilib' 42BITNESS_AUTO = 'auto' 43BITNESS_ALL = [BITNESS_32, BITNESS_64, BITNESS_MULTILIB, BITNESS_AUTO] 44 45# Architectures supported by APEX packages. 46ARCHS_32 = ["arm", "x86"] 47ARCHS_64 = ["arm64", "x86_64"] 48 49# Multilib options 50MULTILIB_32 = '32' 51MULTILIB_64 = '64' 52MULTILIB_BOTH = 'both' 53MULTILIB_FIRST = 'first' 54 55# Directory containing ART tests within an ART APEX (if the package includes 56# any). ART test executables are installed in `bin/art/<arch>`. Segregating 57# tests by architecture is useful on devices supporting more than one 58# architecture, as it permits testing all of them using a single ART APEX 59# package. 60ART_TEST_DIR = 'bin/art' 61 62 63# Test if a given variable is set to a string "true". 64def isEnvTrue(var): 65 return var in os.environ and os.environ[var] == 'true' 66 67 68def extract_apex(apex_path, deapexer_path, debugfs_path, tmpdir): 69 _, apex_name = os.path.split(apex_path) 70 extract_path = os.path.join(tmpdir, apex_name) 71 if os.path.exists(extract_path): 72 shutil.rmtree(extract_path) 73 subprocess.check_call([deapexer_path, '--debugfs', debugfs_path, 74 'extract', apex_path, extract_path], 75 stdout=subprocess.DEVNULL) 76 return extract_path 77 78 79class FSObject: 80 def __init__(self, name, is_dir, is_exec, is_symlink, size): 81 self.name = name 82 self.is_dir = is_dir 83 self.is_exec = is_exec 84 self.is_symlink = is_symlink 85 self.size = size 86 87 def __str__(self): 88 return '%s(dir=%r,exec=%r,symlink=%r,size=%d)' \ 89 % (self.name, self.is_dir, self.is_exec, self.is_symlink, self.size) 90 91 92class TargetApexProvider: 93 def __init__(self, apex): 94 self._folder_cache = {} 95 self._apex = apex 96 97 def get(self, path): 98 apex_dir, name = os.path.split(path) 99 if not apex_dir: 100 apex_dir = '.' 101 apex_map = self.read_dir(apex_dir) 102 return apex_map[name] if name in apex_map else None 103 104 def read_dir(self, apex_dir): 105 if apex_dir in self._folder_cache: 106 return self._folder_cache[apex_dir] 107 apex_map = {} 108 dirname = os.path.join(self._apex, apex_dir) 109 if os.path.exists(dirname): 110 for basename in os.listdir(dirname): 111 filepath = os.path.join(dirname, basename) 112 is_dir = os.path.isdir(filepath) 113 is_exec = os.access(filepath, os.X_OK) 114 is_symlink = os.path.islink(filepath) 115 if is_symlink: 116 # Report the length of the symlink's target's path as file size, like `ls`. 117 size = len(os.readlink(filepath)) 118 else: 119 size = os.path.getsize(filepath) 120 apex_map[basename] = FSObject(basename, is_dir, is_exec, is_symlink, size) 121 self._folder_cache[apex_dir] = apex_map 122 return apex_map 123 124 125class HostApexProvider: 126 def __init__(self, apex, tmpdir): 127 self._tmpdir = tmpdir 128 self._folder_cache = {} 129 self._payload = os.path.join(self._tmpdir, 'apex_payload.zip') 130 # Extract payload to tmpdir. 131 apex_zip = zipfile.ZipFile(apex) 132 apex_zip.extract('apex_payload.zip', tmpdir) 133 134 def __del__(self): 135 # Delete temps. 136 if os.path.exists(self._payload): 137 os.remove(self._payload) 138 139 def get(self, path): 140 apex_dir, name = os.path.split(path) 141 if not apex_dir: 142 apex_dir = '' 143 apex_map = self.read_dir(apex_dir) 144 return apex_map[name] if name in apex_map else None 145 146 def read_dir(self, apex_dir): 147 if apex_dir in self._folder_cache: 148 return self._folder_cache[apex_dir] 149 if not self._folder_cache: 150 self.parse_zip() 151 if apex_dir in self._folder_cache: 152 return self._folder_cache[apex_dir] 153 return {} 154 155 def parse_zip(self): 156 apex_zip = zipfile.ZipFile(self._payload) 157 infos = apex_zip.infolist() 158 for zipinfo in infos: 159 path = zipinfo.filename 160 161 # Assume no empty file is stored. 162 assert path 163 164 def get_octal(val, index): 165 return (val >> (index * 3)) & 0x7 166 167 def bits_is_exec(val): 168 # TODO: Enforce group/other, too? 169 return get_octal(val, 2) & 1 == 1 170 171 is_zipinfo = True 172 while path: 173 apex_dir, base = os.path.split(path) 174 # TODO: If directories are stored, base will be empty. 175 176 if apex_dir not in self._folder_cache: 177 self._folder_cache[apex_dir] = {} 178 dir_map = self._folder_cache[apex_dir] 179 if base not in dir_map: 180 if is_zipinfo: 181 bits = (zipinfo.external_attr >> 16) & 0xFFFF 182 is_dir = get_octal(bits, 4) == 4 183 is_symlink = get_octal(bits, 4) == 2 184 is_exec = bits_is_exec(bits) 185 size = zipinfo.file_size 186 else: 187 is_exec = False # Seems we can't get this easily? 188 is_symlink = False 189 is_dir = True 190 # Use a negative value as an indicator of undefined/unknown size. 191 size = -1 192 dir_map[base] = FSObject(base, is_dir, is_exec, is_symlink, size) 193 is_zipinfo = False 194 path = apex_dir 195 196 197# DO NOT USE DIRECTLY! This is an "abstract" base class. 198class Checker: 199 def __init__(self, provider): 200 self._provider = provider 201 self._errors = 0 202 self._expected_file_globs = set() 203 204 def fail(self, msg, *fail_args): 205 self._errors += 1 206 logging.error(msg, *fail_args) 207 208 def error_count(self): 209 return self._errors 210 211 def reset_errors(self): 212 self._errors = 0 213 214 def is_file(self, path): 215 fs_object = self._provider.get(path) 216 if fs_object is None: 217 return False, 'Could not find %s' 218 if fs_object.is_dir: 219 return False, '%s is a directory' 220 return True, '' 221 222 def is_dir(self, path): 223 fs_object = self._provider.get(path) 224 if fs_object is None: 225 return False, 'Could not find %s' 226 if not fs_object.is_dir: 227 return False, '%s is not a directory' 228 return True, '' 229 230 def check_file(self, path): 231 ok, msg = self.is_file(path) 232 if not ok: 233 self.fail(msg, path) 234 self._expected_file_globs.add(path) 235 return ok 236 237 def check_executable(self, filename): 238 path = 'bin/%s' % filename 239 if not self.check_file(path): 240 return 241 if not self._provider.get(path).is_exec: 242 self.fail('%s is not executable', path) 243 244 def check_executable_symlink(self, filename): 245 path = 'bin/%s' % filename 246 fs_object = self._provider.get(path) 247 if fs_object is None: 248 self.fail('Could not find %s', path) 249 return 250 if fs_object.is_dir: 251 self.fail('%s is a directory', path) 252 return 253 if not fs_object.is_symlink: 254 self.fail('%s is not a symlink', path) 255 self._expected_file_globs.add(path) 256 257 def arch_dirs_for_path(self, path, multilib=None): 258 # Look for target-specific subdirectories for the given directory path. 259 # This is needed because the list of build targets is not propagated 260 # to this script. 261 # 262 # TODO(b/123602136): Pass build target information to this script and fix 263 # all places where this function in used (or similar workarounds). 264 dirs = [] 265 for arch in self.possible_archs(multilib): 266 dir = '%s/%s' % (path, arch) 267 found, _ = self.is_dir(dir) 268 if found: 269 dirs.append(dir) 270 return dirs 271 272 def check_art_test_executable(self, filename, multilib=None): 273 dirs = self.arch_dirs_for_path(ART_TEST_DIR, multilib) 274 if not dirs: 275 self.fail('ART test binary missing: %s', filename) 276 for dir in dirs: 277 test_path = '%s/%s' % (dir, filename) 278 self._expected_file_globs.add(test_path) 279 if not self._provider.get(test_path).is_exec: 280 self.fail('%s is not executable', test_path) 281 282 def check_art_test_data(self, filename): 283 dirs = self.arch_dirs_for_path(ART_TEST_DIR) 284 if not dirs: 285 self.fail('ART test data missing: %s', filename) 286 for dir in dirs: 287 if not self.check_file('%s/%s' % (dir, filename)): 288 return 289 290 def check_single_library(self, filename): 291 lib_path = 'lib/%s' % filename 292 lib64_path = 'lib64/%s' % filename 293 lib_is_file, _ = self.is_file(lib_path) 294 if lib_is_file: 295 self._expected_file_globs.add(lib_path) 296 lib64_is_file, _ = self.is_file(lib64_path) 297 if lib64_is_file: 298 self._expected_file_globs.add(lib64_path) 299 if not lib_is_file and not lib64_is_file: 300 self.fail('Library missing: %s', filename) 301 302 def check_dexpreopt(self, basename): 303 dirs = self.arch_dirs_for_path('javalib') 304 for dir in dirs: 305 for ext in ['art', 'oat', 'vdex']: 306 self.check_file('%s/%s.%s' % (dir, basename, ext)) 307 308 def check_java_library(self, basename): 309 return self.check_file('javalib/%s.jar' % basename) 310 311 def ignore_path(self, path_glob): 312 self._expected_file_globs.add(path_glob) 313 314 def check_optional_art_test_executable(self, filename): 315 for arch in self.possible_archs(): 316 self.ignore_path('%s/%s/%s' % (ART_TEST_DIR, arch, filename)) 317 318 def check_no_superfluous_files(self, dir_path): 319 paths = [] 320 for name in sorted(self._provider.read_dir(dir_path).keys()): 321 if name not in ('.', '..'): 322 paths.append(os.path.join(dir_path, name)) 323 expected_paths = set() 324 dir_prefix = dir_path + '/' 325 for path_glob in self._expected_file_globs: 326 expected_paths |= set(fnmatch.filter(paths, path_glob)) 327 # If there are globs in subdirectories of dir_path we want to match their 328 # path segments at this directory level. 329 if path_glob.startswith(dir_prefix): 330 subpath = path_glob[len(dir_prefix):] 331 subpath_first_segment, _, _ = subpath.partition('/') 332 expected_paths |= set(fnmatch.filter(paths, dir_prefix + subpath_first_segment)) 333 for unexpected_path in set(paths) - expected_paths: 334 self.fail('Unexpected file \'%s\'', unexpected_path) 335 336 # Just here for docs purposes, even if it isn't good Python style. 337 338 def check_symlinked_multilib_executable(self, filename): 339 """Check bin/filename32, and/or bin/filename64, with symlink bin/filename.""" 340 raise NotImplementedError 341 342 def check_symlinked_first_executable(self, filename): 343 """Check bin/filename32, and/or bin/filename64, with symlink bin/filename.""" 344 raise NotImplementedError 345 346 def check_native_library(self, basename): 347 """Check lib/basename.so, and/or lib64/basename.so.""" 348 raise NotImplementedError 349 350 def check_optional_native_library(self, basename_glob): 351 """Allow lib/basename.so and/or lib64/basename.so to exist.""" 352 raise NotImplementedError 353 354 def check_prefer64_library(self, basename): 355 """Check lib64/basename.so, or lib/basename.so on 32 bit only.""" 356 raise NotImplementedError 357 358 def possible_archs(self, multilib=None): 359 """Returns names of possible archs.""" 360 raise NotImplementedError 361 362class Arch32Checker(Checker): 363 def check_symlinked_multilib_executable(self, filename): 364 self.check_executable('%s32' % filename) 365 self.check_executable_symlink(filename) 366 367 def check_symlinked_first_executable(self, filename): 368 self.check_executable('%s32' % filename) 369 self.check_executable_symlink(filename) 370 371 def check_native_library(self, basename): 372 # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve 373 # the precision of this test? 374 self.check_file('lib/%s.so' % basename) 375 376 def check_optional_native_library(self, basename_glob): 377 self.ignore_path('lib/%s.so' % basename_glob) 378 379 def check_prefer64_library(self, basename): 380 self.check_native_library(basename) 381 382 def possible_archs(self, multilib=None): 383 return ARCHS_32 384 385class Arch64Checker(Checker): 386 def check_symlinked_multilib_executable(self, filename): 387 self.check_executable('%s64' % filename) 388 self.check_executable_symlink(filename) 389 390 def check_symlinked_first_executable(self, filename): 391 self.check_executable('%s64' % filename) 392 self.check_executable_symlink(filename) 393 394 def check_native_library(self, basename): 395 # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve 396 # the precision of this test? 397 self.check_file('lib64/%s.so' % basename) 398 399 def check_optional_native_library(self, basename_glob): 400 self.ignore_path('lib64/%s.so' % basename_glob) 401 402 def check_prefer64_library(self, basename): 403 self.check_native_library(basename) 404 405 def possible_archs(self, multilib=None): 406 return ARCHS_64 407 408 409class MultilibChecker(Checker): 410 def check_symlinked_multilib_executable(self, filename): 411 self.check_executable('%s32' % filename) 412 self.check_executable('%s64' % filename) 413 self.check_executable_symlink(filename) 414 415 def check_symlinked_first_executable(self, filename): 416 self.check_executable('%s64' % filename) 417 self.check_executable_symlink(filename) 418 419 def check_native_library(self, basename): 420 # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve 421 # the precision of this test? 422 self.check_file('lib/%s.so' % basename) 423 self.check_file('lib64/%s.so' % basename) 424 425 def check_optional_native_library(self, basename_glob): 426 self.ignore_path('lib/%s.so' % basename_glob) 427 self.ignore_path('lib64/%s.so' % basename_glob) 428 429 def check_prefer64_library(self, basename): 430 self.check_file('lib64/%s.so' % basename) 431 432 def possible_archs(self, multilib=None): 433 if multilib is None or multilib == MULTILIB_BOTH: 434 return ARCHS_32 + ARCHS_64 435 if multilib == MULTILIB_FIRST or multilib == MULTILIB_64: 436 return ARCHS_64 437 elif multilib == MULTILIB_32: 438 return ARCHS_32 439 self.fail('Unrecognized multilib option "%s"', multilib) 440 441 442class ReleaseChecker: 443 def __init__(self, checker): 444 self._checker = checker 445 446 def __str__(self): 447 return 'Release Checker' 448 449 def run(self): 450 # Check the Protocol Buffers APEX manifest. 451 self._checker.check_file('apex_manifest.pb') 452 453 # Check binaries for ART. 454 self._checker.check_executable('dexdump') 455 self._checker.check_executable('dexlist') 456 self._checker.check_executable('dexoptanalyzer') 457 self._checker.check_executable('profman') 458 self._checker.check_symlinked_multilib_executable('dalvikvm') 459 460 # Check exported libraries for ART. 461 self._checker.check_native_library('libdexfile') 462 self._checker.check_native_library('libnativebridge') 463 self._checker.check_native_library('libnativehelper') 464 self._checker.check_native_library('libnativeloader') 465 466 # Check internal libraries for ART. 467 self._checker.check_native_library('libadbconnection') 468 self._checker.check_native_library('libart') 469 self._checker.check_native_library('libart-compiler') 470 self._checker.check_native_library('libart-dexlayout') 471 self._checker.check_native_library('libart-disassembler') 472 self._checker.check_native_library('libartbase') 473 self._checker.check_native_library('libartpalette') 474 self._checker.check_native_library('libartservice') 475 self._checker.check_native_library('libarttools') 476 self._checker.check_native_library('libdt_fd_forward') 477 self._checker.check_native_library('libopenjdkjvm') 478 self._checker.check_native_library('libopenjdkjvmti') 479 self._checker.check_native_library('libprofile') 480 self._checker.check_native_library('libsigchain') 481 482 # Check Java libraries for Managed Core Library. 483 self._checker.check_java_library('apache-xml') 484 self._checker.check_java_library('bouncycastle') 485 self._checker.check_java_library('core-libart') 486 self._checker.check_java_library('core-oj') 487 self._checker.check_java_library('okhttp') 488 if isEnvTrue('EMMA_INSTRUMENT_FRAMEWORK'): 489 # In coverage builds jacoco is added to the list of ART apex jars. 490 self._checker.check_java_library('jacocoagent') 491 492 # Check internal native libraries for Managed Core Library. 493 self._checker.check_native_library('libjavacore') 494 self._checker.check_native_library('libopenjdk') 495 496 # Check internal native library dependencies. 497 # 498 # Any internal dependency not listed here will cause a failure in 499 # NoSuperfluousLibrariesChecker. Internal dependencies are generally just 500 # implementation details, but in the release package we want to track them 501 # because a) they add to the package size and the RAM usage (in particular 502 # if the library is also present in /system or another APEX and hence might 503 # get loaded twice through linker namespace separation), and b) we need to 504 # catch invalid dependencies on /system or other APEXes that should go 505 # through an exported library with stubs (b/128708192 tracks implementing a 506 # better approach for that). 507 self._checker.check_native_library('libbacktrace') 508 self._checker.check_native_library('libbase') 509 self._checker.check_native_library('libc++') 510 self._checker.check_native_library('libdt_socket') 511 self._checker.check_native_library('libjdwp') 512 self._checker.check_native_library('liblz4') 513 self._checker.check_native_library('liblzma') 514 self._checker.check_native_library('libnpt') 515 self._checker.check_native_library('libunwindstack') 516 self._checker.check_native_library('libziparchive') 517 518 # Allow extra dependencies that appear in ASAN builds. 519 self._checker.check_optional_native_library('libclang_rt.asan*') 520 self._checker.check_optional_native_library('libclang_rt.hwasan*') 521 self._checker.check_optional_native_library('libclang_rt.ubsan*') 522 523 # Check dexpreopt files for libcore bootclasspath jars. 524 self._checker.check_dexpreopt('boot') 525 self._checker.check_dexpreopt('boot-apache-xml') 526 self._checker.check_dexpreopt('boot-bouncycastle') 527 self._checker.check_dexpreopt('boot-core-libart') 528 self._checker.check_dexpreopt('boot-okhttp') 529 if isEnvTrue('EMMA_INSTRUMENT_FRAMEWORK'): 530 # In coverage builds the ART boot image includes jacoco. 531 self._checker.check_dexpreopt('boot-jacocoagent') 532 533class ReleaseTargetChecker: 534 def __init__(self, checker): 535 self._checker = checker 536 537 def __str__(self): 538 return 'Release (Target) Checker' 539 540 def run(self): 541 # We don't check for the presence of the JSON APEX manifest (file 542 # `apex_manifest.json`, only present in target APEXes), as it is only 543 # included for compatibility reasons with Android Q and will likely be 544 # removed in Android R. 545 546 # Check binaries for ART. 547 self._checker.check_executable('artd') 548 self._checker.check_executable('oatdump') 549 self._checker.check_executable("odrefresh") 550 self._checker.check_symlinked_multilib_executable('dex2oat') 551 552 # Check internal libraries for ART. 553 self._checker.check_native_library('libperfetto_hprof') 554 self._checker.check_prefer64_library('artd-aidl-ndk') 555 556 # Check internal Java libraries 557 self._checker.check_java_library("service-art") 558 559 # Check exported native libraries for Managed Core Library. 560 self._checker.check_native_library('libandroidio') 561 562 # Check internal native library dependencies. 563 self._checker.check_native_library('libcrypto') 564 self._checker.check_native_library('libexpat') 565 566 567class ReleaseHostChecker: 568 def __init__(self, checker): 569 self._checker = checker 570 571 def __str__(self): 572 return 'Release (Host) Checker' 573 574 def run(self): 575 # Check binaries for ART. 576 self._checker.check_executable('hprof-conv') 577 self._checker.check_symlinked_first_executable('dex2oatd') 578 self._checker.check_symlinked_first_executable('dex2oat') 579 580 # Check exported native libraries for Managed Core Library. 581 self._checker.check_native_library('libicu') 582 self._checker.check_native_library('libandroidio') 583 584 # Check internal libraries for Managed Core Library. 585 self._checker.check_native_library('libexpat-host') 586 self._checker.check_native_library('libz-host') 587 588 589class DebugChecker: 590 def __init__(self, checker): 591 self._checker = checker 592 593 def __str__(self): 594 return 'Debug Checker' 595 596 def run(self): 597 # Check binaries for ART. 598 self._checker.check_executable('dexdiag') 599 self._checker.check_executable('dexanalyze') 600 self._checker.check_executable('dexlayout') 601 self._checker.check_symlinked_multilib_executable('imgdiag') 602 603 # Check debug binaries for ART. 604 self._checker.check_executable('dexlayoutd') 605 self._checker.check_executable('dexoptanalyzerd') 606 self._checker.check_symlinked_multilib_executable('imgdiagd') 607 self._checker.check_executable('profmand') 608 609 # Check exported libraries for ART. 610 self._checker.check_native_library('libdexfiled') 611 612 # Check internal libraries for ART. 613 self._checker.check_native_library('libadbconnectiond') 614 self._checker.check_native_library('libartbased') 615 self._checker.check_native_library('libartd') 616 self._checker.check_native_library('libartd-compiler') 617 self._checker.check_native_library('libartd-dexlayout') 618 self._checker.check_native_library('libartd-disassembler') 619 self._checker.check_native_library('libopenjdkjvmd') 620 self._checker.check_native_library('libopenjdkjvmtid') 621 self._checker.check_native_library('libprofiled') 622 623 # Check internal libraries for Managed Core Library. 624 self._checker.check_native_library('libopenjdkd') 625 626 627class DebugTargetChecker: 628 def __init__(self, checker): 629 self._checker = checker 630 631 def __str__(self): 632 return 'Debug (Target) Checker' 633 634 def run(self): 635 # Check ART debug binaries. 636 self._checker.check_executable('oatdumpd') 637 self._checker.check_symlinked_multilib_executable('dex2oatd') 638 639 # Check ART internal libraries. 640 self._checker.check_native_library('libperfetto_hprofd') 641 642 # Check internal native library dependencies. 643 # 644 # Like in the release package, we check that we don't get other dependencies 645 # besides those listed here. In this case the concern is not bloat, but 646 # rather that we don't get behavioural differences between user (release) 647 # and userdebug/eng builds, which could happen if the debug package has 648 # duplicate library instances where releases don't. In other words, it's 649 # uncontroversial to add debug-only dependencies, as long as they don't make 650 # assumptions on having a single global state (ideally they should have 651 # double_loadable:true, cf. go/double_loadable). Also, like in the release 652 # package we need to look out for dependencies that should go through 653 # exported library stubs (until b/128708192 is fixed). 654 self._checker.check_prefer64_library('libmeminfo') 655 self._checker.check_prefer64_library('libprocinfo') 656 657 658class TestingTargetChecker: 659 def __init__(self, checker): 660 self._checker = checker 661 662 def __str__(self): 663 return 'Testing (Target) Checker' 664 665 def run(self): 666 # Check ART test binaries. 667 self._checker.check_art_test_executable('art_cmdline_tests') 668 self._checker.check_art_test_executable('art_compiler_tests') 669 self._checker.check_art_test_executable('art_dex2oat_tests') 670 self._checker.check_art_test_executable('art_dexanalyze_tests') 671 self._checker.check_art_test_executable('art_dexdiag_tests') 672 self._checker.check_art_test_executable('art_dexdump_tests') 673 self._checker.check_art_test_executable('art_dexlayout_tests') 674 self._checker.check_art_test_executable('art_dexlist_tests') 675 self._checker.check_art_test_executable('art_dexoptanalyzer_tests') 676 self._checker.check_art_test_executable('art_imgdiag_tests') 677 self._checker.check_art_test_executable('art_libartbase_tests') 678 self._checker.check_art_test_executable('art_libartpalette_tests') 679 self._checker.check_art_test_executable('art_libartservice_tests') 680 self._checker.check_art_test_executable('art_libarttools_tests') 681 self._checker.check_art_test_executable('art_libdexfile_support_tests') 682 self._checker.check_art_test_executable('art_libdexfile_tests') 683 self._checker.check_art_test_executable('art_libprofile_tests') 684 self._checker.check_art_test_executable('art_oatdump_tests') 685 self._checker.check_art_test_executable('art_odrefresh_tests') 686 self._checker.check_art_test_executable('art_profman_tests') 687 self._checker.check_art_test_executable('art_runtime_compiler_tests') 688 self._checker.check_art_test_executable('art_runtime_tests') 689 self._checker.check_art_test_executable('art_sigchain_tests') 690 691 # Check ART test (internal) libraries. 692 self._checker.check_native_library('libartd-gtest') 693 self._checker.check_native_library('libartd-simulator-container') 694 self._checker.check_native_library('libartbased-testing') 695 696 # Check ART test tools. 697 self._checker.check_executable('signal_dumper') 698 699 # Check ART jar files which are needed for gtests. 700 self._checker.check_art_test_data('art-gtest-jars-AbstractMethod.jar') 701 self._checker.check_art_test_data('art-gtest-jars-MyClassNatives.jar') 702 self._checker.check_art_test_data('art-gtest-jars-Main.jar') 703 self._checker.check_art_test_data('art-gtest-jars-ProtoCompare.jar') 704 self._checker.check_art_test_data('art-gtest-jars-Transaction.jar') 705 self._checker.check_art_test_data('art-gtest-jars-VerifierDepsMulti.dex') 706 self._checker.check_art_test_data('art-gtest-jars-Nested.jar') 707 self._checker.check_art_test_data('art-gtest-jars-MyClass.jar') 708 self._checker.check_art_test_data('art-gtest-jars-ManyMethods.jar') 709 self._checker.check_art_test_data('art-gtest-jars-GetMethodSignature.jar') 710 self._checker.check_art_test_data('art-gtest-jars-Lookup.jar') 711 self._checker.check_art_test_data('art-gtest-jars-Instrumentation.jar') 712 self._checker.check_art_test_data('art-gtest-jars-MainUncompressedAligned.jar') 713 self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderD.jar') 714 self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderC.jar') 715 self._checker.check_art_test_data('art-gtest-jars-ErroneousA.jar') 716 self._checker.check_art_test_data('art-gtest-jars-HiddenApiSignatures.jar') 717 self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderB.jar') 718 self._checker.check_art_test_data('art-gtest-jars-LinkageTest.dex') 719 self._checker.check_art_test_data('art-gtest-jars-MethodTypes.jar') 720 self._checker.check_art_test_data('art-gtest-jars-ErroneousInit.jar') 721 self._checker.check_art_test_data('art-gtest-jars-VerifierDeps.dex') 722 self._checker.check_art_test_data('art-gtest-jars-StringLiterals.jar') 723 self._checker.check_art_test_data('art-gtest-jars-XandY.jar') 724 self._checker.check_art_test_data('art-gtest-jars-ExceptionHandle.jar') 725 self._checker.check_art_test_data('art-gtest-jars-ImageLayoutB.jar') 726 self._checker.check_art_test_data('art-gtest-jars-Interfaces.jar') 727 self._checker.check_art_test_data('art-gtest-jars-IMTB.jar') 728 self._checker.check_art_test_data('art-gtest-jars-Extension2.jar') 729 self._checker.check_art_test_data('art-gtest-jars-Extension1.jar') 730 self._checker.check_art_test_data('art-gtest-jars-MainEmptyUncompressedAligned.jar') 731 self._checker.check_art_test_data('art-gtest-jars-ErroneousB.jar') 732 self._checker.check_art_test_data('art-gtest-jars-MultiDexModifiedSecondary.jar') 733 self._checker.check_art_test_data('art-gtest-jars-NonStaticLeafMethods.jar') 734 self._checker.check_art_test_data('art-gtest-jars-DefaultMethods.jar') 735 self._checker.check_art_test_data('art-gtest-jars-MultiDexUncompressedAligned.jar') 736 self._checker.check_art_test_data('art-gtest-jars-StaticsFromCode.jar') 737 self._checker.check_art_test_data('art-gtest-jars-ProfileTestMultiDex.jar') 738 self._checker.check_art_test_data('art-gtest-jars-VerifySoftFailDuringClinit.dex') 739 self._checker.check_art_test_data('art-gtest-jars-MainStripped.jar') 740 self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderA.jar') 741 self._checker.check_art_test_data('art-gtest-jars-StaticLeafMethods.jar') 742 self._checker.check_art_test_data('art-gtest-jars-MultiDex.jar') 743 self._checker.check_art_test_data('art-gtest-jars-Packages.jar') 744 self._checker.check_art_test_data('art-gtest-jars-ProtoCompare2.jar') 745 self._checker.check_art_test_data('art-gtest-jars-Statics.jar') 746 self._checker.check_art_test_data('art-gtest-jars-AllFields.jar') 747 self._checker.check_art_test_data('art-gtest-jars-IMTA.jar') 748 self._checker.check_art_test_data('art-gtest-jars-ImageLayoutA.jar') 749 self._checker.check_art_test_data('art-gtest-jars-MainEmptyUncompressed.jar') 750 self._checker.check_art_test_data('art-gtest-jars-Dex2oatVdexTestDex.jar') 751 self._checker.check_art_test_data('art-gtest-jars-Dex2oatVdexPublicSdkDex.dex') 752 753 754class NoSuperfluousBinariesChecker: 755 def __init__(self, checker): 756 self._checker = checker 757 758 def __str__(self): 759 return 'No superfluous binaries checker' 760 761 def run(self): 762 self._checker.check_no_superfluous_files('bin') 763 764 765class NoSuperfluousLibrariesChecker: 766 def __init__(self, checker): 767 self._checker = checker 768 769 def __str__(self): 770 return 'No superfluous libraries checker' 771 772 def run(self): 773 self._checker.check_no_superfluous_files('javalib') 774 self._checker.check_no_superfluous_files('lib') 775 self._checker.check_no_superfluous_files('lib64') 776 777 778class NoSuperfluousArtTestsChecker: 779 def __init__(self, checker): 780 self._checker = checker 781 782 def __str__(self): 783 return 'No superfluous ART tests checker' 784 785 def run(self): 786 for arch in self._checker.possible_archs(): 787 self._checker.check_no_superfluous_files('%s/%s' % (ART_TEST_DIR, arch)) 788 789 790class List: 791 def __init__(self, provider, print_size=False): 792 self._provider = provider 793 self._print_size = print_size 794 795 def print_list(self): 796 797 def print_list_rec(path): 798 apex_map = self._provider.read_dir(path) 799 if apex_map is None: 800 return 801 apex_map = dict(apex_map) 802 if '.' in apex_map: 803 del apex_map['.'] 804 if '..' in apex_map: 805 del apex_map['..'] 806 for (_, val) in sorted(apex_map.items()): 807 val_path = os.path.join(path, val.name) 808 if self._print_size: 809 if val.size < 0: 810 print('[ n/a ] %s' % val_path) 811 else: 812 print('[%11d] %s' % (val.size, val_path)) 813 else: 814 print(val_path) 815 if val.is_dir: 816 print_list_rec(val_path) 817 818 print_list_rec('') 819 820 821class Tree: 822 def __init__(self, provider, title, print_size=False): 823 print('%s' % title) 824 self._provider = provider 825 self._has_next_list = [] 826 self._print_size = print_size 827 828 @staticmethod 829 def get_vertical(has_next_list): 830 string = '' 831 for v in has_next_list: 832 string += '%s ' % ('│' if v else ' ') 833 return string 834 835 @staticmethod 836 def get_last_vertical(last): 837 return '└── ' if last else '├── ' 838 839 def print_tree(self): 840 841 def print_tree_rec(path): 842 apex_map = self._provider.read_dir(path) 843 if apex_map is None: 844 return 845 apex_map = dict(apex_map) 846 if '.' in apex_map: 847 del apex_map['.'] 848 if '..' in apex_map: 849 del apex_map['..'] 850 key_list = list(sorted(apex_map.keys())) 851 for i, key in enumerate(key_list): 852 prev = self.get_vertical(self._has_next_list) 853 last = self.get_last_vertical(i == len(key_list) - 1) 854 val = apex_map[key] 855 if self._print_size: 856 if val.size < 0: 857 print('%s%s[ n/a ] %s' % (prev, last, val.name)) 858 else: 859 print('%s%s[%11d] %s' % (prev, last, val.size, val.name)) 860 else: 861 print('%s%s%s' % (prev, last, val.name)) 862 if val.is_dir: 863 self._has_next_list.append(i < len(key_list) - 1) 864 val_path = os.path.join(path, val.name) 865 print_tree_rec(val_path) 866 self._has_next_list.pop() 867 868 print_tree_rec('') 869 870 871# Note: do not sys.exit early, for __del__ cleanup. 872def art_apex_test_main(test_args): 873 if test_args.host and test_args.flattened: 874 logging.error("Both of --host and --flattened set") 875 return 1 876 if test_args.list and test_args.tree: 877 logging.error("Both of --list and --tree set") 878 return 1 879 if test_args.size and not (test_args.list or test_args.tree): 880 logging.error("--size set but neither --list nor --tree set") 881 return 1 882 if not test_args.flattened and not test_args.tmpdir: 883 logging.error("Need a tmpdir.") 884 return 1 885 if not test_args.flattened and not test_args.host: 886 if not test_args.deapexer: 887 logging.error("Need deapexer.") 888 return 1 889 if not test_args.debugfs: 890 logging.error("Need debugfs.") 891 return 1 892 893 if test_args.host: 894 # Host APEX. 895 if test_args.flavor not in [FLAVOR_DEBUG, FLAVOR_AUTO]: 896 logging.error("Using option --host with non-Debug APEX") 897 return 1 898 # Host APEX is always a debug flavor (for now). 899 test_args.flavor = FLAVOR_DEBUG 900 else: 901 # Device APEX. 902 if test_args.flavor == FLAVOR_AUTO: 903 logging.warning('--flavor=auto, trying to autodetect. This may be incorrect!') 904 # The order of flavors in the list below matters, as the release tag (empty string) will 905 # match any package name. 906 for flavor in [ FLAVOR_DEBUG, FLAVOR_TESTING, FLAVOR_RELEASE ]: 907 flavor_tag = flavor 908 # Special handling for the release flavor, whose name is no longer part of the Release ART 909 # APEX file name (`com.android.art.capex` / `com.android.art`). 910 if flavor == FLAVOR_RELEASE: 911 flavor_tag = '' 912 flavor_pattern = '*.%s*' % flavor_tag 913 if fnmatch.fnmatch(test_args.apex, flavor_pattern): 914 test_args.flavor = flavor 915 logging.warning(' Detected %s flavor', flavor) 916 break 917 if test_args.flavor == FLAVOR_AUTO: 918 logging.error(' Could not detect APEX flavor, neither %s, %s nor %s for \'%s\'', 919 FLAVOR_RELEASE, FLAVOR_DEBUG, FLAVOR_TESTING, test_args.apex) 920 return 1 921 922 try: 923 if test_args.host: 924 apex_provider = HostApexProvider(test_args.apex, test_args.tmpdir) 925 else: 926 apex_dir = test_args.apex 927 if not test_args.flattened: 928 # Extract the apex. It would be nice to use the output from "deapexer list" 929 # to avoid this work, but it doesn't provide info about executable bits. 930 apex_dir = extract_apex(test_args.apex, test_args.deapexer, test_args.debugfs, 931 test_args.tmpdir) 932 apex_provider = TargetApexProvider(apex_dir) 933 except (zipfile.BadZipFile, zipfile.LargeZipFile) as e: 934 logging.error('Failed to create provider: %s', e) 935 return 1 936 937 if test_args.tree: 938 Tree(apex_provider, test_args.apex, test_args.size).print_tree() 939 return 0 940 if test_args.list: 941 List(apex_provider, test_args.size).print_list() 942 return 0 943 944 checkers = [] 945 if test_args.bitness == BITNESS_AUTO: 946 logging.warning('--bitness=auto, trying to autodetect. This may be incorrect!') 947 has_32 = apex_provider.get('lib') is not None 948 has_64 = apex_provider.get('lib64') is not None 949 if has_32 and has_64: 950 logging.warning(' Detected multilib') 951 test_args.bitness = BITNESS_MULTILIB 952 elif has_32: 953 logging.warning(' Detected 32-only') 954 test_args.bitness = BITNESS_32 955 elif has_64: 956 logging.warning(' Detected 64-only') 957 test_args.bitness = BITNESS_64 958 else: 959 logging.error(' Could not detect bitness, neither lib nor lib64 contained.') 960 List(apex_provider).print_list() 961 return 1 962 963 if test_args.bitness == BITNESS_32: 964 base_checker = Arch32Checker(apex_provider) 965 elif test_args.bitness == BITNESS_64: 966 base_checker = Arch64Checker(apex_provider) 967 else: 968 assert test_args.bitness == BITNESS_MULTILIB 969 base_checker = MultilibChecker(apex_provider) 970 971 checkers.append(ReleaseChecker(base_checker)) 972 if test_args.host: 973 checkers.append(ReleaseHostChecker(base_checker)) 974 else: 975 checkers.append(ReleaseTargetChecker(base_checker)) 976 if test_args.flavor == FLAVOR_DEBUG or test_args.flavor == FLAVOR_TESTING: 977 checkers.append(DebugChecker(base_checker)) 978 if not test_args.host: 979 checkers.append(DebugTargetChecker(base_checker)) 980 if test_args.flavor == FLAVOR_TESTING: 981 checkers.append(TestingTargetChecker(base_checker)) 982 983 # These checkers must be last. 984 checkers.append(NoSuperfluousBinariesChecker(base_checker)) 985 checkers.append(NoSuperfluousArtTestsChecker(base_checker)) 986 if not test_args.host: 987 # We only care about superfluous libraries on target, where their absence 988 # can be vital to ensure they get picked up from the right package. 989 checkers.append(NoSuperfluousLibrariesChecker(base_checker)) 990 991 failed = False 992 for checker in checkers: 993 logging.info('%s...', checker) 994 checker.run() 995 if base_checker.error_count() > 0: 996 logging.error('%s FAILED', checker) 997 failed = True 998 else: 999 logging.info('%s SUCCEEDED', checker) 1000 base_checker.reset_errors() 1001 1002 return 1 if failed else 0 1003 1004 1005def art_apex_test_default(test_parser): 1006 if 'ANDROID_PRODUCT_OUT' not in os.environ: 1007 logging.error('No-argument use requires ANDROID_PRODUCT_OUT') 1008 sys.exit(1) 1009 product_out = os.environ['ANDROID_PRODUCT_OUT'] 1010 if 'ANDROID_HOST_OUT' not in os.environ: 1011 logging.error('No-argument use requires ANDROID_HOST_OUT') 1012 sys.exit(1) 1013 host_out = os.environ['ANDROID_HOST_OUT'] 1014 1015 test_args = test_parser.parse_args(['unused']) # For consistency. 1016 test_args.debugfs = '%s/bin/debugfs' % host_out 1017 test_args.tmpdir = '.' 1018 test_args.tree = False 1019 test_args.list = False 1020 test_args.bitness = BITNESS_AUTO 1021 failed = False 1022 1023 if not os.path.exists(test_args.debugfs): 1024 logging.error("Cannot find debugfs (default path %s). Please build it, e.g., m debugfs", 1025 test_args.debugfs) 1026 sys.exit(1) 1027 1028 # TODO: Add host support. 1029 # TODO: Add support for flattened APEX packages. 1030 configs = [ 1031 {'name': 'com.android.art.capex', 'flavor': FLAVOR_RELEASE, 'host': False}, 1032 {'name': 'com.android.art.debug.capex', 'flavor': FLAVOR_DEBUG, 'host': False}, 1033 # Note: The Testing ART APEX is not a Compressed APEX. 1034 {'name': 'com.android.art.testing.apex', 'flavor': FLAVOR_TESTING, 'host': False}, 1035 ] 1036 1037 for config in configs: 1038 logging.info(config['name']) 1039 # TODO: Host will need different path. 1040 test_args.apex = '%s/system/apex/%s' % (product_out, config['name']) 1041 if not os.path.exists(test_args.apex): 1042 failed = True 1043 logging.error("Cannot find APEX %s. Please build it first.", test_args.apex) 1044 continue 1045 test_args.flavor = config['flavor'] 1046 test_args.host = config['host'] 1047 failed = art_apex_test_main(test_args) != 0 1048 1049 if failed: 1050 sys.exit(1) 1051 1052 1053if __name__ == "__main__": 1054 parser = argparse.ArgumentParser(description='Check integrity of an ART APEX.') 1055 1056 parser.add_argument('apex', help='APEX file input') 1057 1058 parser.add_argument('--host', help='Check as host APEX', action='store_true') 1059 1060 parser.add_argument('--flattened', help='Check as flattened (target) APEX', action='store_true') 1061 1062 parser.add_argument('--flavor', help='Check as FLAVOR APEX', choices=FLAVORS_ALL, 1063 default=FLAVOR_AUTO) 1064 1065 parser.add_argument('--list', help='List all files', action='store_true') 1066 parser.add_argument('--tree', help='Print directory tree', action='store_true') 1067 parser.add_argument('--size', help='Print file sizes', action='store_true') 1068 1069 parser.add_argument('--tmpdir', help='Directory for temp files') 1070 parser.add_argument('--deapexer', help='Path to deapexer') 1071 parser.add_argument('--debugfs', help='Path to debugfs') 1072 1073 parser.add_argument('--bitness', help='Bitness to check', choices=BITNESS_ALL, 1074 default=BITNESS_AUTO) 1075 1076 if len(sys.argv) == 1: 1077 art_apex_test_default(parser) 1078 else: 1079 args = parser.parse_args() 1080 1081 if args is None: 1082 sys.exit(1) 1083 1084 exit_code = art_apex_test_main(args) 1085 sys.exit(exit_code) 1086