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 not target_mode: 763 cfg_output_dir = tmp_dir 764 checker_args = f"--arch={host_arch_name.upper()}" 765 else: 766 cfg_output_dir = DEX_LOCATION 767 checker_args = f"--arch={target_arch_name.upper()}" 768 769 if debuggable: 770 checker_args += " --debuggable" 771 772 run_args += [ 773 f'-Xcompiler-option "--dump-cfg={cfg_output_dir}/{cfg_output}" -Xcompiler-option -j1' 774 ] 775 checker_args = f"{checker_args} --print-cfg" 776 777 run_args += [f'--testlib "{testlib}"'] 778 779 resource.setrlimit(resource.RLIMIT_FSIZE, (file_ulimit * 1024, resource.RLIM_INFINITY)) 780 781 # Extract run-test data from the zip file. 782 def unzip(): 783 if env.ART_TEST_RUN_FROM_SOONG: 784 # We already have the unzipped copy of the data. 785 assert target_mode 786 src = Path(ANDROID_BUILD_TOP) / "out" / "zip" / "target" / TEST_NAME 787 assert src.exists(), src 788 shutil.rmtree(tmp_dir) 789 copytree(src, tmp_dir) 790 os.chdir(tmp_dir) 791 return 792 793 # Clear the contents, but keep the directory just in case it is open in terminal. 794 for file in Path(tmp_dir).iterdir(): 795 if file.is_file() or file.is_symlink(): 796 file.unlink() 797 else: 798 shutil.rmtree(file) 799 os.makedirs(f"{tmp_dir}/.unzipped") 800 os.chdir(tmp_dir) 801 m = re.match("[0-9]*([0-9][0-9])-.*", TEST_NAME) 802 assert m, "Can not find test number in " + TEST_NAME 803 SHARD = "HiddenApi" if "hiddenapi" in TEST_NAME else m.group(1) 804 zip_dir = f"{ANDROID_HOST_OUT}/etc/art" 805 if target_mode: 806 zip_file = f"{zip_dir}/art-run-test-target-data-shard{SHARD}.zip" 807 zip_entry = f"target/{TEST_NAME}/" 808 elif runtime == "jvm": 809 zip_file = f"{zip_dir}/art-run-test-jvm-data-shard{SHARD}.zip" 810 zip_entry = f"jvm/{TEST_NAME}/" 811 else: 812 zip_file = f"{zip_dir}/art-run-test-host-data-shard{SHARD}.zip" 813 zip_entry = f"host/{TEST_NAME}/" 814 zip = ZipFile(zip_file, "r") 815 zip_entries = [e for e in zip.namelist() if e.startswith(zip_entry)] 816 zip.extractall(Path(tmp_dir) / ".unzipped", members=zip_entries) 817 for entry in (Path(tmp_dir) / ".unzipped" / zip_entry).iterdir(): 818 entry.rename(Path(tmp_dir) / entry.name) 819 820 unzip() 821 822 def clean_up(passed: bool): 823 if always_clean or (passed and not never_clean): 824 os.chdir(oldwd) 825 shutil.rmtree(tmp_dir) 826 if target_mode: 827 if ON_VM: 828 run(f"{SSH_CMD} \"rm -rf {chroot_dex_location}\"") 829 else: 830 run(f"adb shell rm -rf {chroot_dex_location}") 831 os.remove(tmp_dir_lock) 832 print(f"{TEST_NAME} files deleted from host" + 833 (" and from target" if target_mode else "")) 834 else: 835 print(f"{TEST_NAME} files left in {tmp_dir} on host" + 836 (f" and in {chroot_dex_location} on target" if target_mode else "")) 837 atexit.unregister(clean_up) 838 839 ctx = RunTestContext(Path(tmp_dir), target_mode, chroot, DEX_LOCATION, TEST_NAME) 840 td_info = f"{test_dir}/{info}" 841 for td_file in [td_info, ctx.expected_stdout, ctx.expected_stderr]: 842 assert os.access(td_file, os.R_OK) 843 844 # Create runner (bash script that executes the whole test) 845 def create_runner_script() -> Path: 846 parsed_args = default_run_module.parse_args(shlex.split(" ".join(run_args + args.test_args))) 847 parsed_args.stdout_file = os.path.join(DEX_LOCATION, test_stdout) 848 parsed_args.stderr_file = os.path.join(DEX_LOCATION, test_stderr) 849 850 ctx.run(f"cd {DEX_LOCATION}") 851 if not target_mode: 852 # Make "out" directory accessible from test directory. 853 ctx.run(f"ln -s -f -t {DEX_LOCATION} {ANDROID_BUILD_TOP}/out") 854 # Clear the stdout/stderr files (create empty files). 855 ctx.run(f"echo -n > {test_stdout} && echo -n > {test_stderr}") 856 857 script = Path(tmp_dir) / "run.py" 858 if script.exists(): 859 module = SourceFileLoader("run_" + TEST_NAME, str(script)).load_module() 860 module.run(ctx, parsed_args) 861 else: 862 default_run_module.default_run(ctx, parsed_args) 863 864 runner = Path(tmp_dir) / "run.sh" 865 runner.write_text("\n".join(ctx.runner)) 866 runner.chmod(0o777) 867 return runner 868 869 def do_dump_cfg(): 870 assert run_optimizing, "The CFG can be dumped only in optimizing mode" 871 if target_mode: 872 if ON_VM: 873 run(f'{SCP_CMD} "{SSH_USER}@${SSH_HOST}:{CHROOT}/{cfg_output_dir}/' 874 f'{cfg_output} {dump_cfg_path}"') 875 else: 876 run(f"adb pull {chroot}/{cfg_output_dir}/{cfg_output} {dump_cfg_path}") 877 else: 878 run(f"cp {cfg_output_dir}/{cfg_output} {dump_cfg_path}") 879 880 # Test might not execute anything but we still expect the output files to exist. 881 Path(test_stdout).touch() 882 Path(test_stderr).touch() 883 884 export("TEST_RUNTIME", runtime) 885 886 print(f"{test_dir}: Create runner script...") 887 runner = create_runner_script() 888 889 if args.create_runner: 890 # TODO: Generate better unique names. 891 dst = args.create_runner / TEST_NAME / f"{Path(tmp_dir).name}.sh" 892 assert not dst.exists(), dst 893 dst.parent.mkdir(parents=True, exist_ok=True) 894 copyfile(runner, dst) 895 896 # Script debugging feature - just export the runner script into a directory, 897 # so that it can be compared before/after runner script refactoring. 898 save_runner_dir = os.environ.get("RUN_TEST_DEBUG__SAVE_RUNNER_DIR") 899 if save_runner_dir: 900 name = [a for a in sys.argv[1:] if not a.startswith("--create-runner")] 901 name = urllib.parse.quote(" ".join(name), safe=' ') 902 dst = Path(save_runner_dir) / TEST_NAME / name 903 os.makedirs(dst.parent, exist_ok=True) 904 txt = runner.read_text() 905 txt = txt.replace(Path(tmp_dir).name, "${TMP_DIR}") # Make it deterministic. 906 txt = re.sub('\[run-test:\d+\]', '[run-test:(line-number)]', txt) 907 dst.write_text(txt) 908 909 if args.create_runner or save_runner_dir: 910 sys.exit(0) 911 912 # TODO: Run this in global try-finally once the script is more refactored. 913 atexit.register(clean_up, passed=False) 914 915 print(f"{test_dir}: Run...") 916 if target_mode: 917 # Prepare the on-device test directory 918 if ON_VM: 919 run(f"{SSH_CMD} 'rm -rf {chroot_dex_location} && mkdir -p {chroot_dex_location}'") 920 else: 921 run("adb root") 922 run("adb wait-for-device") 923 run(f"adb shell 'rm -rf {chroot_dex_location} && mkdir -p {chroot_dex_location}'") 924 push_files = [Path(runner.name)] 925 push_files += list(Path(".").glob(f"{TEST_NAME}*.jar")) 926 push_files += list(Path(".").glob(f"expected-*.txt")) 927 push_files += [p for p in [Path("profile"), Path("res")] if p.exists()] 928 push_files = " ".join(map(str, push_files)) 929 if ON_VM: 930 run(f"{SCP_CMD} {push_files} {SSH_USER}@{SSH_HOST}:{chroot_dex_location}") 931 else: 932 run("adb push {} {}".format(push_files, chroot_dex_location)) 933 934 try: 935 if ON_VM: 936 run(f"{SSH_CMD} {CHROOT_CMD} bash {DEX_LOCATION}/run.sh", 937 fail_message=f"Runner {chroot_dex_location}/run.sh failed") 938 else: 939 chroot_prefix = f"chroot {chroot}" if chroot else "" 940 run(f"adb shell {chroot_prefix} sh {DEX_LOCATION}/run.sh", 941 fail_message=f"Runner {chroot_dex_location}/run.sh failed") 942 finally: 943 # Copy the generated CFG to the specified path. 944 if dump_cfg: 945 do_dump_cfg() 946 947 # Copy the on-device stdout/stderr to host. 948 pull_files = [test_stdout, test_stderr, "expected-stdout.txt", "expected-stderr.txt"] 949 if ON_VM: 950 srcs = " ".join(f"{SSH_USER}@{SSH_HOST}:{chroot_dex_location}/{f}" for f in pull_files) 951 run(f"{SCP_CMD} {srcs} .") 952 else: 953 run("adb pull {} .".format(" ".join(f"{chroot_dex_location}/{f}" for f in pull_files))) 954 else: 955 run(str(runner), fail_message=f"Runner {str(runner)} failed") 956 957 # NB: There is no exit code or return value. 958 # Failing tests just raise python exception. 959 os.chdir(tmp_dir) 960 if update_mode: 961 for src, dst in [(test_stdout, os.path.join(test_dir, ctx.expected_stdout.name)), 962 (test_stderr, os.path.join(test_dir, ctx.expected_stderr.name))]: 963 if "[DO_NOT_UPDATE]" not in open(dst).readline(): 964 copyfile(src, dst) 965 966 print("#################### info") 967 run(f'cat "{td_info}" | sed "s/^/# /g"') 968 print("#################### stdout diff") 969 proc_out = run(f'diff --strip-trailing-cr -u ' 970 f'"{ctx.expected_stdout}" "{test_stdout}"', check=False) 971 print("#################### stderr diff") 972 proc_err = run(f'diff --strip-trailing-cr -u ' 973 f'"{ctx.expected_stderr}" "{test_stderr}"', check=False) 974 if strace: 975 print("#################### strace output (trimmed to 3000 lines)") 976 # Some tests do not run dalvikvm, in which case the trace does not exist. 977 run(f'tail -n 3000 "{tmp_dir}/{strace_output}"', check=False) 978 SANITIZE_HOST = os.environ.get("SANITIZE_HOST") 979 if not target_mode and SANITIZE_HOST == "address": 980 # Run the stack script to symbolize any ASAN aborts on the host for SANITIZE_HOST. The 981 # tools used by the given ABI work for both x86 and x86-64. 982 print("#################### symbolizer (trimmed to 3000 lines)") 983 run(f'''echo "ABI: 'x86_64'" | cat - "{test_stdout}" "{test_stderr}"''' 984 f"""| {ANDROID_BUILD_TOP}/development/scripts/stack | tail -n 3000""") 985 print("####################", flush=True) 986 987 try: 988 if proc_out.returncode != 0 or proc_err.returncode != 0: 989 kind = ((["stdout"] if proc_out.returncode != 0 else []) + 990 (["stderr"] if proc_err.returncode != 0 else [])) 991 fail("{} did not match the expected file".format(" and ".join(kind))) 992 993 if run_checker: 994 if target_mode: 995 if ON_VM: 996 run(f'{SCP_CMD} "{SSH_USER}@{SSH_HOST}:{CHROOT}/{cfg_output_dir}/' 997 f'{cfg_output}" "{tmp_dir}"') 998 else: 999 run(f'adb pull "{chroot}/{cfg_output_dir}/{cfg_output}"') 1000 run(f'"{checker}" -q {checker_args} "{cfg_output}" "{tmp_dir}"', 1001 fail_message="CFG checker failed") 1002 finally: 1003 # Copy the generated CFG to the specified path. 1004 if dump_cfg: 1005 do_dump_cfg() 1006 1007 clean_up(passed=True) 1008 print(f"{COLOR_GREEN}{test_dir}: PASSED{COLOR_NORMAL}") 1009