Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame^] | 1 | #!/usr/bin/env python |
| 2 | # Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. |
| 3 | # |
| 4 | # Use of this source code is governed by a BSD-style license |
| 5 | # that can be found in the LICENSE file in the root of the source |
| 6 | # tree. An additional intellectual property rights grant can be found |
| 7 | # in the file PATENTS. All contributing project authors may |
| 8 | # be found in the AUTHORS file in the root of the source tree. |
| 9 | |
| 10 | """Converts and uploads results to the Chrome perf dashboard. |
| 11 | |
| 12 | This conversion step is needed because test/testsupport/perf_test.cc can't |
| 13 | output histograms natively. There is, unfortunately, no C++ API for histograms. |
| 14 | This script is in python so it can depend on Catapult's python API instead. |
| 15 | See histogram_util.py for how this is done. We should move to the C++ API and |
| 16 | delete the scripts in this dir as soon as there is a C++ API (less conversions = |
| 17 | easier to understand). |
| 18 | |
| 19 | This script can't be in recipes, because we can't access the catapult APIs from |
| 20 | there. It needs to be here source-side. |
| 21 | |
| 22 | This script is adapted from the downstream variant like this: |
| 23 | * Follows upstream naming conventions. |
| 24 | * Downstream-only parameters and concepts go away. |
| 25 | * oAuth tokens are generated by luci-auth. |
| 26 | """ |
| 27 | |
| 28 | import argparse |
| 29 | import httplib2 |
| 30 | import json |
| 31 | import sys |
| 32 | import subprocess |
| 33 | import zlib |
| 34 | |
| 35 | import histogram_util |
| 36 | |
| 37 | |
| 38 | def _GenerateOauthToken(): |
| 39 | args = ['luci-auth', 'token'] |
| 40 | p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 41 | if p.wait() == 0: |
| 42 | output = p.stdout.read() |
| 43 | return output.strip() |
| 44 | else: |
| 45 | raise RuntimeError( |
| 46 | 'Error generating authentication token.\nStdout: %s\nStderr:%s' % |
| 47 | (p.stdout.read(), p.stderr.read())) |
| 48 | |
| 49 | |
| 50 | def _SendHistogramSetJson(url, histogram_json, oauth_token): |
| 51 | """Make a HTTP POST with the given JSON to the Performance Dashboard. |
| 52 | |
| 53 | Args: |
| 54 | url: URL of Performance Dashboard instance, e.g. |
| 55 | "https://chromeperf.appspot.com". |
| 56 | histogram_json: a JSON object that contains the data to be sent. |
| 57 | oauth_token: An oauth token to use for authorization. |
| 58 | """ |
| 59 | headers = {'Authorization': 'Bearer %s' % oauth_token} |
| 60 | serialized = json.dumps(histogram_json.AsDicts(), indent=4) |
| 61 | data = zlib.compress(serialized) |
| 62 | |
| 63 | http = httplib2.Http() |
| 64 | response, content = http.request(url + '/add_histograms', method='POST', |
| 65 | body=data, headers=headers) |
| 66 | return response, content |
| 67 | |
| 68 | |
| 69 | def _LoadHistogramSetJson(options): |
| 70 | with options.input_results_file as f: |
| 71 | json_data = json.load(f) |
| 72 | |
| 73 | histograms = histogram_util.LoadHistograms(json_data) |
| 74 | hs = histogram_util.MakeWebRtcHistogramSet( |
| 75 | stats=histograms, |
| 76 | commit_pos=options.commit_position, |
| 77 | commit_hash=options.webrtc_git_hash, |
| 78 | master=options.perf_dashboard_machine_group, |
| 79 | bot=options.bot, |
| 80 | test_suite=options.test_suite, |
| 81 | build_url=options.build_page_url) |
| 82 | |
| 83 | return hs |
| 84 | |
| 85 | |
| 86 | def _CreateParser(): |
| 87 | parser = argparse.ArgumentParser() |
| 88 | parser.add_argument('--perf-dashboard-machine-group', required=True, |
| 89 | help='The "master" the bots are grouped under. This ' |
| 90 | 'string is the group in the the perf dashboard path ' |
| 91 | 'group/bot/perf_id/metric/subtest.') |
| 92 | parser.add_argument('--bot', required=True, |
| 93 | help='The bot running the test (e.g. ' |
| 94 | 'webrtc-win-large-tests).') |
| 95 | parser.add_argument('--test-suite', required=True, |
| 96 | help='The key for the test in the dashboard (i.e. what ' |
| 97 | 'you select in the top-level test suite selector in the ' |
| 98 | 'dashboard') |
| 99 | parser.add_argument('--webrtc-git-hash', required=True, |
| 100 | help='webrtc.googlesource.com commit hash.') |
| 101 | parser.add_argument('--commit-position', type=int, required=True, |
| 102 | help='Commit pos corresponding to the git hash.') |
| 103 | parser.add_argument('--build-page-url', required=True, |
| 104 | help='URL to the build page for this build.') |
| 105 | parser.add_argument('--dashboard-url', required=True, |
| 106 | help='Which dashboard to use.') |
| 107 | parser.add_argument('--input-results-file', type=argparse.FileType(), |
| 108 | required=True, |
| 109 | help='A JSON file with output from WebRTC tests.') |
| 110 | parser.add_argument('--output-json-file', type=argparse.FileType('w'), |
| 111 | help='Where to write the output (for debugging).') |
| 112 | return parser |
| 113 | |
| 114 | |
| 115 | def main(args): |
| 116 | parser = _CreateParser() |
| 117 | options = parser.parse_args(args) |
| 118 | |
| 119 | histogram_json = _LoadHistogramSetJson(options) |
| 120 | |
| 121 | if options.output_json_file: |
| 122 | with options.output_json_file as output_file: |
| 123 | json.dump(histogram_json.AsDicts(), output_file, indent=4) |
| 124 | |
| 125 | oauth_token = _GenerateOauthToken() |
| 126 | response, content = _SendHistogramSetJson( |
| 127 | options.dashboard_url, histogram_json, oauth_token) |
| 128 | |
| 129 | if response.status == 200: |
| 130 | return 0 |
| 131 | else: |
| 132 | print("Upload failed with %d: %s\n\n%s" % (response.status, response.reason, |
| 133 | content)) |
| 134 | return 1 |
| 135 | |
| 136 | |
| 137 | if __name__ == '__main__': |
| 138 | sys.exit(main(sys.argv[1:])) |