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