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