1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright 2011 The Chromium OS Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""The driver script for running performance benchmarks on ChromeOS.""" 8 9from __future__ import print_function 10 11import argparse 12import atexit 13import os 14import signal 15import sys 16 17from experiment_runner import ExperimentRunner 18from experiment_runner import MockExperimentRunner 19from experiment_factory import ExperimentFactory 20from experiment_file import ExperimentFile 21from settings_factory import GlobalSettings 22 23# This import causes pylint to warn about "No name 'logger' in module 24# 'cros_utils'". I do not understand why. The import works fine in python. 25# pylint: disable=no-name-in-module 26from cros_utils import logger 27 28import test_flag 29 30HAS_FAILURE = 1 31ALL_FAILED = 2 32 33 34def SetupParserOptions(parser): 35 """Add all options to the parser.""" 36 parser.add_argument( 37 '--dry_run', 38 dest='dry_run', 39 help=('Parse the experiment file and ' 40 'show what will be done'), 41 action='store_true', 42 default=False) 43 # Allow each of the global fields to be overridden by passing in 44 # options. Add each global field as an option. 45 option_settings = GlobalSettings('') 46 for field_name in option_settings.fields: 47 field = option_settings.fields[field_name] 48 parser.add_argument( 49 '--%s' % field.name, 50 dest=field.name, 51 help=field.description, 52 action='store') 53 54 55def ConvertOptionsToSettings(options): 56 """Convert options passed in into global settings.""" 57 option_settings = GlobalSettings('option_settings') 58 for option_name in options.__dict__: 59 if (options.__dict__[option_name] is not None and 60 option_name in option_settings.fields): 61 option_settings.SetField(option_name, options.__dict__[option_name]) 62 return option_settings 63 64 65def Cleanup(experiment): 66 """Handler function which is registered to the atexit handler.""" 67 experiment.Cleanup() 68 69 70def CallExitHandler(signum, _): 71 """Signal handler that transforms a signal into a call to exit. 72 73 This is useful because functionality registered by "atexit" will 74 be called. It also means you can "catch" the signal by catching 75 the SystemExit exception. 76 """ 77 sys.exit(128 + signum) 78 79 80def RunCrosperf(argv): 81 parser = argparse.ArgumentParser() 82 83 parser.add_argument( 84 '--noschedv2', 85 dest='noschedv2', 86 default=False, 87 action='store_true', 88 help=('Do not use new scheduler. ' 89 'Use original scheduler instead.')) 90 parser.add_argument( 91 '-l', 92 '--log_dir', 93 dest='log_dir', 94 default='', 95 help='The log_dir, default is under <crosperf_logs>/logs') 96 97 SetupParserOptions(parser) 98 options, args = parser.parse_known_args(argv) 99 100 # Convert the relevant options that are passed in into a settings 101 # object which will override settings in the experiment file. 102 option_settings = ConvertOptionsToSettings(options) 103 log_dir = os.path.abspath(os.path.expanduser(options.log_dir)) 104 logger.GetLogger(log_dir) 105 106 if len(args) == 2: 107 experiment_filename = args[1] 108 else: 109 parser.error('Invalid number arguments.') 110 111 working_directory = os.getcwd() 112 if options.dry_run: 113 test_flag.SetTestMode(True) 114 115 experiment_file = ExperimentFile( 116 open(experiment_filename, encoding='utf-8'), option_settings) 117 if not experiment_file.GetGlobalSettings().GetField('name'): 118 experiment_name = os.path.basename(experiment_filename) 119 experiment_file.GetGlobalSettings().SetField('name', experiment_name) 120 experiment = ExperimentFactory().GetExperiment(experiment_file, 121 working_directory, log_dir) 122 123 json_report = experiment_file.GetGlobalSettings().GetField('json_report') 124 125 signal.signal(signal.SIGTERM, CallExitHandler) 126 atexit.register(Cleanup, experiment) 127 128 if options.dry_run: 129 runner = MockExperimentRunner(experiment, json_report) 130 else: 131 runner = ExperimentRunner( 132 experiment, json_report, using_schedv2=(not options.noschedv2)) 133 134 ret = runner.Run() 135 if ret == HAS_FAILURE: 136 raise RuntimeError('One or more benchmarks failed.') 137 if ret == ALL_FAILED: 138 raise RuntimeError('All benchmarks failed to run.') 139 140 141def Main(argv): 142 try: 143 RunCrosperf(argv) 144 except Exception: 145 # Flush buffers before exiting to avoid out of order printing 146 sys.stdout.flush() 147 # Raise exception prints out traceback 148 raise 149 150 151if __name__ == '__main__': 152 Main(sys.argv) 153