Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Functions for adding results to perf dashboard.""" | 6 """Functions for adding results to perf dashboard.""" |
| 7 | 7 |
| 8 import calendar | 8 import calendar |
| 9 import datetime | 9 import datetime |
| 10 import httplib | 10 import httplib |
| 11 import json | 11 import json |
| 12 import logging | |
|
ghost stip (do not use)
2014/09/09 02:20:06
left in from debugging?
| |
| 12 import os | 13 import os |
| 13 import urllib | 14 import urllib |
| 14 import urllib2 | 15 import urllib2 |
| 15 | 16 |
| 16 from slave import slave_utils | 17 from slave import slave_utils |
| 17 | 18 |
| 18 # The paths in the results dashboard URLs for sending and viewing results. | 19 # The paths in the results dashboard URLs for sending and viewing results. |
| 19 SEND_RESULTS_PATH = '/add_point' | 20 SEND_RESULTS_PATH = '/add_point' |
| 20 RESULTS_LINK_PATH = '/report?masters=%s&bots=%s&tests=%s&rev=%s' | 21 RESULTS_LINK_PATH = '/report?masters=%s&bots=%s&tests=%s&rev=%s' |
| 21 | 22 |
| 22 # CACHE_DIR/CACHE_FILENAME will be created in options.build_dir to cache | 23 # CACHE_DIR/CACHE_FILENAME will be created in options.build_dir to cache |
| 23 # results which need to be retried. | 24 # results which need to be retried. |
| 24 CACHE_DIR = 'results_dashboard' | 25 CACHE_DIR = 'results_dashboard' |
| 25 CACHE_FILENAME = 'results_to_retry' | 26 CACHE_FILENAME = 'results_to_retry' |
| 26 | 27 |
| 27 | 28 |
| 29 def SendChartJsonResults(chart_json, ref_json, revision_data, bot, mastername, | |
| 30 buildername, buildnumber, supplemental_dict, url, | |
| 31 build_dir): | |
|
qyearsley
2014/09/09 20:22:54
What about having a function which just constructs
sullivan
2014/09/11 00:25:56
Done.
| |
| 32 # The master name used for the dashboard is the CamelCase name returned by | |
| 33 # GetActiveMaster(), and not the canonical master name with dots. | |
| 34 if not chart_json: | |
| 35 print 'Error: No json output from telemetry.' | |
| 36 print '@@@STEP_EXCEPTION@@@' | |
| 37 master = slave_utils.GetActiveMaster() | |
| 38 point_id, default_rev, versions = _RevisionNumberColumns( | |
| 39 revision_data, master, '') | |
| 40 supplemental_columns = {'default_rev': default_rev} | |
| 41 for key in supplemental_dict: | |
| 42 supplemental_columns[key.replace('a_', '', 1)] = supplemental_dict[key] | |
| 43 fields = { | |
| 44 'master': master, | |
| 45 'bot': bot, | |
| 46 'masterid': mastername, | |
| 47 'buildername': buildername, | |
| 48 'buildnumber': buildnumber, | |
| 49 'point_id': point_id, | |
| 50 'supplemental': supplemental_columns, | |
| 51 'versions': versions, | |
| 52 'chart_data': chart_json, | |
| 53 'is_ref': False, | |
| 54 } | |
| 55 SendResults(fields, url, build_dir) | |
| 56 if ref_json: | |
|
ghost stip (do not use)
2014/09/09 02:20:06
so the file will always exist, but the telemeteriz
sullivan
2014/09/11 00:25:55
In telemetry_utils.TelemetryResultsTracker._GetFil
| |
| 57 fields['is_ref'] = True | |
| 58 fields['chart_data'] = ref_json | |
| 59 SendResults(fields, url, build_dir) | |
| 60 | |
| 61 | |
| 28 def SendResults(data, url, build_dir): | 62 def SendResults(data, url, build_dir): |
| 29 """Sends results to the Chrome Performance Dashboard. | 63 """Sends results to the Chrome Performance Dashboard. |
| 30 | 64 |
| 31 This function tries to send the given data to the dashboard, in addition to | 65 This function tries to send the given data to the dashboard, in addition to |
| 32 any data from the cache file. The cache file contains any data that wasn't | 66 any data from the cache file. The cache file contains any data that wasn't |
| 33 successfully sent in a previous run. | 67 successfully sent in a previous run. |
| 34 | 68 |
| 35 Args: | 69 Args: |
| 36 data: The data to try to send. Must be JSON-serializable. | 70 data: The data to try to send. Must be JSON-serializable. |
| 37 url: Performance Dashboard URL (including schema). | 71 url: Performance Dashboard URL (including schema). |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 171 Each dictionary has the keys "master", "bot", "test", "value", "revision". | 205 Each dictionary has the keys "master", "bot", "test", "value", "revision". |
| 172 The full details of this format are described at http://goo.gl/TcJliv. | 206 The full details of this format are described at http://goo.gl/TcJliv. |
| 173 """ | 207 """ |
| 174 results = [] | 208 results = [] |
| 175 | 209 |
| 176 # The master name used for the dashboard is the CamelCase name returned by | 210 # The master name used for the dashboard is the CamelCase name returned by |
| 177 # GetActiveMaster(), and not the canonical master name with dots. | 211 # GetActiveMaster(), and not the canonical master name with dots. |
| 178 master = slave_utils.GetActiveMaster() | 212 master = slave_utils.GetActiveMaster() |
| 179 | 213 |
| 180 for chart_name, chart_data in sorted(charts.items()): | 214 for chart_name, chart_data in sorted(charts.items()): |
| 181 revision, revision_columns = _RevisionNumberColumns(chart_data, master) | 215 revision, default_rev, revision_columns = _RevisionNumberColumns( |
| 216 chart_data, master) | |
|
ghost stip (do not use)
2014/09/09 02:24:16
this needs a prefix, right? 'r_' ?
eakuefner
2014/09/09 21:00:54
Actually, to clarify, the r_ and a_ prefixes are a
sullivan
2014/09/11 00:25:56
Yep.
| |
| 182 | 217 |
| 183 for trace_name, trace_values in sorted(chart_data['traces'].items()): | 218 for trace_name, trace_values in sorted(chart_data['traces'].items()): |
| 184 is_important = trace_name in chart_data.get('important', []) | 219 is_important = trace_name in chart_data.get('important', []) |
| 185 test_path = _TestPath(test_name, chart_name, trace_name) | 220 test_path = _TestPath(test_name, chart_name, trace_name) |
| 186 result = { | 221 result = { |
| 187 'master': master, | 222 'master': master, |
| 188 'bot': bot, | 223 'bot': bot, |
| 189 'test': test_path, | 224 'test': test_path, |
| 190 'revision': revision, | 225 'revision': revision, |
| 191 'masterid': mastername, | 226 'masterid': mastername, |
| 192 'buildername': buildername, | 227 'buildername': buildername, |
| 193 'buildnumber': buildnumber, | 228 'buildnumber': buildnumber, |
| 194 'supplemental_columns': {} | 229 'supplemental_columns': {'a_default_rev': default_rev} |
| 195 } | 230 } |
| 196 | 231 |
| 197 # Add the supplemental_columns values that were passed in after the | 232 # Add the supplemental_columns values that were passed in after the |
| 198 # calculated revision column values so that these can be overwritten. | 233 # calculated revision column values so that these can be overwritten. |
| 199 result['supplemental_columns'].update(revision_columns) | 234 result['supplemental_columns'].update(revision_columns) |
| 200 result['supplemental_columns'].update(supplemental_columns) | 235 result['supplemental_columns'].update(supplemental_columns) |
| 201 | 236 |
| 202 # Check whether this result is itself a time series (e.g. Endure result). | 237 # Check whether this result is itself a time series (e.g. Endure result). |
| 203 # If so add the key "data", otherwise add "value" and "error". | 238 # If so add the key "data", otherwise add "value" and "error". |
| 204 have_multi_value_data = False | 239 have_multi_value_data = False |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 219 if chart_data.get('stack'): | 254 if chart_data.get('stack'): |
| 220 result['stack'] = chart_data['stack'] | 255 result['stack'] = chart_data['stack'] |
| 221 if is_important: | 256 if is_important: |
| 222 result['important'] = True | 257 result['important'] = True |
| 223 | 258 |
| 224 results.append(result) | 259 results.append(result) |
| 225 | 260 |
| 226 return results | 261 return results |
| 227 | 262 |
| 228 | 263 |
| 229 def _RevisionNumberColumns(data, master): | 264 def _RevisionNumberColumns(data, master, prefix): |
| 230 """Get the revision number and revision-related columns from the given data. | 265 """Get the revision number and revision-related columns from the given data. |
| 231 | 266 |
| 232 Args: | 267 Args: |
| 233 data: A dict of information from one line of the log file. | 268 data: A dict of information from one line of the log file. |
| 234 master: The name of the buildbot master. | 269 master: The name of the buildbot master. |
| 270 prefix: Prefix for keys. 'r_' for old-style json, '' for telemetry json. | |
| 235 | 271 |
| 236 Returns: | 272 Returns: |
| 237 A pair with the revision number (which must be an int), and a dict of | 273 A 3-tuple with the revision number (which must be an int), the default rev |
| 238 version-related supplemental columns. | 274 name, and a dict of version-related supplemental columns. |
| 239 """ | 275 """ |
| 240 def GetTimestamp(): | 276 def GetTimestamp(): |
| 241 """Get the Unix timestamp for the current time.""" | 277 """Get the Unix timestamp for the current time.""" |
| 242 return int(calendar.timegm(datetime.datetime.utcnow().utctimetuple())) | 278 return int(calendar.timegm(datetime.datetime.utcnow().utctimetuple())) |
| 243 | 279 |
| 244 revision_supplemental_columns = {} | 280 revision_supplemental_columns = {} |
| 281 default_rev = None | |
| 245 | 282 |
| 246 # The dashboard requires points' x-values to be integers, and points are | 283 # The dashboard requires points' x-values to be integers, and points are |
| 247 # ordered by this. If the revision can't be parsed as an int, assume that | 284 # ordered by this. If the revision can't be parsed as an int, assume that |
| 248 # it's a git hash and use timestamp as the x-value. | 285 # it's a git hash and use timestamp as the x-value. |
| 249 git_hash = None | 286 git_hash = None |
| 250 try: | 287 try: |
| 251 revision = int(data['rev']) | 288 revision = int(data['rev']) |
| 252 except ValueError: | 289 except ValueError: |
| 253 # The dashboard requires ordered integer revision numbers. If the revision | 290 # The dashboard requires ordered integer revision numbers. If the revision |
| 254 # is not an integer, assume it's a git hash and send a timestamp. | 291 # is not an integer, assume it's a git hash and send a timestamp. |
| 255 revision = GetTimestamp() | 292 revision = GetTimestamp() |
| 256 git_hash = data['rev'] | 293 git_hash = data['rev'] |
| 257 | 294 |
| 258 # Add Chromium version if it was specified, and use timestamp as x-value. | 295 # Add Chromium version if it was specified, and use timestamp as x-value. |
| 259 if 'ver' in data and data['ver'] != 'undefined': | 296 if 'ver' in data and data['ver'] != 'undefined': |
| 260 revision_supplemental_columns['r_chrome_version'] = data['ver'] | 297 revision_supplemental_columns[prefix + 'chrome_version'] = data['ver'] |
| 261 revision_supplemental_columns['a_default_rev'] = 'r_chrome_version' | 298 default_rev = 'r_chrome_version' |
| 262 revision = GetTimestamp() | 299 revision = GetTimestamp() |
| 263 | 300 |
| 264 # Blink builds can have the same chromium revision for two builds. So | 301 # Blink builds can have the same chromium revision for two builds. So |
| 265 # order them by timestamp to get them to show on the dashboard in the | 302 # order them by timestamp to get them to show on the dashboard in the |
| 266 # order they were built. | 303 # order they were built. |
| 267 if master in ['ChromiumWebkit', 'Oilpan']: | 304 if master in ['ChromiumWebkit', 'Oilpan']: |
| 268 if not git_hash: | 305 if not git_hash: |
| 269 revision_supplemental_columns['r_chromium_svn'] = revision | 306 revision_supplemental_columns[prefix + 'chromium_svn'] = revision |
| 270 revision = GetTimestamp() | 307 revision = GetTimestamp() |
| 271 | 308 |
| 272 # Regardless of what the master is, if the given "rev" can't be parsed as | 309 # Regardless of what the master is, if the given "rev" can't be parsed as |
| 273 # an int, we're assuming that it's a git hash. | 310 # an int, we're assuming that it's a git hash. |
| 274 if git_hash: | 311 if git_hash: |
| 275 revision_supplemental_columns['r_chromium'] = git_hash | 312 revision_supplemental_columns[prefix + 'chromium'] = git_hash |
| 276 | 313 |
| 277 # For Oilpan, send the webkit_rev as r_oilpan since we are getting | 314 # For Oilpan, send the webkit_rev as r_oilpan since we are getting |
| 278 # the oilpan branch revision instead of the Blink trunk revision | 315 # the oilpan branch revision instead of the Blink trunk revision |
| 279 # and set r_oilpan to be the dashboard default revision. | 316 # and set r_oilpan to be the dashboard default revision. |
| 280 if master == 'Oilpan': | 317 if master == 'Oilpan': |
| 281 revision_supplemental_columns['r_oilpan'] = data['webkit_rev'] | 318 revision_supplemental_columns[prefix + 'oilpan'] = data['webkit_rev'] |
| 282 revision_supplemental_columns['a_default_rev'] = 'r_oilpan' | 319 default_rev = 'r_oilpan' |
| 283 else: | 320 else: |
| 284 # For other revision data, add it if it's present and not undefined: | 321 # For other revision data, add it if it's present and not undefined: |
| 285 for key in ['webkit_rev', 'webrtc_rev', 'v8_rev']: | 322 for key in ['webkit_rev', 'webrtc_rev', 'v8_rev']: |
| 286 if key in data and data[key] != 'undefined': | 323 if key in data and data[key] != 'undefined': |
| 287 revision_supplemental_columns['r_' + key] = data[key] | 324 revision_supplemental_columns[prefix + key] = data[key] |
| 288 | 325 |
| 289 # If possible, also send the git hash. | 326 # If possible, also send the git hash. |
| 290 # If no other "default revision" type is specified already, use the git hash. | 327 # If no other "default revision" type is specified already, use the git hash. |
| 291 # This will change how it is displayed on the perf dashboard. | 328 # This will change how it is displayed on the perf dashboard. |
| 292 if 'git_revision' in data and data['git_revision'] != 'undefined': | 329 if 'git_revision' in data and data['git_revision'] != 'undefined': |
| 293 revision_supplemental_columns['r_chromium'] = data['git_revision'] | 330 revision_supplemental_columns[prefix + 'chromium'] = data['git_revision'] |
| 294 if 'a_default_rev' not in revision_supplemental_columns: | 331 if not default_rev: |
| 295 revision_supplemental_columns['a_default_rev'] = 'r_chromium' | 332 default_rev = 'r_chromium' |
| 296 | 333 |
| 297 return revision, revision_supplemental_columns | 334 return revision, default_rev, revision_supplemental_columns |
| 298 | 335 |
| 299 | 336 |
| 300 def _TestPath(test_name, chart_name, trace_name): | 337 def _TestPath(test_name, chart_name, trace_name): |
| 301 """Get the slash-separated test path to send. | 338 """Get the slash-separated test path to send. |
| 302 | 339 |
| 303 Args: | 340 Args: |
| 304 test: Test name. Typically, this will be a top-level 'test suite' name. | 341 test: Test name. Typically, this will be a top-level 'test suite' name. |
| 305 chart_name: Name of a chart where multiple trace lines are grouped. If the | 342 chart_name: Name of a chart where multiple trace lines are grouped. If the |
| 306 chart name is the same as the trace name, that signifies that this is | 343 chart name is the same as the trace name, that signifies that this is |
| 307 the main trace for the chart. | 344 the main trace for the chart. |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 360 def _LinkAnnotation(url, data): | 397 def _LinkAnnotation(url, data): |
| 361 """Prints a link annotation with a link to the dashboard if possible. | 398 """Prints a link annotation with a link to the dashboard if possible. |
| 362 | 399 |
| 363 Args: | 400 Args: |
| 364 url: The Performance Dashboard URL, e.g. "https://chromeperf.appspot.com" | 401 url: The Performance Dashboard URL, e.g. "https://chromeperf.appspot.com" |
| 365 data: The data that's being sent to the dashboard. | 402 data: The data that's being sent to the dashboard. |
| 366 | 403 |
| 367 Returns: | 404 Returns: |
| 368 An annotation to print, or None. | 405 An annotation to print, or None. |
| 369 """ | 406 """ |
| 370 if not data or type(data) is not list: | 407 if not data: |
| 371 return None | 408 return None |
| 372 point = data[0] | 409 if type(data) is list: |
| 410 master, bot, test, revision = ( | |
| 411 data[0]['master'], data[0]['bot'], data[0]['test'], data[0]['revision']) | |
| 412 else: | |
| 413 master, bot, test, revision = ( | |
| 414 data['master'], data['bot'], data['chart_data']['benchmark_name'], | |
| 415 data['point_id']) | |
| 373 results_link = url + RESULTS_LINK_PATH % ( | 416 results_link = url + RESULTS_LINK_PATH % ( |
| 374 urllib.quote(point['master']), | 417 urllib.quote(master), urllib.quote(bot), urllib.quote(test.split('/')[0]), |
| 375 urllib.quote(point['bot']), | 418 revision) |
| 376 urllib.quote(point['test'].split('/')[0]), | |
| 377 point['revision']) | |
| 378 return '@@@STEP_LINK@%s@%s@@@' % ('Results Dashboard', results_link) | 419 return '@@@STEP_LINK@%s@%s@@@' % ('Results Dashboard', results_link) |
| OLD | NEW |