1#!/usr/bin/env python3 2# 3# Copyright (C) 2007 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import os, sys, glob, re, shutil, subprocess, shlex, resource, atexit 18 19import default_run as default_run_module 20 21from default_run import get_target_arch 22from importlib.machinery import SourceFileLoader 23from inspect import currentframe, getframeinfo, FrameInfo 24from pathlib import Path 25from shutil import copyfile 26from testrunner import env 27from typing import Optional, Dict, List 28from zipfile import ZipFile 29 30COLOR = (os.environ.get("LUCI_CONTEXT") == None) # Disable colors on LUCI. 31COLOR_BLUE = '\033[94m' if COLOR else '' 32COLOR_GREEN = '\033[92m' if COLOR else '' 33COLOR_NORMAL = '\033[0m' if COLOR else '' 34COLOR_RED = '\033[91m' if COLOR else '' 35 36# Helper class which allows us to access the environment using syntax sugar. 37# E.g. `env.ANDROID_BUILD_TOP` instead of `os.environ["ANDROID_BUILD_TOP"]`. 38class Environment: 39 40 def __getattr__(self, name): 41 return os.environ.get(name) 42 43 def __setattr__(self, name, value): 44 os.environ[name] = str(value) 45 46 47# Context passed to individual tests to let them customize the behaviour. 48class RunTestContext: 49 50 def __init__(self, tmp_dir: Path, target: bool, chroot, dex_location, test_name) -> None: 51 self.env = Environment() 52 self.target = target 53 self.chroot = chroot 54 self.dex_location = dex_location 55 self.test_name = test_name 56 57 # Note: The expected path can be modified by the tests. 58 self.expected_stdout = tmp_dir / "expected-stdout.txt" 59 self.expected_stderr = tmp_dir / "expected-stderr.txt" 60 61 self.runner: List[str] = ["#!/bin/bash"] 62 63 def echo(self, text): 64 self.run(f"echo {text} > {test_stdout}") 65 66 def export(self, **env: str) -> None: 67 self.runner.append("") 68 for name, value in env.items(): 69 self.runner.append(f"export {name}={value}") 70 71 # Add "runner" script command. It is not executed now. 72 # All "runner" commands are executed later via single bash call. 73 def run(self, cmd: str, check: bool=True, expected_exit_code: int=0, desc:str = None) -> None: 74 if cmd == "true": 75 return 76 cmd_esc = cmd.replace("'", r"'\''") 77 self.runner.append("") 78 self.runner.append(f"echo '{COLOR_BLUE}$$ {cmd_esc}{COLOR_NORMAL}'") 79 self.runner.append(cmd) 80 81 # Check the exit code. 82 if check: 83 caller = getframeinfo(currentframe().f_back) # type: ignore 84 source = "{}:{}".format(Path(caller.filename).name, caller.lineno) 85 msg = f"{self.test_name} FAILED: [{source}] " 86 msg += "{} returned exit code ${{exit_code}}.".format(desc or "Command") 87 if expected_exit_code: 88 msg += f" Expected {expected_exit_code}." 89 self.runner.append( 90 f"exit_code=$?; if [ $exit_code -ne {expected_exit_code} ]; then " 91 f"echo {COLOR_RED}{msg}{COLOR_NORMAL}; exit 100; " 92 f"fi; ") 93 else: 94 self.runner.append("true; # Ignore previous exit code") 95 96 # Execute the default runner (possibly with modified arguments). 97 def default_run(self, args, **kwargs): 98 default_run_module.default_run(self, args, **kwargs) 99 100 101# TODO: Replace with 'def main():' (which might change variables from globals to locals) 102if True: 103 progdir = os.path.dirname(__file__) 104 oldwd = os.getcwd() 105 os.chdir(progdir) 106 test_dir = "test-{}".format(os.getpid()) 107 TMPDIR = os.environ.get("TMPDIR") 108 USER = os.environ.get("USER") 109 PYTHON3 = os.environ.get("PYTHON3") 110 if not TMPDIR: 111 tmp_dir = f"/tmp/{USER}/{test_dir}" 112 else: 113 tmp_dir = f"{TMPDIR}/{test_dir}" 114 checker = f"{progdir}/../tools/checker/checker.py" 115 116 ON_VM = os.environ.get("ART_TEST_ON_VM") 117 SSH_USER = os.environ.get("ART_TEST_SSH_USER") 118 SSH_HOST = os.environ.get("ART_TEST_SSH_HOST") 119 SSH_PORT = os.environ.get("ART_TEST_SSH_PORT") 120 SSH_CMD = os.environ.get("ART_SSH_CMD") 121 SCP_CMD = os.environ.get("ART_SCP_CMD") 122 CHROOT = os.environ.get("ART_TEST_CHROOT") 123 CHROOT_CMD = os.environ.get("ART_CHROOT_CMD") 124 125 def fail(message: str, caller:Optional[FrameInfo]=None): 126 caller = caller or getframeinfo(currentframe().f_back) # type: ignore 127 assert caller 128 source = "{}:{}".format(Path(caller.filename).name, caller.lineno) 129 print(f"{COLOR_RED}{TEST_NAME} FAILED: [{source}] {message}{COLOR_NORMAL}", 130 file=sys.stderr) 131 sys.exit(1) 132 133 def run(cmdline: str, check=True, fail_message=None) -> subprocess.CompletedProcess: 134 print(f"{COLOR_BLUE}$ {cmdline}{COLOR_NORMAL}", flush=True) 135 proc = subprocess.run([cmdline], 136 shell=True, 137 executable="/bin/bash", 138 stderr=subprocess.STDOUT) 139 if (check and proc.returncode != 0): 140 if fail_message: 141 # If we have custom fail message, exit without printing the full backtrace. 142 fail(fail_message, getframeinfo(currentframe().f_back)) # type: ignore 143 raise Exception(f"Command failed (exit code {proc.returncode})") 144 return proc 145 146 def export(env: str, value: str) -> None: 147 os.environ[env] = value 148 globals()[env] = value 149 150 def error(msg) -> None: 151 print(msg, file=sys.stderr, flush=True) 152 153 # ANDROID_BUILD_TOP is not set in a build environment. 154 ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP") 155 if not ANDROID_BUILD_TOP: 156 export("ANDROID_BUILD_TOP", oldwd) 157 158 export("JAVA", "java") 159 export("JAVAC", "javac -g -Xlint:-options -source 1.8 -target 1.8") 160 export("PYTHON3", 161 f"{ANDROID_BUILD_TOP}/prebuilts/build-tools/path/linux-x86/python3") 162 export("RUN", f"{PYTHON3} {progdir}/etc/run-test-jar") 163 export("DEX_LOCATION", f"/data/run-test/{test_dir}") 164 165 # OUT_DIR defaults to out, and may be relative to ANDROID_BUILD_TOP. 166 # Convert it to an absolute path, since we cd into the tmp_dir to run the tests. 167 OUT_DIR = os.environ.get("OUT_DIR", "") 168 export("OUT_DIR", OUT_DIR or "out") 169 if not OUT_DIR.startswith("/"): 170 export("OUT_DIR", f"{ANDROID_BUILD_TOP}/{OUT_DIR}") 171 172# ANDROID_HOST_OUT is not set in a build environment. 173 ANDROID_HOST_OUT = os.environ.get("ANDROID_HOST_OUT") 174 if not ANDROID_HOST_OUT: 175 export("ANDROID_HOST_OUT", f"{OUT_DIR}/host/linux-x86") 176 177 host_lib_root = ANDROID_HOST_OUT 178 chroot = "" 179 info = "info.txt" 180 run_cmd = "run" 181 test_stdout = "test-stdout.txt" 182 test_stderr = "test-stderr.txt" 183 cfg_output = "graph.cfg" 184 strace_output = "strace-output.txt" 185 lib = "libartd.so" 186 testlib = "arttestd" 187 run_args = [] 188 run_checker = "no" 189 190 quiet = "no" 191 debuggable = "no" 192 prebuild_mode = "yes" 193 target_mode = "yes" 194 dev_mode = "no" 195 create_runner = "no" 196 update_mode = "no" 197 debug_mode = "no" 198 relocate = "no" 199 runtime = "art" 200 usage = "no" 201 suffix64 = "" 202 trace = "false" 203 trace_stream = "false" 204 basic_verify = "false" 205 gc_verify = "false" 206 gc_stress = "false" 207 jvmti_trace_stress = "false" 208 jvmti_field_stress = "false" 209 jvmti_step_stress = "false" 210 jvmti_redefine_stress = "false" 211 strace = "false" 212 always_clean = "no" 213 never_clean = "no" 214 have_image = "yes" 215 android_root = "/system" 216 bisection_search = "no" 217 timeout = "" 218 suspend_timeout = "500000" 219 run_optimizing = "false" 220 dump_cfg = "false" 221 dump_cfg_path = "" 222 # To cause tests to fail fast, limit the file sizes created by dx, dex2oat and 223 # ART output to approximately 128MB. This should be more than sufficient 224 # for any test while still catching cases of runaway output. 225 # Set a hard limit to encourage ART developers to increase the ulimit here if 226 # needed to support a test case rather than resetting the limit in the run 227 # script for the particular test in question. Adjust this if needed for 228 # particular configurations. 229 file_ulimit = 128000 230 231 args = list(sys.argv) 232 arg = args[0] 233 234 def shift(): 235 global arg 236 args.pop(0) 237 arg = args[0] if args else "" 238 239 shift() 240 241 while True: 242 if arg == "--host": 243 target_mode = "no" 244 DEX_LOCATION = tmp_dir 245 run_args += ["--host"] 246 os.environ["RUN_MODE"] = "host" 247 shift() 248 elif arg == "--quiet": 249 quiet = "yes" 250 shift() 251 elif arg == "--use-java-home": 252 JAVA_HOME = os.environ.get("JAVA_HOME") 253 if JAVA_HOME: 254 export("JAVA", f"{JAVA_HOME}/bin/java") 255 export("JAVAC", f"{JAVA_HOME}/bin/javac -g") 256 else: 257 error("Passed --use-java-home without JAVA_HOME variable set!") 258 usage = "yes" 259 shift() 260 elif arg == "--jvm": 261 target_mode = "no" 262 DEX_LOCATION = tmp_dir 263 runtime = "jvm" 264 prebuild_mode = "no" 265 run_args += ["--jvm"] 266 shift() 267 elif arg == "-O": 268 lib = "libart.so" 269 testlib = "arttest" 270 run_args += ["-O"] 271 shift() 272 elif arg == "--dalvik": 273 lib = "libdvm.so" 274 runtime = "dalvik" 275 shift() 276 elif arg == "--no-image": 277 have_image = "no" 278 shift() 279 elif arg == "--relocate": 280 relocate = "yes" 281 shift() 282 elif arg == "--no-relocate": 283 relocate = "no" 284 shift() 285 elif arg == "--prebuild": 286 run_args += ["--prebuild"] 287 prebuild_mode = "yes" 288 shift() 289 elif arg == "--compact-dex-level": 290 option = arg 291 shift() 292 run_args += [f'"{option}" "{arg}"'] 293 shift() 294 elif arg == "--strip-dex": 295 run_args += ["--strip-dex"] 296 shift() 297 elif arg == "--debuggable": 298 run_args += ["-Xcompiler-option --debuggable"] 299 debuggable = "yes" 300 shift() 301 elif arg == "--no-prebuild": 302 run_args += ["--no-prebuild"] 303 prebuild_mode = "no" 304 shift() 305 elif arg == "--gcverify": 306 basic_verify = "true" 307 gc_verify = "true" 308 shift() 309 elif arg == "--gcstress": 310 basic_verify = "true" 311 gc_stress = "true" 312 shift() 313 elif arg == "--jvmti-step-stress": 314 jvmti_step_stress = "true" 315 os.environ["JVMTI_STEP_STRESS"] = "true" 316 shift() 317 elif arg == "--jvmti-redefine-stress": 318 jvmti_redefine_stress = "true" 319 os.environ["JVMTI_REDEFINE_STRESS"] = "true" 320 shift() 321 elif arg == "--jvmti-field-stress": 322 jvmti_field_stress = "true" 323 os.environ["JVMTI_FIELD_STRESS"] = "true" 324 shift() 325 elif arg == "--jvmti-trace-stress": 326 jvmti_trace_stress = "true" 327 os.environ["JVMTI_TRACE_STRESS"] = "true" 328 shift() 329 elif arg == "--suspend-timeout": 330 shift() 331 suspend_timeout = arg 332 shift() 333 elif arg == "--image": 334 shift() 335 image = arg 336 run_args += [f'--image "{image}"'] 337 shift() 338 elif arg == "-Xcompiler-option": 339 shift() 340 option = arg 341 run_args += [f'-Xcompiler-option "{option}"'] 342 shift() 343 elif arg == "--runtime-option": 344 shift() 345 option = arg 346 run_args += [f'--runtime-option "{option}"'] 347 shift() 348 elif arg == "--gdb-arg": 349 shift() 350 gdb_arg = arg 351 run_args += [f'--gdb-arg "{gdb_arg}"'] 352 shift() 353 elif arg == "--gdb-dex2oat-args": 354 shift() 355 gdb_dex2oat_args = arg 356 run_args += ['--gdb-dex2oat-args "{gdb_dex2oat_args}"'] 357 shift() 358 elif arg == "--debug": 359 run_args += ["--debug"] 360 shift() 361 elif arg == "--debug-wrap-agent": 362 run_args += ["--debug-wrap-agent"] 363 shift() 364 elif arg == "--with-agent": 365 shift() 366 option = arg 367 run_args += [f'--with-agent "{arg}"'] 368 shift() 369 elif arg == "--debug-agent": 370 shift() 371 option = arg 372 run_args += [f'--debug-agent "{arg}"'] 373 shift() 374 elif arg == "--dump-cfg": 375 shift() 376 dump_cfg = "true" 377 dump_cfg_path = arg 378 shift() 379 elif arg == "--gdb": 380 run_args += ["--gdb"] 381 dev_mode = "yes" 382 shift() 383 elif arg == "--gdb-dex2oat": 384 run_args += ["--gdb-dex2oat"] 385 dev_mode = "yes" 386 shift() 387 elif arg == "--gdbserver-bin": 388 shift() 389 run_args += [f'--gdbserver-bin "{arg}"'] 390 shift() 391 elif arg == "--gdbserver-port": 392 shift() 393 run_args += [f'--gdbserver-port "{arg}"'] 394 shift() 395 elif arg == "--gdbserver": 396 run_args += ["--gdbserver"] 397 dev_mode = "yes" 398 shift() 399 elif arg == "--strace": 400 strace = "yes" 401 run_args += [ 402 f'--invoke-with=strace --invoke-with=-o --invoke-with="{tmp_dir}/{strace_output}"' 403 ] 404 timeout = timeout or "1800" 405 shift() 406 elif arg == "--zygote": 407 run_args += ["--zygote"] 408 shift() 409 elif arg == "--interpreter": 410 run_args += ["--interpreter"] 411 shift() 412 elif arg == "--jit": 413 run_args += ["--jit"] 414 shift() 415 elif arg == "--baseline": 416 run_args += ["--baseline"] 417 shift() 418 elif arg == "--optimizing": 419 run_optimizing = "true" 420 shift() 421 elif arg == "--no-verify": 422 run_args += ["--no-verify"] 423 shift() 424 elif arg == "--verify-soft-fail": 425 run_args += ["--verify-soft-fail"] 426 os.environ["VERIFY_SOFT_FAIL"] = "true" 427 shift() 428 elif arg == "--no-optimize": 429 run_args += ["--no-optimize"] 430 shift() 431 elif arg == "--no-precise": 432 run_args += ["--no-precise"] 433 shift() 434 elif arg.startswith("--android-log-tags"): 435 run_args += [arg] 436 shift() 437 elif arg == "--external-log-tags": 438 run_args += ["--external-log-tags"] 439 shift() 440 elif arg == "--invoke-with": 441 shift() 442 what = arg 443 if not arg: 444 error("missing argument to --invoke-with") 445 usage = "yes" 446 break 447 run_args += [f'--invoke-with "{what}"'] 448 shift() 449 elif arg == "--create-runner": 450 run_args += ["--create-runner --dry-run"] 451 dev_mode = "yes" 452 never_clean = "yes" 453 create_runner = "yes" 454 shift() 455 elif arg == "--dev": 456 dev_mode = "yes" 457 shift() 458 elif arg == "--temp-path": 459 shift() 460 if not arg: 461 error("missing argument to --temp-path") 462 usage = "yes" 463 break 464 shift() 465 elif arg == "--chroot": 466 shift() 467 if not arg: 468 error("missing argument to --chroot") 469 usage = "yes" 470 break 471 chroot = arg 472 run_args += [f'--chroot "{arg}"'] 473 shift() 474 elif arg == "--simpleperf": 475 run_args += ["--simpleperf"] 476 shift() 477 elif arg == "--android-root": 478 shift() 479 if not arg: 480 error("missing argument to --android-root") 481 usage = "yes" 482 break 483 android_root = arg 484 run_args += [f'--android-root "{arg}"'] 485 shift() 486 elif arg == "--android-art-root": 487 shift() 488 if not arg: 489 error("missing argument to --android-art-root") 490 usage = "yes" 491 break 492 run_args += [f'--android-art-root "{arg}"'] 493 shift() 494 elif arg == "--android-tzdata-root": 495 shift() 496 if not arg: 497 error("missing argument to --android-tzdata-root") 498 usage = "yes" 499 break 500 run_args += [f'--android-tzdata-root "{arg}"'] 501 shift() 502 elif arg == "--update": 503 update_mode = "yes" 504 shift() 505 elif arg == "--help": 506 usage = "yes" 507 shift() 508 elif arg == "--64": 509 run_args += ["--64"] 510 suffix64 = "64" 511 shift() 512 elif arg == "--bionic": 513 # soong linux_bionic builds are 64bit only. 514 run_args += ["--bionic --host --64"] 515 suffix64 = "64" 516 target_mode = "no" 517 DEX_LOCATION = tmp_dir 518 host_lib_root = f"{OUT_DIR}/soong/host/linux_bionic-x86" 519 shift() 520 elif arg == "--runtime-extracted-zipapex": 521 shift() 522 # TODO Should we allow the java.library.path to search the zipapex too? 523 # Not needed at the moment and adding it will be complicated so for now 524 # we'll ignore this. 525 run_args += [f'--host --runtime-extracted-zipapex "{arg}"'] 526 target_mode = "no" 527 DEX_LOCATION = tmp_dir 528 shift() 529 elif arg == "--runtime-zipapex": 530 shift() 531 # TODO Should we allow the java.library.path to search the zipapex too? 532 # Not needed at the moment and adding it will be complicated so for now 533 # we'll ignore this. 534 run_args += [f'--host --runtime-zipapex "{arg}"'] 535 target_mode = "no" 536 DEX_LOCATION = tmp_dir 537 # apex_payload.zip is quite large we need a high enough ulimit to 538 # extract it. 512mb should be good enough. 539 file_ulimit = 512000 540 shift() 541 elif arg == "--timeout": 542 shift() 543 if not arg: 544 error("missing argument to --timeout") 545 usage = "yes" 546 break 547 timeout = arg 548 shift() 549 elif arg == "--trace": 550 trace = "true" 551 shift() 552 elif arg == "--stream": 553 trace_stream = "true" 554 shift() 555 elif arg == "--always-clean": 556 always_clean = "yes" 557 shift() 558 elif arg == "--never-clean": 559 never_clean = "yes" 560 shift() 561 elif arg == "--dex2oat-swap": 562 run_args += ["--dex2oat-swap"] 563 shift() 564 elif arg == "--instruction-set-features": 565 shift() 566 run_args += [f'--instruction-set-features "{arg}"'] 567 shift() 568 elif arg == "--bisection-search": 569 bisection_search = "yes" 570 shift() 571 elif arg == "--vdex": 572 run_args += ["--vdex"] 573 shift() 574 elif arg == "--dm": 575 run_args += ["--dm"] 576 shift() 577 elif arg == "--vdex-filter": 578 shift() 579 filter = arg 580 run_args += ['--vdex-filter "{filter}"'] 581 shift() 582 elif arg == "--random-profile": 583 run_args += ["--random-profile"] 584 shift() 585 elif arg == "--dex2oat-jobs": 586 shift() 587 run_args += [f'-Xcompiler-option "-j{arg}"'] 588 shift() 589 elif arg.startswith("--"): 590 error(f"unknown option: {arg}") 591 usage = "yes" 592 break 593 else: 594 break 595 596 export("DEX_LOCATION", DEX_LOCATION) 597 598 if usage == "no" and not arg: 599 error("missing test to run") 600 usage = "yes" 601 602# The DEX_LOCATION with the chroot prefix, if any. 603 chroot_dex_location = f"{chroot}{DEX_LOCATION}" 604 605 # tmp_dir may be relative, resolve. 606 os.chdir(oldwd) 607 tmp_dir = os.path.realpath(tmp_dir) 608 os.chdir(progdir) 609 if not tmp_dir: 610 error(f"Failed to resolve {tmp_dir}") 611 sys.exit(1) 612 os.makedirs(tmp_dir, exist_ok=True) 613 614 # Add thread suspend timeout flag 615 if runtime != "jvm": 616 run_args += [ 617 f'--runtime-option "-XX:ThreadSuspendTimeout={suspend_timeout}"' 618 ] 619 620 if basic_verify == "true": 621 # Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests. 622 run_args += [ 623 "--runtime-option -Xgc:preverify --runtime-option -Xgc:postverify " 624 "--runtime-option -XX:HspaceCompactForOOMMinIntervalMs=0" 625 ] 626 if gc_verify == "true": 627 run_args += [ 628 "--runtime-option -Xgc:preverify_rosalloc --runtime-option " 629 "-Xgc:postverify_rosalloc" 630 ] 631 if gc_stress == "true": 632 run_args += [ 633 "--gc-stress --runtime-option -Xgc:gcstress --runtime-option -Xms2m " 634 "--runtime-option -Xmx16m" 635 ] 636 if jvmti_redefine_stress == "true": 637 run_args += ["--no-app-image --jvmti-redefine-stress"] 638 if jvmti_step_stress == "true": 639 run_args += ["--no-app-image --jvmti-step-stress"] 640 if jvmti_field_stress == "true": 641 run_args += ["--no-app-image --jvmti-field-stress"] 642 if jvmti_trace_stress == "true": 643 run_args += ["--no-app-image --jvmti-trace-stress"] 644 if trace == "true": 645 run_args += [ 646 "--runtime-option -Xmethod-trace --runtime-option " 647 "-Xmethod-trace-file-size:2000000" 648 ] 649 if trace_stream == "true": 650 # Streaming mode uses the file size as the buffer size. So output gets really large. Drop 651 # the ability to analyze the file and just write to /dev/null. 652 run_args += ["--runtime-option -Xmethod-trace-file:/dev/null"] 653 # Enable streaming mode. 654 run_args += ["--runtime-option -Xmethod-trace-stream"] 655 else: 656 run_args += [ 657 f'--runtime-option "-Xmethod-trace-file:{DEX_LOCATION}/trace.bin"' 658 ] 659 elif trace_stream == "true": 660 error("Cannot use --stream without --trace.") 661 sys.exit(1) 662 if timeout: 663 run_args += [f'--timeout "{timeout}"'] 664 665# Most interesting target architecture variables are Makefile variables, not environment variables. 666# Try to map the suffix64 flag and what we find in {ANDROID_PRODUCT_OUT}/data/art-test to an architecture name. 667 668 def guess_target_arch_name(): 669 return get_target_arch(suffix64 == "64") 670 671 def guess_host_arch_name(): 672 if suffix64 == "64": 673 return "x86_64" 674 else: 675 return "x86" 676 677 if target_mode == "no": 678 if runtime == "jvm": 679 if prebuild_mode == "yes": 680 error("--prebuild with --jvm is unsupported") 681 sys.exit(1) 682 else: 683 # ART/Dalvik host mode. 684 if chroot: 685 error("--chroot with --host is unsupported") 686 sys.exit(1) 687 688 if runtime != "jvm": 689 run_args += [f'--lib "{lib}"'] 690 691 ANDROID_PRODUCT_OUT = os.environ.get("ANDROID_PRODUCT_OUT") 692 if runtime == "dalvik": 693 if target_mode == "no": 694 framework = f"{ANDROID_PRODUCT_OUT}/system/framework" 695 bpath = f"{framework}/core-icu4j.jar:{framework}/core-libart.jar:{framework}/core-oj.jar:{framework}/conscrypt.jar:{framework}/okhttp.jar:{framework}/bouncycastle.jar:{framework}/ext.jar" 696 run_args += [f'--boot --runtime-option "-Xbootclasspath:{bpath}"'] 697 else: 698 pass # defaults to using target BOOTCLASSPATH 699 elif runtime == "art": 700 if target_mode == "no": 701 host_arch_name = guess_host_arch_name() 702 run_args += [ 703 f'--boot "{ANDROID_HOST_OUT}/apex/art_boot_images/javalib/boot.art"' 704 ] 705 run_args += [ 706 f'--runtime-option "-Djava.library.path={host_lib_root}/lib{suffix64}:{host_lib_root}/nativetest{suffix64}"' 707 ] 708 else: 709 target_arch_name = guess_target_arch_name() 710 # Note that libarttest(d).so and other test libraries that depend on ART 711 # internal libraries must not be in this path for JNI libraries - they 712 # need to be loaded through LD_LIBRARY_PATH and 713 # NATIVELOADER_DEFAULT_NAMESPACE_LIBS instead. 714 run_args += [ 715 f'--runtime-option "-Djava.library.path=/data/nativetest{suffix64}/art/{target_arch_name}"' 716 ] 717 run_args += ['--boot "/system/framework/art_boot_images/boot.art"'] 718 if relocate == "yes": 719 run_args += ["--relocate"] 720 else: 721 run_args += ["--no-relocate"] 722 elif runtime == "jvm": 723 # TODO: Detect whether the host is 32-bit or 64-bit. 724 run_args += [ 725 f'--runtime-option "-Djava.library.path={ANDROID_HOST_OUT}/lib64:{ANDROID_HOST_OUT}/nativetest64"' 726 ] 727 728 if have_image == "no": 729 if runtime != "art": 730 error("--no-image is only supported on the art runtime") 731 sys.exit(1) 732 run_args += ["--no-image"] 733 734 if create_runner == "yes" and target_mode == "yes": 735 error("--create-runner does not function for non --host tests") 736 usage = "yes" 737 738 if dev_mode == "yes" and update_mode == "yes": 739 error("--dev and --update are mutually exclusive") 740 usage = "yes" 741 742 if dev_mode == "yes" and quiet == "yes": 743 error("--dev and --quiet are mutually exclusive") 744 usage = "yes" 745 746 if bisection_search == "yes" and prebuild_mode == "yes": 747 error("--bisection-search and --prebuild are mutually exclusive") 748 usage = "yes" 749 750# TODO: Chroot-based bisection search is not supported yet (see below); implement it. 751 if bisection_search == "yes" and chroot: 752 error("--chroot with --bisection-search is unsupported") 753 sys.exit(1) 754 755 if usage == "no": 756 if not arg or arg == "-": 757 test_dir = os.path.basename(oldwd) 758 else: 759 test_dir = arg 760 761 if not os.path.isdir(test_dir): 762 td2 = glob.glob(f"{test_dir}-*") 763 if len(td2) == 1 and os.path.isdir(td2[0]): 764 test_dir = td2[0] 765 else: 766 error(f"{test_dir}: no such test directory") 767 usage = "yes" 768 # Shift to get rid of the test name argument. The rest of the arguments 769 # will get passed to the test run. 770 shift() 771 772 if usage == "yes": 773 prog = os.path.basename(__file__) 774 # pyformat: disable 775 help=( 776 "usage:\n" 777 f" $prog --help Print this message.\n" 778 f" $prog [options] [test-name] Run test normally.\n" 779 f" $prog --dev [options] [test-name] Development mode\n" 780 "(dumps to stdout).\n" 781 f" $prog --create-runner [options] [test-name]\n" 782 " Creates a runner script for use with other \n" 783 "tools (e.g. parallel_run.py).\n" 784 " The script will only run the test portion, and \n" 785 "share oat and dex files.\n" 786 f" $prog --update [options] [test-name] Update mode\n" 787 "(replaces expected-stdout.txt and expected-stderr.txt).\n" 788 ' Omitting the test name or specifying "-" will use the\n' 789 "current directory.\n" 790 " Runtime Options:\n" 791 " -O Run non-debug rather than debug build (off by default).\n" 792 " -Xcompiler-option Pass an option to the compiler.\n" 793 " --runtime-option Pass an option to the runtime.\n" 794 " --compact-dex-level Specify a compact dex level to the compiler.\n" 795 " --debug Wait for the default debugger to attach.\n" 796 " --debug-agent <agent-path>\n" 797 " Wait for the given debugger agent to attach. Currently\n" 798 " only supported on host.\n" 799 " --debug-wrap-agent use libwrapagentproperties and tools/libjdwp-compat.props\n" 800 " to load the debugger agent specified by --debug-agent.\n" 801 " --with-agent <agent> Run the test with the given agent loaded with -agentpath:\n" 802 " --debuggable Whether to compile Java code for a debugger.\n" 803 " --gdb Run under gdb; incompatible with some tests.\n" 804 " --gdb-dex2oat Run dex2oat under the prebuilt lldb.\n" 805 " --gdbserver Start gdbserver (defaults to port :5039).\n" 806 " --gdbserver-port <port>\n" 807 " Start gdbserver with the given COMM (see man gdbserver).\n" 808 " --gdbserver-bin <binary>\n" 809 " Use the given binary as gdbserver.\n" 810 " --gdb-arg Pass an option to gdb or gdbserver.\n" 811 " --gdb-dex2oat-args Pass options separated by ';' to lldb for dex2oat.\n" 812 " --simpleperf Wraps the dalvikvm invocation in 'simpleperf record ...\n" 813 " ... simpleperf report' and dumps stats to stdout.\n" 814 " --temp-path [path] Location where to execute the tests.\n" 815 " --interpreter Enable interpreter only mode (off by default).\n" 816 " --jit Enable jit (off by default).\n" 817 " --optimizing Enable optimizing compiler (default).\n" 818 " --no-verify Turn off verification (on by default).\n" 819 " --verify-soft-fail Force soft fail verification (off by default).\n" 820 " Verification is enabled if neither --no-verify\n" 821 " nor --verify-soft-fail is specified.\n" 822 " --no-optimize Turn off optimization (on by default).\n" 823 " --no-precise Turn off precise GC (on by default).\n" 824 " --zygote Spawn the process from the Zygote.\n" 825 "If used, then the\n" 826 " other runtime options are ignored.\n" 827 " --prebuild Run dex2oat on the files before starting test. (default)\n" 828 " --no-prebuild Do not run dex2oat on the files before starting\n" 829 " the test.\n" 830 " --strip-dex Strip the dex files before starting test.\n" 831 " --relocate Force the use of relocating in the test, making\n" 832 " the image and oat files be relocated to a random\n" 833 " address before running.\n" 834 " --no-relocate Force the use of no relocating in the test. (default)\n" 835 " --image Run the test using a precompiled boot image. (default)\n" 836 " --no-image Run the test without a precompiled boot image.\n" 837 " --host Use the host-mode virtual machine.\n" 838 " --invoke-with Pass --invoke-with option to runtime.\n" 839 " --dalvik Use Dalvik (off by default).\n" 840 " --jvm Use a host-local RI virtual machine.\n" 841 " --use-java-home Use the JAVA_HOME environment variable\n" 842 " to find the java compiler and runtime\n" 843 " (if applicable) to run the test with.\n" 844 " --64 Run the test in 64-bit mode\n" 845 " --bionic Use the (host, 64-bit only) linux_bionic libc runtime\n" 846 " --runtime-zipapex [file]\n" 847 " Use the given zipapex file to provide runtime binaries\n" 848 " --runtime-extracted-zipapex [dir]\n" 849 " Use the given extracted zipapex directory to provide\n" 850 " runtime binaries\n" 851 " --timeout n Test timeout in seconds\n" 852 " --trace Run with method tracing\n" 853 " --strace Run with syscall tracing from strace.\n" 854 " --stream Run method tracing in streaming mode (requires --trace)\n" 855 " --gcstress Run with gc stress testing\n" 856 " --gcverify Run with gc verification\n" 857 " --jvmti-trace-stress Run with jvmti method tracing stress testing\n" 858 " --jvmti-step-stress Run with jvmti single step stress testing\n" 859 " --jvmti-redefine-stress\n" 860 " Run with jvmti method redefinition stress testing\n" 861 " --always-clean Delete the test files even if the test fails.\n" 862 " --never-clean Keep the test files even if the test succeeds.\n" 863 " --chroot [newroot] Run with root directory set to newroot.\n" 864 " --android-root [path] The path on target for the android root. (/system by default).\n" 865 " --android-i18n-root [path]\n" 866 " The path on target for the i18n module root.\n" 867 " (/apex/com.android.i18n by default).\n" 868 " --android-art-root [path]\n" 869 " The path on target for the ART module root.\n" 870 " (/apex/com.android.art by default).\n" 871 " --android-tzdata-root [path]\n" 872 " The path on target for the Android Time Zone Data root.\n" 873 " (/apex/com.android.tzdata by default).\n" 874 " --dex2oat-swap Use a dex2oat swap file.\n" 875 " --instruction-set-features [string]\n" 876 " Set instruction-set-features for compilation.\n" 877 " --quiet Don't print anything except failure messages\n" 878 " --external-log-tags Use ANDROID_LOG_TAGS to set a custom logging level for\n" 879 " a test run.\n" 880 " --bisection-search Perform bisection bug search.\n" 881 " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild.\n" 882 " --suspend-timeout Change thread suspend timeout ms (default 500000).\n" 883 " --dex2oat-jobs Number of dex2oat jobs.\n" 884 ) 885 # pyformat: enable 886 error(help) 887 sys.exit(1) 888 889 os.chdir(test_dir) 890 test_dir = os.getcwd() 891 892 TEST_NAME = os.path.basename(test_dir) 893 export("TEST_NAME", TEST_NAME) 894 895 # Tests named '<number>-checker-*' will also have their CFGs verified with 896 # Checker when compiled with Optimizing on host. 897 # Additionally, if the user specifies that the CFG must be dumped, it will 898 # run the checker for any type of test to generate the CFG. 899 if re.match("[0-9]+-checker-", TEST_NAME) or dump_cfg == "true": 900 if runtime == "art" and run_optimizing == "true": 901 # In no-prebuild or no-image mode, the compiler only quickens so disable the checker. 902 if prebuild_mode == "yes": 903 run_checker = "yes" 904 905 if target_mode == "no": 906 cfg_output_dir = tmp_dir 907 checker_args = f"--arch={host_arch_name.upper()}" 908 else: 909 cfg_output_dir = DEX_LOCATION 910 checker_args = f"--arch={target_arch_name.upper()}" 911 912 if debuggable == "yes": 913 checker_args += " --debuggable" 914 915 run_args += [ 916 f'-Xcompiler-option "--dump-cfg={cfg_output_dir}/{cfg_output}" -Xcompiler-option -j1' 917 ] 918 checker_args = f"{checker_args} --print-cfg" 919 920 run_args += [f'--testlib "{testlib}"'] 921 922 resource.setrlimit(resource.RLIMIT_FSIZE, (file_ulimit * 1024, resource.RLIM_INFINITY)) 923 924 # Extract run-test data from the zip file. 925 shutil.rmtree(tmp_dir) 926 os.makedirs(f"{tmp_dir}/.unzipped") 927 os.chdir(tmp_dir) 928 m = re.match("[0-9]*([0-9][0-9])-.*", TEST_NAME) 929 assert m, "Can not find test number in " + TEST_NAME 930 SHARD = "HiddenApi" if "hiddenapi" in TEST_NAME else m.group(1) 931 if target_mode == "yes": 932 zip_file = f"{ANDROID_HOST_OUT}/etc/art/art-run-test-target-data-shard{SHARD}.zip" 933 zip_entry = f"target/{TEST_NAME}/" 934 elif runtime == "jvm": 935 zip_file = f"{ANDROID_HOST_OUT}/etc/art/art-run-test-jvm-data-shard{SHARD}.zip" 936 zip_entry = f"jvm/{TEST_NAME}/" 937 else: 938 zip_file = f"{ANDROID_HOST_OUT}/etc/art/art-run-test-host-data-shard{SHARD}.zip" 939 zip_entry = f"host/{TEST_NAME}/" 940 zip = ZipFile(zip_file, "r") 941 zip_entries = [e for e in zip.namelist() if e.startswith(zip_entry)] 942 zip.extractall(Path(tmp_dir) / ".unzipped", members=zip_entries) 943 for entry in (Path(tmp_dir) / ".unzipped" / zip_entry).iterdir(): 944 entry.rename(Path(tmp_dir) / entry.name) 945 946 def clean_up(passed: bool): 947 if always_clean == "yes" or (passed and never_clean == "no"): 948 os.chdir(oldwd) 949 shutil.rmtree(tmp_dir) 950 if target_mode == "yes": 951 if ON_VM: 952 run(f"{SSH_CMD} \"rm -rf {chroot_dex_location}\"") 953 else: 954 run(f"adb shell rm -rf {chroot_dex_location}") 955 print(f"{TEST_NAME} files deleted from host" + 956 (" and from target" if target_mode == "yes" else "")) 957 else: 958 print(f"{TEST_NAME} files left in {tmp_dir} on host" + 959 (f" and in {chroot_dex_location} on target" if target_mode == "yes" else "")) 960 atexit.unregister(clean_up) 961 # TODO: Run this in global try-finally once the script is more refactored. 962 atexit.register(clean_up, passed=False) 963 964 ctx = RunTestContext(Path(tmp_dir), target_mode == "yes", chroot, DEX_LOCATION, TEST_NAME) 965 td_info = f"{test_dir}/{info}" 966 for td_file in [td_info, ctx.expected_stdout, ctx.expected_stderr]: 967 assert os.access(td_file, os.R_OK) 968 969 joined_run_args = " ".join(run_args) 970 joined_args = " ".join(args) 971 972 # Create runner (bash script that executes the whole test) 973 def create_runner_script() -> Path: 974 parsed_args = default_run_module.parse_args(shlex.split(" ".join(run_args + args))) 975 parsed_args.stdout_file = os.path.join(DEX_LOCATION, test_stdout) 976 parsed_args.stderr_file = os.path.join(DEX_LOCATION, test_stderr) 977 978 ctx.run(f"cd {DEX_LOCATION}") 979 if target_mode != "yes": 980 # Make "out" directory accessible from test directory. 981 ctx.run(f"ln -s -f -t {DEX_LOCATION} {ANDROID_BUILD_TOP}/out") 982 # Clear the stdout/stderr files (create empty files). 983 ctx.run(f"echo -n > {test_stdout} && echo -n > {test_stderr}") 984 985 script = Path(tmp_dir) / "run.py" 986 if script.exists(): 987 module = SourceFileLoader("run_" + TEST_NAME, str(script)).load_module() 988 module.run(ctx, parsed_args) 989 else: 990 default_run_module.default_run(ctx, parsed_args) 991 992 runner = Path(tmp_dir) / "run.sh" 993 runner.write_text("\n".join(ctx.runner)) 994 runner.chmod(0o777) 995 return runner 996 997 # Test might not execute anything but we still expect the output files to exist. 998 Path(test_stdout).touch() 999 Path(test_stderr).touch() 1000 1001 export("TEST_RUNTIME", runtime) 1002 1003 print(f"{test_dir}: Create runner script...") 1004 runner = create_runner_script() 1005 1006 print(f"{test_dir}: Run...") 1007 if target_mode == "yes": 1008 # Prepare the on-device test directory 1009 if ON_VM: 1010 run(f"{SSH_CMD} 'rm -rf {chroot_dex_location} && mkdir -p {chroot_dex_location}'") 1011 else: 1012 run("adb root") 1013 run("adb wait-for-device") 1014 run(f"adb shell 'rm -rf {chroot_dex_location} && mkdir -p {chroot_dex_location}'") 1015 push_files = [Path(runner.name)] 1016 push_files += list(Path(".").glob(f"{TEST_NAME}*.jar")) 1017 push_files += list(Path(".").glob(f"expected-*.txt")) 1018 push_files += [p for p in [Path("profile"), Path("res")] if p.exists()] 1019 push_files = " ".join(map(str, push_files)) 1020 if ON_VM: 1021 run(f"{SCP_CMD} {push_files} {SSH_USER}@{SSH_HOST}:{chroot_dex_location}") 1022 else: 1023 run("adb push {} {}".format(push_files, chroot_dex_location)) 1024 1025 if ON_VM: 1026 run(f"{SSH_CMD} {CHROOT_CMD} bash {DEX_LOCATION}/run.sh", 1027 fail_message=f"Runner {chroot_dex_location}/run.sh failed") 1028 else: 1029 chroot_prefix = f"chroot {chroot}" if chroot else "" 1030 run(f"adb shell {chroot_prefix} sh {DEX_LOCATION}/run.sh", 1031 fail_message=f"Runner {chroot_dex_location}/run.sh failed") 1032 1033 # Copy the on-device stdout/stderr to host. 1034 pull_files = [test_stdout, test_stderr, "expected-stdout.txt", "expected-stderr.txt"] 1035 if ON_VM: 1036 srcs = " ".join(f"{SSH_USER}@{SSH_HOST}:{chroot_dex_location}/{f}" for f in pull_files) 1037 run(f"{SCP_CMD} {srcs} .") 1038 else: 1039 run("adb pull {} .".format(" ".join(f"{chroot_dex_location}/{f}" for f in pull_files))) 1040 else: 1041 run(str(runner), fail_message=f"Runner {str(runner)} failed") 1042 1043 # NB: There is no exit code or return value. 1044 # Failing tests just raise python exception. 1045 os.chdir(tmp_dir) 1046 if update_mode == "yes": 1047 for src, dst in [(test_stdout, os.path.join(test_dir, ctx.expected_stdout.name)), 1048 (test_stderr, os.path.join(test_dir, ctx.expected_stderr.name))]: 1049 if "[DO_NOT_UPDATE]" not in open(dst).readline(): 1050 copyfile(src, dst) 1051 1052 print("#################### info") 1053 run(f'cat "{td_info}" | sed "s/^/# /g"') 1054 print("#################### stdout diff") 1055 proc_out = run(f'diff --strip-trailing-cr -u ' 1056 f'"{ctx.expected_stdout}" "{test_stdout}"', check=False) 1057 print("#################### stderr diff") 1058 proc_err = run(f'diff --strip-trailing-cr -u ' 1059 f'"{ctx.expected_stderr}" "{test_stderr}"', check=False) 1060 if strace == "yes": 1061 print("#################### strace output (trimmed to 3000 lines)") 1062 # Some tests do not run dalvikvm, in which case the trace does not exist. 1063 run(f'tail -n 3000 "{tmp_dir}/{strace_output}"', check=False) 1064 SANITIZE_HOST = os.environ.get("SANITIZE_HOST") 1065 if target_mode == "no" and SANITIZE_HOST == "address": 1066 # Run the stack script to symbolize any ASAN aborts on the host for SANITIZE_HOST. The 1067 # tools used by the given ABI work for both x86 and x86-64. 1068 print("#################### symbolizer (trimmed to 3000 lines)") 1069 run(f'''echo "ABI: 'x86_64'" | cat - "{test_stdout}" "{test_stderr}"''' 1070 f"""| {ANDROID_BUILD_TOP}/development/scripts/stack | tail -n 3000""") 1071 print("####################", flush=True) 1072 if proc_out.returncode != 0 or proc_err.returncode != 0: 1073 kind = ((["stdout"] if proc_out.returncode != 0 else []) + 1074 (["stderr"] if proc_err.returncode != 0 else [])) 1075 fail("{} did not match the expected file".format(" and ".join(kind))) 1076 1077 if run_checker == "yes": 1078 if target_mode == "yes": 1079 if ON_VM: 1080 run(f'{SCP_CMD} "{SSH_USER}@${SSH_HOST}:{CHROOT}/{cfg_output_dir}/{cfg_output}"') 1081 else: 1082 run(f'adb pull "{chroot}/{cfg_output_dir}/{cfg_output}"') 1083 run(f'"{checker}" -q {checker_args} "{cfg_output}" "{tmp_dir}"', 1084 fail_message="CFG checker failed") 1085 1086 # Copy the generated CFG to the specified path. 1087 if dump_cfg == "true": 1088 assert run_optimizing == "true", "The CFG can be dumped only in optimizing mode" 1089 if target_mode == "yes": 1090 if ON_VM: 1091 run(f'{SCP_CMD} "{SSH_USER}@${SSH_HOST}:{CHROOT}/{cfg_output_dir}/{cfg_output} {dump_cfg_output}"') 1092 else: 1093 run(f"adb pull {chroot}/{cfg_output_dir}/{cfg_output} {dump_cfg_path}") 1094 else: 1095 run(f"cp {cfg_output_dir}/{cfg_output} {dump_cfg_path}") 1096 1097 clean_up(passed=True) 1098 print(f"{COLOR_GREEN}{test_dir}: PASSED{COLOR_NORMAL}") 1099