1*cc02d7e2SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*cc02d7e2SAndroid Build Coastguard Worker 3*cc02d7e2SAndroid Build Coastguard Worker# Copyright 2023 gRPC authors. 4*cc02d7e2SAndroid Build Coastguard Worker# 5*cc02d7e2SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*cc02d7e2SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*cc02d7e2SAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*cc02d7e2SAndroid Build Coastguard Worker# 9*cc02d7e2SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*cc02d7e2SAndroid Build Coastguard Worker# 11*cc02d7e2SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*cc02d7e2SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*cc02d7e2SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*cc02d7e2SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*cc02d7e2SAndroid Build Coastguard Worker# limitations under the License. 16*cc02d7e2SAndroid Build Coastguard Worker""" 17*cc02d7e2SAndroid Build Coastguard WorkerLocal QPS benchmark runner for the OSS Benchmark loadtest configurations. 18*cc02d7e2SAndroid Build Coastguard Worker 19*cc02d7e2SAndroid Build Coastguard WorkerThis tool will run a scenario locally, either already extracted from 20*cc02d7e2SAndroid Build Coastguard Workerscenario_config_exporter, or extracted from a benchmark loadtest config. The 21*cc02d7e2SAndroid Build Coastguard Workerdriver, client, and server all in the same process. You can run the process 22*cc02d7e2SAndroid Build Coastguard Workerunder a custom runner using the --runner_cmd="<COMMAND>" flag, and with custom 23*cc02d7e2SAndroid Build Coastguard Workerenvironment variables if needed. 24*cc02d7e2SAndroid Build Coastguard Worker 25*cc02d7e2SAndroid Build Coastguard WorkerThis example will run an optimized build of the loadtest under gdb 26*cc02d7e2SAndroid Build Coastguard Worker 27*cc02d7e2SAndroid Build Coastguard WorkerGRPC_VERBOSITY=debug \ 28*cc02d7e2SAndroid Build Coastguard Worker bazel run \ 29*cc02d7e2SAndroid Build Coastguard Worker --config=opt \ 30*cc02d7e2SAndroid Build Coastguard Worker --cxxopt="-gmlt" \ 31*cc02d7e2SAndroid Build Coastguard Worker test/cpp/qps:scenario_runner -- \ 32*cc02d7e2SAndroid Build Coastguard Worker --loadtest_file=/path/to/loadtest.config \ 33*cc02d7e2SAndroid Build Coastguard Worker --runner_cmd="gdb --args" 34*cc02d7e2SAndroid Build Coastguard Worker 35*cc02d7e2SAndroid Build Coastguard WorkerThis builds the binary and runs: 36*cc02d7e2SAndroid Build Coastguard Worker 37*cc02d7e2SAndroid Build Coastguard Worker gdb --args bazel-bin/.../scenario_runner -- \ 38*cc02d7e2SAndroid Build Coastguard Worker --loadtest_config=/tmp/path/extracted_scenario_json.config 39*cc02d7e2SAndroid Build Coastguard Worker 40*cc02d7e2SAndroid Build Coastguard Worker 41*cc02d7e2SAndroid Build Coastguard WorkerIf you have already extracted the JSON scenario using scenario_config_exporter, 42*cc02d7e2SAndroid Build Coastguard Workeryou can replace `--loadtest_file=loadtest.yaml` with 43*cc02d7e2SAndroid Build Coastguard Worker`--scenario_file=scenario.json`. 44*cc02d7e2SAndroid Build Coastguard Worker 45*cc02d7e2SAndroid Build Coastguard Worker 46*cc02d7e2SAndroid Build Coastguard WorkerOther --runner_cmd examples: 47*cc02d7e2SAndroid Build Coastguard Worker --runner_cmd="perf record -F 777 -o $(pwd)/perf.data -g --event=cpu-cycles", 48*cc02d7e2SAndroid Build Coastguard Worker --runner_cmd="perf stat record -o $(pwd)/perf.stat.data", 49*cc02d7e2SAndroid Build Coastguard Worker" 50*cc02d7e2SAndroid Build Coastguard Worker""" 51*cc02d7e2SAndroid Build Coastguard Worker 52*cc02d7e2SAndroid Build Coastguard Worker 53*cc02d7e2SAndroid Build Coastguard Workerimport os 54*cc02d7e2SAndroid Build Coastguard Workerimport subprocess 55*cc02d7e2SAndroid Build Coastguard Workerimport sys 56*cc02d7e2SAndroid Build Coastguard Workerimport tempfile 57*cc02d7e2SAndroid Build Coastguard Worker 58*cc02d7e2SAndroid Build Coastguard Workerfrom absl import app 59*cc02d7e2SAndroid Build Coastguard Workerfrom absl import flags 60*cc02d7e2SAndroid Build Coastguard Workerimport yaml 61*cc02d7e2SAndroid Build Coastguard Worker 62*cc02d7e2SAndroid Build Coastguard Worker_LOADTEST_YAML = flags.DEFINE_string( 63*cc02d7e2SAndroid Build Coastguard Worker "loadtest_file", default=None, help="Path to the benchmark loadtest file" 64*cc02d7e2SAndroid Build Coastguard Worker) 65*cc02d7e2SAndroid Build Coastguard Worker_SCENARIO_JSON = flags.DEFINE_string( 66*cc02d7e2SAndroid Build Coastguard Worker "scenario_file", default=None, help="Path to a scenario JSON file" 67*cc02d7e2SAndroid Build Coastguard Worker) 68*cc02d7e2SAndroid Build Coastguard Worker_RUNNER_CMD = flags.DEFINE_string( 69*cc02d7e2SAndroid Build Coastguard Worker "runner_cmd", 70*cc02d7e2SAndroid Build Coastguard Worker default="", 71*cc02d7e2SAndroid Build Coastguard Worker help="Run the scearnio runner under a custom command (example: bazel ... --cmd='perf lock record -o $(pwd)/out')", 72*cc02d7e2SAndroid Build Coastguard Worker) 73*cc02d7e2SAndroid Build Coastguard Worker_RUN_FIRST = flags.DEFINE_bool( 74*cc02d7e2SAndroid Build Coastguard Worker "run_first", 75*cc02d7e2SAndroid Build Coastguard Worker default=False, 76*cc02d7e2SAndroid Build Coastguard Worker help="Only run the first scenario in the loadtest", 77*cc02d7e2SAndroid Build Coastguard Worker) 78*cc02d7e2SAndroid Build Coastguard Worker_RUN_ALL = flags.DEFINE_bool( 79*cc02d7e2SAndroid Build Coastguard Worker "run_all", default=False, help="Run all scenarios in the loadtest" 80*cc02d7e2SAndroid Build Coastguard Worker) 81*cc02d7e2SAndroid Build Coastguard Worker 82*cc02d7e2SAndroid Build Coastguard Worker 83*cc02d7e2SAndroid Build Coastguard Workerdef run_command(filename): 84*cc02d7e2SAndroid Build Coastguard Worker cmd = [ 85*cc02d7e2SAndroid Build Coastguard Worker os.path.join( 86*cc02d7e2SAndroid Build Coastguard Worker os.path.dirname(os.path.abspath(__file__)), 87*cc02d7e2SAndroid Build Coastguard Worker "scenario_runner_cc", 88*cc02d7e2SAndroid Build Coastguard Worker ), 89*cc02d7e2SAndroid Build Coastguard Worker "--loadtest_config", 90*cc02d7e2SAndroid Build Coastguard Worker filename, 91*cc02d7e2SAndroid Build Coastguard Worker ] 92*cc02d7e2SAndroid Build Coastguard Worker if _RUNNER_CMD.value: 93*cc02d7e2SAndroid Build Coastguard Worker cmd = _RUNNER_CMD.value.split(" ") + cmd 94*cc02d7e2SAndroid Build Coastguard Worker print(cmd) 95*cc02d7e2SAndroid Build Coastguard Worker subprocess.run(cmd, check=True) 96*cc02d7e2SAndroid Build Coastguard Worker if _RUN_FIRST.value: 97*cc02d7e2SAndroid Build Coastguard Worker print("Exiting due to --run_first") 98*cc02d7e2SAndroid Build Coastguard Worker sys.exit(0) 99*cc02d7e2SAndroid Build Coastguard Worker 100*cc02d7e2SAndroid Build Coastguard Worker 101*cc02d7e2SAndroid Build Coastguard Workerdef run_loadtests(): 102*cc02d7e2SAndroid Build Coastguard Worker loadtests = [] 103*cc02d7e2SAndroid Build Coastguard Worker with open( 104*cc02d7e2SAndroid Build Coastguard Worker os.path.join( 105*cc02d7e2SAndroid Build Coastguard Worker os.path.dirname(os.path.abspath(__file__)), _LOADTEST_YAML.value 106*cc02d7e2SAndroid Build Coastguard Worker ) 107*cc02d7e2SAndroid Build Coastguard Worker ) as f: 108*cc02d7e2SAndroid Build Coastguard Worker loadtests = list(yaml.safe_load_all(f)) 109*cc02d7e2SAndroid Build Coastguard Worker if len(loadtests) > 1 and not (_RUN_FIRST.value or _RUN_ALL.value): 110*cc02d7e2SAndroid Build Coastguard Worker print( 111*cc02d7e2SAndroid Build Coastguard Worker "The loadtest configuration file contains more than one loadtest. Please specify --run_first or --run_all.", 112*cc02d7e2SAndroid Build Coastguard Worker file=sys.stderr, 113*cc02d7e2SAndroid Build Coastguard Worker ) 114*cc02d7e2SAndroid Build Coastguard Worker sys.exit(1) 115*cc02d7e2SAndroid Build Coastguard Worker for loadtest in loadtests: 116*cc02d7e2SAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile() as tmp_f: 117*cc02d7e2SAndroid Build Coastguard Worker tmp_f.write( 118*cc02d7e2SAndroid Build Coastguard Worker "".join(loadtest["spec"]["scenariosJSON"]).encode("utf-8") 119*cc02d7e2SAndroid Build Coastguard Worker ) 120*cc02d7e2SAndroid Build Coastguard Worker tmp_f.flush() 121*cc02d7e2SAndroid Build Coastguard Worker run_command(tmp_f.name) 122*cc02d7e2SAndroid Build Coastguard Worker 123*cc02d7e2SAndroid Build Coastguard Worker 124*cc02d7e2SAndroid Build Coastguard Workerdef run_scenario_file(): 125*cc02d7e2SAndroid Build Coastguard Worker run_command(_SCENARIO_JSON.value) 126*cc02d7e2SAndroid Build Coastguard Worker 127*cc02d7e2SAndroid Build Coastguard Worker 128*cc02d7e2SAndroid Build Coastguard Workerdef main(args): 129*cc02d7e2SAndroid Build Coastguard Worker if _LOADTEST_YAML.value: 130*cc02d7e2SAndroid Build Coastguard Worker run_loadtests() 131*cc02d7e2SAndroid Build Coastguard Worker elif _SCENARIO_JSON.value: 132*cc02d7e2SAndroid Build Coastguard Worker run_scenario_file() 133*cc02d7e2SAndroid Build Coastguard Worker else: 134*cc02d7e2SAndroid Build Coastguard Worker "You must provide either a scenario.json or loadtest.yaml" 135*cc02d7e2SAndroid Build Coastguard Worker 136*cc02d7e2SAndroid Build Coastguard Worker 137*cc02d7e2SAndroid Build Coastguard Workerif __name__ == "__main__": 138*cc02d7e2SAndroid Build Coastguard Worker app.run(main) 139