1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/env vpython 2*8975f5c5SAndroid Build Coastguard Worker# 3*8975f5c5SAndroid Build Coastguard Worker# Copyright 2021 The ANGLE Project Authors. All rights reserved. 4*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 5*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file. 6*8975f5c5SAndroid Build Coastguard Worker# 7*8975f5c5SAndroid Build Coastguard Worker# process_angle_perf_results.py: 8*8975f5c5SAndroid Build Coastguard Worker# Perf result merging and upload. Adapted from the Chromium script: 9*8975f5c5SAndroid Build Coastguard Worker# https://chromium.googlesource.com/chromium/src/+/main/tools/perf/process_perf_results.py 10*8975f5c5SAndroid Build Coastguard Worker 11*8975f5c5SAndroid Build Coastguard Workerfrom __future__ import print_function 12*8975f5c5SAndroid Build Coastguard Worker 13*8975f5c5SAndroid Build Coastguard Workerimport argparse 14*8975f5c5SAndroid Build Coastguard Workerimport collections 15*8975f5c5SAndroid Build Coastguard Workerimport datetime 16*8975f5c5SAndroid Build Coastguard Workerimport json 17*8975f5c5SAndroid Build Coastguard Workerimport logging 18*8975f5c5SAndroid Build Coastguard Workerimport multiprocessing 19*8975f5c5SAndroid Build Coastguard Workerimport os 20*8975f5c5SAndroid Build Coastguard Workerimport pathlib 21*8975f5c5SAndroid Build Coastguard Workerimport shutil 22*8975f5c5SAndroid Build Coastguard Workerimport subprocess 23*8975f5c5SAndroid Build Coastguard Workerimport sys 24*8975f5c5SAndroid Build Coastguard Workerimport tempfile 25*8975f5c5SAndroid Build Coastguard Workerimport time 26*8975f5c5SAndroid Build Coastguard Workerimport uuid 27*8975f5c5SAndroid Build Coastguard Worker 28*8975f5c5SAndroid Build Coastguard Workerlogging.basicConfig( 29*8975f5c5SAndroid Build Coastguard Worker level=logging.INFO, 30*8975f5c5SAndroid Build Coastguard Worker format='(%(levelname)s) %(asctime)s pid=%(process)d' 31*8975f5c5SAndroid Build Coastguard Worker ' %(module)s.%(funcName)s:%(lineno)d %(message)s') 32*8975f5c5SAndroid Build Coastguard Worker 33*8975f5c5SAndroid Build Coastguard WorkerPY_UTILS = str(pathlib.Path(__file__).resolve().parents[1] / 'src' / 'tests' / 'py_utils') 34*8975f5c5SAndroid Build Coastguard Workerif PY_UTILS not in sys.path: 35*8975f5c5SAndroid Build Coastguard Worker os.stat(PY_UTILS) and sys.path.insert(0, PY_UTILS) 36*8975f5c5SAndroid Build Coastguard Workerimport angle_metrics 37*8975f5c5SAndroid Build Coastguard Workerimport angle_path_util 38*8975f5c5SAndroid Build Coastguard Worker 39*8975f5c5SAndroid Build Coastguard Workerangle_path_util.AddDepsDirToPath('tools/perf') 40*8975f5c5SAndroid Build Coastguard Workerfrom core import path_util 41*8975f5c5SAndroid Build Coastguard Worker 42*8975f5c5SAndroid Build Coastguard Workerpath_util.AddTelemetryToPath() 43*8975f5c5SAndroid Build Coastguard Workerfrom core import upload_results_to_perf_dashboard 44*8975f5c5SAndroid Build Coastguard Workerfrom core import results_merger 45*8975f5c5SAndroid Build Coastguard Worker 46*8975f5c5SAndroid Build Coastguard Workerpath_util.AddAndroidPylibToPath() 47*8975f5c5SAndroid Build Coastguard Workertry: 48*8975f5c5SAndroid Build Coastguard Worker from pylib.utils import logdog_helper 49*8975f5c5SAndroid Build Coastguard Workerexcept ImportError: 50*8975f5c5SAndroid Build Coastguard Worker pass 51*8975f5c5SAndroid Build Coastguard Worker 52*8975f5c5SAndroid Build Coastguard Workerpath_util.AddTracingToPath() 53*8975f5c5SAndroid Build Coastguard Workerfrom tracing.value import histogram 54*8975f5c5SAndroid Build Coastguard Workerfrom tracing.value import histogram_set 55*8975f5c5SAndroid Build Coastguard Workerfrom tracing.value.diagnostics import generic_set 56*8975f5c5SAndroid Build Coastguard Workerfrom tracing.value.diagnostics import reserved_infos 57*8975f5c5SAndroid Build Coastguard Worker 58*8975f5c5SAndroid Build Coastguard WorkerRESULTS_URL = 'https://chromeperf.appspot.com' 59*8975f5c5SAndroid Build Coastguard WorkerJSON_CONTENT_TYPE = 'application/json' 60*8975f5c5SAndroid Build Coastguard WorkerMACHINE_GROUP = 'ANGLE' 61*8975f5c5SAndroid Build Coastguard WorkerBUILD_URL = 'https://ci.chromium.org/ui/p/angle/builders/ci/%s/%d' 62*8975f5c5SAndroid Build Coastguard Worker 63*8975f5c5SAndroid Build Coastguard WorkerGSUTIL_PY_PATH = str( 64*8975f5c5SAndroid Build Coastguard Worker pathlib.Path(__file__).resolve().parents[1] / 'third_party' / 'depot_tools' / 'gsutil.py') 65*8975f5c5SAndroid Build Coastguard Worker 66*8975f5c5SAndroid Build Coastguard Worker 67*8975f5c5SAndroid Build Coastguard Workerdef _upload_perf_results(json_to_upload, name, configuration_name, build_properties, 68*8975f5c5SAndroid Build Coastguard Worker output_json_file): 69*8975f5c5SAndroid Build Coastguard Worker """Upload the contents of result JSON(s) to the perf dashboard.""" 70*8975f5c5SAndroid Build Coastguard Worker args = [ 71*8975f5c5SAndroid Build Coastguard Worker '--buildername', 72*8975f5c5SAndroid Build Coastguard Worker build_properties['buildername'], 73*8975f5c5SAndroid Build Coastguard Worker '--buildnumber', 74*8975f5c5SAndroid Build Coastguard Worker str(build_properties['buildnumber']), 75*8975f5c5SAndroid Build Coastguard Worker '--name', 76*8975f5c5SAndroid Build Coastguard Worker name, 77*8975f5c5SAndroid Build Coastguard Worker '--configuration-name', 78*8975f5c5SAndroid Build Coastguard Worker configuration_name, 79*8975f5c5SAndroid Build Coastguard Worker '--results-file', 80*8975f5c5SAndroid Build Coastguard Worker json_to_upload, 81*8975f5c5SAndroid Build Coastguard Worker '--results-url', 82*8975f5c5SAndroid Build Coastguard Worker RESULTS_URL, 83*8975f5c5SAndroid Build Coastguard Worker '--output-json-file', 84*8975f5c5SAndroid Build Coastguard Worker output_json_file, 85*8975f5c5SAndroid Build Coastguard Worker '--perf-dashboard-machine-group', 86*8975f5c5SAndroid Build Coastguard Worker MACHINE_GROUP, 87*8975f5c5SAndroid Build Coastguard Worker '--got-angle-revision', 88*8975f5c5SAndroid Build Coastguard Worker build_properties['got_angle_revision'], 89*8975f5c5SAndroid Build Coastguard Worker '--send-as-histograms', 90*8975f5c5SAndroid Build Coastguard Worker '--project', 91*8975f5c5SAndroid Build Coastguard Worker 'angle', 92*8975f5c5SAndroid Build Coastguard Worker ] 93*8975f5c5SAndroid Build Coastguard Worker 94*8975f5c5SAndroid Build Coastguard Worker if build_properties.get('git_revision'): 95*8975f5c5SAndroid Build Coastguard Worker args.append('--git-revision') 96*8975f5c5SAndroid Build Coastguard Worker args.append(build_properties['git_revision']) 97*8975f5c5SAndroid Build Coastguard Worker 98*8975f5c5SAndroid Build Coastguard Worker #TODO(crbug.com/1072729): log this in top level 99*8975f5c5SAndroid Build Coastguard Worker logging.info('upload_results_to_perf_dashboard: %s.' % args) 100*8975f5c5SAndroid Build Coastguard Worker 101*8975f5c5SAndroid Build Coastguard Worker return upload_results_to_perf_dashboard.main(args) 102*8975f5c5SAndroid Build Coastguard Worker 103*8975f5c5SAndroid Build Coastguard Worker 104*8975f5c5SAndroid Build Coastguard Workerdef _merge_json_output(output_json, jsons_to_merge, extra_links, test_cross_device=False): 105*8975f5c5SAndroid Build Coastguard Worker """Merges the contents of one or more results JSONs. 106*8975f5c5SAndroid Build Coastguard Worker 107*8975f5c5SAndroid Build Coastguard Worker Args: 108*8975f5c5SAndroid Build Coastguard Worker output_json: A path to a JSON file to which the merged results should be 109*8975f5c5SAndroid Build Coastguard Worker written. 110*8975f5c5SAndroid Build Coastguard Worker jsons_to_merge: A list of JSON files that should be merged. 111*8975f5c5SAndroid Build Coastguard Worker extra_links: a (key, value) map in which keys are the human-readable strings 112*8975f5c5SAndroid Build Coastguard Worker which describe the data, and value is logdog url that contain the data. 113*8975f5c5SAndroid Build Coastguard Worker """ 114*8975f5c5SAndroid Build Coastguard Worker begin_time = time.time() 115*8975f5c5SAndroid Build Coastguard Worker merged_results = results_merger.merge_test_results(jsons_to_merge, test_cross_device) 116*8975f5c5SAndroid Build Coastguard Worker 117*8975f5c5SAndroid Build Coastguard Worker # Only append the perf results links if present 118*8975f5c5SAndroid Build Coastguard Worker if extra_links: 119*8975f5c5SAndroid Build Coastguard Worker merged_results['links'] = extra_links 120*8975f5c5SAndroid Build Coastguard Worker 121*8975f5c5SAndroid Build Coastguard Worker with open(output_json, 'w') as f: 122*8975f5c5SAndroid Build Coastguard Worker json.dump(merged_results, f) 123*8975f5c5SAndroid Build Coastguard Worker 124*8975f5c5SAndroid Build Coastguard Worker end_time = time.time() 125*8975f5c5SAndroid Build Coastguard Worker print_duration('Merging json test results', begin_time, end_time) 126*8975f5c5SAndroid Build Coastguard Worker return 0 127*8975f5c5SAndroid Build Coastguard Worker 128*8975f5c5SAndroid Build Coastguard Worker 129*8975f5c5SAndroid Build Coastguard Workerdef _handle_perf_json_test_results(benchmark_directory_map, test_results_list): 130*8975f5c5SAndroid Build Coastguard Worker """Checks the test_results.json under each folder: 131*8975f5c5SAndroid Build Coastguard Worker 132*8975f5c5SAndroid Build Coastguard Worker 1. mark the benchmark 'enabled' if tests results are found 133*8975f5c5SAndroid Build Coastguard Worker 2. add the json content to a list for non-ref. 134*8975f5c5SAndroid Build Coastguard Worker """ 135*8975f5c5SAndroid Build Coastguard Worker begin_time = time.time() 136*8975f5c5SAndroid Build Coastguard Worker benchmark_enabled_map = {} 137*8975f5c5SAndroid Build Coastguard Worker for benchmark_name, directories in benchmark_directory_map.items(): 138*8975f5c5SAndroid Build Coastguard Worker for directory in directories: 139*8975f5c5SAndroid Build Coastguard Worker # Obtain the test name we are running 140*8975f5c5SAndroid Build Coastguard Worker is_ref = '.reference' in benchmark_name 141*8975f5c5SAndroid Build Coastguard Worker enabled = True 142*8975f5c5SAndroid Build Coastguard Worker try: 143*8975f5c5SAndroid Build Coastguard Worker with open(os.path.join(directory, 'test_results.json')) as json_data: 144*8975f5c5SAndroid Build Coastguard Worker json_results = json.load(json_data) 145*8975f5c5SAndroid Build Coastguard Worker if not json_results: 146*8975f5c5SAndroid Build Coastguard Worker # Output is null meaning the test didn't produce any results. 147*8975f5c5SAndroid Build Coastguard Worker # Want to output an error and continue loading the rest of the 148*8975f5c5SAndroid Build Coastguard Worker # test results. 149*8975f5c5SAndroid Build Coastguard Worker logging.warning('No results produced for %s, skipping upload' % directory) 150*8975f5c5SAndroid Build Coastguard Worker continue 151*8975f5c5SAndroid Build Coastguard Worker if json_results.get('version') == 3: 152*8975f5c5SAndroid Build Coastguard Worker # Non-telemetry tests don't have written json results but 153*8975f5c5SAndroid Build Coastguard Worker # if they are executing then they are enabled and will generate 154*8975f5c5SAndroid Build Coastguard Worker # chartjson results. 155*8975f5c5SAndroid Build Coastguard Worker if not bool(json_results.get('tests')): 156*8975f5c5SAndroid Build Coastguard Worker enabled = False 157*8975f5c5SAndroid Build Coastguard Worker if not is_ref: 158*8975f5c5SAndroid Build Coastguard Worker # We don't need to upload reference build data to the 159*8975f5c5SAndroid Build Coastguard Worker # flakiness dashboard since we don't monitor the ref build 160*8975f5c5SAndroid Build Coastguard Worker test_results_list.append(json_results) 161*8975f5c5SAndroid Build Coastguard Worker except IOError as e: 162*8975f5c5SAndroid Build Coastguard Worker # TODO(crbug.com/936602): Figure out how to surface these errors. Should 163*8975f5c5SAndroid Build Coastguard Worker # we have a non-zero exit code if we error out? 164*8975f5c5SAndroid Build Coastguard Worker logging.error('Failed to obtain test results for %s: %s', benchmark_name, e) 165*8975f5c5SAndroid Build Coastguard Worker continue 166*8975f5c5SAndroid Build Coastguard Worker if not enabled: 167*8975f5c5SAndroid Build Coastguard Worker # We don't upload disabled benchmarks or tests that are run 168*8975f5c5SAndroid Build Coastguard Worker # as a smoke test 169*8975f5c5SAndroid Build Coastguard Worker logging.info('Benchmark %s ran no tests on at least one shard' % benchmark_name) 170*8975f5c5SAndroid Build Coastguard Worker continue 171*8975f5c5SAndroid Build Coastguard Worker benchmark_enabled_map[benchmark_name] = True 172*8975f5c5SAndroid Build Coastguard Worker 173*8975f5c5SAndroid Build Coastguard Worker end_time = time.time() 174*8975f5c5SAndroid Build Coastguard Worker print_duration('Analyzing perf json test results', begin_time, end_time) 175*8975f5c5SAndroid Build Coastguard Worker return benchmark_enabled_map 176*8975f5c5SAndroid Build Coastguard Worker 177*8975f5c5SAndroid Build Coastguard Worker 178*8975f5c5SAndroid Build Coastguard Workerdef _generate_unique_logdog_filename(name_prefix): 179*8975f5c5SAndroid Build Coastguard Worker return name_prefix + '_' + str(uuid.uuid4()) 180*8975f5c5SAndroid Build Coastguard Worker 181*8975f5c5SAndroid Build Coastguard Worker 182*8975f5c5SAndroid Build Coastguard Workerdef _handle_perf_logs(benchmark_directory_map, extra_links): 183*8975f5c5SAndroid Build Coastguard Worker """ Upload benchmark logs to logdog and add a page entry for them. """ 184*8975f5c5SAndroid Build Coastguard Worker begin_time = time.time() 185*8975f5c5SAndroid Build Coastguard Worker benchmark_logs_links = collections.defaultdict(list) 186*8975f5c5SAndroid Build Coastguard Worker 187*8975f5c5SAndroid Build Coastguard Worker for benchmark_name, directories in benchmark_directory_map.items(): 188*8975f5c5SAndroid Build Coastguard Worker for directory in directories: 189*8975f5c5SAndroid Build Coastguard Worker benchmark_log_file = os.path.join(directory, 'benchmark_log.txt') 190*8975f5c5SAndroid Build Coastguard Worker if os.path.exists(benchmark_log_file): 191*8975f5c5SAndroid Build Coastguard Worker with open(benchmark_log_file) as f: 192*8975f5c5SAndroid Build Coastguard Worker uploaded_link = logdog_helper.text( 193*8975f5c5SAndroid Build Coastguard Worker name=_generate_unique_logdog_filename(benchmark_name), data=f.read()) 194*8975f5c5SAndroid Build Coastguard Worker benchmark_logs_links[benchmark_name].append(uploaded_link) 195*8975f5c5SAndroid Build Coastguard Worker 196*8975f5c5SAndroid Build Coastguard Worker logdog_file_name = _generate_unique_logdog_filename('Benchmarks_Logs') 197*8975f5c5SAndroid Build Coastguard Worker logdog_stream = logdog_helper.text( 198*8975f5c5SAndroid Build Coastguard Worker logdog_file_name, 199*8975f5c5SAndroid Build Coastguard Worker json.dumps(benchmark_logs_links, sort_keys=True, indent=4, separators=(',', ': ')), 200*8975f5c5SAndroid Build Coastguard Worker content_type=JSON_CONTENT_TYPE) 201*8975f5c5SAndroid Build Coastguard Worker extra_links['Benchmarks logs'] = logdog_stream 202*8975f5c5SAndroid Build Coastguard Worker end_time = time.time() 203*8975f5c5SAndroid Build Coastguard Worker print_duration('Generating perf log streams', begin_time, end_time) 204*8975f5c5SAndroid Build Coastguard Worker 205*8975f5c5SAndroid Build Coastguard Worker 206*8975f5c5SAndroid Build Coastguard Workerdef _handle_benchmarks_shard_map(benchmarks_shard_map_file, extra_links): 207*8975f5c5SAndroid Build Coastguard Worker begin_time = time.time() 208*8975f5c5SAndroid Build Coastguard Worker with open(benchmarks_shard_map_file) as f: 209*8975f5c5SAndroid Build Coastguard Worker benchmarks_shard_data = f.read() 210*8975f5c5SAndroid Build Coastguard Worker logdog_file_name = _generate_unique_logdog_filename('Benchmarks_Shard_Map') 211*8975f5c5SAndroid Build Coastguard Worker logdog_stream = logdog_helper.text( 212*8975f5c5SAndroid Build Coastguard Worker logdog_file_name, benchmarks_shard_data, content_type=JSON_CONTENT_TYPE) 213*8975f5c5SAndroid Build Coastguard Worker extra_links['Benchmarks shard map'] = logdog_stream 214*8975f5c5SAndroid Build Coastguard Worker end_time = time.time() 215*8975f5c5SAndroid Build Coastguard Worker print_duration('Generating benchmark shard map stream', begin_time, end_time) 216*8975f5c5SAndroid Build Coastguard Worker 217*8975f5c5SAndroid Build Coastguard Worker 218*8975f5c5SAndroid Build Coastguard Workerdef _get_benchmark_name(directory): 219*8975f5c5SAndroid Build Coastguard Worker return os.path.basename(directory).replace(" benchmark", "") 220*8975f5c5SAndroid Build Coastguard Worker 221*8975f5c5SAndroid Build Coastguard Worker 222*8975f5c5SAndroid Build Coastguard Workerdef _scan_output_dir(task_output_dir): 223*8975f5c5SAndroid Build Coastguard Worker benchmark_directory_map = {} 224*8975f5c5SAndroid Build Coastguard Worker benchmarks_shard_map_file = None 225*8975f5c5SAndroid Build Coastguard Worker 226*8975f5c5SAndroid Build Coastguard Worker directory_list = [ 227*8975f5c5SAndroid Build Coastguard Worker f for f in os.listdir(task_output_dir) 228*8975f5c5SAndroid Build Coastguard Worker if not os.path.isfile(os.path.join(task_output_dir, f)) 229*8975f5c5SAndroid Build Coastguard Worker ] 230*8975f5c5SAndroid Build Coastguard Worker benchmark_directory_list = [] 231*8975f5c5SAndroid Build Coastguard Worker for directory in directory_list: 232*8975f5c5SAndroid Build Coastguard Worker for f in os.listdir(os.path.join(task_output_dir, directory)): 233*8975f5c5SAndroid Build Coastguard Worker path = os.path.join(task_output_dir, directory, f) 234*8975f5c5SAndroid Build Coastguard Worker if os.path.isdir(path): 235*8975f5c5SAndroid Build Coastguard Worker benchmark_directory_list.append(path) 236*8975f5c5SAndroid Build Coastguard Worker elif path.endswith('benchmarks_shard_map.json'): 237*8975f5c5SAndroid Build Coastguard Worker benchmarks_shard_map_file = path 238*8975f5c5SAndroid Build Coastguard Worker # Now create a map of benchmark name to the list of directories 239*8975f5c5SAndroid Build Coastguard Worker # the lists were written to. 240*8975f5c5SAndroid Build Coastguard Worker for directory in benchmark_directory_list: 241*8975f5c5SAndroid Build Coastguard Worker benchmark_name = _get_benchmark_name(directory) 242*8975f5c5SAndroid Build Coastguard Worker logging.debug('Found benchmark %s directory %s' % (benchmark_name, directory)) 243*8975f5c5SAndroid Build Coastguard Worker if benchmark_name in benchmark_directory_map.keys(): 244*8975f5c5SAndroid Build Coastguard Worker benchmark_directory_map[benchmark_name].append(directory) 245*8975f5c5SAndroid Build Coastguard Worker else: 246*8975f5c5SAndroid Build Coastguard Worker benchmark_directory_map[benchmark_name] = [directory] 247*8975f5c5SAndroid Build Coastguard Worker 248*8975f5c5SAndroid Build Coastguard Worker return benchmark_directory_map, benchmarks_shard_map_file 249*8975f5c5SAndroid Build Coastguard Worker 250*8975f5c5SAndroid Build Coastguard Worker 251*8975f5c5SAndroid Build Coastguard Workerdef _upload_to_skia_perf(benchmark_directory_map, benchmark_enabled_map, build_properties_map): 252*8975f5c5SAndroid Build Coastguard Worker metric_filenames = [] 253*8975f5c5SAndroid Build Coastguard Worker 254*8975f5c5SAndroid Build Coastguard Worker for benchmark_name, directories in benchmark_directory_map.items(): 255*8975f5c5SAndroid Build Coastguard Worker if not benchmark_enabled_map.get(benchmark_name, False): 256*8975f5c5SAndroid Build Coastguard Worker continue 257*8975f5c5SAndroid Build Coastguard Worker 258*8975f5c5SAndroid Build Coastguard Worker for directory in directories: 259*8975f5c5SAndroid Build Coastguard Worker metric_filenames.append(os.path.join(directory, 'angle_metrics.json')) 260*8975f5c5SAndroid Build Coastguard Worker 261*8975f5c5SAndroid Build Coastguard Worker assert metric_filenames 262*8975f5c5SAndroid Build Coastguard Worker 263*8975f5c5SAndroid Build Coastguard Worker buildername = build_properties_map['buildername'] # e.g. win10-nvidia-gtx1660-perf 264*8975f5c5SAndroid Build Coastguard Worker skia_data = { 265*8975f5c5SAndroid Build Coastguard Worker 'version': 1, 266*8975f5c5SAndroid Build Coastguard Worker 'git_hash': build_properties_map['got_angle_revision'], 267*8975f5c5SAndroid Build Coastguard Worker 'key': { 268*8975f5c5SAndroid Build Coastguard Worker 'buildername': buildername, 269*8975f5c5SAndroid Build Coastguard Worker }, 270*8975f5c5SAndroid Build Coastguard Worker 'results': angle_metrics.ConvertToSkiaPerf(metric_filenames), 271*8975f5c5SAndroid Build Coastguard Worker } 272*8975f5c5SAndroid Build Coastguard Worker 273*8975f5c5SAndroid Build Coastguard Worker skia_perf_dir = tempfile.mkdtemp('skia_perf') 274*8975f5c5SAndroid Build Coastguard Worker try: 275*8975f5c5SAndroid Build Coastguard Worker local_file = os.path.join(skia_perf_dir, '%s.%s.json' % (buildername, time.time())) 276*8975f5c5SAndroid Build Coastguard Worker with open(local_file, 'w') as f: 277*8975f5c5SAndroid Build Coastguard Worker json.dump(skia_data, f, indent=2) 278*8975f5c5SAndroid Build Coastguard Worker gs_dir = 'gs://angle-perf-skia/angle_perftests/%s/' % ( 279*8975f5c5SAndroid Build Coastguard Worker datetime.datetime.now().strftime('%Y/%m/%d/%H')) 280*8975f5c5SAndroid Build Coastguard Worker upload_cmd = ['vpython3', GSUTIL_PY_PATH, 'cp', local_file, gs_dir] 281*8975f5c5SAndroid Build Coastguard Worker logging.info('Skia upload: %s', ' '.join(upload_cmd)) 282*8975f5c5SAndroid Build Coastguard Worker subprocess.check_call(upload_cmd) 283*8975f5c5SAndroid Build Coastguard Worker finally: 284*8975f5c5SAndroid Build Coastguard Worker shutil.rmtree(skia_perf_dir) 285*8975f5c5SAndroid Build Coastguard Worker 286*8975f5c5SAndroid Build Coastguard Worker 287*8975f5c5SAndroid Build Coastguard Workerdef process_perf_results(output_json, 288*8975f5c5SAndroid Build Coastguard Worker configuration_name, 289*8975f5c5SAndroid Build Coastguard Worker build_properties, 290*8975f5c5SAndroid Build Coastguard Worker task_output_dir, 291*8975f5c5SAndroid Build Coastguard Worker smoke_test_mode, 292*8975f5c5SAndroid Build Coastguard Worker output_results_dir, 293*8975f5c5SAndroid Build Coastguard Worker lightweight=False, 294*8975f5c5SAndroid Build Coastguard Worker skip_perf=False): 295*8975f5c5SAndroid Build Coastguard Worker """Process perf results. 296*8975f5c5SAndroid Build Coastguard Worker 297*8975f5c5SAndroid Build Coastguard Worker Consists of merging the json-test-format output, uploading the perf test 298*8975f5c5SAndroid Build Coastguard Worker output (histogram), and store the benchmark logs in logdog. 299*8975f5c5SAndroid Build Coastguard Worker 300*8975f5c5SAndroid Build Coastguard Worker Each directory in the task_output_dir represents one benchmark 301*8975f5c5SAndroid Build Coastguard Worker that was run. Within this directory, there is a subdirectory with the name 302*8975f5c5SAndroid Build Coastguard Worker of the benchmark that was run. In that subdirectory, there is a 303*8975f5c5SAndroid Build Coastguard Worker perftest-output.json file containing the performance results in histogram 304*8975f5c5SAndroid Build Coastguard Worker format and an output.json file containing the json test results for the 305*8975f5c5SAndroid Build Coastguard Worker benchmark. 306*8975f5c5SAndroid Build Coastguard Worker 307*8975f5c5SAndroid Build Coastguard Worker Returns: 308*8975f5c5SAndroid Build Coastguard Worker (return_code, upload_results_map): 309*8975f5c5SAndroid Build Coastguard Worker return_code is 0 if the whole operation is successful, non zero otherwise. 310*8975f5c5SAndroid Build Coastguard Worker benchmark_upload_result_map: the dictionary that describe which benchmarks 311*8975f5c5SAndroid Build Coastguard Worker were successfully uploaded. 312*8975f5c5SAndroid Build Coastguard Worker """ 313*8975f5c5SAndroid Build Coastguard Worker handle_perf = not lightweight or not skip_perf 314*8975f5c5SAndroid Build Coastguard Worker handle_non_perf = not lightweight or skip_perf 315*8975f5c5SAndroid Build Coastguard Worker logging.info('lightweight mode: %r; handle_perf: %r; handle_non_perf: %r' % 316*8975f5c5SAndroid Build Coastguard Worker (lightweight, handle_perf, handle_non_perf)) 317*8975f5c5SAndroid Build Coastguard Worker 318*8975f5c5SAndroid Build Coastguard Worker begin_time = time.time() 319*8975f5c5SAndroid Build Coastguard Worker return_code = 0 320*8975f5c5SAndroid Build Coastguard Worker benchmark_upload_result_map = {} 321*8975f5c5SAndroid Build Coastguard Worker 322*8975f5c5SAndroid Build Coastguard Worker benchmark_directory_map, benchmarks_shard_map_file = _scan_output_dir(task_output_dir) 323*8975f5c5SAndroid Build Coastguard Worker 324*8975f5c5SAndroid Build Coastguard Worker test_results_list = [] 325*8975f5c5SAndroid Build Coastguard Worker extra_links = {} 326*8975f5c5SAndroid Build Coastguard Worker 327*8975f5c5SAndroid Build Coastguard Worker if handle_non_perf: 328*8975f5c5SAndroid Build Coastguard Worker # First, upload benchmarks shard map to logdog and add a page 329*8975f5c5SAndroid Build Coastguard Worker # entry for it in extra_links. 330*8975f5c5SAndroid Build Coastguard Worker if benchmarks_shard_map_file: 331*8975f5c5SAndroid Build Coastguard Worker _handle_benchmarks_shard_map(benchmarks_shard_map_file, extra_links) 332*8975f5c5SAndroid Build Coastguard Worker 333*8975f5c5SAndroid Build Coastguard Worker # Second, upload all the benchmark logs to logdog and add a page entry for 334*8975f5c5SAndroid Build Coastguard Worker # those links in extra_links. 335*8975f5c5SAndroid Build Coastguard Worker _handle_perf_logs(benchmark_directory_map, extra_links) 336*8975f5c5SAndroid Build Coastguard Worker 337*8975f5c5SAndroid Build Coastguard Worker # Then try to obtain the list of json test results to merge 338*8975f5c5SAndroid Build Coastguard Worker # and determine the status of each benchmark. 339*8975f5c5SAndroid Build Coastguard Worker benchmark_enabled_map = _handle_perf_json_test_results(benchmark_directory_map, 340*8975f5c5SAndroid Build Coastguard Worker test_results_list) 341*8975f5c5SAndroid Build Coastguard Worker 342*8975f5c5SAndroid Build Coastguard Worker if not smoke_test_mode and handle_perf: 343*8975f5c5SAndroid Build Coastguard Worker build_properties_map = json.loads(build_properties) 344*8975f5c5SAndroid Build Coastguard Worker if not configuration_name: 345*8975f5c5SAndroid Build Coastguard Worker # we are deprecating perf-id crbug.com/817823 346*8975f5c5SAndroid Build Coastguard Worker configuration_name = build_properties_map['buildername'] 347*8975f5c5SAndroid Build Coastguard Worker 348*8975f5c5SAndroid Build Coastguard Worker try: 349*8975f5c5SAndroid Build Coastguard Worker return_code, benchmark_upload_result_map = _handle_perf_results( 350*8975f5c5SAndroid Build Coastguard Worker benchmark_enabled_map, benchmark_directory_map, configuration_name, 351*8975f5c5SAndroid Build Coastguard Worker build_properties_map, extra_links, output_results_dir) 352*8975f5c5SAndroid Build Coastguard Worker except Exception: 353*8975f5c5SAndroid Build Coastguard Worker logging.exception('Error handling perf results jsons') 354*8975f5c5SAndroid Build Coastguard Worker return_code = 1 355*8975f5c5SAndroid Build Coastguard Worker 356*8975f5c5SAndroid Build Coastguard Worker try: 357*8975f5c5SAndroid Build Coastguard Worker _upload_to_skia_perf(benchmark_directory_map, benchmark_enabled_map, 358*8975f5c5SAndroid Build Coastguard Worker build_properties_map) 359*8975f5c5SAndroid Build Coastguard Worker except Exception: 360*8975f5c5SAndroid Build Coastguard Worker logging.exception('Error uploading to skia perf') 361*8975f5c5SAndroid Build Coastguard Worker return_code = 1 362*8975f5c5SAndroid Build Coastguard Worker 363*8975f5c5SAndroid Build Coastguard Worker if handle_non_perf: 364*8975f5c5SAndroid Build Coastguard Worker # Finally, merge all test results json, add the extra links and write out to 365*8975f5c5SAndroid Build Coastguard Worker # output location 366*8975f5c5SAndroid Build Coastguard Worker try: 367*8975f5c5SAndroid Build Coastguard Worker _merge_json_output(output_json, test_results_list, extra_links) 368*8975f5c5SAndroid Build Coastguard Worker except Exception: 369*8975f5c5SAndroid Build Coastguard Worker logging.exception('Error handling test results jsons.') 370*8975f5c5SAndroid Build Coastguard Worker 371*8975f5c5SAndroid Build Coastguard Worker end_time = time.time() 372*8975f5c5SAndroid Build Coastguard Worker print_duration('Total process_perf_results', begin_time, end_time) 373*8975f5c5SAndroid Build Coastguard Worker return return_code, benchmark_upload_result_map 374*8975f5c5SAndroid Build Coastguard Worker 375*8975f5c5SAndroid Build Coastguard Worker 376*8975f5c5SAndroid Build Coastguard Workerdef _merge_histogram_results(histogram_lists): 377*8975f5c5SAndroid Build Coastguard Worker merged_results = [] 378*8975f5c5SAndroid Build Coastguard Worker for histogram_list in histogram_lists: 379*8975f5c5SAndroid Build Coastguard Worker merged_results += histogram_list 380*8975f5c5SAndroid Build Coastguard Worker 381*8975f5c5SAndroid Build Coastguard Worker return merged_results 382*8975f5c5SAndroid Build Coastguard Worker 383*8975f5c5SAndroid Build Coastguard Worker 384*8975f5c5SAndroid Build Coastguard Workerdef _load_histogram_set_from_dict(data): 385*8975f5c5SAndroid Build Coastguard Worker histograms = histogram_set.HistogramSet() 386*8975f5c5SAndroid Build Coastguard Worker histograms.ImportDicts(data) 387*8975f5c5SAndroid Build Coastguard Worker return histograms 388*8975f5c5SAndroid Build Coastguard Worker 389*8975f5c5SAndroid Build Coastguard Worker 390*8975f5c5SAndroid Build Coastguard Workerdef _add_build_info(results, benchmark_name, build_properties): 391*8975f5c5SAndroid Build Coastguard Worker histograms = _load_histogram_set_from_dict(results) 392*8975f5c5SAndroid Build Coastguard Worker 393*8975f5c5SAndroid Build Coastguard Worker common_diagnostics = { 394*8975f5c5SAndroid Build Coastguard Worker reserved_infos.MASTERS: 395*8975f5c5SAndroid Build Coastguard Worker build_properties['builder_group'], 396*8975f5c5SAndroid Build Coastguard Worker reserved_infos.BOTS: 397*8975f5c5SAndroid Build Coastguard Worker build_properties['buildername'], 398*8975f5c5SAndroid Build Coastguard Worker reserved_infos.POINT_ID: 399*8975f5c5SAndroid Build Coastguard Worker build_properties['angle_commit_pos'], 400*8975f5c5SAndroid Build Coastguard Worker reserved_infos.BENCHMARKS: 401*8975f5c5SAndroid Build Coastguard Worker benchmark_name, 402*8975f5c5SAndroid Build Coastguard Worker reserved_infos.ANGLE_REVISIONS: 403*8975f5c5SAndroid Build Coastguard Worker build_properties['got_angle_revision'], 404*8975f5c5SAndroid Build Coastguard Worker reserved_infos.BUILD_URLS: 405*8975f5c5SAndroid Build Coastguard Worker BUILD_URL % (build_properties['buildername'], build_properties['buildnumber']), 406*8975f5c5SAndroid Build Coastguard Worker } 407*8975f5c5SAndroid Build Coastguard Worker 408*8975f5c5SAndroid Build Coastguard Worker for k, v in common_diagnostics.items(): 409*8975f5c5SAndroid Build Coastguard Worker histograms.AddSharedDiagnosticToAllHistograms(k.name, generic_set.GenericSet([v])) 410*8975f5c5SAndroid Build Coastguard Worker 411*8975f5c5SAndroid Build Coastguard Worker return histograms.AsDicts() 412*8975f5c5SAndroid Build Coastguard Worker 413*8975f5c5SAndroid Build Coastguard Worker 414*8975f5c5SAndroid Build Coastguard Workerdef _merge_perf_results(benchmark_name, results_filename, directories, build_properties): 415*8975f5c5SAndroid Build Coastguard Worker begin_time = time.time() 416*8975f5c5SAndroid Build Coastguard Worker collected_results = [] 417*8975f5c5SAndroid Build Coastguard Worker for directory in directories: 418*8975f5c5SAndroid Build Coastguard Worker filename = os.path.join(directory, 'perf_results.json') 419*8975f5c5SAndroid Build Coastguard Worker try: 420*8975f5c5SAndroid Build Coastguard Worker with open(filename) as pf: 421*8975f5c5SAndroid Build Coastguard Worker collected_results.append(json.load(pf)) 422*8975f5c5SAndroid Build Coastguard Worker except IOError as e: 423*8975f5c5SAndroid Build Coastguard Worker # TODO(crbug.com/936602): Figure out how to surface these errors. Should 424*8975f5c5SAndroid Build Coastguard Worker # we have a non-zero exit code if we error out? 425*8975f5c5SAndroid Build Coastguard Worker logging.error('Failed to obtain perf results from %s: %s', directory, e) 426*8975f5c5SAndroid Build Coastguard Worker if not collected_results: 427*8975f5c5SAndroid Build Coastguard Worker logging.error('Failed to obtain any perf results from %s.', benchmark_name) 428*8975f5c5SAndroid Build Coastguard Worker return 429*8975f5c5SAndroid Build Coastguard Worker 430*8975f5c5SAndroid Build Coastguard Worker # Assuming that multiple shards will be histogram set 431*8975f5c5SAndroid Build Coastguard Worker # Non-telemetry benchmarks only ever run on one shard 432*8975f5c5SAndroid Build Coastguard Worker merged_results = [] 433*8975f5c5SAndroid Build Coastguard Worker assert (isinstance(collected_results[0], list)) 434*8975f5c5SAndroid Build Coastguard Worker merged_results = _merge_histogram_results(collected_results) 435*8975f5c5SAndroid Build Coastguard Worker 436*8975f5c5SAndroid Build Coastguard Worker # Write additional histogram build info. 437*8975f5c5SAndroid Build Coastguard Worker merged_results = _add_build_info(merged_results, benchmark_name, build_properties) 438*8975f5c5SAndroid Build Coastguard Worker 439*8975f5c5SAndroid Build Coastguard Worker with open(results_filename, 'w') as rf: 440*8975f5c5SAndroid Build Coastguard Worker json.dump(merged_results, rf) 441*8975f5c5SAndroid Build Coastguard Worker 442*8975f5c5SAndroid Build Coastguard Worker end_time = time.time() 443*8975f5c5SAndroid Build Coastguard Worker print_duration(('%s results merging' % (benchmark_name)), begin_time, end_time) 444*8975f5c5SAndroid Build Coastguard Worker 445*8975f5c5SAndroid Build Coastguard Worker 446*8975f5c5SAndroid Build Coastguard Workerdef _upload_individual(benchmark_name, directories, configuration_name, build_properties, 447*8975f5c5SAndroid Build Coastguard Worker output_json_file): 448*8975f5c5SAndroid Build Coastguard Worker tmpfile_dir = tempfile.mkdtemp() 449*8975f5c5SAndroid Build Coastguard Worker try: 450*8975f5c5SAndroid Build Coastguard Worker upload_begin_time = time.time() 451*8975f5c5SAndroid Build Coastguard Worker # There are potentially multiple directores with results, re-write and 452*8975f5c5SAndroid Build Coastguard Worker # merge them if necessary 453*8975f5c5SAndroid Build Coastguard Worker results_filename = None 454*8975f5c5SAndroid Build Coastguard Worker if len(directories) > 1: 455*8975f5c5SAndroid Build Coastguard Worker merge_perf_dir = os.path.join(os.path.abspath(tmpfile_dir), benchmark_name) 456*8975f5c5SAndroid Build Coastguard Worker if not os.path.exists(merge_perf_dir): 457*8975f5c5SAndroid Build Coastguard Worker os.makedirs(merge_perf_dir) 458*8975f5c5SAndroid Build Coastguard Worker results_filename = os.path.join(merge_perf_dir, 'merged_perf_results.json') 459*8975f5c5SAndroid Build Coastguard Worker _merge_perf_results(benchmark_name, results_filename, directories, build_properties) 460*8975f5c5SAndroid Build Coastguard Worker else: 461*8975f5c5SAndroid Build Coastguard Worker # It was only written to one shard, use that shards data 462*8975f5c5SAndroid Build Coastguard Worker results_filename = os.path.join(directories[0], 'perf_results.json') 463*8975f5c5SAndroid Build Coastguard Worker 464*8975f5c5SAndroid Build Coastguard Worker results_size_in_mib = os.path.getsize(results_filename) / (2**20) 465*8975f5c5SAndroid Build Coastguard Worker logging.info('Uploading perf results from %s benchmark (size %s Mib)' % 466*8975f5c5SAndroid Build Coastguard Worker (benchmark_name, results_size_in_mib)) 467*8975f5c5SAndroid Build Coastguard Worker upload_return_code = _upload_perf_results(results_filename, benchmark_name, 468*8975f5c5SAndroid Build Coastguard Worker configuration_name, build_properties, 469*8975f5c5SAndroid Build Coastguard Worker output_json_file) 470*8975f5c5SAndroid Build Coastguard Worker upload_end_time = time.time() 471*8975f5c5SAndroid Build Coastguard Worker print_duration(('%s upload time' % (benchmark_name)), upload_begin_time, upload_end_time) 472*8975f5c5SAndroid Build Coastguard Worker return (benchmark_name, upload_return_code == 0) 473*8975f5c5SAndroid Build Coastguard Worker finally: 474*8975f5c5SAndroid Build Coastguard Worker shutil.rmtree(tmpfile_dir) 475*8975f5c5SAndroid Build Coastguard Worker 476*8975f5c5SAndroid Build Coastguard Worker 477*8975f5c5SAndroid Build Coastguard Workerdef _upload_individual_benchmark(params): 478*8975f5c5SAndroid Build Coastguard Worker try: 479*8975f5c5SAndroid Build Coastguard Worker return _upload_individual(*params) 480*8975f5c5SAndroid Build Coastguard Worker except Exception: 481*8975f5c5SAndroid Build Coastguard Worker benchmark_name = params[0] 482*8975f5c5SAndroid Build Coastguard Worker upload_succeed = False 483*8975f5c5SAndroid Build Coastguard Worker logging.exception('Error uploading perf result of %s' % benchmark_name) 484*8975f5c5SAndroid Build Coastguard Worker return benchmark_name, upload_succeed 485*8975f5c5SAndroid Build Coastguard Worker 486*8975f5c5SAndroid Build Coastguard Worker 487*8975f5c5SAndroid Build Coastguard Workerdef _GetCpuCount(log=True): 488*8975f5c5SAndroid Build Coastguard Worker try: 489*8975f5c5SAndroid Build Coastguard Worker cpu_count = multiprocessing.cpu_count() 490*8975f5c5SAndroid Build Coastguard Worker if sys.platform == 'win32': 491*8975f5c5SAndroid Build Coastguard Worker # TODO(crbug.com/1190269) - we can't use more than 56 492*8975f5c5SAndroid Build Coastguard Worker # cores on Windows or Python3 may hang. 493*8975f5c5SAndroid Build Coastguard Worker cpu_count = min(cpu_count, 56) 494*8975f5c5SAndroid Build Coastguard Worker return cpu_count 495*8975f5c5SAndroid Build Coastguard Worker except NotImplementedError: 496*8975f5c5SAndroid Build Coastguard Worker if log: 497*8975f5c5SAndroid Build Coastguard Worker logging.warn('Failed to get a CPU count for this bot. See crbug.com/947035.') 498*8975f5c5SAndroid Build Coastguard Worker # TODO(crbug.com/948281): This is currently set to 4 since the mac masters 499*8975f5c5SAndroid Build Coastguard Worker # only have 4 cores. Once we move to all-linux, this can be increased or 500*8975f5c5SAndroid Build Coastguard Worker # we can even delete this whole function and use multiprocessing.cpu_count() 501*8975f5c5SAndroid Build Coastguard Worker # directly. 502*8975f5c5SAndroid Build Coastguard Worker return 4 503*8975f5c5SAndroid Build Coastguard Worker 504*8975f5c5SAndroid Build Coastguard Worker 505*8975f5c5SAndroid Build Coastguard Workerdef _load_shard_id_from_test_results(directory): 506*8975f5c5SAndroid Build Coastguard Worker shard_id = None 507*8975f5c5SAndroid Build Coastguard Worker test_json_path = os.path.join(directory, 'test_results.json') 508*8975f5c5SAndroid Build Coastguard Worker try: 509*8975f5c5SAndroid Build Coastguard Worker with open(test_json_path) as f: 510*8975f5c5SAndroid Build Coastguard Worker test_json = json.load(f) 511*8975f5c5SAndroid Build Coastguard Worker all_results = test_json['tests'] 512*8975f5c5SAndroid Build Coastguard Worker for _, benchmark_results in all_results.items(): 513*8975f5c5SAndroid Build Coastguard Worker for _, measurement_result in benchmark_results.items(): 514*8975f5c5SAndroid Build Coastguard Worker shard_id = measurement_result['shard'] 515*8975f5c5SAndroid Build Coastguard Worker break 516*8975f5c5SAndroid Build Coastguard Worker except IOError as e: 517*8975f5c5SAndroid Build Coastguard Worker logging.error('Failed to open test_results.json from %s: %s', test_json_path, e) 518*8975f5c5SAndroid Build Coastguard Worker except KeyError as e: 519*8975f5c5SAndroid Build Coastguard Worker logging.error('Failed to locate results in test_results.json: %s', e) 520*8975f5c5SAndroid Build Coastguard Worker return shard_id 521*8975f5c5SAndroid Build Coastguard Worker 522*8975f5c5SAndroid Build Coastguard Worker 523*8975f5c5SAndroid Build Coastguard Workerdef _find_device_id_by_shard_id(benchmarks_shard_map_file, shard_id): 524*8975f5c5SAndroid Build Coastguard Worker try: 525*8975f5c5SAndroid Build Coastguard Worker with open(benchmarks_shard_map_file) as f: 526*8975f5c5SAndroid Build Coastguard Worker shard_map_json = json.load(f) 527*8975f5c5SAndroid Build Coastguard Worker device_id = shard_map_json['extra_infos']['bot #%s' % shard_id] 528*8975f5c5SAndroid Build Coastguard Worker except KeyError as e: 529*8975f5c5SAndroid Build Coastguard Worker logging.error('Failed to locate device name in shard map: %s', e) 530*8975f5c5SAndroid Build Coastguard Worker return device_id 531*8975f5c5SAndroid Build Coastguard Worker 532*8975f5c5SAndroid Build Coastguard Worker 533*8975f5c5SAndroid Build Coastguard Workerdef _update_perf_json_with_summary_on_device_id(directory, device_id): 534*8975f5c5SAndroid Build Coastguard Worker perf_json_path = os.path.join(directory, 'perf_results.json') 535*8975f5c5SAndroid Build Coastguard Worker try: 536*8975f5c5SAndroid Build Coastguard Worker with open(perf_json_path, 'r') as f: 537*8975f5c5SAndroid Build Coastguard Worker perf_json = json.load(f) 538*8975f5c5SAndroid Build Coastguard Worker except IOError as e: 539*8975f5c5SAndroid Build Coastguard Worker logging.error('Failed to open perf_results.json from %s: %s', perf_json_path, e) 540*8975f5c5SAndroid Build Coastguard Worker summary_key_guid = str(uuid.uuid4()) 541*8975f5c5SAndroid Build Coastguard Worker summary_key_generic_set = { 542*8975f5c5SAndroid Build Coastguard Worker 'values': ['device_id'], 543*8975f5c5SAndroid Build Coastguard Worker 'guid': summary_key_guid, 544*8975f5c5SAndroid Build Coastguard Worker 'type': 'GenericSet' 545*8975f5c5SAndroid Build Coastguard Worker } 546*8975f5c5SAndroid Build Coastguard Worker perf_json.insert(0, summary_key_generic_set) 547*8975f5c5SAndroid Build Coastguard Worker logging.info('Inserted summary key generic set for perf result in %s: %s', directory, 548*8975f5c5SAndroid Build Coastguard Worker summary_key_generic_set) 549*8975f5c5SAndroid Build Coastguard Worker stories_guids = set() 550*8975f5c5SAndroid Build Coastguard Worker for entry in perf_json: 551*8975f5c5SAndroid Build Coastguard Worker if 'diagnostics' in entry: 552*8975f5c5SAndroid Build Coastguard Worker entry['diagnostics']['summaryKeys'] = summary_key_guid 553*8975f5c5SAndroid Build Coastguard Worker stories_guids.add(entry['diagnostics']['stories']) 554*8975f5c5SAndroid Build Coastguard Worker for entry in perf_json: 555*8975f5c5SAndroid Build Coastguard Worker if 'guid' in entry and entry['guid'] in stories_guids: 556*8975f5c5SAndroid Build Coastguard Worker entry['values'].append(device_id) 557*8975f5c5SAndroid Build Coastguard Worker try: 558*8975f5c5SAndroid Build Coastguard Worker with open(perf_json_path, 'w') as f: 559*8975f5c5SAndroid Build Coastguard Worker json.dump(perf_json, f) 560*8975f5c5SAndroid Build Coastguard Worker except IOError as e: 561*8975f5c5SAndroid Build Coastguard Worker logging.error('Failed to writing perf_results.json to %s: %s', perf_json_path, e) 562*8975f5c5SAndroid Build Coastguard Worker logging.info('Finished adding device id %s in perf result.', device_id) 563*8975f5c5SAndroid Build Coastguard Worker 564*8975f5c5SAndroid Build Coastguard Worker 565*8975f5c5SAndroid Build Coastguard Workerdef _handle_perf_results(benchmark_enabled_map, benchmark_directory_map, configuration_name, 566*8975f5c5SAndroid Build Coastguard Worker build_properties, extra_links, output_results_dir): 567*8975f5c5SAndroid Build Coastguard Worker """ 568*8975f5c5SAndroid Build Coastguard Worker Upload perf results to the perf dashboard. 569*8975f5c5SAndroid Build Coastguard Worker 570*8975f5c5SAndroid Build Coastguard Worker This method also upload the perf results to logdog and augment it to 571*8975f5c5SAndroid Build Coastguard Worker |extra_links|. 572*8975f5c5SAndroid Build Coastguard Worker 573*8975f5c5SAndroid Build Coastguard Worker Returns: 574*8975f5c5SAndroid Build Coastguard Worker (return_code, benchmark_upload_result_map) 575*8975f5c5SAndroid Build Coastguard Worker return_code is 0 if this upload to perf dashboard successfully, 1 576*8975f5c5SAndroid Build Coastguard Worker otherwise. 577*8975f5c5SAndroid Build Coastguard Worker benchmark_upload_result_map is a dictionary describes which benchmark 578*8975f5c5SAndroid Build Coastguard Worker was successfully uploaded. 579*8975f5c5SAndroid Build Coastguard Worker """ 580*8975f5c5SAndroid Build Coastguard Worker begin_time = time.time() 581*8975f5c5SAndroid Build Coastguard Worker # Upload all eligible benchmarks to the perf dashboard 582*8975f5c5SAndroid Build Coastguard Worker results_dict = {} 583*8975f5c5SAndroid Build Coastguard Worker 584*8975f5c5SAndroid Build Coastguard Worker invocations = [] 585*8975f5c5SAndroid Build Coastguard Worker for benchmark_name, directories in benchmark_directory_map.items(): 586*8975f5c5SAndroid Build Coastguard Worker if not benchmark_enabled_map.get(benchmark_name, False): 587*8975f5c5SAndroid Build Coastguard Worker continue 588*8975f5c5SAndroid Build Coastguard Worker # Create a place to write the perf results that you will write out to 589*8975f5c5SAndroid Build Coastguard Worker # logdog. 590*8975f5c5SAndroid Build Coastguard Worker output_json_file = os.path.join(output_results_dir, (str(uuid.uuid4()) + benchmark_name)) 591*8975f5c5SAndroid Build Coastguard Worker results_dict[benchmark_name] = output_json_file 592*8975f5c5SAndroid Build Coastguard Worker #TODO(crbug.com/1072729): pass final arguments instead of build properties 593*8975f5c5SAndroid Build Coastguard Worker # and configuration_name 594*8975f5c5SAndroid Build Coastguard Worker invocations.append( 595*8975f5c5SAndroid Build Coastguard Worker (benchmark_name, directories, configuration_name, build_properties, output_json_file)) 596*8975f5c5SAndroid Build Coastguard Worker 597*8975f5c5SAndroid Build Coastguard Worker # Kick off the uploads in multiple processes 598*8975f5c5SAndroid Build Coastguard Worker # crbug.com/1035930: We are hitting HTTP Response 429. Limit ourselves 599*8975f5c5SAndroid Build Coastguard Worker # to 2 processes to avoid this error. Uncomment the following code once 600*8975f5c5SAndroid Build Coastguard Worker # the problem is fixed on the dashboard side. 601*8975f5c5SAndroid Build Coastguard Worker # pool = multiprocessing.Pool(_GetCpuCount()) 602*8975f5c5SAndroid Build Coastguard Worker pool = multiprocessing.Pool(2) 603*8975f5c5SAndroid Build Coastguard Worker upload_result_timeout = False 604*8975f5c5SAndroid Build Coastguard Worker try: 605*8975f5c5SAndroid Build Coastguard Worker async_result = pool.map_async(_upload_individual_benchmark, invocations) 606*8975f5c5SAndroid Build Coastguard Worker # TODO(crbug.com/947035): What timeout is reasonable? 607*8975f5c5SAndroid Build Coastguard Worker results = async_result.get(timeout=4000) 608*8975f5c5SAndroid Build Coastguard Worker except multiprocessing.TimeoutError: 609*8975f5c5SAndroid Build Coastguard Worker upload_result_timeout = True 610*8975f5c5SAndroid Build Coastguard Worker logging.error('Timeout uploading benchmarks to perf dashboard in parallel') 611*8975f5c5SAndroid Build Coastguard Worker results = [] 612*8975f5c5SAndroid Build Coastguard Worker for benchmark_name in benchmark_directory_map: 613*8975f5c5SAndroid Build Coastguard Worker results.append((benchmark_name, False)) 614*8975f5c5SAndroid Build Coastguard Worker finally: 615*8975f5c5SAndroid Build Coastguard Worker pool.terminate() 616*8975f5c5SAndroid Build Coastguard Worker 617*8975f5c5SAndroid Build Coastguard Worker # Keep a mapping of benchmarks to their upload results 618*8975f5c5SAndroid Build Coastguard Worker benchmark_upload_result_map = {} 619*8975f5c5SAndroid Build Coastguard Worker for r in results: 620*8975f5c5SAndroid Build Coastguard Worker benchmark_upload_result_map[r[0]] = r[1] 621*8975f5c5SAndroid Build Coastguard Worker 622*8975f5c5SAndroid Build Coastguard Worker logdog_dict = {} 623*8975f5c5SAndroid Build Coastguard Worker upload_failures_counter = 0 624*8975f5c5SAndroid Build Coastguard Worker logdog_stream = None 625*8975f5c5SAndroid Build Coastguard Worker logdog_label = 'Results Dashboard' 626*8975f5c5SAndroid Build Coastguard Worker for benchmark_name, output_file in results_dict.items(): 627*8975f5c5SAndroid Build Coastguard Worker upload_succeed = benchmark_upload_result_map[benchmark_name] 628*8975f5c5SAndroid Build Coastguard Worker if not upload_succeed: 629*8975f5c5SAndroid Build Coastguard Worker upload_failures_counter += 1 630*8975f5c5SAndroid Build Coastguard Worker is_reference = '.reference' in benchmark_name 631*8975f5c5SAndroid Build Coastguard Worker _write_perf_data_to_logfile( 632*8975f5c5SAndroid Build Coastguard Worker benchmark_name, 633*8975f5c5SAndroid Build Coastguard Worker output_file, 634*8975f5c5SAndroid Build Coastguard Worker configuration_name, 635*8975f5c5SAndroid Build Coastguard Worker build_properties, 636*8975f5c5SAndroid Build Coastguard Worker logdog_dict, 637*8975f5c5SAndroid Build Coastguard Worker is_reference, 638*8975f5c5SAndroid Build Coastguard Worker upload_failure=not upload_succeed) 639*8975f5c5SAndroid Build Coastguard Worker 640*8975f5c5SAndroid Build Coastguard Worker logdog_file_name = _generate_unique_logdog_filename('Results_Dashboard_') 641*8975f5c5SAndroid Build Coastguard Worker logdog_stream = logdog_helper.text( 642*8975f5c5SAndroid Build Coastguard Worker logdog_file_name, 643*8975f5c5SAndroid Build Coastguard Worker json.dumps(dict(logdog_dict), sort_keys=True, indent=4, separators=(',', ': ')), 644*8975f5c5SAndroid Build Coastguard Worker content_type=JSON_CONTENT_TYPE) 645*8975f5c5SAndroid Build Coastguard Worker if upload_failures_counter > 0: 646*8975f5c5SAndroid Build Coastguard Worker logdog_label += (' %s merge script perf data upload failures' % upload_failures_counter) 647*8975f5c5SAndroid Build Coastguard Worker extra_links[logdog_label] = logdog_stream 648*8975f5c5SAndroid Build Coastguard Worker end_time = time.time() 649*8975f5c5SAndroid Build Coastguard Worker print_duration('Uploading results to perf dashboard', begin_time, end_time) 650*8975f5c5SAndroid Build Coastguard Worker if upload_result_timeout or upload_failures_counter > 0: 651*8975f5c5SAndroid Build Coastguard Worker return 1, benchmark_upload_result_map 652*8975f5c5SAndroid Build Coastguard Worker return 0, benchmark_upload_result_map 653*8975f5c5SAndroid Build Coastguard Worker 654*8975f5c5SAndroid Build Coastguard Worker 655*8975f5c5SAndroid Build Coastguard Workerdef _write_perf_data_to_logfile(benchmark_name, output_file, configuration_name, build_properties, 656*8975f5c5SAndroid Build Coastguard Worker logdog_dict, is_ref, upload_failure): 657*8975f5c5SAndroid Build Coastguard Worker viewer_url = None 658*8975f5c5SAndroid Build Coastguard Worker # logdog file to write perf results to 659*8975f5c5SAndroid Build Coastguard Worker if os.path.exists(output_file): 660*8975f5c5SAndroid Build Coastguard Worker results = None 661*8975f5c5SAndroid Build Coastguard Worker with open(output_file) as f: 662*8975f5c5SAndroid Build Coastguard Worker try: 663*8975f5c5SAndroid Build Coastguard Worker results = json.load(f) 664*8975f5c5SAndroid Build Coastguard Worker except ValueError: 665*8975f5c5SAndroid Build Coastguard Worker logging.error('Error parsing perf results JSON for benchmark %s' % benchmark_name) 666*8975f5c5SAndroid Build Coastguard Worker if results: 667*8975f5c5SAndroid Build Coastguard Worker try: 668*8975f5c5SAndroid Build Coastguard Worker json_fname = _generate_unique_logdog_filename(benchmark_name) 669*8975f5c5SAndroid Build Coastguard Worker output_json_file = logdog_helper.open_text(json_fname) 670*8975f5c5SAndroid Build Coastguard Worker json.dump(results, output_json_file, indent=4, separators=(',', ': ')) 671*8975f5c5SAndroid Build Coastguard Worker except ValueError as e: 672*8975f5c5SAndroid Build Coastguard Worker logging.error('ValueError: "%s" while dumping output to logdog' % e) 673*8975f5c5SAndroid Build Coastguard Worker finally: 674*8975f5c5SAndroid Build Coastguard Worker output_json_file.close() 675*8975f5c5SAndroid Build Coastguard Worker viewer_url = output_json_file.get_viewer_url() 676*8975f5c5SAndroid Build Coastguard Worker else: 677*8975f5c5SAndroid Build Coastguard Worker logging.warning("Perf results JSON file doesn't exist for benchmark %s" % benchmark_name) 678*8975f5c5SAndroid Build Coastguard Worker 679*8975f5c5SAndroid Build Coastguard Worker base_benchmark_name = benchmark_name.replace('.reference', '') 680*8975f5c5SAndroid Build Coastguard Worker 681*8975f5c5SAndroid Build Coastguard Worker if base_benchmark_name not in logdog_dict: 682*8975f5c5SAndroid Build Coastguard Worker logdog_dict[base_benchmark_name] = {} 683*8975f5c5SAndroid Build Coastguard Worker 684*8975f5c5SAndroid Build Coastguard Worker # add links for the perf results and the dashboard url to 685*8975f5c5SAndroid Build Coastguard Worker # the logs section of buildbot 686*8975f5c5SAndroid Build Coastguard Worker if is_ref: 687*8975f5c5SAndroid Build Coastguard Worker if viewer_url: 688*8975f5c5SAndroid Build Coastguard Worker logdog_dict[base_benchmark_name]['perf_results_ref'] = viewer_url 689*8975f5c5SAndroid Build Coastguard Worker if upload_failure: 690*8975f5c5SAndroid Build Coastguard Worker logdog_dict[base_benchmark_name]['ref_upload_failed'] = 'True' 691*8975f5c5SAndroid Build Coastguard Worker else: 692*8975f5c5SAndroid Build Coastguard Worker # TODO(jmadill): Figure out if we can get a dashboard URL here. http://anglebug.com/40096778 693*8975f5c5SAndroid Build Coastguard Worker # logdog_dict[base_benchmark_name]['dashboard_url'] = ( 694*8975f5c5SAndroid Build Coastguard Worker # upload_results_to_perf_dashboard.GetDashboardUrl(benchmark_name, configuration_name, 695*8975f5c5SAndroid Build Coastguard Worker # RESULTS_URL, 696*8975f5c5SAndroid Build Coastguard Worker # build_properties['got_revision_cp'], 697*8975f5c5SAndroid Build Coastguard Worker # _GetMachineGroup(build_properties))) 698*8975f5c5SAndroid Build Coastguard Worker if viewer_url: 699*8975f5c5SAndroid Build Coastguard Worker logdog_dict[base_benchmark_name]['perf_results'] = viewer_url 700*8975f5c5SAndroid Build Coastguard Worker if upload_failure: 701*8975f5c5SAndroid Build Coastguard Worker logdog_dict[base_benchmark_name]['upload_failed'] = 'True' 702*8975f5c5SAndroid Build Coastguard Worker 703*8975f5c5SAndroid Build Coastguard Worker 704*8975f5c5SAndroid Build Coastguard Workerdef print_duration(step, start, end): 705*8975f5c5SAndroid Build Coastguard Worker logging.info('Duration of %s: %d seconds' % (step, end - start)) 706*8975f5c5SAndroid Build Coastguard Worker 707*8975f5c5SAndroid Build Coastguard Worker 708*8975f5c5SAndroid Build Coastguard Workerdef main(): 709*8975f5c5SAndroid Build Coastguard Worker """ See collect_task.collect_task for more on the merge script API. """ 710*8975f5c5SAndroid Build Coastguard Worker logging.info(sys.argv) 711*8975f5c5SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 712*8975f5c5SAndroid Build Coastguard Worker # configuration-name (previously perf-id) is the name of bot the tests run on 713*8975f5c5SAndroid Build Coastguard Worker # For example, buildbot-test is the name of the android-go-perf bot 714*8975f5c5SAndroid Build Coastguard Worker # configuration-name and results-url are set in the json file which is going 715*8975f5c5SAndroid Build Coastguard Worker # away tools/perf/core/chromium.perf.fyi.extras.json 716*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--configuration-name', help=argparse.SUPPRESS) 717*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--build-properties', help=argparse.SUPPRESS) 718*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--summary-json', required=True, help=argparse.SUPPRESS) 719*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--task-output-dir', required=True, help=argparse.SUPPRESS) 720*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('-o', '--output-json', required=True, help=argparse.SUPPRESS) 721*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 722*8975f5c5SAndroid Build Coastguard Worker '--skip-perf', 723*8975f5c5SAndroid Build Coastguard Worker action='store_true', 724*8975f5c5SAndroid Build Coastguard Worker help='In lightweight mode, using --skip-perf will skip the performance' 725*8975f5c5SAndroid Build Coastguard Worker ' data handling.') 726*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 727*8975f5c5SAndroid Build Coastguard Worker '--lightweight', 728*8975f5c5SAndroid Build Coastguard Worker action='store_true', 729*8975f5c5SAndroid Build Coastguard Worker help='Choose the lightweight mode in which the perf result handling' 730*8975f5c5SAndroid Build Coastguard Worker ' is performed on a separate VM.') 731*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('json_files', nargs='*', help=argparse.SUPPRESS) 732*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 733*8975f5c5SAndroid Build Coastguard Worker '--smoke-test-mode', 734*8975f5c5SAndroid Build Coastguard Worker action='store_true', 735*8975f5c5SAndroid Build Coastguard Worker help='This test should be run in smoke test mode' 736*8975f5c5SAndroid Build Coastguard Worker ' meaning it does not upload to the perf dashboard') 737*8975f5c5SAndroid Build Coastguard Worker 738*8975f5c5SAndroid Build Coastguard Worker args = parser.parse_args() 739*8975f5c5SAndroid Build Coastguard Worker 740*8975f5c5SAndroid Build Coastguard Worker with open(args.summary_json) as f: 741*8975f5c5SAndroid Build Coastguard Worker shard_summary = json.load(f) 742*8975f5c5SAndroid Build Coastguard Worker shard_failed = any(int(shard.get('exit_code', 1)) != 0 for shard in shard_summary['shards']) 743*8975f5c5SAndroid Build Coastguard Worker 744*8975f5c5SAndroid Build Coastguard Worker output_results_dir = tempfile.mkdtemp('outputresults') 745*8975f5c5SAndroid Build Coastguard Worker try: 746*8975f5c5SAndroid Build Coastguard Worker return_code, _ = process_perf_results(args.output_json, args.configuration_name, 747*8975f5c5SAndroid Build Coastguard Worker args.build_properties, args.task_output_dir, 748*8975f5c5SAndroid Build Coastguard Worker args.smoke_test_mode, output_results_dir, 749*8975f5c5SAndroid Build Coastguard Worker args.lightweight, args.skip_perf) 750*8975f5c5SAndroid Build Coastguard Worker except Exception: 751*8975f5c5SAndroid Build Coastguard Worker logging.exception('process_perf_results raised an exception') 752*8975f5c5SAndroid Build Coastguard Worker return_code = 1 753*8975f5c5SAndroid Build Coastguard Worker finally: 754*8975f5c5SAndroid Build Coastguard Worker shutil.rmtree(output_results_dir) 755*8975f5c5SAndroid Build Coastguard Worker 756*8975f5c5SAndroid Build Coastguard Worker if return_code != 0 and shard_failed: 757*8975f5c5SAndroid Build Coastguard Worker logging.warning('Perf processing failed but one or more shards failed earlier') 758*8975f5c5SAndroid Build Coastguard Worker return_code = 0 # Enables the failed build info to be rendered normally 759*8975f5c5SAndroid Build Coastguard Worker 760*8975f5c5SAndroid Build Coastguard Worker return return_code 761*8975f5c5SAndroid Build Coastguard Worker 762*8975f5c5SAndroid Build Coastguard Worker 763*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__': 764*8975f5c5SAndroid Build Coastguard Worker sys.exit(main()) 765