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