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