• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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