11cb0ef41Sopenharmony_ci# Copyright 2017 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci# found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_cifrom contextlib import contextmanager 61cb0ef41Sopenharmony_ciimport os 71cb0ef41Sopenharmony_ciimport re 81cb0ef41Sopenharmony_ciimport signal 91cb0ef41Sopenharmony_ciimport subprocess 101cb0ef41Sopenharmony_ciimport sys 111cb0ef41Sopenharmony_ciimport threading 121cb0ef41Sopenharmony_ciimport time 131cb0ef41Sopenharmony_ci 141cb0ef41Sopenharmony_cifrom ..local.android import ( 151cb0ef41Sopenharmony_ci android_driver, CommandFailedException, TimeoutException) 161cb0ef41Sopenharmony_cifrom ..local import utils 171cb0ef41Sopenharmony_cifrom ..objects import output 181cb0ef41Sopenharmony_ci 191cb0ef41Sopenharmony_ciBASE_DIR = os.path.normpath( 201cb0ef41Sopenharmony_ci os.path.join(os.path.dirname(os.path.abspath(__file__)), '..' , '..', '..')) 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ciSEM_INVALID_VALUE = -1 231cb0ef41Sopenharmony_ciSEM_NOGPFAULTERRORBOX = 0x0002 # Microsoft Platform SDK WinBase.h 241cb0ef41Sopenharmony_ci 251cb0ef41Sopenharmony_ci 261cb0ef41Sopenharmony_cidef setup_testing(): 271cb0ef41Sopenharmony_ci """For testing only: We use threading under the hood instead of 281cb0ef41Sopenharmony_ci multiprocessing to make coverage work. Signal handling is only supported 291cb0ef41Sopenharmony_ci in the main thread, so we disable it for testing. 301cb0ef41Sopenharmony_ci """ 311cb0ef41Sopenharmony_ci signal.signal = lambda *_: None 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_ci 341cb0ef41Sopenharmony_ciclass AbortException(Exception): 351cb0ef41Sopenharmony_ci """Indicates early abort on SIGINT, SIGTERM or internal hard timeout.""" 361cb0ef41Sopenharmony_ci pass 371cb0ef41Sopenharmony_ci 381cb0ef41Sopenharmony_ci 391cb0ef41Sopenharmony_ci@contextmanager 401cb0ef41Sopenharmony_cidef handle_sigterm(process, abort_fun, enabled): 411cb0ef41Sopenharmony_ci """Call`abort_fun` on sigterm and restore previous handler to prevent 421cb0ef41Sopenharmony_ci erroneous termination of an already terminated process. 431cb0ef41Sopenharmony_ci 441cb0ef41Sopenharmony_ci Args: 451cb0ef41Sopenharmony_ci process: The process to terminate. 461cb0ef41Sopenharmony_ci abort_fun: Function taking two parameters: the process to terminate and 471cb0ef41Sopenharmony_ci an array with a boolean for storing if an abort occured. 481cb0ef41Sopenharmony_ci enabled: If False, this wrapper will be a no-op. 491cb0ef41Sopenharmony_ci """ 501cb0ef41Sopenharmony_ci # Variable to communicate with the signal handler. 511cb0ef41Sopenharmony_ci abort_occured = [False] 521cb0ef41Sopenharmony_ci def handler(signum, frame): 531cb0ef41Sopenharmony_ci abort_fun(process, abort_occured) 541cb0ef41Sopenharmony_ci 551cb0ef41Sopenharmony_ci if enabled: 561cb0ef41Sopenharmony_ci previous = signal.signal(signal.SIGTERM, handler) 571cb0ef41Sopenharmony_ci try: 581cb0ef41Sopenharmony_ci yield 591cb0ef41Sopenharmony_ci finally: 601cb0ef41Sopenharmony_ci if enabled: 611cb0ef41Sopenharmony_ci signal.signal(signal.SIGTERM, previous) 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ci if abort_occured[0]: 641cb0ef41Sopenharmony_ci raise AbortException() 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_ci 671cb0ef41Sopenharmony_ciclass BaseCommand(object): 681cb0ef41Sopenharmony_ci def __init__(self, shell, args=None, cmd_prefix=None, timeout=60, env=None, 691cb0ef41Sopenharmony_ci verbose=False, resources_func=None, handle_sigterm=False): 701cb0ef41Sopenharmony_ci """Initialize the command. 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_ci Args: 731cb0ef41Sopenharmony_ci shell: The name of the executable (e.g. d8). 741cb0ef41Sopenharmony_ci args: List of args to pass to the executable. 751cb0ef41Sopenharmony_ci cmd_prefix: Prefix of command (e.g. a wrapper script). 761cb0ef41Sopenharmony_ci timeout: Timeout in seconds. 771cb0ef41Sopenharmony_ci env: Environment dict for execution. 781cb0ef41Sopenharmony_ci verbose: Print additional output. 791cb0ef41Sopenharmony_ci resources_func: Callable, returning all test files needed by this command. 801cb0ef41Sopenharmony_ci handle_sigterm: Flag indicating if SIGTERM will be used to terminate the 811cb0ef41Sopenharmony_ci underlying process. Should not be used from the main thread, e.g. when 821cb0ef41Sopenharmony_ci using a command to list tests. 831cb0ef41Sopenharmony_ci """ 841cb0ef41Sopenharmony_ci assert(timeout > 0) 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ci self.shell = shell 871cb0ef41Sopenharmony_ci self.args = args or [] 881cb0ef41Sopenharmony_ci self.cmd_prefix = cmd_prefix or [] 891cb0ef41Sopenharmony_ci self.timeout = timeout 901cb0ef41Sopenharmony_ci self.env = env or {} 911cb0ef41Sopenharmony_ci self.verbose = verbose 921cb0ef41Sopenharmony_ci self.handle_sigterm = handle_sigterm 931cb0ef41Sopenharmony_ci 941cb0ef41Sopenharmony_ci def execute(self): 951cb0ef41Sopenharmony_ci if self.verbose: 961cb0ef41Sopenharmony_ci print('# %s' % self) 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_ci process = self._start_process() 991cb0ef41Sopenharmony_ci 1001cb0ef41Sopenharmony_ci with handle_sigterm(process, self._abort, self.handle_sigterm): 1011cb0ef41Sopenharmony_ci # Variable to communicate with the timer. 1021cb0ef41Sopenharmony_ci timeout_occured = [False] 1031cb0ef41Sopenharmony_ci timer = threading.Timer( 1041cb0ef41Sopenharmony_ci self.timeout, self._abort, [process, timeout_occured]) 1051cb0ef41Sopenharmony_ci timer.start() 1061cb0ef41Sopenharmony_ci 1071cb0ef41Sopenharmony_ci start_time = time.time() 1081cb0ef41Sopenharmony_ci stdout, stderr = process.communicate() 1091cb0ef41Sopenharmony_ci duration = time.time() - start_time 1101cb0ef41Sopenharmony_ci 1111cb0ef41Sopenharmony_ci timer.cancel() 1121cb0ef41Sopenharmony_ci 1131cb0ef41Sopenharmony_ci return output.Output( 1141cb0ef41Sopenharmony_ci process.returncode, 1151cb0ef41Sopenharmony_ci timeout_occured[0], 1161cb0ef41Sopenharmony_ci stdout.decode('utf-8', 'replace'), 1171cb0ef41Sopenharmony_ci stderr.decode('utf-8', 'replace'), 1181cb0ef41Sopenharmony_ci process.pid, 1191cb0ef41Sopenharmony_ci duration 1201cb0ef41Sopenharmony_ci ) 1211cb0ef41Sopenharmony_ci 1221cb0ef41Sopenharmony_ci def _start_process(self): 1231cb0ef41Sopenharmony_ci try: 1241cb0ef41Sopenharmony_ci return subprocess.Popen( 1251cb0ef41Sopenharmony_ci args=self._get_popen_args(), 1261cb0ef41Sopenharmony_ci stdout=subprocess.PIPE, 1271cb0ef41Sopenharmony_ci stderr=subprocess.PIPE, 1281cb0ef41Sopenharmony_ci env=self._get_env(), 1291cb0ef41Sopenharmony_ci ) 1301cb0ef41Sopenharmony_ci except Exception as e: 1311cb0ef41Sopenharmony_ci sys.stderr.write('Error executing: %s\n' % self) 1321cb0ef41Sopenharmony_ci raise e 1331cb0ef41Sopenharmony_ci 1341cb0ef41Sopenharmony_ci def _get_popen_args(self): 1351cb0ef41Sopenharmony_ci return self._to_args_list() 1361cb0ef41Sopenharmony_ci 1371cb0ef41Sopenharmony_ci def _get_env(self): 1381cb0ef41Sopenharmony_ci env = os.environ.copy() 1391cb0ef41Sopenharmony_ci env.update(self.env) 1401cb0ef41Sopenharmony_ci # GTest shard information is read by the V8 tests runner. Make sure it 1411cb0ef41Sopenharmony_ci # doesn't leak into the execution of gtests we're wrapping. Those might 1421cb0ef41Sopenharmony_ci # otherwise apply a second level of sharding and as a result skip tests. 1431cb0ef41Sopenharmony_ci env.pop('GTEST_TOTAL_SHARDS', None) 1441cb0ef41Sopenharmony_ci env.pop('GTEST_SHARD_INDEX', None) 1451cb0ef41Sopenharmony_ci return env 1461cb0ef41Sopenharmony_ci 1471cb0ef41Sopenharmony_ci def _kill_process(self, process): 1481cb0ef41Sopenharmony_ci raise NotImplementedError() 1491cb0ef41Sopenharmony_ci 1501cb0ef41Sopenharmony_ci def _abort(self, process, abort_called): 1511cb0ef41Sopenharmony_ci abort_called[0] = True 1521cb0ef41Sopenharmony_ci started_as = self.to_string(relative=True) 1531cb0ef41Sopenharmony_ci process_text = 'process %d started as:\n %s\n' % (process.pid, started_as) 1541cb0ef41Sopenharmony_ci try: 1551cb0ef41Sopenharmony_ci print('Attempting to kill ' + process_text) 1561cb0ef41Sopenharmony_ci sys.stdout.flush() 1571cb0ef41Sopenharmony_ci self._kill_process(process) 1581cb0ef41Sopenharmony_ci except OSError as e: 1591cb0ef41Sopenharmony_ci print(e) 1601cb0ef41Sopenharmony_ci print('Unruly ' + process_text) 1611cb0ef41Sopenharmony_ci sys.stdout.flush() 1621cb0ef41Sopenharmony_ci 1631cb0ef41Sopenharmony_ci def __str__(self): 1641cb0ef41Sopenharmony_ci return self.to_string() 1651cb0ef41Sopenharmony_ci 1661cb0ef41Sopenharmony_ci def to_string(self, relative=False): 1671cb0ef41Sopenharmony_ci def escape(part): 1681cb0ef41Sopenharmony_ci # Escape spaces. We may need to escape more characters for this to work 1691cb0ef41Sopenharmony_ci # properly. 1701cb0ef41Sopenharmony_ci if ' ' in part: 1711cb0ef41Sopenharmony_ci return '"%s"' % part 1721cb0ef41Sopenharmony_ci return part 1731cb0ef41Sopenharmony_ci 1741cb0ef41Sopenharmony_ci parts = map(escape, self._to_args_list()) 1751cb0ef41Sopenharmony_ci cmd = ' '.join(parts) 1761cb0ef41Sopenharmony_ci if relative: 1771cb0ef41Sopenharmony_ci cmd = cmd.replace(os.getcwd() + os.sep, '') 1781cb0ef41Sopenharmony_ci return cmd 1791cb0ef41Sopenharmony_ci 1801cb0ef41Sopenharmony_ci def _to_args_list(self): 1811cb0ef41Sopenharmony_ci return self.cmd_prefix + [self.shell] + self.args 1821cb0ef41Sopenharmony_ci 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ciclass PosixCommand(BaseCommand): 1851cb0ef41Sopenharmony_ci # TODO(machenbach): Use base process start without shell once 1861cb0ef41Sopenharmony_ci # https://crbug.com/v8/8889 is resolved. 1871cb0ef41Sopenharmony_ci def _start_process(self): 1881cb0ef41Sopenharmony_ci def wrapped(arg): 1891cb0ef41Sopenharmony_ci if set('() \'"') & set(arg): 1901cb0ef41Sopenharmony_ci return "'%s'" % arg.replace("'", "'\"'\"'") 1911cb0ef41Sopenharmony_ci return arg 1921cb0ef41Sopenharmony_ci try: 1931cb0ef41Sopenharmony_ci return subprocess.Popen( 1941cb0ef41Sopenharmony_ci args=' '.join(map(wrapped, self._get_popen_args())), 1951cb0ef41Sopenharmony_ci stdout=subprocess.PIPE, 1961cb0ef41Sopenharmony_ci stderr=subprocess.PIPE, 1971cb0ef41Sopenharmony_ci env=self._get_env(), 1981cb0ef41Sopenharmony_ci shell=True, 1991cb0ef41Sopenharmony_ci # Make the new shell create its own process group. This allows to kill 2001cb0ef41Sopenharmony_ci # all spawned processes reliably (https://crbug.com/v8/8292). 2011cb0ef41Sopenharmony_ci preexec_fn=os.setsid, 2021cb0ef41Sopenharmony_ci ) 2031cb0ef41Sopenharmony_ci except Exception as e: 2041cb0ef41Sopenharmony_ci sys.stderr.write('Error executing: %s\n' % self) 2051cb0ef41Sopenharmony_ci raise e 2061cb0ef41Sopenharmony_ci 2071cb0ef41Sopenharmony_ci def _kill_process(self, process): 2081cb0ef41Sopenharmony_ci # Kill the whole process group (PID == GPID after setsid). 2091cb0ef41Sopenharmony_ci os.killpg(process.pid, signal.SIGKILL) 2101cb0ef41Sopenharmony_ci 2111cb0ef41Sopenharmony_ci 2121cb0ef41Sopenharmony_cidef taskkill_windows(process, verbose=False, force=True): 2131cb0ef41Sopenharmony_ci force_flag = ' /F' if force else '' 2141cb0ef41Sopenharmony_ci tk = subprocess.Popen( 2151cb0ef41Sopenharmony_ci 'taskkill /T%s /PID %d' % (force_flag, process.pid), 2161cb0ef41Sopenharmony_ci stdout=subprocess.PIPE, 2171cb0ef41Sopenharmony_ci stderr=subprocess.PIPE, 2181cb0ef41Sopenharmony_ci ) 2191cb0ef41Sopenharmony_ci stdout, stderr = tk.communicate() 2201cb0ef41Sopenharmony_ci if verbose: 2211cb0ef41Sopenharmony_ci print('Taskkill results for %d' % process.pid) 2221cb0ef41Sopenharmony_ci print(stdout) 2231cb0ef41Sopenharmony_ci print(stderr) 2241cb0ef41Sopenharmony_ci print('Return code: %d' % tk.returncode) 2251cb0ef41Sopenharmony_ci sys.stdout.flush() 2261cb0ef41Sopenharmony_ci 2271cb0ef41Sopenharmony_ci 2281cb0ef41Sopenharmony_ciclass WindowsCommand(BaseCommand): 2291cb0ef41Sopenharmony_ci def _start_process(self, **kwargs): 2301cb0ef41Sopenharmony_ci # Try to change the error mode to avoid dialogs on fatal errors. Don't 2311cb0ef41Sopenharmony_ci # touch any existing error mode flags by merging the existing error mode. 2321cb0ef41Sopenharmony_ci # See http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx. 2331cb0ef41Sopenharmony_ci def set_error_mode(mode): 2341cb0ef41Sopenharmony_ci prev_error_mode = SEM_INVALID_VALUE 2351cb0ef41Sopenharmony_ci try: 2361cb0ef41Sopenharmony_ci import ctypes 2371cb0ef41Sopenharmony_ci prev_error_mode = ( 2381cb0ef41Sopenharmony_ci ctypes.windll.kernel32.SetErrorMode(mode)) #@UndefinedVariable 2391cb0ef41Sopenharmony_ci except ImportError: 2401cb0ef41Sopenharmony_ci pass 2411cb0ef41Sopenharmony_ci return prev_error_mode 2421cb0ef41Sopenharmony_ci 2431cb0ef41Sopenharmony_ci error_mode = SEM_NOGPFAULTERRORBOX 2441cb0ef41Sopenharmony_ci prev_error_mode = set_error_mode(error_mode) 2451cb0ef41Sopenharmony_ci set_error_mode(error_mode | prev_error_mode) 2461cb0ef41Sopenharmony_ci 2471cb0ef41Sopenharmony_ci try: 2481cb0ef41Sopenharmony_ci return super(WindowsCommand, self)._start_process(**kwargs) 2491cb0ef41Sopenharmony_ci finally: 2501cb0ef41Sopenharmony_ci if prev_error_mode != SEM_INVALID_VALUE: 2511cb0ef41Sopenharmony_ci set_error_mode(prev_error_mode) 2521cb0ef41Sopenharmony_ci 2531cb0ef41Sopenharmony_ci def _get_popen_args(self): 2541cb0ef41Sopenharmony_ci return subprocess.list2cmdline(self._to_args_list()) 2551cb0ef41Sopenharmony_ci 2561cb0ef41Sopenharmony_ci def _kill_process(self, process): 2571cb0ef41Sopenharmony_ci taskkill_windows(process, self.verbose) 2581cb0ef41Sopenharmony_ci 2591cb0ef41Sopenharmony_ci 2601cb0ef41Sopenharmony_ciclass AndroidCommand(BaseCommand): 2611cb0ef41Sopenharmony_ci # This must be initialized before creating any instances of this class. 2621cb0ef41Sopenharmony_ci driver = None 2631cb0ef41Sopenharmony_ci 2641cb0ef41Sopenharmony_ci def __init__(self, shell, args=None, cmd_prefix=None, timeout=60, env=None, 2651cb0ef41Sopenharmony_ci verbose=False, resources_func=None, handle_sigterm=False): 2661cb0ef41Sopenharmony_ci """Initialize the command and all files that need to be pushed to the 2671cb0ef41Sopenharmony_ci Android device. 2681cb0ef41Sopenharmony_ci """ 2691cb0ef41Sopenharmony_ci self.shell_name = os.path.basename(shell) 2701cb0ef41Sopenharmony_ci self.shell_dir = os.path.dirname(shell) 2711cb0ef41Sopenharmony_ci self.files_to_push = (resources_func or (lambda: []))() 2721cb0ef41Sopenharmony_ci 2731cb0ef41Sopenharmony_ci # Make all paths in arguments relative and also prepare files from arguments 2741cb0ef41Sopenharmony_ci # for pushing to the device. 2751cb0ef41Sopenharmony_ci rel_args = [] 2761cb0ef41Sopenharmony_ci find_path_re = re.compile(r'.*(%s/[^\'"]+).*' % re.escape(BASE_DIR)) 2771cb0ef41Sopenharmony_ci for arg in (args or []): 2781cb0ef41Sopenharmony_ci match = find_path_re.match(arg) 2791cb0ef41Sopenharmony_ci if match: 2801cb0ef41Sopenharmony_ci self.files_to_push.append(match.group(1)) 2811cb0ef41Sopenharmony_ci rel_args.append( 2821cb0ef41Sopenharmony_ci re.sub(r'(.*)%s/(.*)' % re.escape(BASE_DIR), r'\1\2', arg)) 2831cb0ef41Sopenharmony_ci 2841cb0ef41Sopenharmony_ci super(AndroidCommand, self).__init__( 2851cb0ef41Sopenharmony_ci shell, args=rel_args, cmd_prefix=cmd_prefix, timeout=timeout, env=env, 2861cb0ef41Sopenharmony_ci verbose=verbose, handle_sigterm=handle_sigterm) 2871cb0ef41Sopenharmony_ci 2881cb0ef41Sopenharmony_ci def execute(self, **additional_popen_kwargs): 2891cb0ef41Sopenharmony_ci """Execute the command on the device. 2901cb0ef41Sopenharmony_ci 2911cb0ef41Sopenharmony_ci This pushes all required files to the device and then runs the command. 2921cb0ef41Sopenharmony_ci """ 2931cb0ef41Sopenharmony_ci if self.verbose: 2941cb0ef41Sopenharmony_ci print('# %s' % self) 2951cb0ef41Sopenharmony_ci 2961cb0ef41Sopenharmony_ci self.driver.push_executable(self.shell_dir, 'bin', self.shell_name) 2971cb0ef41Sopenharmony_ci 2981cb0ef41Sopenharmony_ci for abs_file in self.files_to_push: 2991cb0ef41Sopenharmony_ci abs_dir = os.path.dirname(abs_file) 3001cb0ef41Sopenharmony_ci file_name = os.path.basename(abs_file) 3011cb0ef41Sopenharmony_ci rel_dir = os.path.relpath(abs_dir, BASE_DIR) 3021cb0ef41Sopenharmony_ci self.driver.push_file(abs_dir, file_name, rel_dir) 3031cb0ef41Sopenharmony_ci 3041cb0ef41Sopenharmony_ci start_time = time.time() 3051cb0ef41Sopenharmony_ci return_code = 0 3061cb0ef41Sopenharmony_ci timed_out = False 3071cb0ef41Sopenharmony_ci try: 3081cb0ef41Sopenharmony_ci stdout = self.driver.run( 3091cb0ef41Sopenharmony_ci 'bin', self.shell_name, self.args, '.', self.timeout, self.env) 3101cb0ef41Sopenharmony_ci except CommandFailedException as e: 3111cb0ef41Sopenharmony_ci return_code = e.status 3121cb0ef41Sopenharmony_ci stdout = e.output 3131cb0ef41Sopenharmony_ci except TimeoutException as e: 3141cb0ef41Sopenharmony_ci return_code = 1 3151cb0ef41Sopenharmony_ci timed_out = True 3161cb0ef41Sopenharmony_ci # Sadly the Android driver doesn't provide output on timeout. 3171cb0ef41Sopenharmony_ci stdout = '' 3181cb0ef41Sopenharmony_ci 3191cb0ef41Sopenharmony_ci duration = time.time() - start_time 3201cb0ef41Sopenharmony_ci return output.Output( 3211cb0ef41Sopenharmony_ci return_code, 3221cb0ef41Sopenharmony_ci timed_out, 3231cb0ef41Sopenharmony_ci stdout, 3241cb0ef41Sopenharmony_ci '', # No stderr available. 3251cb0ef41Sopenharmony_ci -1, # No pid available. 3261cb0ef41Sopenharmony_ci duration, 3271cb0ef41Sopenharmony_ci ) 3281cb0ef41Sopenharmony_ci 3291cb0ef41Sopenharmony_ci 3301cb0ef41Sopenharmony_ciCommand = None 3311cb0ef41Sopenharmony_cidef setup(target_os, device): 3321cb0ef41Sopenharmony_ci """Set the Command class to the OS-specific version.""" 3331cb0ef41Sopenharmony_ci global Command 3341cb0ef41Sopenharmony_ci if target_os == 'android': 3351cb0ef41Sopenharmony_ci AndroidCommand.driver = android_driver(device) 3361cb0ef41Sopenharmony_ci Command = AndroidCommand 3371cb0ef41Sopenharmony_ci elif target_os == 'windows': 3381cb0ef41Sopenharmony_ci Command = WindowsCommand 3391cb0ef41Sopenharmony_ci else: 3401cb0ef41Sopenharmony_ci Command = PosixCommand 3411cb0ef41Sopenharmony_ci 3421cb0ef41Sopenharmony_cidef tear_down(): 3431cb0ef41Sopenharmony_ci """Clean up after using commands.""" 3441cb0ef41Sopenharmony_ci if Command == AndroidCommand: 3451cb0ef41Sopenharmony_ci AndroidCommand.driver.tear_down() 346