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