| 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 |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 171 Each dictionary has the keys "master", "bot", "test", "value", "revision". | 171 Each dictionary has the keys "master", "bot", "test", "value", "revision". |
| 172 The full details of this format are described at http://goo.gl/TcJliv. | 172 The full details of this format are described at http://goo.gl/TcJliv. |
| 173 """ | 173 """ |
| 174 results = [] | 174 results = [] |
| 175 | 175 |
| 176 # The master name used for the dashboard is the CamelCase name returned by | 176 # The master name used for the dashboard is the CamelCase name returned by |
| 177 # GetActiveMaster(), and not the canonical master name with dots. | 177 # GetActiveMaster(), and not the canonical master name with dots. |
| 178 master = slave_utils.GetActiveMaster() | 178 master = slave_utils.GetActiveMaster() |
| 179 | 179 |
| 180 for chart_name, chart_data in sorted(charts.items()): | 180 for chart_name, chart_data in sorted(charts.items()): |
| 181 revision, default_rev, revision_columns = _RevisionNumberColumns( | 181 revision, revision_columns = _RevisionNumberColumns(chart_data, master) |
| 182 chart_data, master, 'r_') | |
| 183 | 182 |
| 184 for trace_name, trace_values in sorted(chart_data['traces'].items()): | 183 for trace_name, trace_values in sorted(chart_data['traces'].items()): |
| 185 is_important = trace_name in chart_data.get('important', []) | 184 is_important = trace_name in chart_data.get('important', []) |
| 186 test_path = _TestPath(test_name, chart_name, trace_name) | 185 test_path = _TestPath(test_name, chart_name, trace_name) |
| 187 result = { | 186 result = { |
| 188 'master': master, | 187 'master': master, |
| 189 'bot': bot, | 188 'bot': bot, |
| 190 'test': test_path, | 189 'test': test_path, |
| 191 'revision': revision, | 190 'revision': revision, |
| 192 'masterid': mastername, | 191 'masterid': mastername, |
| 193 'buildername': buildername, | 192 'buildername': buildername, |
| 194 'buildnumber': buildnumber, | 193 'buildnumber': buildnumber, |
| 195 'supplemental_columns': {} | 194 'supplemental_columns': {} |
| 196 } | 195 } |
| 197 if default_rev: | |
| 198 supplemental_columns['a_default_rev'] = default_rev | |
| 199 | 196 |
| 200 # Add the supplemental_columns values that were passed in after the | 197 # Add the supplemental_columns values that were passed in after the |
| 201 # calculated revision column values so that these can be overwritten. | 198 # calculated revision column values so that these can be overwritten. |
| 202 result['supplemental_columns'].update(revision_columns) | 199 result['supplemental_columns'].update(revision_columns) |
| 203 result['supplemental_columns'].update(supplemental_columns) | 200 result['supplemental_columns'].update(supplemental_columns) |
| 204 | 201 |
| 205 # Check whether this result is itself a time series (e.g. Endure result). | 202 # Check whether this result is itself a time series (e.g. Endure result). |
| 206 # If so add the key "data", otherwise add "value" and "error". | 203 # If so add the key "data", otherwise add "value" and "error". |
| 207 have_multi_value_data = False | 204 have_multi_value_data = False |
| 208 for value in trace_values: | 205 for value in trace_values: |
| (...skipping 13 matching lines...) Expand all Loading... |
| 222 if chart_data.get('stack'): | 219 if chart_data.get('stack'): |
| 223 result['stack'] = chart_data['stack'] | 220 result['stack'] = chart_data['stack'] |
| 224 if is_important: | 221 if is_important: |
| 225 result['important'] = True | 222 result['important'] = True |
| 226 | 223 |
| 227 results.append(result) | 224 results.append(result) |
| 228 | 225 |
| 229 return results | 226 return results |
| 230 | 227 |
| 231 | 228 |
| 232 def MakeDashboardJsonV1(chart_json, revision_data, bot, mastername, | 229 def _RevisionNumberColumns(data, master): |
| 233 buildername, buildnumber, supplemental_dict, is_ref): | |
| 234 """Generates Dashboard JSON in the new Telemetry format. | |
| 235 | |
| 236 Args: | |
| 237 chart_json: The json output by telemetry | |
| 238 revision_data: Data about revisions to include in the upload | |
| 239 bot: A string which comes from perf_id, e.g. linux-release. | |
| 240 mastername: Buildbot master name, e.g. chromium.perf. | |
| 241 buildername: Builder name (for stdio links). | |
| 242 buildnumber: Build number (for stdio links). | |
| 243 supplemental_columns: A dictionary of extra data to send with a point. | |
| 244 is_ref: True if this is a reference build, False otherwise. | |
| 245 | |
| 246 Returns: | |
| 247 A dictionary in the format accepted by the perf dashboard. | |
| 248 """ | |
| 249 if not chart_json: | |
| 250 print 'Error: No json output from telemetry.' | |
| 251 print '@@@STEP_EXCEPTION@@@' | |
| 252 # The master name used for the dashboard is the CamelCase name returned by | |
| 253 # GetActiveMaster(), and not the canonical master name with dots. | |
| 254 master = slave_utils.GetActiveMaster() | |
| 255 point_id, default_rev, versions = _RevisionNumberColumns( | |
| 256 revision_data, master, '') | |
| 257 supplemental_columns = {} | |
| 258 if default_rev: | |
| 259 supplemental_columns['default_rev'] = default_rev | |
| 260 for key in supplemental_dict: | |
| 261 supplemental_columns[key.replace('a_', '', 1)] = supplemental_dict[key] | |
| 262 fields = { | |
| 263 'master': master, | |
| 264 'bot': bot, | |
| 265 'masterid': mastername, | |
| 266 'buildername': buildername, | |
| 267 'buildnumber': buildnumber, | |
| 268 'point_id': point_id, | |
| 269 'supplemental': supplemental_columns, | |
| 270 'versions': versions, | |
| 271 'chart_data': chart_json, | |
| 272 'is_ref': is_ref, | |
| 273 } | |
| 274 return fields | |
| 275 | |
| 276 | |
| 277 def _GetTimestamp(): | |
| 278 """Get the Unix timestamp for the current time.""" | |
| 279 return int(calendar.timegm(datetime.datetime.utcnow().utctimetuple())) | |
| 280 | |
| 281 | |
| 282 def _RevisionNumberColumns(data, master, prefix): | |
| 283 """Get the revision number and revision-related columns from the given data. | 230 """Get the revision number and revision-related columns from the given data. |
| 284 | 231 |
| 285 Args: | 232 Args: |
| 286 data: A dict of information from one line of the log file. | 233 data: A dict of information from one line of the log file. |
| 287 master: The name of the buildbot master. | 234 master: The name of the buildbot master. |
| 288 prefix: Prefix for keys. 'r_' for old-style json, '' for telemetry json. | |
| 289 | 235 |
| 290 Returns: | 236 Returns: |
| 291 A 3-tuple with the revision number (which must be an int), the default rev | 237 A pair with the revision number (which must be an int), and a dict of |
| 292 name, and a dict of version-related supplemental columns. | 238 version-related supplemental columns. |
| 293 """ | 239 """ |
| 240 def GetTimestamp(): |
| 241 """Get the Unix timestamp for the current time.""" |
| 242 return int(calendar.timegm(datetime.datetime.utcnow().utctimetuple())) |
| 243 |
| 294 revision_supplemental_columns = {} | 244 revision_supplemental_columns = {} |
| 295 default_rev = None | |
| 296 | 245 |
| 297 # The dashboard requires points' x-values to be integers, and points are | 246 # The dashboard requires points' x-values to be integers, and points are |
| 298 # ordered by this. If the revision can't be parsed as an int, assume that | 247 # ordered by this. If the revision can't be parsed as an int, assume that |
| 299 # it's a git hash and use timestamp as the x-value. | 248 # it's a git hash and use timestamp as the x-value. |
| 300 git_hash = None | 249 git_hash = None |
| 301 try: | 250 try: |
| 302 revision = int(data['rev']) | 251 revision = int(data['rev']) |
| 303 except ValueError: | 252 except ValueError: |
| 304 # The dashboard requires ordered integer revision numbers. If the revision | 253 # The dashboard requires ordered integer revision numbers. If the revision |
| 305 # is not an integer, assume it's a git hash and send a timestamp. | 254 # is not an integer, assume it's a git hash and send a timestamp. |
| 306 revision = _GetTimestamp() | 255 revision = GetTimestamp() |
| 307 git_hash = data['rev'] | 256 git_hash = data['rev'] |
| 308 | 257 |
| 309 # Add Chromium version if it was specified, and use timestamp as x-value. | 258 # Add Chromium version if it was specified, and use timestamp as x-value. |
| 310 if 'ver' in data and data['ver'] != 'undefined': | 259 if 'ver' in data and data['ver'] != 'undefined': |
| 311 revision_supplemental_columns[prefix + 'chrome_version'] = data['ver'] | 260 revision_supplemental_columns['r_chrome_version'] = data['ver'] |
| 312 default_rev = 'r_chrome_version' | 261 revision_supplemental_columns['a_default_rev'] = 'r_chrome_version' |
| 313 revision = _GetTimestamp() | 262 revision = GetTimestamp() |
| 314 | 263 |
| 315 # Blink builds can have the same chromium revision for two builds. So | 264 # Blink builds can have the same chromium revision for two builds. So |
| 316 # order them by timestamp to get them to show on the dashboard in the | 265 # order them by timestamp to get them to show on the dashboard in the |
| 317 # order they were built. | 266 # order they were built. |
| 318 if master in ['ChromiumWebkit', 'Oilpan']: | 267 if master in ['ChromiumWebkit', 'Oilpan']: |
| 319 if not git_hash: | 268 if not git_hash: |
| 320 revision_supplemental_columns[prefix + 'chromium_svn'] = revision | 269 revision_supplemental_columns['r_chromium_svn'] = revision |
| 321 revision = _GetTimestamp() | 270 revision = GetTimestamp() |
| 322 | 271 |
| 323 # Regardless of what the master is, if the given "rev" can't be parsed as | 272 # Regardless of what the master is, if the given "rev" can't be parsed as |
| 324 # an int, we're assuming that it's a git hash. | 273 # an int, we're assuming that it's a git hash. |
| 325 if git_hash: | 274 if git_hash: |
| 326 revision_supplemental_columns[prefix + 'chromium'] = git_hash | 275 revision_supplemental_columns['r_chromium'] = git_hash |
| 327 | 276 |
| 328 # For Oilpan, send the webkit_rev as r_oilpan since we are getting | 277 # For Oilpan, send the webkit_rev as r_oilpan since we are getting |
| 329 # the oilpan branch revision instead of the Blink trunk revision | 278 # the oilpan branch revision instead of the Blink trunk revision |
| 330 # and set r_oilpan to be the dashboard default revision. | 279 # and set r_oilpan to be the dashboard default revision. |
| 331 if master == 'Oilpan': | 280 if master == 'Oilpan': |
| 332 revision_supplemental_columns[prefix + 'oilpan'] = data['webkit_rev'] | 281 revision_supplemental_columns['r_oilpan'] = data['webkit_rev'] |
| 333 default_rev = 'r_oilpan' | 282 revision_supplemental_columns['a_default_rev'] = 'r_oilpan' |
| 334 else: | 283 else: |
| 335 # For other revision data, add it if it's present and not undefined: | 284 # For other revision data, add it if it's present and not undefined: |
| 336 for key in ['webkit_rev', 'webrtc_rev', 'v8_rev']: | 285 for key in ['webkit_rev', 'webrtc_rev', 'v8_rev']: |
| 337 if key in data and data[key] != 'undefined': | 286 if key in data and data[key] != 'undefined': |
| 338 revision_supplemental_columns[prefix + key] = data[key] | 287 revision_supplemental_columns['r_' + key] = data[key] |
| 339 | 288 |
| 340 # If possible, also send the git hash. | 289 # If possible, also send the git hash. |
| 341 # If no other "default revision" type is specified already, use the git hash. | 290 # If no other "default revision" type is specified already, use the git hash. |
| 342 # This will change how it is displayed on the perf dashboard. | 291 # This will change how it is displayed on the perf dashboard. |
| 343 if 'git_revision' in data and data['git_revision'] != 'undefined': | 292 if 'git_revision' in data and data['git_revision'] != 'undefined': |
| 344 revision_supplemental_columns[prefix + 'chromium'] = data['git_revision'] | 293 revision_supplemental_columns['r_chromium'] = data['git_revision'] |
| 345 if not default_rev: | 294 if 'a_default_rev' not in revision_supplemental_columns: |
| 346 default_rev = 'r_chromium' | 295 revision_supplemental_columns['a_default_rev'] = 'r_chromium' |
| 347 | 296 |
| 348 return revision, default_rev, revision_supplemental_columns | 297 return revision, revision_supplemental_columns |
| 349 | 298 |
| 350 | 299 |
| 351 def _TestPath(test_name, chart_name, trace_name): | 300 def _TestPath(test_name, chart_name, trace_name): |
| 352 """Get the slash-separated test path to send. | 301 """Get the slash-separated test path to send. |
| 353 | 302 |
| 354 Args: | 303 Args: |
| 355 test: Test name. Typically, this will be a top-level 'test suite' name. | 304 test: Test name. Typically, this will be a top-level 'test suite' name. |
| 356 chart_name: Name of a chart where multiple trace lines are grouped. If the | 305 chart_name: Name of a chart where multiple trace lines are grouped. If the |
| 357 chart name is the same as the trace name, that signifies that this is | 306 chart name is the same as the trace name, that signifies that this is |
| 358 the main trace for the chart. | 307 the main trace for the chart. |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 411 def _LinkAnnotation(url, data): | 360 def _LinkAnnotation(url, data): |
| 412 """Prints a link annotation with a link to the dashboard if possible. | 361 """Prints a link annotation with a link to the dashboard if possible. |
| 413 | 362 |
| 414 Args: | 363 Args: |
| 415 url: The Performance Dashboard URL, e.g. "https://chromeperf.appspot.com" | 364 url: The Performance Dashboard URL, e.g. "https://chromeperf.appspot.com" |
| 416 data: The data that's being sent to the dashboard. | 365 data: The data that's being sent to the dashboard. |
| 417 | 366 |
| 418 Returns: | 367 Returns: |
| 419 An annotation to print, or None. | 368 An annotation to print, or None. |
| 420 """ | 369 """ |
| 421 if not data: | 370 if not data or type(data) is not list: |
| 422 return None | 371 return None |
| 423 if isinstance(data, list): | 372 point = data[0] |
| 424 master, bot, test, revision = ( | |
| 425 data[0]['master'], data[0]['bot'], data[0]['test'], data[0]['revision']) | |
| 426 else: | |
| 427 master, bot, test, revision = ( | |
| 428 data['master'], data['bot'], data['chart_data']['benchmark_name'], | |
| 429 data['point_id']) | |
| 430 results_link = url + RESULTS_LINK_PATH % ( | 373 results_link = url + RESULTS_LINK_PATH % ( |
| 431 urllib.quote(master), urllib.quote(bot), urllib.quote(test.split('/')[0]), | 374 urllib.quote(point['master']), |
| 432 revision) | 375 urllib.quote(point['bot']), |
| 376 urllib.quote(point['test'].split('/')[0]), |
| 377 point['revision']) |
| 433 return '@@@STEP_LINK@%s@%s@@@' % ('Results Dashboard', results_link) | 378 return '@@@STEP_LINK@%s@%s@@@' % ('Results Dashboard', results_link) |
| OLD | NEW |