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