aboutsummaryrefslogtreecommitdiff
path: root/crosperf/crosperf.py
blob: aace2c805be9b2abe4e7720166730ef217ace72a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright 2011 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""The driver script for running performance benchmarks on ChromeOS."""


import argparse
import atexit
import os
import signal
import sys

# This import causes pylint to warn about "No name 'logger' in module
# 'cros_utils'". I do not understand why. The import works fine in python.
# pylint: disable=no-name-in-module
from cros_utils import logger
from experiment_factory import ExperimentFactory
from experiment_file import ExperimentFile
from experiment_runner import ExperimentRunner
from experiment_runner import MockExperimentRunner
from settings_factory import GlobalSettings
import test_flag


HAS_FAILURE = 1
ALL_FAILED = 2


def SetupParserOptions(parser):
    """Add all options to the parser."""
    parser.add_argument(
        "--dry_run",
        dest="dry_run",
        help=("Parse the experiment file and " "show what will be done"),
        action="store_true",
        default=False,
    )
    # Allow each of the global fields to be overridden by passing in
    # options. Add each global field as an option.
    option_settings = GlobalSettings("")
    for field_name in option_settings.fields:
        field = option_settings.fields[field_name]
        parser.add_argument(
            "--%s" % field.name,
            dest=field.name,
            help=field.description,
            action="store",
        )


def ConvertOptionsToSettings(options):
    """Convert options passed in into global settings."""
    option_settings = GlobalSettings("option_settings")
    for option_name in options.__dict__:
        if (
            options.__dict__[option_name] is not None
            and option_name in option_settings.fields
        ):
            option_settings.SetField(option_name, options.__dict__[option_name])
    return option_settings


def Cleanup(experiment):
    """Handler function which is registered to the atexit handler."""
    experiment.Cleanup()


def CallExitHandler(signum, _):
    """Signal handler that transforms a signal into a call to exit.

    This is useful because functionality registered by "atexit" will
    be called. It also means you can "catch" the signal by catching
    the SystemExit exception.
    """
    sys.exit(128 + signum)


def RunCrosperf(argv):
    parser = argparse.ArgumentParser()

    parser.add_argument(
        "--noschedv2",
        dest="noschedv2",
        default=False,
        action="store_true",
        help=("Do not use new scheduler. " "Use original scheduler instead."),
    )
    parser.add_argument(
        "-l",
        "--log_dir",
        dest="log_dir",
        default="",
        help="The log_dir, default is under <crosperf_logs>/logs",
    )

    SetupParserOptions(parser)
    options, args = parser.parse_known_args(argv)

    # Convert the relevant options that are passed in into a settings
    # object which will override settings in the experiment file.
    option_settings = ConvertOptionsToSettings(options)
    log_dir = os.path.abspath(os.path.expanduser(options.log_dir))
    logger.GetLogger(log_dir)

    if len(args) == 2:
        experiment_filename = args[1]
    else:
        parser.error("Invalid number arguments.")

    working_directory = os.getcwd()
    if options.dry_run:
        test_flag.SetTestMode(True)

    experiment_file = ExperimentFile(
        open(experiment_filename, encoding="utf-8"), option_settings
    )
    if not experiment_file.GetGlobalSettings().GetField("name"):
        experiment_name = os.path.basename(experiment_filename)
        experiment_file.GetGlobalSettings().SetField("name", experiment_name)
    experiment = ExperimentFactory().GetExperiment(
        experiment_file, working_directory, log_dir
    )

    json_report = experiment_file.GetGlobalSettings().GetField("json_report")

    signal.signal(signal.SIGTERM, CallExitHandler)
    atexit.register(Cleanup, experiment)

    if options.dry_run:
        runner = MockExperimentRunner(experiment, json_report)
    else:
        runner = ExperimentRunner(
            experiment, json_report, using_schedv2=(not options.noschedv2)
        )

    ret = runner.Run()
    if ret == HAS_FAILURE:
        raise RuntimeError("One or more benchmarks failed.")
    if ret == ALL_FAILED:
        raise RuntimeError("All benchmarks failed to run.")


def Main(argv):
    try:
        RunCrosperf(argv)
    except Exception:
        # Flush buffers before exiting to avoid out of order printing
        sys.stdout.flush()
        # Raise exception prints out traceback
        raise


if __name__ == "__main__":
    Main(sys.argv)