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