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 | |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 10 | """Adds build info to perf results and uploads them. |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 11 | |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 12 | The tests don't know which bot executed the tests or at what revision, so we |
Patrik Höglund | 83245bd | 2020-01-30 09:33:57 +0100 | [diff] [blame] | 13 | need to take their output and enrich it with this information. We load the proto |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 14 | from the tests, add the build information as shared diagnostics and then |
| 15 | upload it to the dashboard. |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 16 | |
| 17 | This script can't be in recipes, because we can't access the catapult APIs from |
| 18 | there. It needs to be here source-side. |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 19 | """ |
| 20 | |
| 21 | import argparse |
| 22 | import httplib2 |
| 23 | import json |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 24 | import os |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 25 | import sys |
| 26 | import subprocess |
| 27 | import zlib |
| 28 | |
Patrik Höglund | 83245bd | 2020-01-30 09:33:57 +0100 | [diff] [blame] | 29 | # We just yank the python scripts we require into the PYTHONPATH. You could also |
| 30 | # imagine a solution where we use for instance protobuf:py_proto_runtime to copy |
| 31 | # catapult and protobuf code to out/, but this approach is allowed by |
| 32 | # convention. Fortunately neither catapult nor protobuf require any build rules |
| 33 | # to be executed. We can't do this for the histogram proto stub though because |
| 34 | # it's generated; see _LoadHistogramSetFromProto. |
| 35 | # |
| 36 | # It would be better if there was an equivalent to py_binary in GN, but there's |
| 37 | # not. |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 38 | SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) |
| 39 | CHECKOUT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir)) |
| 40 | sys.path.insert(0, os.path.join(CHECKOUT_ROOT, 'third_party', 'catapult', |
| 41 | 'tracing')) |
Patrik Höglund | 83245bd | 2020-01-30 09:33:57 +0100 | [diff] [blame] | 42 | sys.path.insert(0, os.path.join(CHECKOUT_ROOT, 'third_party', 'protobuf', |
| 43 | 'python')) |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 44 | |
| 45 | from tracing.value import histogram_set |
| 46 | from tracing.value.diagnostics import generic_set |
| 47 | from tracing.value.diagnostics import reserved_infos |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 48 | |
Patrik Höglund | 8f47b27 | 2020-03-11 14:20:14 +0100 | [diff] [blame^] | 49 | from google.protobuf import json_format |
| 50 | |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 51 | |
| 52 | def _GenerateOauthToken(): |
| 53 | args = ['luci-auth', 'token'] |
| 54 | p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 55 | if p.wait() == 0: |
| 56 | output = p.stdout.read() |
| 57 | return output.strip() |
| 58 | else: |
| 59 | raise RuntimeError( |
| 60 | 'Error generating authentication token.\nStdout: %s\nStderr:%s' % |
| 61 | (p.stdout.read(), p.stderr.read())) |
| 62 | |
| 63 | |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 64 | def _SendHistogramSet(url, histograms, oauth_token): |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 65 | """Make a HTTP POST with the given JSON to the Performance Dashboard. |
| 66 | |
| 67 | Args: |
| 68 | url: URL of Performance Dashboard instance, e.g. |
| 69 | "https://chromeperf.appspot.com". |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 70 | histograms: a histogram set object that contains the data to be sent. |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 71 | oauth_token: An oauth token to use for authorization. |
| 72 | """ |
| 73 | headers = {'Authorization': 'Bearer %s' % oauth_token} |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 74 | serialized = json.dumps(histograms.AsDicts(), indent=4) |
| 75 | |
| 76 | if url.startswith('http://localhost'): |
| 77 | # The catapult server turns off compression in developer mode. |
| 78 | data = serialized |
| 79 | else: |
| 80 | data = zlib.compress(serialized) |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 81 | |
Patrik Höglund | 7252457 | 2020-02-14 14:14:56 +0100 | [diff] [blame] | 82 | print 'Sending %d bytes to %s.' % (len(data), url + '/add_histograms') |
| 83 | |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 84 | http = httplib2.Http() |
| 85 | response, content = http.request(url + '/add_histograms', method='POST', |
| 86 | body=data, headers=headers) |
| 87 | return response, content |
| 88 | |
| 89 | |
Patrik Höglund | 83245bd | 2020-01-30 09:33:57 +0100 | [diff] [blame] | 90 | def _LoadHistogramSetFromProto(options): |
| 91 | # The webrtc_dashboard_upload gn rule will build the protobuf stub for python, |
| 92 | # so put it in the path for this script before we attempt to import it. |
| 93 | histogram_proto_path = os.path.join(options.outdir, 'pyproto', 'tracing', |
| 94 | 'tracing', 'proto') |
| 95 | sys.path.insert(0, histogram_proto_path) |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 96 | |
Patrik Höglund | 8f47b27 | 2020-03-11 14:20:14 +0100 | [diff] [blame^] | 97 | # TODO(https://crbug.com/1029452): Get rid of this import hack once we can |
| 98 | # just hand the contents of input_results_file straight to the histogram set. |
| 99 | try: |
| 100 | import histogram_pb2 |
| 101 | except ImportError: |
| 102 | raise ImportError('Could not find histogram_pb2. You need to build the ' |
| 103 | 'webrtc_dashboard_upload target before invoking this ' |
| 104 | 'script. Expected to find ' |
| 105 | 'histogram_pb2 in %s.' % histogram_proto_path) |
Patrik Höglund | 7427fc6 | 2020-03-10 10:42:40 +0100 | [diff] [blame] | 106 | |
Patrik Höglund | 8f47b27 | 2020-03-11 14:20:14 +0100 | [diff] [blame^] | 107 | with options.input_results_file as f: |
| 108 | histograms = histogram_pb2.HistogramSet() |
| 109 | histograms.ParseFromString(f.read()) |
| 110 | |
| 111 | # TODO(https://crbug.com/1029452): Don't convert to JSON as a middle step once |
| 112 | # there is a proto de-serializer ready in catapult. |
| 113 | json_data = json.loads(json_format.MessageToJson(histograms)) |
| 114 | hs = histogram_set.HistogramSet() |
| 115 | hs.ImportDicts(json_data) |
Patrik Höglund | 83245bd | 2020-01-30 09:33:57 +0100 | [diff] [blame] | 116 | return hs |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 117 | |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 118 | |
| 119 | def _AddBuildInfo(histograms, options): |
| 120 | common_diagnostics = { |
| 121 | reserved_infos.MASTERS: options.perf_dashboard_machine_group, |
| 122 | reserved_infos.BOTS: options.bot, |
| 123 | reserved_infos.POINT_ID: options.commit_position, |
| 124 | reserved_infos.BENCHMARKS: options.test_suite, |
| 125 | reserved_infos.WEBRTC_REVISIONS: str(options.webrtc_git_hash), |
| 126 | reserved_infos.BUILD_URLS: options.build_page_url, |
| 127 | } |
| 128 | |
| 129 | for k, v in common_diagnostics.items(): |
| 130 | histograms.AddSharedDiagnosticToAllHistograms( |
| 131 | k.name, generic_set.GenericSet([v])) |
| 132 | |
| 133 | |
Patrik Höglund | 8f47b27 | 2020-03-11 14:20:14 +0100 | [diff] [blame^] | 134 | # TODO(https://crbug.com/1029452): Remove this once |
| 135 | # https://chromium-review.googlesource.com/c/catapult/+/2094312 lands. |
| 136 | def _HackSummaryOptions(histograms): |
| 137 | for histogram in histograms: |
| 138 | histogram.CustomizeSummaryOptions({ |
| 139 | 'avg': False, |
| 140 | 'std': False, |
| 141 | 'count': False, |
| 142 | 'sum': False, |
| 143 | 'min': False, |
| 144 | 'max': False, |
| 145 | 'nans': False, |
| 146 | }) |
| 147 | |
| 148 | |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 149 | def _DumpOutput(histograms, output_file): |
| 150 | with output_file: |
| 151 | json.dump(histograms.AsDicts(), output_file, indent=4) |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 152 | |
| 153 | |
| 154 | def _CreateParser(): |
| 155 | parser = argparse.ArgumentParser() |
| 156 | parser.add_argument('--perf-dashboard-machine-group', required=True, |
| 157 | help='The "master" the bots are grouped under. This ' |
| 158 | 'string is the group in the the perf dashboard path ' |
| 159 | 'group/bot/perf_id/metric/subtest.') |
| 160 | parser.add_argument('--bot', required=True, |
| 161 | help='The bot running the test (e.g. ' |
| 162 | 'webrtc-win-large-tests).') |
| 163 | parser.add_argument('--test-suite', required=True, |
| 164 | help='The key for the test in the dashboard (i.e. what ' |
| 165 | 'you select in the top-level test suite selector in the ' |
| 166 | 'dashboard') |
| 167 | parser.add_argument('--webrtc-git-hash', required=True, |
| 168 | help='webrtc.googlesource.com commit hash.') |
| 169 | parser.add_argument('--commit-position', type=int, required=True, |
| 170 | help='Commit pos corresponding to the git hash.') |
| 171 | parser.add_argument('--build-page-url', required=True, |
| 172 | help='URL to the build page for this build.') |
| 173 | parser.add_argument('--dashboard-url', required=True, |
| 174 | help='Which dashboard to use.') |
Patrik Höglund | 8f47b27 | 2020-03-11 14:20:14 +0100 | [diff] [blame^] | 175 | parser.add_argument('--input-results-file', type=argparse.FileType(), |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 176 | required=True, |
| 177 | help='A JSON file with output from WebRTC tests.') |
| 178 | parser.add_argument('--output-json-file', type=argparse.FileType('w'), |
| 179 | help='Where to write the output (for debugging).') |
Patrik Höglund | 83245bd | 2020-01-30 09:33:57 +0100 | [diff] [blame] | 180 | parser.add_argument('--outdir', required=True, |
| 181 | help='Path to the local out/ dir (usually out/Default)') |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 182 | return parser |
| 183 | |
| 184 | |
| 185 | def main(args): |
| 186 | parser = _CreateParser() |
| 187 | options = parser.parse_args(args) |
| 188 | |
Patrik Höglund | 83245bd | 2020-01-30 09:33:57 +0100 | [diff] [blame] | 189 | histograms = _LoadHistogramSetFromProto(options) |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 190 | _AddBuildInfo(histograms, options) |
Patrik Höglund | 7427fc6 | 2020-03-10 10:42:40 +0100 | [diff] [blame] | 191 | _HackSummaryOptions(histograms) |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 192 | |
| 193 | if options.output_json_file: |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 194 | _DumpOutput(histograms, options.output_json_file) |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 195 | |
| 196 | oauth_token = _GenerateOauthToken() |
Patrik Höglund | abea268 | 2020-01-17 13:36:29 +0100 | [diff] [blame] | 197 | response, content = _SendHistogramSet( |
| 198 | options.dashboard_url, histograms, oauth_token) |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 199 | |
| 200 | if response.status == 200: |
Patrik Höglund | 7252457 | 2020-02-14 14:14:56 +0100 | [diff] [blame] | 201 | print 'Received 200 from dashboard.' |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 202 | return 0 |
| 203 | else: |
Patrik Höglund | 7252457 | 2020-02-14 14:14:56 +0100 | [diff] [blame] | 204 | print('Upload failed with %d: %s\n\n%s' % (response.status, response.reason, |
Patrik Höglund | cb0b874 | 2019-11-18 13:46:38 +0100 | [diff] [blame] | 205 | content)) |
| 206 | return 1 |
| 207 | |
| 208 | |
| 209 | if __name__ == '__main__': |
| 210 | sys.exit(main(sys.argv[1:])) |