xref: /aosp_15_r20/art/tools/bisection_search/bisection_search.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*795d594fSAndroid Build Coastguard Worker#
3*795d594fSAndroid Build Coastguard Worker# Copyright (C) 2016 The Android Open Source Project
4*795d594fSAndroid Build Coastguard Worker#
5*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*795d594fSAndroid Build Coastguard Worker#
9*795d594fSAndroid Build Coastguard Worker#   http://www.apache.org/licenses/LICENSE-2.0
10*795d594fSAndroid Build Coastguard Worker#
11*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*795d594fSAndroid Build Coastguard Worker# limitations under the License.
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker"""Performs bisection bug search on methods and optimizations.
18*795d594fSAndroid Build Coastguard Worker
19*795d594fSAndroid Build Coastguard WorkerSee README.md.
20*795d594fSAndroid Build Coastguard Worker
21*795d594fSAndroid Build Coastguard WorkerExample usage:
22*795d594fSAndroid Build Coastguard Worker./bisection-search.py -cp classes.dex --expected-output output Test
23*795d594fSAndroid Build Coastguard Worker"""
24*795d594fSAndroid Build Coastguard Worker
25*795d594fSAndroid Build Coastguard Workerimport abc
26*795d594fSAndroid Build Coastguard Workerimport argparse
27*795d594fSAndroid Build Coastguard Workerimport os
28*795d594fSAndroid Build Coastguard Workerimport re
29*795d594fSAndroid Build Coastguard Workerimport shlex
30*795d594fSAndroid Build Coastguard Workerimport sys
31*795d594fSAndroid Build Coastguard Worker
32*795d594fSAndroid Build Coastguard Workerfrom subprocess import call
33*795d594fSAndroid Build Coastguard Workerfrom tempfile import NamedTemporaryFile
34*795d594fSAndroid Build Coastguard Worker
35*795d594fSAndroid Build Coastguard Workersys.path.append(os.path.dirname(os.path.dirname(
36*795d594fSAndroid Build Coastguard Worker        os.path.realpath(__file__))))
37*795d594fSAndroid Build Coastguard Worker
38*795d594fSAndroid Build Coastguard Workerfrom common.common import DeviceTestEnv
39*795d594fSAndroid Build Coastguard Workerfrom common.common import FatalError
40*795d594fSAndroid Build Coastguard Workerfrom common.common import GetEnvVariableOrError
41*795d594fSAndroid Build Coastguard Workerfrom common.common import HostTestEnv
42*795d594fSAndroid Build Coastguard Workerfrom common.common import LogSeverity
43*795d594fSAndroid Build Coastguard Workerfrom common.common import RetCode
44*795d594fSAndroid Build Coastguard Worker
45*795d594fSAndroid Build Coastguard Worker
46*795d594fSAndroid Build Coastguard Worker# Passes that are never disabled during search process because disabling them
47*795d594fSAndroid Build Coastguard Worker# would compromise correctness.
48*795d594fSAndroid Build Coastguard WorkerMANDATORY_PASSES = ['dex_cache_array_fixups_arm',
49*795d594fSAndroid Build Coastguard Worker                    'instruction_simplifier$before_codegen',
50*795d594fSAndroid Build Coastguard Worker                    'pc_relative_fixups_x86',
51*795d594fSAndroid Build Coastguard Worker                    'x86_memory_operand_generation']
52*795d594fSAndroid Build Coastguard Worker
53*795d594fSAndroid Build Coastguard Worker# Passes that show up as optimizations in compiler verbose output but aren't
54*795d594fSAndroid Build Coastguard Worker# driven by run-passes mechanism. They are mandatory and will always run, we
55*795d594fSAndroid Build Coastguard Worker# never pass them to --run-passes.
56*795d594fSAndroid Build Coastguard WorkerNON_PASSES = ['builder', 'prepare_for_register_allocation',
57*795d594fSAndroid Build Coastguard Worker              'liveness', 'register']
58*795d594fSAndroid Build Coastguard Worker
59*795d594fSAndroid Build Coastguard Worker# If present in raw cmd, this tag will be replaced with runtime arguments
60*795d594fSAndroid Build Coastguard Worker# controlling the bisection search. Otherwise arguments will be placed on second
61*795d594fSAndroid Build Coastguard Worker# position in the command.
62*795d594fSAndroid Build Coastguard WorkerRAW_CMD_RUNTIME_ARGS_TAG = '{ARGS}'
63*795d594fSAndroid Build Coastguard Worker
64*795d594fSAndroid Build Coastguard Worker# Default boot image path relative to ANDROID_HOST_OUT.
65*795d594fSAndroid Build Coastguard WorkerDEFAULT_IMAGE_RELATIVE_PATH = 'apex/com.android.art/javalib/boot.art'
66*795d594fSAndroid Build Coastguard Worker
67*795d594fSAndroid Build Coastguard Workerclass Dex2OatWrapperTestable(object):
68*795d594fSAndroid Build Coastguard Worker  """Class representing a testable compilation.
69*795d594fSAndroid Build Coastguard Worker
70*795d594fSAndroid Build Coastguard Worker  Accepts filters on compiled methods and optimization passes.
71*795d594fSAndroid Build Coastguard Worker  """
72*795d594fSAndroid Build Coastguard Worker
73*795d594fSAndroid Build Coastguard Worker  def __init__(self, base_cmd, test_env, expected_retcode=None,
74*795d594fSAndroid Build Coastguard Worker               output_checker=None, verbose=False):
75*795d594fSAndroid Build Coastguard Worker    """Constructor.
76*795d594fSAndroid Build Coastguard Worker
77*795d594fSAndroid Build Coastguard Worker    Args:
78*795d594fSAndroid Build Coastguard Worker      base_cmd: list of strings, base command to run.
79*795d594fSAndroid Build Coastguard Worker      test_env: ITestEnv.
80*795d594fSAndroid Build Coastguard Worker      expected_retcode: RetCode, expected normalized return code.
81*795d594fSAndroid Build Coastguard Worker      output_checker: IOutputCheck, output checker.
82*795d594fSAndroid Build Coastguard Worker      verbose: bool, enable verbose output.
83*795d594fSAndroid Build Coastguard Worker    """
84*795d594fSAndroid Build Coastguard Worker    self._base_cmd = base_cmd
85*795d594fSAndroid Build Coastguard Worker    self._test_env = test_env
86*795d594fSAndroid Build Coastguard Worker    self._expected_retcode = expected_retcode
87*795d594fSAndroid Build Coastguard Worker    self._output_checker = output_checker
88*795d594fSAndroid Build Coastguard Worker    self._compiled_methods_path = self._test_env.CreateFile('compiled_methods')
89*795d594fSAndroid Build Coastguard Worker    self._passes_to_run_path = self._test_env.CreateFile('run_passes')
90*795d594fSAndroid Build Coastguard Worker    self._verbose = verbose
91*795d594fSAndroid Build Coastguard Worker    if RAW_CMD_RUNTIME_ARGS_TAG in self._base_cmd:
92*795d594fSAndroid Build Coastguard Worker      self._arguments_position = self._base_cmd.index(RAW_CMD_RUNTIME_ARGS_TAG)
93*795d594fSAndroid Build Coastguard Worker      self._base_cmd.pop(self._arguments_position)
94*795d594fSAndroid Build Coastguard Worker    else:
95*795d594fSAndroid Build Coastguard Worker      self._arguments_position = 1
96*795d594fSAndroid Build Coastguard Worker
97*795d594fSAndroid Build Coastguard Worker  def Test(self, compiled_methods, passes_to_run=None):
98*795d594fSAndroid Build Coastguard Worker    """Tests compilation with compiled_methods and run_passes switches active.
99*795d594fSAndroid Build Coastguard Worker
100*795d594fSAndroid Build Coastguard Worker    If compiled_methods is None then compiles all methods.
101*795d594fSAndroid Build Coastguard Worker    If passes_to_run is None then runs default passes.
102*795d594fSAndroid Build Coastguard Worker
103*795d594fSAndroid Build Coastguard Worker    Args:
104*795d594fSAndroid Build Coastguard Worker      compiled_methods: list of strings representing methods to compile or None.
105*795d594fSAndroid Build Coastguard Worker      passes_to_run: list of strings representing passes to run or None.
106*795d594fSAndroid Build Coastguard Worker
107*795d594fSAndroid Build Coastguard Worker    Returns:
108*795d594fSAndroid Build Coastguard Worker      True if test passes with given settings. False otherwise.
109*795d594fSAndroid Build Coastguard Worker    """
110*795d594fSAndroid Build Coastguard Worker    if self._verbose:
111*795d594fSAndroid Build Coastguard Worker      print('Testing methods: {0} passes: {1}.'.format(
112*795d594fSAndroid Build Coastguard Worker          compiled_methods, passes_to_run))
113*795d594fSAndroid Build Coastguard Worker    cmd = self._PrepareCmd(compiled_methods=compiled_methods,
114*795d594fSAndroid Build Coastguard Worker                           passes_to_run=passes_to_run)
115*795d594fSAndroid Build Coastguard Worker    (output, ret_code) = self._test_env.RunCommand(
116*795d594fSAndroid Build Coastguard Worker        cmd, LogSeverity.ERROR)
117*795d594fSAndroid Build Coastguard Worker    res = True
118*795d594fSAndroid Build Coastguard Worker    if self._expected_retcode:
119*795d594fSAndroid Build Coastguard Worker      res = self._expected_retcode == ret_code
120*795d594fSAndroid Build Coastguard Worker    if self._output_checker:
121*795d594fSAndroid Build Coastguard Worker      res = res and self._output_checker.Check(output)
122*795d594fSAndroid Build Coastguard Worker    if self._verbose:
123*795d594fSAndroid Build Coastguard Worker      print('Test passed: {0}.'.format(res))
124*795d594fSAndroid Build Coastguard Worker    return res
125*795d594fSAndroid Build Coastguard Worker
126*795d594fSAndroid Build Coastguard Worker  def GetAllMethods(self):
127*795d594fSAndroid Build Coastguard Worker    """Get methods compiled during the test.
128*795d594fSAndroid Build Coastguard Worker
129*795d594fSAndroid Build Coastguard Worker    Returns:
130*795d594fSAndroid Build Coastguard Worker      List of strings representing methods compiled during the test.
131*795d594fSAndroid Build Coastguard Worker
132*795d594fSAndroid Build Coastguard Worker    Raises:
133*795d594fSAndroid Build Coastguard Worker      FatalError: An error occurred when retrieving methods list.
134*795d594fSAndroid Build Coastguard Worker    """
135*795d594fSAndroid Build Coastguard Worker    cmd = self._PrepareCmd()
136*795d594fSAndroid Build Coastguard Worker    (output, _) = self._test_env.RunCommand(cmd, LogSeverity.INFO)
137*795d594fSAndroid Build Coastguard Worker    match_methods = re.findall(r'Building ([^\n]+)\n', output)
138*795d594fSAndroid Build Coastguard Worker    if not match_methods:
139*795d594fSAndroid Build Coastguard Worker      raise FatalError('Failed to retrieve methods list. '
140*795d594fSAndroid Build Coastguard Worker                       'Not recognized output format.')
141*795d594fSAndroid Build Coastguard Worker    return match_methods
142*795d594fSAndroid Build Coastguard Worker
143*795d594fSAndroid Build Coastguard Worker  def GetAllPassesForMethod(self, compiled_method):
144*795d594fSAndroid Build Coastguard Worker    """Get all optimization passes ran for a method during the test.
145*795d594fSAndroid Build Coastguard Worker
146*795d594fSAndroid Build Coastguard Worker    Args:
147*795d594fSAndroid Build Coastguard Worker      compiled_method: string representing method to compile.
148*795d594fSAndroid Build Coastguard Worker
149*795d594fSAndroid Build Coastguard Worker    Returns:
150*795d594fSAndroid Build Coastguard Worker      List of strings representing passes ran for compiled_method during test.
151*795d594fSAndroid Build Coastguard Worker
152*795d594fSAndroid Build Coastguard Worker    Raises:
153*795d594fSAndroid Build Coastguard Worker      FatalError: An error occurred when retrieving passes list.
154*795d594fSAndroid Build Coastguard Worker    """
155*795d594fSAndroid Build Coastguard Worker    cmd = self._PrepareCmd(compiled_methods=[compiled_method])
156*795d594fSAndroid Build Coastguard Worker    (output, _) = self._test_env.RunCommand(cmd, LogSeverity.INFO)
157*795d594fSAndroid Build Coastguard Worker    match_passes = re.findall(r'Starting pass: ([^\n]+)\n', output)
158*795d594fSAndroid Build Coastguard Worker    if not match_passes:
159*795d594fSAndroid Build Coastguard Worker      raise FatalError('Failed to retrieve passes list. '
160*795d594fSAndroid Build Coastguard Worker                       'Not recognized output format.')
161*795d594fSAndroid Build Coastguard Worker    return [p for p in match_passes if p not in NON_PASSES]
162*795d594fSAndroid Build Coastguard Worker
163*795d594fSAndroid Build Coastguard Worker  def _PrepareCmd(self, compiled_methods=None, passes_to_run=None):
164*795d594fSAndroid Build Coastguard Worker    """Prepare command to run."""
165*795d594fSAndroid Build Coastguard Worker    cmd = self._base_cmd[0:self._arguments_position]
166*795d594fSAndroid Build Coastguard Worker    # insert additional arguments before the first argument
167*795d594fSAndroid Build Coastguard Worker    if passes_to_run is not None:
168*795d594fSAndroid Build Coastguard Worker      self._test_env.WriteLines(self._passes_to_run_path, passes_to_run)
169*795d594fSAndroid Build Coastguard Worker      cmd += ['-Xcompiler-option', '--run-passes={0}'.format(
170*795d594fSAndroid Build Coastguard Worker          self._passes_to_run_path)]
171*795d594fSAndroid Build Coastguard Worker    cmd += ['-Xcompiler-option', '--runtime-arg', '-Xcompiler-option',
172*795d594fSAndroid Build Coastguard Worker            '-verbose:compiler', '-Xcompiler-option', '-j1']
173*795d594fSAndroid Build Coastguard Worker    cmd += self._base_cmd[self._arguments_position:]
174*795d594fSAndroid Build Coastguard Worker    return cmd
175*795d594fSAndroid Build Coastguard Worker
176*795d594fSAndroid Build Coastguard Worker
177*795d594fSAndroid Build Coastguard Workerclass IOutputCheck(object):
178*795d594fSAndroid Build Coastguard Worker  """Abstract output checking class.
179*795d594fSAndroid Build Coastguard Worker
180*795d594fSAndroid Build Coastguard Worker  Checks if output is correct.
181*795d594fSAndroid Build Coastguard Worker  """
182*795d594fSAndroid Build Coastguard Worker  __meta_class__ = abc.ABCMeta
183*795d594fSAndroid Build Coastguard Worker
184*795d594fSAndroid Build Coastguard Worker  @abc.abstractmethod
185*795d594fSAndroid Build Coastguard Worker  def Check(self, output):
186*795d594fSAndroid Build Coastguard Worker    """Check if output is correct.
187*795d594fSAndroid Build Coastguard Worker
188*795d594fSAndroid Build Coastguard Worker    Args:
189*795d594fSAndroid Build Coastguard Worker      output: string, output to check.
190*795d594fSAndroid Build Coastguard Worker
191*795d594fSAndroid Build Coastguard Worker    Returns:
192*795d594fSAndroid Build Coastguard Worker      boolean, True if output is correct, False otherwise.
193*795d594fSAndroid Build Coastguard Worker    """
194*795d594fSAndroid Build Coastguard Worker
195*795d594fSAndroid Build Coastguard Worker
196*795d594fSAndroid Build Coastguard Workerclass EqualsOutputCheck(IOutputCheck):
197*795d594fSAndroid Build Coastguard Worker  """Concrete output checking class checking for equality to expected output."""
198*795d594fSAndroid Build Coastguard Worker
199*795d594fSAndroid Build Coastguard Worker  def __init__(self, expected_output):
200*795d594fSAndroid Build Coastguard Worker    """Constructor.
201*795d594fSAndroid Build Coastguard Worker
202*795d594fSAndroid Build Coastguard Worker    Args:
203*795d594fSAndroid Build Coastguard Worker      expected_output: string, expected output.
204*795d594fSAndroid Build Coastguard Worker    """
205*795d594fSAndroid Build Coastguard Worker    self._expected_output = expected_output
206*795d594fSAndroid Build Coastguard Worker
207*795d594fSAndroid Build Coastguard Worker  def Check(self, output):
208*795d594fSAndroid Build Coastguard Worker    """See base class."""
209*795d594fSAndroid Build Coastguard Worker    return self._expected_output == output
210*795d594fSAndroid Build Coastguard Worker
211*795d594fSAndroid Build Coastguard Worker
212*795d594fSAndroid Build Coastguard Workerclass ExternalScriptOutputCheck(IOutputCheck):
213*795d594fSAndroid Build Coastguard Worker  """Concrete output checking class calling an external script.
214*795d594fSAndroid Build Coastguard Worker
215*795d594fSAndroid Build Coastguard Worker  The script should accept two arguments, path to expected output and path to
216*795d594fSAndroid Build Coastguard Worker  program output. It should exit with 0 return code if outputs are equivalent
217*795d594fSAndroid Build Coastguard Worker  and with different return code otherwise.
218*795d594fSAndroid Build Coastguard Worker  """
219*795d594fSAndroid Build Coastguard Worker
220*795d594fSAndroid Build Coastguard Worker  def __init__(self, script_path, expected_output_path, logfile):
221*795d594fSAndroid Build Coastguard Worker    """Constructor.
222*795d594fSAndroid Build Coastguard Worker
223*795d594fSAndroid Build Coastguard Worker    Args:
224*795d594fSAndroid Build Coastguard Worker      script_path: string, path to checking script.
225*795d594fSAndroid Build Coastguard Worker      expected_output_path: string, path to file with expected output.
226*795d594fSAndroid Build Coastguard Worker      logfile: file handle, logfile.
227*795d594fSAndroid Build Coastguard Worker    """
228*795d594fSAndroid Build Coastguard Worker    self._script_path = script_path
229*795d594fSAndroid Build Coastguard Worker    self._expected_output_path = expected_output_path
230*795d594fSAndroid Build Coastguard Worker    self._logfile = logfile
231*795d594fSAndroid Build Coastguard Worker
232*795d594fSAndroid Build Coastguard Worker  def Check(self, output):
233*795d594fSAndroid Build Coastguard Worker    """See base class."""
234*795d594fSAndroid Build Coastguard Worker    ret_code = None
235*795d594fSAndroid Build Coastguard Worker    with NamedTemporaryFile(mode='w', delete=False) as temp_file:
236*795d594fSAndroid Build Coastguard Worker      temp_file.write(output)
237*795d594fSAndroid Build Coastguard Worker      temp_file.flush()
238*795d594fSAndroid Build Coastguard Worker      ret_code = call(
239*795d594fSAndroid Build Coastguard Worker          [self._script_path, self._expected_output_path, temp_file.name],
240*795d594fSAndroid Build Coastguard Worker          stdout=self._logfile, stderr=self._logfile, universal_newlines=True)
241*795d594fSAndroid Build Coastguard Worker    return ret_code == 0
242*795d594fSAndroid Build Coastguard Worker
243*795d594fSAndroid Build Coastguard Worker
244*795d594fSAndroid Build Coastguard Workerdef BinarySearch(start, end, test):
245*795d594fSAndroid Build Coastguard Worker  """Binary search integers using test function to guide the process."""
246*795d594fSAndroid Build Coastguard Worker  while start < end:
247*795d594fSAndroid Build Coastguard Worker    mid = (start + end) // 2
248*795d594fSAndroid Build Coastguard Worker    if test(mid):
249*795d594fSAndroid Build Coastguard Worker      start = mid + 1
250*795d594fSAndroid Build Coastguard Worker    else:
251*795d594fSAndroid Build Coastguard Worker      end = mid
252*795d594fSAndroid Build Coastguard Worker  return start
253*795d594fSAndroid Build Coastguard Worker
254*795d594fSAndroid Build Coastguard Worker
255*795d594fSAndroid Build Coastguard Workerdef FilterPasses(passes, cutoff_idx):
256*795d594fSAndroid Build Coastguard Worker  """Filters passes list according to cutoff_idx but keeps mandatory passes."""
257*795d594fSAndroid Build Coastguard Worker  return [opt_pass for idx, opt_pass in enumerate(passes)
258*795d594fSAndroid Build Coastguard Worker          if opt_pass in MANDATORY_PASSES or idx < cutoff_idx]
259*795d594fSAndroid Build Coastguard Worker
260*795d594fSAndroid Build Coastguard Worker
261*795d594fSAndroid Build Coastguard Workerdef BugSearch(testable):
262*795d594fSAndroid Build Coastguard Worker  """Find buggy (method, optimization pass) pair for a given testable.
263*795d594fSAndroid Build Coastguard Worker
264*795d594fSAndroid Build Coastguard Worker  Args:
265*795d594fSAndroid Build Coastguard Worker    testable: Dex2OatWrapperTestable.
266*795d594fSAndroid Build Coastguard Worker
267*795d594fSAndroid Build Coastguard Worker  Returns:
268*795d594fSAndroid Build Coastguard Worker    (string, string) tuple. First element is name of method which when compiled
269*795d594fSAndroid Build Coastguard Worker    exposes test failure. Second element is name of optimization pass such that
270*795d594fSAndroid Build Coastguard Worker    for aforementioned method running all passes up to and excluding the pass
271*795d594fSAndroid Build Coastguard Worker    results in test passing but running all passes up to and including the pass
272*795d594fSAndroid Build Coastguard Worker    results in test failing.
273*795d594fSAndroid Build Coastguard Worker
274*795d594fSAndroid Build Coastguard Worker    (None, None) if test passes when compiling all methods.
275*795d594fSAndroid Build Coastguard Worker    (string, None) if a method is found which exposes the failure, but the
276*795d594fSAndroid Build Coastguard Worker      failure happens even when running just mandatory passes.
277*795d594fSAndroid Build Coastguard Worker
278*795d594fSAndroid Build Coastguard Worker  Raises:
279*795d594fSAndroid Build Coastguard Worker    FatalError: Testable fails with no methods compiled.
280*795d594fSAndroid Build Coastguard Worker    AssertionError: Method failed for all passes when bisecting methods, but
281*795d594fSAndroid Build Coastguard Worker    passed when bisecting passes. Possible sporadic failure.
282*795d594fSAndroid Build Coastguard Worker  """
283*795d594fSAndroid Build Coastguard Worker  all_methods = testable.GetAllMethods()
284*795d594fSAndroid Build Coastguard Worker  faulty_method_idx = BinarySearch(
285*795d594fSAndroid Build Coastguard Worker      0,
286*795d594fSAndroid Build Coastguard Worker      len(all_methods) + 1,
287*795d594fSAndroid Build Coastguard Worker      lambda mid: testable.Test(all_methods[0:mid]))
288*795d594fSAndroid Build Coastguard Worker  if faulty_method_idx == len(all_methods) + 1:
289*795d594fSAndroid Build Coastguard Worker    return (None, None)
290*795d594fSAndroid Build Coastguard Worker  if faulty_method_idx == 0:
291*795d594fSAndroid Build Coastguard Worker    raise FatalError('Testable fails with no methods compiled.')
292*795d594fSAndroid Build Coastguard Worker  faulty_method = all_methods[faulty_method_idx - 1]
293*795d594fSAndroid Build Coastguard Worker  all_passes = testable.GetAllPassesForMethod(faulty_method)
294*795d594fSAndroid Build Coastguard Worker  faulty_pass_idx = BinarySearch(
295*795d594fSAndroid Build Coastguard Worker      0,
296*795d594fSAndroid Build Coastguard Worker      len(all_passes) + 1,
297*795d594fSAndroid Build Coastguard Worker      lambda mid: testable.Test([faulty_method],
298*795d594fSAndroid Build Coastguard Worker                                FilterPasses(all_passes, mid)))
299*795d594fSAndroid Build Coastguard Worker  if faulty_pass_idx == 0:
300*795d594fSAndroid Build Coastguard Worker    return (faulty_method, None)
301*795d594fSAndroid Build Coastguard Worker  assert faulty_pass_idx != len(all_passes) + 1, ('Method must fail for some '
302*795d594fSAndroid Build Coastguard Worker                                                  'passes.')
303*795d594fSAndroid Build Coastguard Worker  faulty_pass = all_passes[faulty_pass_idx - 1]
304*795d594fSAndroid Build Coastguard Worker  return (faulty_method, faulty_pass)
305*795d594fSAndroid Build Coastguard Worker
306*795d594fSAndroid Build Coastguard Worker
307*795d594fSAndroid Build Coastguard Workerdef PrepareParser():
308*795d594fSAndroid Build Coastguard Worker  """Prepares argument parser."""
309*795d594fSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(
310*795d594fSAndroid Build Coastguard Worker      description='Tool for finding compiler bugs. Either --raw-cmd or both '
311*795d594fSAndroid Build Coastguard Worker                  '-cp and --class are required.')
312*795d594fSAndroid Build Coastguard Worker  command_opts = parser.add_argument_group('dalvikvm command options')
313*795d594fSAndroid Build Coastguard Worker  command_opts.add_argument('-cp', '--classpath', type=str, help='classpath')
314*795d594fSAndroid Build Coastguard Worker  command_opts.add_argument('--class', dest='classname', type=str,
315*795d594fSAndroid Build Coastguard Worker                            help='name of main class')
316*795d594fSAndroid Build Coastguard Worker  command_opts.add_argument('--lib', type=str, default='libart.so',
317*795d594fSAndroid Build Coastguard Worker                            help='lib to use, default: libart.so')
318*795d594fSAndroid Build Coastguard Worker  command_opts.add_argument('--dalvikvm-option', dest='dalvikvm_opts',
319*795d594fSAndroid Build Coastguard Worker                            metavar='OPT', nargs='*', default=[],
320*795d594fSAndroid Build Coastguard Worker                            help='additional dalvikvm option')
321*795d594fSAndroid Build Coastguard Worker  command_opts.add_argument('--arg', dest='test_args', nargs='*', default=[],
322*795d594fSAndroid Build Coastguard Worker                            metavar='ARG', help='argument passed to test')
323*795d594fSAndroid Build Coastguard Worker  command_opts.add_argument('--image', type=str, help='path to image')
324*795d594fSAndroid Build Coastguard Worker  command_opts.add_argument('--raw-cmd', type=str,
325*795d594fSAndroid Build Coastguard Worker                            help='bisect with this command, ignore other '
326*795d594fSAndroid Build Coastguard Worker                                 'command options')
327*795d594fSAndroid Build Coastguard Worker  bisection_opts = parser.add_argument_group('bisection options')
328*795d594fSAndroid Build Coastguard Worker  bisection_opts.add_argument('--64', dest='x64', action='store_true',
329*795d594fSAndroid Build Coastguard Worker                              default=False, help='x64 mode')
330*795d594fSAndroid Build Coastguard Worker  bisection_opts.add_argument(
331*795d594fSAndroid Build Coastguard Worker      '--device', action='store_true', default=False, help='run on device')
332*795d594fSAndroid Build Coastguard Worker  bisection_opts.add_argument(
333*795d594fSAndroid Build Coastguard Worker      '--device-serial', help='device serial number, implies --device')
334*795d594fSAndroid Build Coastguard Worker  bisection_opts.add_argument('--expected-output', type=str,
335*795d594fSAndroid Build Coastguard Worker                              help='file containing expected output')
336*795d594fSAndroid Build Coastguard Worker  bisection_opts.add_argument(
337*795d594fSAndroid Build Coastguard Worker      '--expected-retcode', type=str, help='expected normalized return code',
338*795d594fSAndroid Build Coastguard Worker      choices=[RetCode.SUCCESS.name, RetCode.TIMEOUT.name, RetCode.ERROR.name])
339*795d594fSAndroid Build Coastguard Worker  bisection_opts.add_argument(
340*795d594fSAndroid Build Coastguard Worker      '--check-script', type=str,
341*795d594fSAndroid Build Coastguard Worker      help='script comparing output and expected output')
342*795d594fSAndroid Build Coastguard Worker  bisection_opts.add_argument(
343*795d594fSAndroid Build Coastguard Worker      '--logfile', type=str, help='custom logfile location')
344*795d594fSAndroid Build Coastguard Worker  bisection_opts.add_argument('--cleanup', action='store_true',
345*795d594fSAndroid Build Coastguard Worker                              default=False, help='clean up after bisecting')
346*795d594fSAndroid Build Coastguard Worker  bisection_opts.add_argument('--timeout', type=int, default=60,
347*795d594fSAndroid Build Coastguard Worker                              help='if timeout seconds pass assume test failed')
348*795d594fSAndroid Build Coastguard Worker  bisection_opts.add_argument('--verbose', action='store_true',
349*795d594fSAndroid Build Coastguard Worker                              default=False, help='enable verbose output')
350*795d594fSAndroid Build Coastguard Worker  return parser
351*795d594fSAndroid Build Coastguard Worker
352*795d594fSAndroid Build Coastguard Worker
353*795d594fSAndroid Build Coastguard Workerdef PrepareBaseCommand(args, classpath):
354*795d594fSAndroid Build Coastguard Worker  """Prepares base command used to run test."""
355*795d594fSAndroid Build Coastguard Worker  if args.raw_cmd:
356*795d594fSAndroid Build Coastguard Worker    return shlex.split(args.raw_cmd)
357*795d594fSAndroid Build Coastguard Worker  else:
358*795d594fSAndroid Build Coastguard Worker    base_cmd = ['dalvikvm64'] if args.x64 else ['dalvikvm32']
359*795d594fSAndroid Build Coastguard Worker    if not args.device:
360*795d594fSAndroid Build Coastguard Worker      base_cmd += ['-XXlib:{0}'.format(args.lib)]
361*795d594fSAndroid Build Coastguard Worker      if not args.image:
362*795d594fSAndroid Build Coastguard Worker        image_path = (GetEnvVariableOrError('ANDROID_HOST_OUT') +
363*795d594fSAndroid Build Coastguard Worker                      DEFAULT_IMAGE_RELATIVE_PATH)
364*795d594fSAndroid Build Coastguard Worker      else:
365*795d594fSAndroid Build Coastguard Worker        image_path = args.image
366*795d594fSAndroid Build Coastguard Worker      base_cmd += ['-Ximage:{0}'.format(image_path)]
367*795d594fSAndroid Build Coastguard Worker    if args.dalvikvm_opts:
368*795d594fSAndroid Build Coastguard Worker      base_cmd += args.dalvikvm_opts
369*795d594fSAndroid Build Coastguard Worker    base_cmd += ['-cp', classpath, args.classname] + args.test_args
370*795d594fSAndroid Build Coastguard Worker  return base_cmd
371*795d594fSAndroid Build Coastguard Worker
372*795d594fSAndroid Build Coastguard Worker
373*795d594fSAndroid Build Coastguard Workerdef main():
374*795d594fSAndroid Build Coastguard Worker  # Parse arguments
375*795d594fSAndroid Build Coastguard Worker  parser = PrepareParser()
376*795d594fSAndroid Build Coastguard Worker  args = parser.parse_args()
377*795d594fSAndroid Build Coastguard Worker  if not args.raw_cmd and (not args.classpath or not args.classname):
378*795d594fSAndroid Build Coastguard Worker    parser.error('Either --raw-cmd or both -cp and --class are required')
379*795d594fSAndroid Build Coastguard Worker  if args.device_serial:
380*795d594fSAndroid Build Coastguard Worker    args.device = True
381*795d594fSAndroid Build Coastguard Worker  if args.expected_retcode:
382*795d594fSAndroid Build Coastguard Worker    args.expected_retcode = RetCode[args.expected_retcode]
383*795d594fSAndroid Build Coastguard Worker  if not args.expected_retcode and not args.check_script:
384*795d594fSAndroid Build Coastguard Worker    args.expected_retcode = RetCode.SUCCESS
385*795d594fSAndroid Build Coastguard Worker
386*795d594fSAndroid Build Coastguard Worker  # Prepare environment
387*795d594fSAndroid Build Coastguard Worker  classpath = args.classpath
388*795d594fSAndroid Build Coastguard Worker  if args.device:
389*795d594fSAndroid Build Coastguard Worker    test_env = DeviceTestEnv(
390*795d594fSAndroid Build Coastguard Worker        'bisection_search_', args.cleanup, args.logfile, args.timeout,
391*795d594fSAndroid Build Coastguard Worker        args.device_serial)
392*795d594fSAndroid Build Coastguard Worker    if classpath:
393*795d594fSAndroid Build Coastguard Worker      classpath = test_env.PushClasspath(classpath)
394*795d594fSAndroid Build Coastguard Worker  else:
395*795d594fSAndroid Build Coastguard Worker    test_env = HostTestEnv(
396*795d594fSAndroid Build Coastguard Worker        'bisection_search_', args.cleanup, args.logfile, args.timeout, args.x64)
397*795d594fSAndroid Build Coastguard Worker  base_cmd = PrepareBaseCommand(args, classpath)
398*795d594fSAndroid Build Coastguard Worker  output_checker = None
399*795d594fSAndroid Build Coastguard Worker  if args.expected_output:
400*795d594fSAndroid Build Coastguard Worker    if args.check_script:
401*795d594fSAndroid Build Coastguard Worker      output_checker = ExternalScriptOutputCheck(
402*795d594fSAndroid Build Coastguard Worker          args.check_script, args.expected_output, test_env.logfile)
403*795d594fSAndroid Build Coastguard Worker    else:
404*795d594fSAndroid Build Coastguard Worker      with open(args.expected_output, 'r') as expected_output_file:
405*795d594fSAndroid Build Coastguard Worker        output_checker = EqualsOutputCheck(expected_output_file.read())
406*795d594fSAndroid Build Coastguard Worker
407*795d594fSAndroid Build Coastguard Worker  # Perform the search
408*795d594fSAndroid Build Coastguard Worker  try:
409*795d594fSAndroid Build Coastguard Worker    testable = Dex2OatWrapperTestable(base_cmd, test_env, args.expected_retcode,
410*795d594fSAndroid Build Coastguard Worker                                      output_checker, args.verbose)
411*795d594fSAndroid Build Coastguard Worker    if testable.Test(compiled_methods=[]):
412*795d594fSAndroid Build Coastguard Worker      (method, opt_pass) = BugSearch(testable)
413*795d594fSAndroid Build Coastguard Worker    else:
414*795d594fSAndroid Build Coastguard Worker      print('Testable fails with no methods compiled.')
415*795d594fSAndroid Build Coastguard Worker      sys.exit(1)
416*795d594fSAndroid Build Coastguard Worker  except Exception as e:
417*795d594fSAndroid Build Coastguard Worker    print('Error occurred.\nLogfile: {0}'.format(test_env.logfile.name))
418*795d594fSAndroid Build Coastguard Worker    test_env.logfile.write('Exception: {0}\n'.format(e))
419*795d594fSAndroid Build Coastguard Worker    raise
420*795d594fSAndroid Build Coastguard Worker
421*795d594fSAndroid Build Coastguard Worker  # Report results
422*795d594fSAndroid Build Coastguard Worker  if method is None:
423*795d594fSAndroid Build Coastguard Worker    print('Couldn\'t find any bugs.')
424*795d594fSAndroid Build Coastguard Worker  elif opt_pass is None:
425*795d594fSAndroid Build Coastguard Worker    print('Faulty method: {0}. Fails with just mandatory passes.'.format(
426*795d594fSAndroid Build Coastguard Worker        method))
427*795d594fSAndroid Build Coastguard Worker  else:
428*795d594fSAndroid Build Coastguard Worker    print('Faulty method and pass: {0}, {1}.'.format(method, opt_pass))
429*795d594fSAndroid Build Coastguard Worker  print('Logfile: {0}'.format(test_env.logfile.name))
430*795d594fSAndroid Build Coastguard Worker  sys.exit(0)
431*795d594fSAndroid Build Coastguard Worker
432*795d594fSAndroid Build Coastguard Worker
433*795d594fSAndroid Build Coastguard Workerif __name__ == '__main__':
434*795d594fSAndroid Build Coastguard Worker  main()
435