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