| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright 2014 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 """A tool that uploads data to the performance dashboard.""" | |
| 7 | |
| 8 import argparse | |
| 9 import httplib | |
| 10 import json | |
| 11 import pprint | |
| 12 import re | |
| 13 import sys | |
| 14 import urllib | |
| 15 import urllib2 | |
| 16 | |
| 17 # TODO(yzshen): The following are missing currently: | |
| 18 # (1) CL range on the dashboard; | |
| 19 # (2) improvement direction on the dashboard; | |
| 20 # (3) a link from the build step pointing to the dashboard page. | |
| 21 | |
| 22 | |
| 23 _PERF_LINE_FORMAT = r"""^\s*([^\s/]+) # chart name | |
| 24 (/([^\s/]+))? # trace name (optional, separated with | |
| 25 # the chart name by a '/') | |
| 26 \s+(\S+) # value | |
| 27 \s+(\S+) # units | |
| 28 \s*$""" | |
| 29 | |
| 30 _PRODUCTION_SERVER = "https://chromeperf.appspot.com" | |
| 31 _TESTING_SERVER = "https://chrome-perf.googleplex.com" | |
| 32 | |
| 33 | |
| 34 def UploadPerfData(master_name, perf_id, test_name, builder_name, build_number, | |
| 35 revision, perf_data, point_id, dry_run=False, | |
| 36 testing_dashboard=True): | |
| 37 """Uploads perf data. | |
| 38 | |
| 39 Args: | |
| 40 Please see the help for command-line args. | |
| 41 | |
| 42 Returns: | |
| 43 A boolean value indicating whether the operation succeeded or not. | |
| 44 """ | |
| 45 | |
| 46 def _ConvertToUploadFormat(): | |
| 47 """Converts perf data to the format that the server understands. | |
| 48 | |
| 49 Returns: | |
| 50 A dictionary that (after being converted to JSON) conforms to the server | |
| 51 format. | |
| 52 """ | |
| 53 charts = {} | |
| 54 line_format = re.compile(_PERF_LINE_FORMAT, re.VERBOSE) | |
| 55 for line in perf_data: | |
| 56 match = re.match(line_format, line) | |
| 57 assert match, "Unable to parse the following input: %s" % line | |
| 58 | |
| 59 chart_name = match.group(1) | |
| 60 trace_name = match.group(3) if match.group(3) else "summary" | |
| 61 | |
| 62 if chart_name not in charts: | |
| 63 charts[chart_name] = {} | |
| 64 charts[chart_name][trace_name] = { | |
| 65 "type": "scalar", | |
| 66 "value": float(match.group(4)), | |
| 67 "units": match.group(5) | |
| 68 } | |
| 69 | |
| 70 return { | |
| 71 "master": master_name, | |
| 72 "bot": perf_id, | |
| 73 "masterid": master_name, | |
| 74 "buildername": builder_name, | |
| 75 "buildnumber": build_number, | |
| 76 "versions": { | |
| 77 "mojo": revision | |
| 78 }, | |
| 79 "point_id": point_id, | |
| 80 "supplemental": {}, | |
| 81 "chart_data": { | |
| 82 "format_version": "1.0", | |
| 83 "benchmark_name": test_name, | |
| 84 "charts": charts | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 class _UploadException(Exception): | |
| 89 pass | |
| 90 | |
| 91 def _Upload(server_url, json_data): | |
| 92 """Make an HTTP POST with the given data to the performance dashboard. | |
| 93 | |
| 94 Args: | |
| 95 server_url: URL of the performance dashboard instance. | |
| 96 json_data: JSON string that contains the data to be sent. | |
| 97 | |
| 98 Raises: | |
| 99 _UploadException: An error occurred during uploading. | |
| 100 """ | |
| 101 # When data is provided to urllib2.Request, a POST is sent instead of GET. | |
| 102 # The data must be in the application/x-www-form-urlencoded format. | |
| 103 data = urllib.urlencode({"data": json_data}) | |
| 104 req = urllib2.Request("%s/add_point" % server_url, data) | |
| 105 try: | |
| 106 urllib2.urlopen(req) | |
| 107 except urllib2.HTTPError as e: | |
| 108 raise _UploadException("HTTPError: %d. Response: %s\n" | |
| 109 "JSON: %s\n" % (e.code, e.read(), json_data)) | |
| 110 except urllib2.URLError as e: | |
| 111 raise _UploadException("URLError: %s for JSON %s\n" % | |
| 112 (str(e.reason), json_data)) | |
| 113 except httplib.HTTPException as e: | |
| 114 raise _UploadException("HTTPException for JSON %s\n" % json_data) | |
| 115 | |
| 116 formatted_data = _ConvertToUploadFormat() | |
| 117 | |
| 118 server_url = _TESTING_SERVER if testing_dashboard else _PRODUCTION_SERVER | |
| 119 | |
| 120 if dry_run: | |
| 121 print "Won't upload because --dry-run is specified." | |
| 122 print "Server: %s" % server_url | |
| 123 print "Data:" | |
| 124 pprint.pprint(formatted_data) | |
| 125 else: | |
| 126 print "Uploading data to %s ..." % server_url | |
| 127 try: | |
| 128 _Upload(server_url, json.dumps(formatted_data)) | |
| 129 except _UploadException as e: | |
| 130 print e | |
| 131 return False | |
| 132 print "Done successfully." | |
| 133 | |
| 134 return True | |
| 135 | |
| 136 | |
| 137 def main(): | |
| 138 parser = argparse.ArgumentParser( | |
| 139 description="A tool that uploads data to the performance dashboard.") | |
| 140 | |
| 141 parser.add_argument( | |
| 142 "--master-name", required=True, | |
| 143 help="Buildbot master name, used to construct link to buildbot log by " | |
| 144 "the dashboard, and also as the top-level category for the data.") | |
| 145 parser.add_argument( | |
| 146 "--perf-id", required=True, | |
| 147 help="Used as the second-level category for the data, usually the " | |
| 148 "platform type.") | |
| 149 parser.add_argument( | |
| 150 "--test-name", required=True, | |
| 151 help="Name of the test that the perf data was generated from.") | |
| 152 parser.add_argument( | |
| 153 "--builder-name", required=True, | |
| 154 help="Buildbot builder name, used to construct link to buildbot log by " | |
| 155 "the dashboard.") | |
| 156 parser.add_argument( | |
| 157 "--build-number", required=True, type=int, | |
| 158 help="Build number, used to construct link to buildbot log by the " | |
| 159 "dashboard.") | |
| 160 parser.add_argument( | |
| 161 "--revision", required=True, help="The mojo git commit hash.") | |
| 162 parser.add_argument( | |
| 163 "--perf-data", required=True, metavar="foo_perf.log", | |
| 164 type=argparse.FileType("r"), | |
| 165 help="A text file containing the perf data. Each line is a data point in " | |
| 166 "the following format: chart_name[/trace_name] value units") | |
| 167 parser.add_argument( | |
| 168 "--point-id", required=True, type=int, | |
| 169 help="The x coordinate for the data points.") | |
| 170 parser.add_argument( | |
| 171 "--dry-run", action="store_true", | |
| 172 help="Display the server URL and the data to upload, but not actually " | |
| 173 "upload the data.") | |
| 174 server_group = parser.add_mutually_exclusive_group() | |
| 175 server_group.add_argument( | |
| 176 "--testing-dashboard", action="store_true", default=True, | |
| 177 help="Upload the data to the testing dashboard (default).") | |
| 178 server_group.add_argument( | |
| 179 "--production-dashboard", dest="testing_dashboard", action="store_false", | |
| 180 default=False, help="Upload the data to the production dashboard.") | |
| 181 args = parser.parse_args() | |
| 182 | |
| 183 result = UploadPerfData(args.master_name, args.perf_id, args.test_name, | |
| 184 args.builder_name, args.build_number, args.revision, | |
| 185 args.perf_data, args.point_id, args.dry_run, | |
| 186 args.testing_dashboard) | |
| 187 return 0 if result else 1 | |
| 188 | |
| 189 | |
| 190 if __name__ == '__main__': | |
| 191 sys.exit(main()) | |
| OLD | NEW |