| #!/usr/bin/env vpython |
| # |
| # Copyright 2021 The ANGLE Project Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| # |
| # process_angle_perf_results.py: |
| # Perf result merging and upload. Adapted from the Chromium script: |
| # https://chromium.googlesource.com/chromium/src/+/main/tools/perf/process_perf_results.py |
| |
| from __future__ import print_function |
| |
| import os |
| import six |
| import sys |
| # Swarming runs merge scripts with its python and some really old modules. |
| if __name__ == '__main__' and 'ensure_binary' not in dir(six): |
| print('spawning via vpython due to an old six version (%s)' % six.__version__) |
| import subprocess |
| sys.exit(subprocess.call(['vpython', os.path.realpath(__file__)] + sys.argv[1:])) |
| |
| import argparse |
| import collections |
| import json |
| import logging |
| import multiprocessing |
| import shutil |
| import tempfile |
| import time |
| import uuid |
| |
| logging.basicConfig( |
| level=logging.INFO, |
| format='(%(levelname)s) %(asctime)s pid=%(process)d' |
| ' %(module)s.%(funcName)s:%(lineno)d %(message)s') |
| |
| d = os.path.dirname |
| ANGLE_DIR = d(d(os.path.realpath(__file__))) |
| sys.path.append(os.path.join(ANGLE_DIR, 'tools', 'perf')) |
| import cross_device_test_config |
| |
| from core import path_util |
| |
| path_util.AddTelemetryToPath() |
| from core import upload_results_to_perf_dashboard |
| from core import bot_platforms |
| from core import results_merger |
| |
| path_util.AddAndroidPylibToPath() |
| try: |
| from pylib.utils import logdog_helper |
| except ImportError: |
| pass |
| |
| path_util.AddTracingToPath() |
| from tracing.value import histogram |
| from tracing.value import histogram_set |
| from tracing.value.diagnostics import generic_set |
| from tracing.value.diagnostics import reserved_infos |
| |
| RESULTS_URL = 'https://chromeperf.appspot.com' |
| JSON_CONTENT_TYPE = 'application/json' |
| MACHINE_GROUP = 'ANGLE' |
| BUILD_URL = 'https://ci.chromium.org/ui/p/angle/builders/ci/%s/%d' |
| |
| |
| def _upload_perf_results(json_to_upload, name, configuration_name, build_properties, |
| output_json_file): |
| """Upload the contents of result JSON(s) to the perf dashboard.""" |
| args = [ |
| '--buildername', |
| build_properties['buildername'], |
| '--buildnumber', |
| build_properties['buildnumber'], |
| '--name', |
| name, |
| '--configuration-name', |
| configuration_name, |
| '--results-file', |
| json_to_upload, |
| '--results-url', |
| RESULTS_URL, |
| '--output-json-file', |
| output_json_file, |
| '--perf-dashboard-machine-group', |
| MACHINE_GROUP, |
| '--got-angle-revision', |
| build_properties['got_angle_revision'], |
| '--send-as-histograms', |
| '--project', |
| 'angle', |
| ] |
| |
| if build_properties.get('git_revision'): |
| args.append('--git-revision') |
| args.append(build_properties['git_revision']) |
| |
| #TODO(crbug.com/1072729): log this in top level |
| logging.info('upload_results_to_perf_dashboard: %s.' % args) |
| |
| return upload_results_to_perf_dashboard.main(args) |
| |
| |
| def _merge_json_output(output_json, jsons_to_merge, extra_links, test_cross_device=False): |
| """Merges the contents of one or more results JSONs. |
| |
| Args: |
| output_json: A path to a JSON file to which the merged results should be |
| written. |
| jsons_to_merge: A list of JSON files that should be merged. |
| extra_links: a (key, value) map in which keys are the human-readable strings |
| which describe the data, and value is logdog url that contain the data. |
| """ |
| begin_time = time.time() |
| merged_results = results_merger.merge_test_results(jsons_to_merge, test_cross_device) |
| |
| # Only append the perf results links if present |
| if extra_links: |
| merged_results['links'] = extra_links |
| |
| with open(output_json, 'w') as f: |
| json.dump(merged_results, f) |
| |
| end_time = time.time() |
| print_duration('Merging json test results', begin_time, end_time) |
| return 0 |
| |
| |
| def _handle_perf_json_test_results(benchmark_directory_map, test_results_list): |
| """Checks the test_results.json under each folder: |
| |
| 1. mark the benchmark 'enabled' if tests results are found |
| 2. add the json content to a list for non-ref. |
| """ |
| begin_time = time.time() |
| benchmark_enabled_map = {} |
| for benchmark_name, directories in benchmark_directory_map.items(): |
| for directory in directories: |
| # Obtain the test name we are running |
| is_ref = '.reference' in benchmark_name |
| enabled = True |
| try: |
| with open(os.path.join(directory, 'test_results.json')) as json_data: |
| json_results = json.load(json_data) |
| if not json_results: |
| # Output is null meaning the test didn't produce any results. |
| # Want to output an error and continue loading the rest of the |
| # test results. |
| logging.warning('No results produced for %s, skipping upload' % directory) |
| continue |
| if json_results.get('version') == 3: |
| # Non-telemetry tests don't have written json results but |
| # if they are executing then they are enabled and will generate |
| # chartjson results. |
| if not bool(json_results.get('tests')): |
| enabled = False |
| if not is_ref: |
| # We don't need to upload reference build data to the |
| # flakiness dashboard since we don't monitor the ref build |
| test_results_list.append(json_results) |
| except IOError as e: |
| # TODO(crbug.com/936602): Figure out how to surface these errors. Should |
| # we have a non-zero exit code if we error out? |
| logging.error('Failed to obtain test results for %s: %s', benchmark_name, e) |
| continue |
| if not enabled: |
| # We don't upload disabled benchmarks or tests that are run |
| # as a smoke test |
| logging.info('Benchmark %s ran no tests on at least one shard' % benchmark_name) |
| continue |
| benchmark_enabled_map[benchmark_name] = True |
| |
| end_time = time.time() |
| print_duration('Analyzing perf json test results', begin_time, end_time) |
| return benchmark_enabled_map |
| |
| |
| def _generate_unique_logdog_filename(name_prefix): |
| return name_prefix + '_' + str(uuid.uuid4()) |
| |
| |
| def _handle_perf_logs(benchmark_directory_map, extra_links): |
| """ Upload benchmark logs to logdog and add a page entry for them. """ |
| begin_time = time.time() |
| benchmark_logs_links = collections.defaultdict(list) |
| |
| for benchmark_name, directories in benchmark_directory_map.items(): |
| for directory in directories: |
| benchmark_log_file = os.path.join(directory, 'benchmark_log.txt') |
| if os.path.exists(benchmark_log_file): |
| with open(benchmark_log_file) as f: |
| uploaded_link = logdog_helper.text( |
| name=_generate_unique_logdog_filename(benchmark_name), data=f.read()) |
| benchmark_logs_links[benchmark_name].append(uploaded_link) |
| |
| logdog_file_name = _generate_unique_logdog_filename('Benchmarks_Logs') |
| logdog_stream = logdog_helper.text( |
| logdog_file_name, |
| json.dumps(benchmark_logs_links, sort_keys=True, indent=4, separators=(',', ': ')), |
| content_type=JSON_CONTENT_TYPE) |
| extra_links['Benchmarks logs'] = logdog_stream |
| end_time = time.time() |
| print_duration('Generating perf log streams', begin_time, end_time) |
| |
| |
| def _handle_benchmarks_shard_map(benchmarks_shard_map_file, extra_links): |
| begin_time = time.time() |
| with open(benchmarks_shard_map_file) as f: |
| benchmarks_shard_data = f.read() |
| logdog_file_name = _generate_unique_logdog_filename('Benchmarks_Shard_Map') |
| logdog_stream = logdog_helper.text( |
| logdog_file_name, benchmarks_shard_data, content_type=JSON_CONTENT_TYPE) |
| extra_links['Benchmarks shard map'] = logdog_stream |
| end_time = time.time() |
| print_duration('Generating benchmark shard map stream', begin_time, end_time) |
| |
| |
| def _get_benchmark_name(directory): |
| return os.path.basename(directory).replace(" benchmark", "") |
| |
| |
| def _scan_output_dir(task_output_dir): |
| benchmark_directory_map = {} |
| benchmarks_shard_map_file = None |
| |
| directory_list = [ |
| f for f in os.listdir(task_output_dir) |
| if not os.path.isfile(os.path.join(task_output_dir, f)) |
| ] |
| benchmark_directory_list = [] |
| for directory in directory_list: |
| for f in os.listdir(os.path.join(task_output_dir, directory)): |
| path = os.path.join(task_output_dir, directory, f) |
| if os.path.isdir(path): |
| benchmark_directory_list.append(path) |
| elif path.endswith('benchmarks_shard_map.json'): |
| benchmarks_shard_map_file = path |
| # Now create a map of benchmark name to the list of directories |
| # the lists were written to. |
| for directory in benchmark_directory_list: |
| benchmark_name = _get_benchmark_name(directory) |
| if benchmark_name in benchmark_directory_map.keys(): |
| benchmark_directory_map[benchmark_name].append(directory) |
| else: |
| benchmark_directory_map[benchmark_name] = [directory] |
| |
| return benchmark_directory_map, benchmarks_shard_map_file |
| |
| |
| def process_perf_results(output_json, |
| configuration_name, |
| build_properties, |
| task_output_dir, |
| smoke_test_mode, |
| output_results_dir, |
| lightweight=False, |
| skip_perf=False): |
| """Process perf results. |
| |
| Consists of merging the json-test-format output, uploading the perf test |
| output (histogram), and store the benchmark logs in logdog. |
| |
| Each directory in the task_output_dir represents one benchmark |
| that was run. Within this directory, there is a subdirectory with the name |
| of the benchmark that was run. In that subdirectory, there is a |
| perftest-output.json file containing the performance results in histogram |
| format and an output.json file containing the json test results for the |
| benchmark. |
| |
| Returns: |
| (return_code, upload_results_map): |
| return_code is 0 if the whole operation is successful, non zero otherwise. |
| benchmark_upload_result_map: the dictionary that describe which benchmarks |
| were successfully uploaded. |
| """ |
| handle_perf = not lightweight or not skip_perf |
| handle_non_perf = not lightweight or skip_perf |
| logging.info('lightweight mode: %r; handle_perf: %r; handle_non_perf: %r' % |
| (lightweight, handle_perf, handle_non_perf)) |
| |
| begin_time = time.time() |
| return_code = 0 |
| benchmark_upload_result_map = {} |
| |
| benchmark_directory_map, benchmarks_shard_map_file = _scan_output_dir(task_output_dir) |
| |
| test_results_list = [] |
| extra_links = {} |
| |
| if handle_non_perf: |
| # First, upload benchmarks shard map to logdog and add a page |
| # entry for it in extra_links. |
| if benchmarks_shard_map_file: |
| _handle_benchmarks_shard_map(benchmarks_shard_map_file, extra_links) |
| |
| # Second, upload all the benchmark logs to logdog and add a page entry for |
| # those links in extra_links. |
| _handle_perf_logs(benchmark_directory_map, extra_links) |
| |
| # Then try to obtain the list of json test results to merge |
| # and determine the status of each benchmark. |
| benchmark_enabled_map = _handle_perf_json_test_results(benchmark_directory_map, |
| test_results_list) |
| |
| build_properties_map = json.loads(build_properties) |
| if not configuration_name: |
| # we are deprecating perf-id crbug.com/817823 |
| configuration_name = build_properties_map['buildername'] |
| |
| _update_perf_results_for_calibration(benchmarks_shard_map_file, benchmark_enabled_map, |
| benchmark_directory_map, configuration_name) |
| if not smoke_test_mode and handle_perf: |
| try: |
| return_code, benchmark_upload_result_map = _handle_perf_results( |
| benchmark_enabled_map, benchmark_directory_map, configuration_name, |
| build_properties_map, extra_links, output_results_dir) |
| except Exception: |
| logging.exception('Error handling perf results jsons') |
| return_code = 1 |
| |
| if handle_non_perf: |
| # Finally, merge all test results json, add the extra links and write out to |
| # output location |
| try: |
| _merge_json_output(output_json, test_results_list, extra_links, |
| configuration_name in cross_device_test_config.TARGET_DEVICES) |
| except Exception: |
| logging.exception('Error handling test results jsons.') |
| |
| end_time = time.time() |
| print_duration('Total process_perf_results', begin_time, end_time) |
| return return_code, benchmark_upload_result_map |
| |
| |
| def _merge_histogram_results(histogram_lists): |
| merged_results = [] |
| for histogram_list in histogram_lists: |
| merged_results += histogram_list |
| |
| return merged_results |
| |
| |
| def _load_histogram_set_from_dict(data): |
| histograms = histogram_set.HistogramSet() |
| histograms.ImportDicts(data) |
| return histograms |
| |
| |
| def _add_build_info(results, benchmark_name, build_properties): |
| histograms = _load_histogram_set_from_dict(results) |
| |
| common_diagnostics = { |
| reserved_infos.MASTERS: |
| build_properties['builder_group'], |
| reserved_infos.BOTS: |
| build_properties['buildername'], |
| reserved_infos.POINT_ID: |
| build_properties['angle_commit_pos'], |
| reserved_infos.BENCHMARKS: |
| benchmark_name, |
| reserved_infos.ANGLE_REVISIONS: |
| build_properties['got_angle_revision'], |
| reserved_infos.BUILD_URLS: |
| BUILD_URL % (build_properties['buildername'], build_properties['buildnumber']), |
| } |
| |
| for k, v in common_diagnostics.items(): |
| histograms.AddSharedDiagnosticToAllHistograms(k.name, generic_set.GenericSet([v])) |
| |
| return histograms.AsDicts() |
| |
| |
| def _merge_perf_results(benchmark_name, results_filename, directories, build_properties): |
| begin_time = time.time() |
| collected_results = [] |
| for directory in directories: |
| filename = os.path.join(directory, 'perf_results.json') |
| try: |
| with open(filename) as pf: |
| collected_results.append(json.load(pf)) |
| except IOError as e: |
| # TODO(crbug.com/936602): Figure out how to surface these errors. Should |
| # we have a non-zero exit code if we error out? |
| logging.error('Failed to obtain perf results from %s: %s', directory, e) |
| if not collected_results: |
| logging.error('Failed to obtain any perf results from %s.', benchmark_name) |
| return |
| |
| # Assuming that multiple shards will be histogram set |
| # Non-telemetry benchmarks only ever run on one shard |
| merged_results = [] |
| assert (isinstance(collected_results[0], list)) |
| merged_results = _merge_histogram_results(collected_results) |
| |
| # Write additional histogram build info. |
| merged_results = _add_build_info(merged_results, benchmark_name, build_properties) |
| |
| with open(results_filename, 'w') as rf: |
| json.dump(merged_results, rf) |
| |
| end_time = time.time() |
| print_duration(('%s results merging' % (benchmark_name)), begin_time, end_time) |
| |
| |
| def _upload_individual(benchmark_name, directories, configuration_name, build_properties, |
| output_json_file): |
| tmpfile_dir = tempfile.mkdtemp() |
| try: |
| upload_begin_time = time.time() |
| # There are potentially multiple directores with results, re-write and |
| # merge them if necessary |
| results_filename = None |
| if len(directories) > 1: |
| merge_perf_dir = os.path.join(os.path.abspath(tmpfile_dir), benchmark_name) |
| if not os.path.exists(merge_perf_dir): |
| os.makedirs(merge_perf_dir) |
| results_filename = os.path.join(merge_perf_dir, 'merged_perf_results.json') |
| _merge_perf_results(benchmark_name, results_filename, directories, build_properties) |
| else: |
| # It was only written to one shard, use that shards data |
| results_filename = os.path.join(directories[0], 'perf_results.json') |
| |
| results_size_in_mib = os.path.getsize(results_filename) / (2**20) |
| logging.info('Uploading perf results from %s benchmark (size %s Mib)' % |
| (benchmark_name, results_size_in_mib)) |
| with open(output_json_file, 'w') as oj: |
| upload_return_code = _upload_perf_results(results_filename, benchmark_name, |
| configuration_name, build_properties, oj) |
| upload_end_time = time.time() |
| print_duration(('%s upload time' % (benchmark_name)), upload_begin_time, |
| upload_end_time) |
| return (benchmark_name, upload_return_code == 0) |
| finally: |
| shutil.rmtree(tmpfile_dir) |
| |
| |
| def _upload_individual_benchmark(params): |
| try: |
| return _upload_individual(*params) |
| except Exception: |
| benchmark_name = params[0] |
| upload_succeed = False |
| logging.exception('Error uploading perf result of %s' % benchmark_name) |
| return benchmark_name, upload_succeed |
| |
| |
| def _GetCpuCount(log=True): |
| try: |
| cpu_count = multiprocessing.cpu_count() |
| if sys.platform == 'win32': |
| # TODO(crbug.com/1190269) - we can't use more than 56 |
| # cores on Windows or Python3 may hang. |
| cpu_count = min(cpu_count, 56) |
| return cpu_count |
| except NotImplementedError: |
| if log: |
| logging.warn('Failed to get a CPU count for this bot. See crbug.com/947035.') |
| # TODO(crbug.com/948281): This is currently set to 4 since the mac masters |
| # only have 4 cores. Once we move to all-linux, this can be increased or |
| # we can even delete this whole function and use multiprocessing.cpu_count() |
| # directly. |
| return 4 |
| |
| |
| def _load_shard_id_from_test_results(directory): |
| shard_id = None |
| test_json_path = os.path.join(directory, 'test_results.json') |
| try: |
| with open(test_json_path) as f: |
| test_json = json.load(f) |
| all_results = test_json['tests'] |
| for _, benchmark_results in all_results.items(): |
| for _, measurement_result in benchmark_results.items(): |
| shard_id = measurement_result['shard'] |
| break |
| except IOError as e: |
| logging.error('Failed to open test_results.json from %s: %s', test_json_path, e) |
| except KeyError as e: |
| logging.error('Failed to locate results in test_results.json: %s', e) |
| return shard_id |
| |
| |
| def _find_device_id_by_shard_id(benchmarks_shard_map_file, shard_id): |
| try: |
| with open(benchmarks_shard_map_file) as f: |
| shard_map_json = json.load(f) |
| device_id = shard_map_json['extra_infos']['bot #%s' % shard_id] |
| except KeyError as e: |
| logging.error('Failed to locate device name in shard map: %s', e) |
| return device_id |
| |
| |
| def _update_perf_json_with_summary_on_device_id(directory, device_id): |
| perf_json_path = os.path.join(directory, 'perf_results.json') |
| try: |
| with open(perf_json_path, 'r') as f: |
| perf_json = json.load(f) |
| except IOError as e: |
| logging.error('Failed to open perf_results.json from %s: %s', perf_json_path, e) |
| summary_key_guid = str(uuid.uuid4()) |
| summary_key_generic_set = { |
| 'values': ['device_id'], |
| 'guid': summary_key_guid, |
| 'type': 'GenericSet' |
| } |
| perf_json.insert(0, summary_key_generic_set) |
| logging.info('Inserted summary key generic set for perf result in %s: %s', directory, |
| summary_key_generic_set) |
| stories_guids = set() |
| for entry in perf_json: |
| if 'diagnostics' in entry: |
| entry['diagnostics']['summaryKeys'] = summary_key_guid |
| stories_guids.add(entry['diagnostics']['stories']) |
| for entry in perf_json: |
| if 'guid' in entry and entry['guid'] in stories_guids: |
| entry['values'].append(device_id) |
| try: |
| with open(perf_json_path, 'w') as f: |
| json.dump(perf_json, f) |
| except IOError as e: |
| logging.error('Failed to writing perf_results.json to %s: %s', perf_json_path, e) |
| logging.info('Finished adding device id %s in perf result.', device_id) |
| |
| |
| def _should_add_device_id_in_perf_result(builder_name): |
| # We should always add device id in calibration builders. |
| # For testing purpose, adding fyi as well for faster turnaround, because |
| # calibration builders run every 24 hours. |
| return any([builder_name == p.name for p in bot_platforms.CALIBRATION_PLATFORMS |
| ]) or (builder_name == 'android-pixel2-perf-fyi') |
| |
| |
| def _update_perf_results_for_calibration(benchmarks_shard_map_file, benchmark_enabled_map, |
| benchmark_directory_map, configuration_name): |
| if not _should_add_device_id_in_perf_result(configuration_name): |
| return |
| logging.info('Updating perf results for %s.', configuration_name) |
| for benchmark_name, directories in benchmark_directory_map.items(): |
| if not benchmark_enabled_map.get(benchmark_name, False): |
| continue |
| for directory in directories: |
| shard_id = _load_shard_id_from_test_results(directory) |
| device_id = _find_device_id_by_shard_id(benchmarks_shard_map_file, shard_id) |
| _update_perf_json_with_summary_on_device_id(directory, device_id) |
| |
| |
| def _handle_perf_results(benchmark_enabled_map, benchmark_directory_map, configuration_name, |
| build_properties, extra_links, output_results_dir): |
| """ |
| Upload perf results to the perf dashboard. |
| |
| This method also upload the perf results to logdog and augment it to |
| |extra_links|. |
| |
| Returns: |
| (return_code, benchmark_upload_result_map) |
| return_code is 0 if this upload to perf dashboard successfully, 1 |
| otherwise. |
| benchmark_upload_result_map is a dictionary describes which benchmark |
| was successfully uploaded. |
| """ |
| begin_time = time.time() |
| # Upload all eligible benchmarks to the perf dashboard |
| results_dict = {} |
| |
| invocations = [] |
| for benchmark_name, directories in benchmark_directory_map.items(): |
| if not benchmark_enabled_map.get(benchmark_name, False): |
| continue |
| # Create a place to write the perf results that you will write out to |
| # logdog. |
| output_json_file = os.path.join(output_results_dir, (str(uuid.uuid4()) + benchmark_name)) |
| results_dict[benchmark_name] = output_json_file |
| #TODO(crbug.com/1072729): pass final arguments instead of build properties |
| # and configuration_name |
| invocations.append( |
| (benchmark_name, directories, configuration_name, build_properties, output_json_file)) |
| |
| # Kick off the uploads in multiple processes |
| # crbug.com/1035930: We are hitting HTTP Response 429. Limit ourselves |
| # to 2 processes to avoid this error. Uncomment the following code once |
| # the problem is fixed on the dashboard side. |
| # pool = multiprocessing.Pool(_GetCpuCount()) |
| pool = multiprocessing.Pool(2) |
| upload_result_timeout = False |
| try: |
| async_result = pool.map_async(_upload_individual_benchmark, invocations) |
| # TODO(crbug.com/947035): What timeout is reasonable? |
| results = async_result.get(timeout=4000) |
| except multiprocessing.TimeoutError: |
| upload_result_timeout = True |
| logging.error('Timeout uploading benchmarks to perf dashboard in parallel') |
| results = [] |
| for benchmark_name in benchmark_directory_map: |
| results.append((benchmark_name, False)) |
| finally: |
| pool.terminate() |
| |
| # Keep a mapping of benchmarks to their upload results |
| benchmark_upload_result_map = {} |
| for r in results: |
| benchmark_upload_result_map[r[0]] = r[1] |
| |
| logdog_dict = {} |
| upload_failures_counter = 0 |
| logdog_stream = None |
| logdog_label = 'Results Dashboard' |
| for benchmark_name, output_file in results_dict.items(): |
| upload_succeed = benchmark_upload_result_map[benchmark_name] |
| if not upload_succeed: |
| upload_failures_counter += 1 |
| is_reference = '.reference' in benchmark_name |
| _write_perf_data_to_logfile( |
| benchmark_name, |
| output_file, |
| configuration_name, |
| build_properties, |
| logdog_dict, |
| is_reference, |
| upload_failure=not upload_succeed) |
| |
| logdog_file_name = _generate_unique_logdog_filename('Results_Dashboard_') |
| logdog_stream = logdog_helper.text( |
| logdog_file_name, |
| json.dumps(dict(logdog_dict), sort_keys=True, indent=4, separators=(',', ': ')), |
| content_type=JSON_CONTENT_TYPE) |
| if upload_failures_counter > 0: |
| logdog_label += (' %s merge script perf data upload failures' % upload_failures_counter) |
| extra_links[logdog_label] = logdog_stream |
| end_time = time.time() |
| print_duration('Uploading results to perf dashboard', begin_time, end_time) |
| if upload_result_timeout or upload_failures_counter > 0: |
| return 1, benchmark_upload_result_map |
| return 0, benchmark_upload_result_map |
| |
| |
| def _write_perf_data_to_logfile(benchmark_name, output_file, configuration_name, build_properties, |
| logdog_dict, is_ref, upload_failure): |
| viewer_url = None |
| # logdog file to write perf results to |
| if os.path.exists(output_file): |
| results = None |
| with open(output_file) as f: |
| try: |
| results = json.load(f) |
| except ValueError: |
| logging.error('Error parsing perf results JSON for benchmark %s' % benchmark_name) |
| if results: |
| try: |
| json_fname = _generate_unique_logdog_filename(benchmark_name) |
| output_json_file = logdog_helper.open_text(json_fname) |
| json.dump(results, output_json_file, indent=4, separators=(',', ': ')) |
| except ValueError as e: |
| logging.error('ValueError: "%s" while dumping output to logdog' % e) |
| finally: |
| output_json_file.close() |
| viewer_url = output_json_file.get_viewer_url() |
| else: |
| logging.warning("Perf results JSON file doesn't exist for benchmark %s" % benchmark_name) |
| |
| base_benchmark_name = benchmark_name.replace('.reference', '') |
| |
| if base_benchmark_name not in logdog_dict: |
| logdog_dict[base_benchmark_name] = {} |
| |
| # add links for the perf results and the dashboard url to |
| # the logs section of buildbot |
| if is_ref: |
| if viewer_url: |
| logdog_dict[base_benchmark_name]['perf_results_ref'] = viewer_url |
| if upload_failure: |
| logdog_dict[base_benchmark_name]['ref_upload_failed'] = 'True' |
| else: |
| # TODO(jmadill): Figure out if we can get a dashboard URL here. http://anglebug.com/6090 |
| # logdog_dict[base_benchmark_name]['dashboard_url'] = ( |
| # upload_results_to_perf_dashboard.GetDashboardUrl(benchmark_name, configuration_name, |
| # RESULTS_URL, |
| # build_properties['got_revision_cp'], |
| # _GetMachineGroup(build_properties))) |
| if viewer_url: |
| logdog_dict[base_benchmark_name]['perf_results'] = viewer_url |
| if upload_failure: |
| logdog_dict[base_benchmark_name]['upload_failed'] = 'True' |
| |
| |
| def print_duration(step, start, end): |
| logging.info('Duration of %s: %d seconds' % (step, end - start)) |
| |
| |
| def main(): |
| """ See collect_task.collect_task for more on the merge script API. """ |
| logging.info(sys.argv) |
| parser = argparse.ArgumentParser() |
| # configuration-name (previously perf-id) is the name of bot the tests run on |
| # For example, buildbot-test is the name of the android-go-perf bot |
| # configuration-name and results-url are set in the json file which is going |
| # away tools/perf/core/chromium.perf.fyi.extras.json |
| parser.add_argument('--configuration-name', help=argparse.SUPPRESS) |
| |
| parser.add_argument('--build-properties', help=argparse.SUPPRESS) |
| parser.add_argument('--summary-json', help=argparse.SUPPRESS) |
| parser.add_argument('--task-output-dir', help=argparse.SUPPRESS) |
| parser.add_argument('-o', '--output-json', required=True, help=argparse.SUPPRESS) |
| parser.add_argument( |
| '--skip-perf', |
| action='store_true', |
| help='In lightweight mode, using --skip-perf will skip the performance' |
| ' data handling.') |
| parser.add_argument( |
| '--lightweight', |
| action='store_true', |
| help='Choose the lightweight mode in which the perf result handling' |
| ' is performed on a separate VM.') |
| parser.add_argument('json_files', nargs='*', help=argparse.SUPPRESS) |
| parser.add_argument( |
| '--smoke-test-mode', |
| action='store_true', |
| help='This test should be run in smoke test mode' |
| ' meaning it does not upload to the perf dashboard') |
| |
| args = parser.parse_args() |
| |
| output_results_dir = tempfile.mkdtemp('outputresults') |
| try: |
| return_code, _ = process_perf_results(args.output_json, args.configuration_name, |
| args.build_properties, args.task_output_dir, |
| args.smoke_test_mode, output_results_dir, |
| args.lightweight, args.skip_perf) |
| return return_code |
| finally: |
| shutil.rmtree(output_results_dir) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |