xref: /aosp_15_r20/external/toolchain-utils/crosperf/crosperf.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
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