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