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 = env.ART_TEST_ON_VM 117 SSH_USER = env.ART_TEST_SSH_USER 118 SSH_HOST = env.ART_TEST_SSH_HOST 119 SSH_PORT = env.ART_TEST_SSH_PORT 120 SSH_CMD = env.ART_SSH_CMD 121 SCP_CMD = env.ART_SCP_CMD 122 CHROOT = env.ART_TEST_CHROOT 123 CHROOT_CMD = env.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 == "--strip-dex": 290 run_args += ["--strip-dex"] 291 shift() 292 elif arg == "--debuggable": 293 run_args += ["-Xcompiler-option --debuggable"] 294 debuggable = "yes" 295 shift() 296 elif arg == "--no-prebuild": 297 run_args += ["--no-prebuild"] 298 prebuild_mode = "no" 299 shift() 300 elif arg == "--gcverify": 301 basic_verify = "true" 302 gc_verify = "true" 303 shift() 304 elif arg == "--gcstress": 305 basic_verify = "true" 306 gc_stress = "true" 307 shift() 308 elif arg == "--jvmti-step-stress": 309 jvmti_step_stress = "true" 310 os.environ["JVMTI_STEP_STRESS"] = "true" 311 shift() 312 elif arg == "--jvmti-redefine-stress": 313 jvmti_redefine_stress = "true" 314 os.environ["JVMTI_REDEFINE_STRESS"] = "true" 315 shift() 316 elif arg == "--jvmti-field-stress": 317 jvmti_field_stress = "true" 318 os.environ["JVMTI_FIELD_STRESS"] = "true" 319 shift() 320 elif arg == "--jvmti-trace-stress": 321 jvmti_trace_stress = "true" 322 os.environ["JVMTI_TRACE_STRESS"] = "true" 323 shift() 324 elif arg == "--suspend-timeout": 325 shift() 326 suspend_timeout = arg 327 shift() 328 elif arg == "--image": 329 shift() 330 image = arg 331 run_args += [f'--image "{image}"'] 332 shift() 333 elif arg == "-Xcompiler-option": 334 shift() 335 option = arg 336 run_args += [f'-Xcompiler-option "{option}"'] 337 shift() 338 elif arg == "--runtime-option": 339 shift() 340 option = arg 341 run_args += [f'--runtime-option "{option}"'] 342 shift() 343 elif arg == "--gdb-arg": 344 shift() 345 gdb_arg = arg 346 run_args += [f'--gdb-arg "{gdb_arg}"'] 347 shift() 348 elif arg == "--gdb-dex2oat-args": 349 shift() 350 gdb_dex2oat_args = arg 351 run_args += [f'--gdb-dex2oat-args="{gdb_dex2oat_args}"'] 352 shift() 353 elif arg == "--debug": 354 run_args += ["--debug"] 355 shift() 356 elif arg == "--debug-wrap-agent": 357 run_args += ["--debug-wrap-agent"] 358 shift() 359 elif arg == "--with-agent": 360 shift() 361 option = arg 362 run_args += [f'--with-agent "{arg}"'] 363 shift() 364 elif arg == "--debug-agent": 365 shift() 366 option = arg 367 run_args += [f'--debug-agent "{arg}"'] 368 shift() 369 elif arg == "--dump-cfg": 370 shift() 371 dump_cfg = "true" 372 dump_cfg_path = arg 373 shift() 374 elif arg == "--gdb": 375 run_args += ["--gdb"] 376 dev_mode = "yes" 377 shift() 378 elif arg == "--gdb-dex2oat": 379 run_args += ["--gdb-dex2oat"] 380 dev_mode = "yes" 381 shift() 382 elif arg == "--gdbserver-bin": 383 shift() 384 run_args += [f'--gdbserver-bin "{arg}"'] 385 shift() 386 elif arg == "--gdbserver-port": 387 shift() 388 run_args += [f'--gdbserver-port "{arg}"'] 389 shift() 390 elif arg == "--gdbserver": 391 run_args += ["--gdbserver"] 392 dev_mode = "yes" 393 shift() 394 elif arg == "--strace": 395 strace = "yes" 396 run_args += [ 397 f'--invoke-with=strace --invoke-with=-o --invoke-with="{tmp_dir}/{strace_output}"' 398 ] 399 timeout = timeout or "1800" 400 shift() 401 elif arg == "--zygote": 402 run_args += ["--zygote"] 403 shift() 404 elif arg == "--interpreter": 405 run_args += ["--interpreter"] 406 shift() 407 elif arg == "--switch-interpreter": 408 run_args += ["--switch-interpreter"] 409 shift() 410 elif arg == "--jit": 411 run_args += ["--jit"] 412 shift() 413 elif arg == "--baseline": 414 run_args += ["--baseline"] 415 shift() 416 elif arg == "--optimizing": 417 run_optimizing = "true" 418 shift() 419 elif arg == "--no-verify": 420 run_args += ["--no-verify"] 421 shift() 422 elif arg == "--verify-soft-fail": 423 run_args += ["--verify-soft-fail"] 424 os.environ["VERIFY_SOFT_FAIL"] = "true" 425 shift() 426 elif arg == "--no-optimize": 427 run_args += ["--no-optimize"] 428 shift() 429 elif arg == "--no-precise": 430 run_args += ["--no-precise"] 431 shift() 432 elif arg.startswith("--android-log-tags"): 433 run_args += [arg] 434 shift() 435 elif arg == "--external-log-tags": 436 run_args += ["--external-log-tags"] 437 shift() 438 elif arg == "--invoke-with": 439 shift() 440 what = arg 441 if not arg: 442 error("missing argument to --invoke-with") 443 usage = "yes" 444 break 445 run_args += [f'--invoke-with "{what}"'] 446 shift() 447 elif arg == "--create-runner": 448 run_args += ["--create-runner --dry-run"] 449 dev_mode = "yes" 450 never_clean = "yes" 451 create_runner = "yes" 452 shift() 453 elif arg == "--dev": 454 dev_mode = "yes" 455 shift() 456 elif arg == "--temp-path": 457 shift() 458 if not arg: 459 error("missing argument to --temp-path") 460 usage = "yes" 461 break 462 shift() 463 elif arg == "--chroot": 464 shift() 465 if not arg: 466 error("missing argument to --chroot") 467 usage = "yes" 468 break 469 chroot = arg 470 run_args += [f'--chroot "{arg}"'] 471 shift() 472 elif arg == "--simpleperf": 473 run_args += ["--simpleperf"] 474 shift() 475 elif arg == "--android-root": 476 shift() 477 if not arg: 478 error("missing argument to --android-root") 479 usage = "yes" 480 break 481 android_root = arg 482 run_args += [f'--android-root "{arg}"'] 483 shift() 484 elif arg == "--android-art-root": 485 shift() 486 if not arg: 487 error("missing argument to --android-art-root") 488 usage = "yes" 489 break 490 run_args += [f'--android-art-root "{arg}"'] 491 shift() 492 elif arg == "--android-tzdata-root": 493 shift() 494 if not arg: 495 error("missing argument to --android-tzdata-root") 496 usage = "yes" 497 break 498 run_args += [f'--android-tzdata-root "{arg}"'] 499 shift() 500 elif arg == "--update": 501 update_mode = "yes" 502 shift() 503 elif arg == "--help": 504 usage = "yes" 505 shift() 506 elif arg == "--64": 507 run_args += ["--64"] 508 suffix64 = "64" 509 shift() 510 elif arg == "--bionic": 511 # soong linux_bionic builds are 64bit only. 512 run_args += ["--bionic --host --64"] 513 suffix64 = "64" 514 target_mode = "no" 515 DEX_LOCATION = tmp_dir 516 host_lib_root = f"{OUT_DIR}/soong/host/linux_bionic-x86" 517 shift() 518 elif arg == "--timeout": 519 shift() 520 if not arg: 521 error("missing argument to --timeout") 522 usage = "yes" 523 break 524 timeout = arg 525 shift() 526 elif arg == "--trace": 527 trace = "true" 528 shift() 529 elif arg == "--stream": 530 trace_stream = "true" 531 shift() 532 elif arg == "--always-clean": 533 always_clean = "yes" 534 shift() 535 elif arg == "--never-clean": 536 never_clean = "yes" 537 shift() 538 elif arg == "--dex2oat-swap": 539 run_args += ["--dex2oat-swap"] 540 shift() 541 elif arg == "--instruction-set-features": 542 shift() 543 run_args += [f'--instruction-set-features "{arg}"'] 544 shift() 545 elif arg == "--bisection-search": 546 bisection_search = "yes" 547 shift() 548 elif arg == "--vdex": 549 run_args += ["--vdex"] 550 shift() 551 elif arg == "--dm": 552 run_args += ["--dm"] 553 shift() 554 elif arg == "--vdex-filter": 555 shift() 556 filter = arg 557 run_args += ['--vdex-filter "{filter}"'] 558 shift() 559 elif arg == "--random-profile": 560 run_args += ["--random-profile"] 561 shift() 562 elif arg == "--dex2oat-jobs": 563 shift() 564 run_args += [f'-Xcompiler-option "-j{arg}"'] 565 shift() 566 elif arg.startswith("--"): 567 error(f"unknown option: {arg}") 568 usage = "yes" 569 break 570 else: 571 break 572 573 export("DEX_LOCATION", DEX_LOCATION) 574 575 if usage == "no" and not arg: 576 error("missing test to run") 577 usage = "yes" 578 579# The DEX_LOCATION with the chroot prefix, if any. 580 chroot_dex_location = f"{chroot}{DEX_LOCATION}" 581 582 # tmp_dir may be relative, resolve. 583 os.chdir(oldwd) 584 tmp_dir = os.path.realpath(tmp_dir) 585 os.chdir(progdir) 586 if not tmp_dir: 587 error(f"Failed to resolve {tmp_dir}") 588 sys.exit(1) 589 os.makedirs(tmp_dir, exist_ok=True) 590 591 # Add thread suspend timeout flag 592 if runtime != "jvm": 593 run_args += [ 594 f'--runtime-option "-XX:ThreadSuspendTimeout={suspend_timeout}"' 595 ] 596 597 if basic_verify == "true": 598 # Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests. 599 run_args += [ 600 "--runtime-option -Xgc:preverify --runtime-option -Xgc:postverify " 601 "--runtime-option -XX:HspaceCompactForOOMMinIntervalMs=0" 602 ] 603 if gc_verify == "true": 604 run_args += [ 605 "--runtime-option -Xgc:preverify_rosalloc --runtime-option " 606 "-Xgc:postverify_rosalloc" 607 ] 608 if gc_stress == "true": 609 run_args += [ 610 "--gc-stress --runtime-option -Xgc:gcstress --runtime-option -Xms2m " 611 "--runtime-option -Xmx16m" 612 ] 613 if jvmti_redefine_stress == "true": 614 run_args += ["--no-app-image --jvmti-redefine-stress"] 615 if jvmti_step_stress == "true": 616 run_args += ["--no-app-image --jvmti-step-stress"] 617 if jvmti_field_stress == "true": 618 run_args += ["--no-app-image --jvmti-field-stress"] 619 if jvmti_trace_stress == "true": 620 run_args += ["--no-app-image --jvmti-trace-stress"] 621 if trace == "true": 622 run_args += [ 623 "--runtime-option -Xmethod-trace --runtime-option " 624 "-Xmethod-trace-file-size:2000000" 625 ] 626 if trace_stream == "true": 627 # Streaming mode uses the file size as the buffer size. So output gets really large. Drop 628 # the ability to analyze the file and just write to /dev/null. 629 run_args += ["--runtime-option -Xmethod-trace-file:/dev/null"] 630 # Enable streaming mode. 631 run_args += ["--runtime-option -Xmethod-trace-stream"] 632 else: 633 run_args += [ 634 f'--runtime-option "-Xmethod-trace-file:{DEX_LOCATION}/trace.bin"' 635 ] 636 elif trace_stream == "true": 637 error("Cannot use --stream without --trace.") 638 sys.exit(1) 639 if timeout: 640 run_args += [f'--timeout "{timeout}"'] 641 642# Most interesting target architecture variables are Makefile variables, not environment variables. 643# Try to map the suffix64 flag and what we find in {ANDROID_PRODUCT_OUT}/data/art-test to an architecture name. 644 645 def guess_target_arch_name(): 646 return get_target_arch(suffix64 == "64") 647 648 def guess_host_arch_name(): 649 if suffix64 == "64": 650 return "x86_64" 651 else: 652 return "x86" 653 654 if target_mode == "no": 655 if runtime == "jvm": 656 if prebuild_mode == "yes": 657 error("--prebuild with --jvm is unsupported") 658 sys.exit(1) 659 else: 660 # ART/Dalvik host mode. 661 if chroot: 662 error("--chroot with --host is unsupported") 663 sys.exit(1) 664 665 if runtime != "jvm": 666 run_args += [f'--lib "{lib}"'] 667 668 ANDROID_PRODUCT_OUT = os.environ.get("ANDROID_PRODUCT_OUT") 669 if runtime == "dalvik": 670 if target_mode == "no": 671 framework = f"{ANDROID_PRODUCT_OUT}/system/framework" 672 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" 673 run_args += [f'--boot --runtime-option "-Xbootclasspath:{bpath}"'] 674 else: 675 pass # defaults to using target BOOTCLASSPATH 676 elif runtime == "art": 677 if target_mode == "no": 678 host_arch_name = guess_host_arch_name() 679 run_args += [ 680 f'--boot "{ANDROID_HOST_OUT}/apex/art_boot_images/javalib/boot.art"' 681 ] 682 run_args += [ 683 f'--runtime-option "-Djava.library.path={host_lib_root}/lib{suffix64}:{host_lib_root}/nativetest{suffix64}"' 684 ] 685 else: 686 target_arch_name = guess_target_arch_name() 687 # Note that libarttest(d).so and other test libraries that depend on ART 688 # internal libraries must not be in this path for JNI libraries - they 689 # need to be loaded through LD_LIBRARY_PATH and 690 # NATIVELOADER_DEFAULT_NAMESPACE_LIBS instead. 691 run_args += [ 692 f'--runtime-option "-Djava.library.path=/data/nativetest{suffix64}/art/{target_arch_name}"' 693 ] 694 run_args += ['--boot "/system/framework/art_boot_images/boot.art"'] 695 if relocate == "yes": 696 run_args += ["--relocate"] 697 else: 698 run_args += ["--no-relocate"] 699 elif runtime == "jvm": 700 # TODO: Detect whether the host is 32-bit or 64-bit. 701 run_args += [ 702 f'--runtime-option "-Djava.library.path={ANDROID_HOST_OUT}/lib64:{ANDROID_HOST_OUT}/nativetest64"' 703 ] 704 705 if have_image == "no": 706 if runtime != "art": 707 error("--no-image is only supported on the art runtime") 708 sys.exit(1) 709 run_args += ["--no-image"] 710 711 if create_runner == "yes" and target_mode == "yes": 712 error("--create-runner does not function for non --host tests") 713 usage = "yes" 714 715 if dev_mode == "yes" and update_mode == "yes": 716 error("--dev and --update are mutually exclusive") 717 usage = "yes" 718 719 if dev_mode == "yes" and quiet == "yes": 720 error("--dev and --quiet are mutually exclusive") 721 usage = "yes" 722 723 if bisection_search == "yes" and prebuild_mode == "yes": 724 error("--bisection-search and --prebuild are mutually exclusive") 725 usage = "yes" 726 727# TODO: Chroot-based bisection search is not supported yet (see below); implement it. 728 if bisection_search == "yes" and chroot: 729 error("--chroot with --bisection-search is unsupported") 730 sys.exit(1) 731 732 if usage == "no": 733 if not arg or arg == "-": 734 test_dir = os.path.basename(oldwd) 735 else: 736 test_dir = arg 737 738 if not os.path.isdir(test_dir): 739 td2 = glob.glob(f"{test_dir}-*") 740 if len(td2) == 1 and os.path.isdir(td2[0]): 741 test_dir = td2[0] 742 else: 743 error(f"{test_dir}: no such test directory") 744 usage = "yes" 745 # Shift to get rid of the test name argument. The rest of the arguments 746 # will get passed to the test run. 747 shift() 748 749 if usage == "yes": 750 prog = os.path.basename(__file__) 751 # pyformat: disable 752 help=( 753 "usage:\n" 754 f" $prog --help Print this message.\n" 755 f" $prog [options] [test-name] Run test normally.\n" 756 f" $prog --dev [options] [test-name] Development mode\n" 757 "(dumps to stdout).\n" 758 f" $prog --create-runner [options] [test-name]\n" 759 " Creates a runner script for use with other \n" 760 "tools (e.g. parallel_run.py).\n" 761 " The script will only run the test portion, and \n" 762 "share oat and dex files.\n" 763 f" $prog --update [options] [test-name] Update mode\n" 764 "(replaces expected-stdout.txt and expected-stderr.txt).\n" 765 ' Omitting the test name or specifying "-" will use the\n' 766 "current directory.\n" 767 " Runtime Options:\n" 768 " -O Run non-debug rather than debug build (off by default).\n" 769 " -Xcompiler-option Pass an option to the compiler.\n" 770 " --runtime-option Pass an option to the runtime.\n" 771 " --debug Wait for the default debugger to attach.\n" 772 " --debug-agent <agent-path>\n" 773 " Wait for the given debugger agent to attach. Currently\n" 774 " only supported on host.\n" 775 " --debug-wrap-agent use libwrapagentproperties and tools/libjdwp-compat.props\n" 776 " to load the debugger agent specified by --debug-agent.\n" 777 " --with-agent <agent> Run the test with the given agent loaded with -agentpath:\n" 778 " --debuggable Whether to compile Java code for a debugger.\n" 779 " --gdb Run under gdb; incompatible with some tests.\n" 780 " --gdb-dex2oat Run dex2oat under the prebuilt gdb.\n" 781 " --gdbserver Start gdbserver (defaults to port :5039).\n" 782 " --gdbserver-port <port>\n" 783 " Start gdbserver with the given COMM (see man gdbserver).\n" 784 " --gdbserver-bin <binary>\n" 785 " Use the given binary as gdbserver.\n" 786 " --gdb-arg Pass an option to gdb or gdbserver.\n" 787 " --gdb-dex2oat-args Pass options separated by ';' to gdb for dex2oat.\n" 788 " --simpleperf Wraps the dalvikvm invocation in 'simpleperf record ...\n" 789 " ... simpleperf report' and dumps stats to stdout.\n" 790 " --temp-path [path] Location where to execute the tests.\n" 791 " --interpreter Enable interpreter only mode (off by default).\n" 792 " --jit Enable jit (off by default).\n" 793 " --optimizing Enable optimizing compiler (default).\n" 794 " --no-verify Turn off verification (on by default).\n" 795 " --verify-soft-fail Force soft fail verification (off by default).\n" 796 " Verification is enabled if neither --no-verify\n" 797 " nor --verify-soft-fail is specified.\n" 798 " --no-optimize Turn off optimization (on by default).\n" 799 " --no-precise Turn off precise GC (on by default).\n" 800 " --zygote Spawn the process from the Zygote.\n" 801 "If used, then the\n" 802 " other runtime options are ignored.\n" 803 " --prebuild Run dex2oat on the files before starting test. (default)\n" 804 " --no-prebuild Do not run dex2oat on the files before starting\n" 805 " the test.\n" 806 " --strip-dex Strip the dex files before starting test.\n" 807 " --relocate Force the use of relocating in the test, making\n" 808 " the image and oat files be relocated to a random\n" 809 " address before running.\n" 810 " --no-relocate Force the use of no relocating in the test. (default)\n" 811 " --image Run the test using a precompiled boot image. (default)\n" 812 " --no-image Run the test without a precompiled boot image.\n" 813 " --host Use the host-mode virtual machine.\n" 814 " --invoke-with Pass --invoke-with option to runtime.\n" 815 " --dalvik Use Dalvik (off by default).\n" 816 " --jvm Use a host-local RI virtual machine.\n" 817 " --use-java-home Use the JAVA_HOME environment variable\n" 818 " to find the java compiler and runtime\n" 819 " (if applicable) to run the test with.\n" 820 " --64 Run the test in 64-bit mode\n" 821 " --bionic Use the (host, 64-bit only) linux_bionic libc runtime\n" 822 " --timeout n Test timeout in seconds\n" 823 " --trace Run with method tracing\n" 824 " --strace Run with syscall tracing from strace.\n" 825 " --stream Run method tracing in streaming mode (requires --trace)\n" 826 " --gcstress Run with gc stress testing\n" 827 " --gcverify Run with gc verification\n" 828 " --jvmti-trace-stress Run with jvmti method tracing stress testing\n" 829 " --jvmti-step-stress Run with jvmti single step stress testing\n" 830 " --jvmti-redefine-stress\n" 831 " Run with jvmti method redefinition stress testing\n" 832 " --always-clean Delete the test files even if the test fails.\n" 833 " --never-clean Keep the test files even if the test succeeds.\n" 834 " --chroot [newroot] Run with root directory set to newroot.\n" 835 " --android-root [path] The path on target for the android root. (/system by default).\n" 836 " --android-i18n-root [path]\n" 837 " The path on target for the i18n module root.\n" 838 " (/apex/com.android.i18n by default).\n" 839 " --android-art-root [path]\n" 840 " The path on target for the ART module root.\n" 841 " (/apex/com.android.art by default).\n" 842 " --android-tzdata-root [path]\n" 843 " The path on target for the Android Time Zone Data root.\n" 844 " (/apex/com.android.tzdata by default).\n" 845 " --dex2oat-swap Use a dex2oat swap file.\n" 846 " --instruction-set-features [string]\n" 847 " Set instruction-set-features for compilation.\n" 848 " --quiet Don't print anything except failure messages\n" 849 " --external-log-tags Use ANDROID_LOG_TAGS to set a custom logging level for\n" 850 " a test run.\n" 851 " --bisection-search Perform bisection bug search.\n" 852 " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild.\n" 853 " --suspend-timeout Change thread suspend timeout ms (default 500000).\n" 854 " --dex2oat-jobs Number of dex2oat jobs.\n" 855 ) 856 # pyformat: enable 857 error(help) 858 sys.exit(1) 859 860 os.chdir(test_dir) 861 test_dir = os.getcwd() 862 863 TEST_NAME = os.path.basename(test_dir) 864 export("TEST_NAME", TEST_NAME) 865 866 # Tests named '<number>-checker-*' will also have their CFGs verified with 867 # Checker when compiled with Optimizing on host. 868 # Additionally, if the user specifies that the CFG must be dumped, it will 869 # run the checker for any type of test to generate the CFG. 870 if re.match("[0-9]+-checker-", TEST_NAME) or dump_cfg == "true": 871 if runtime == "art" and run_optimizing == "true": 872 # In no-prebuild or no-image mode, the compiler only quickens so disable the checker. 873 if prebuild_mode == "yes": 874 run_checker = "yes" 875 876 if target_mode == "no": 877 cfg_output_dir = tmp_dir 878 checker_args = f"--arch={host_arch_name.upper()}" 879 else: 880 cfg_output_dir = DEX_LOCATION 881 checker_args = f"--arch={target_arch_name.upper()}" 882 883 if debuggable == "yes": 884 checker_args += " --debuggable" 885 886 run_args += [ 887 f'-Xcompiler-option "--dump-cfg={cfg_output_dir}/{cfg_output}" -Xcompiler-option -j1' 888 ] 889 checker_args = f"{checker_args} --print-cfg" 890 891 run_args += [f'--testlib "{testlib}"'] 892 893 resource.setrlimit(resource.RLIMIT_FSIZE, (file_ulimit * 1024, resource.RLIM_INFINITY)) 894 895 # Extract run-test data from the zip file. 896 shutil.rmtree(tmp_dir) 897 os.makedirs(f"{tmp_dir}/.unzipped") 898 os.chdir(tmp_dir) 899 m = re.match("[0-9]*([0-9][0-9])-.*", TEST_NAME) 900 assert m, "Can not find test number in " + TEST_NAME 901 SHARD = "HiddenApi" if "hiddenapi" in TEST_NAME else m.group(1) 902 if target_mode == "yes": 903 zip_file = f"{ANDROID_HOST_OUT}/etc/art/art-run-test-target-data-shard{SHARD}.zip" 904 zip_entry = f"target/{TEST_NAME}/" 905 elif runtime == "jvm": 906 zip_file = f"{ANDROID_HOST_OUT}/etc/art/art-run-test-jvm-data-shard{SHARD}.zip" 907 zip_entry = f"jvm/{TEST_NAME}/" 908 else: 909 zip_file = f"{ANDROID_HOST_OUT}/etc/art/art-run-test-host-data-shard{SHARD}.zip" 910 zip_entry = f"host/{TEST_NAME}/" 911 zip = ZipFile(zip_file, "r") 912 zip_entries = [e for e in zip.namelist() if e.startswith(zip_entry)] 913 zip.extractall(Path(tmp_dir) / ".unzipped", members=zip_entries) 914 for entry in (Path(tmp_dir) / ".unzipped" / zip_entry).iterdir(): 915 entry.rename(Path(tmp_dir) / entry.name) 916 917 def clean_up(passed: bool): 918 if always_clean == "yes" or (passed and never_clean == "no"): 919 os.chdir(oldwd) 920 shutil.rmtree(tmp_dir) 921 if target_mode == "yes": 922 if ON_VM: 923 run(f"{SSH_CMD} \"rm -rf {chroot_dex_location}\"") 924 else: 925 run(f"adb shell rm -rf {chroot_dex_location}") 926 print(f"{TEST_NAME} files deleted from host" + 927 (" and from target" if target_mode == "yes" else "")) 928 else: 929 print(f"{TEST_NAME} files left in {tmp_dir} on host" + 930 (f" and in {chroot_dex_location} on target" if target_mode == "yes" else "")) 931 atexit.unregister(clean_up) 932 # TODO: Run this in global try-finally once the script is more refactored. 933 atexit.register(clean_up, passed=False) 934 935 ctx = RunTestContext(Path(tmp_dir), target_mode == "yes", chroot, DEX_LOCATION, TEST_NAME) 936 td_info = f"{test_dir}/{info}" 937 for td_file in [td_info, ctx.expected_stdout, ctx.expected_stderr]: 938 assert os.access(td_file, os.R_OK) 939 940 joined_run_args = " ".join(run_args) 941 joined_args = " ".join(args) 942 943 # Create runner (bash script that executes the whole test) 944 def create_runner_script() -> Path: 945 parsed_args = default_run_module.parse_args(shlex.split(" ".join(run_args + args))) 946 parsed_args.stdout_file = os.path.join(DEX_LOCATION, test_stdout) 947 parsed_args.stderr_file = os.path.join(DEX_LOCATION, test_stderr) 948 949 ctx.run(f"cd {DEX_LOCATION}") 950 if target_mode != "yes": 951 # Make "out" directory accessible from test directory. 952 ctx.run(f"ln -s -f -t {DEX_LOCATION} {ANDROID_BUILD_TOP}/out") 953 # Clear the stdout/stderr files (create empty files). 954 ctx.run(f"echo -n > {test_stdout} && echo -n > {test_stderr}") 955 956 script = Path(tmp_dir) / "run.py" 957 if script.exists(): 958 module = SourceFileLoader("run_" + TEST_NAME, str(script)).load_module() 959 module.run(ctx, parsed_args) 960 else: 961 default_run_module.default_run(ctx, parsed_args) 962 963 runner = Path(tmp_dir) / "run.sh" 964 runner.write_text("\n".join(ctx.runner)) 965 runner.chmod(0o777) 966 return runner 967 968 def do_dump_cfg(): 969 assert run_optimizing == "true", ("The CFG can be dumped only in" 970 " optimizing mode") 971 if target_mode == "yes": 972 if ON_VM: 973 run(f'{SCP_CMD} "{SSH_USER}@${SSH_HOST}:{CHROOT}/{cfg_output_dir}/' 974 f'{cfg_output} {dump_cfg_path}"') 975 else: 976 run(f"adb pull {chroot}/{cfg_output_dir}/{cfg_output} {dump_cfg_path}") 977 else: 978 run(f"cp {cfg_output_dir}/{cfg_output} {dump_cfg_path}") 979 980 # Test might not execute anything but we still expect the output files to exist. 981 Path(test_stdout).touch() 982 Path(test_stderr).touch() 983 984 export("TEST_RUNTIME", runtime) 985 986 print(f"{test_dir}: Create runner script...") 987 runner = create_runner_script() 988 989 print(f"{test_dir}: Run...") 990 if target_mode == "yes": 991 # Prepare the on-device test directory 992 if ON_VM: 993 run(f"{SSH_CMD} 'rm -rf {chroot_dex_location} && mkdir -p {chroot_dex_location}'") 994 else: 995 run("adb root") 996 run("adb wait-for-device") 997 run(f"adb shell 'rm -rf {chroot_dex_location} && mkdir -p {chroot_dex_location}'") 998 push_files = [Path(runner.name)] 999 push_files += list(Path(".").glob(f"{TEST_NAME}*.jar")) 1000 push_files += list(Path(".").glob(f"expected-*.txt")) 1001 push_files += [p for p in [Path("profile"), Path("res")] if p.exists()] 1002 push_files = " ".join(map(str, push_files)) 1003 if ON_VM: 1004 run(f"{SCP_CMD} {push_files} {SSH_USER}@{SSH_HOST}:{chroot_dex_location}") 1005 else: 1006 run("adb push {} {}".format(push_files, chroot_dex_location)) 1007 1008 try: 1009 if ON_VM: 1010 run(f"{SSH_CMD} {CHROOT_CMD} bash {DEX_LOCATION}/run.sh", 1011 fail_message=f"Runner {chroot_dex_location}/run.sh failed") 1012 else: 1013 chroot_prefix = f"chroot {chroot}" if chroot else "" 1014 run(f"adb shell {chroot_prefix} sh {DEX_LOCATION}/run.sh", 1015 fail_message=f"Runner {chroot_dex_location}/run.sh failed") 1016 finally: 1017 # Copy the generated CFG to the specified path. 1018 if dump_cfg == "true": 1019 do_dump_cfg() 1020 1021 # Copy the on-device stdout/stderr to host. 1022 pull_files = [test_stdout, test_stderr, "expected-stdout.txt", "expected-stderr.txt"] 1023 if ON_VM: 1024 srcs = " ".join(f"{SSH_USER}@{SSH_HOST}:{chroot_dex_location}/{f}" for f in pull_files) 1025 run(f"{SCP_CMD} {srcs} .") 1026 else: 1027 run("adb pull {} .".format(" ".join(f"{chroot_dex_location}/{f}" for f in pull_files))) 1028 else: 1029 run(str(runner), fail_message=f"Runner {str(runner)} failed") 1030 1031 # NB: There is no exit code or return value. 1032 # Failing tests just raise python exception. 1033 os.chdir(tmp_dir) 1034 if update_mode == "yes": 1035 for src, dst in [(test_stdout, os.path.join(test_dir, ctx.expected_stdout.name)), 1036 (test_stderr, os.path.join(test_dir, ctx.expected_stderr.name))]: 1037 if "[DO_NOT_UPDATE]" not in open(dst).readline(): 1038 copyfile(src, dst) 1039 1040 print("#################### info") 1041 run(f'cat "{td_info}" | sed "s/^/# /g"') 1042 print("#################### stdout diff") 1043 proc_out = run(f'diff --strip-trailing-cr -u ' 1044 f'"{ctx.expected_stdout}" "{test_stdout}"', check=False) 1045 print("#################### stderr diff") 1046 proc_err = run(f'diff --strip-trailing-cr -u ' 1047 f'"{ctx.expected_stderr}" "{test_stderr}"', check=False) 1048 if strace == "yes": 1049 print("#################### strace output (trimmed to 3000 lines)") 1050 # Some tests do not run dalvikvm, in which case the trace does not exist. 1051 run(f'tail -n 3000 "{tmp_dir}/{strace_output}"', check=False) 1052 SANITIZE_HOST = os.environ.get("SANITIZE_HOST") 1053 if target_mode == "no" and SANITIZE_HOST == "address": 1054 # Run the stack script to symbolize any ASAN aborts on the host for SANITIZE_HOST. The 1055 # tools used by the given ABI work for both x86 and x86-64. 1056 print("#################### symbolizer (trimmed to 3000 lines)") 1057 run(f'''echo "ABI: 'x86_64'" | cat - "{test_stdout}" "{test_stderr}"''' 1058 f"""| {ANDROID_BUILD_TOP}/development/scripts/stack | tail -n 3000""") 1059 print("####################", flush=True) 1060 1061 try: 1062 if proc_out.returncode != 0 or proc_err.returncode != 0: 1063 kind = ((["stdout"] if proc_out.returncode != 0 else []) + 1064 (["stderr"] if proc_err.returncode != 0 else [])) 1065 fail("{} did not match the expected file".format(" and ".join(kind))) 1066 1067 if run_checker == "yes": 1068 if target_mode == "yes": 1069 if ON_VM: 1070 run(f'{SCP_CMD} "{SSH_USER}@{SSH_HOST}:{CHROOT}/{cfg_output_dir}/' 1071 f'{cfg_output}" "{tmp_dir}"') 1072 else: 1073 run(f'adb pull "{chroot}/{cfg_output_dir}/{cfg_output}"') 1074 run(f'"{checker}" -q {checker_args} "{cfg_output}" "{tmp_dir}"', 1075 fail_message="CFG checker failed") 1076 finally: 1077 # Copy the generated CFG to the specified path. 1078 if dump_cfg == "true": 1079 do_dump_cfg() 1080 1081 clean_up(passed=True) 1082 print(f"{COLOR_GREEN}{test_dir}: PASSED{COLOR_NORMAL}") 1083