1#!/usr/bin/env python 2# Copyright (c) 2020 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 10 11import httplib2 12import json 13import subprocess 14import zlib 15 16from tracing.value import histogram 17from tracing.value import histogram_set 18from tracing.value.diagnostics import generic_set 19from tracing.value.diagnostics import reserved_infos 20 21 22def _GenerateOauthToken(): 23 args = ['luci-auth', 'token'] 24 p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 25 if p.wait() == 0: 26 output = p.stdout.read() 27 return output.strip() 28 else: 29 raise RuntimeError( 30 'Error generating authentication token.\nStdout: %s\nStderr:%s' % 31 (p.stdout.read(), p.stderr.read())) 32 33 34def _SendHistogramSet(url, histograms, oauth_token): 35 """Make a HTTP POST with the given JSON to the Performance Dashboard. 36 37 Args: 38 url: URL of Performance Dashboard instance, e.g. 39 "https://chromeperf.appspot.com". 40 histograms: a histogram set object that contains the data to be sent. 41 oauth_token: An oauth token to use for authorization. 42 """ 43 headers = {'Authorization': 'Bearer %s' % oauth_token} 44 45 serialized = json.dumps(_ApplyHacks(histograms.AsDicts()), indent=4) 46 47 if url.startswith('http://localhost'): 48 # The catapult server turns off compression in developer mode. 49 data = serialized 50 else: 51 data = zlib.compress(serialized) 52 53 print 'Sending %d bytes to %s.' % (len(data), url + '/add_histograms') 54 55 http = httplib2.Http() 56 response, content = http.request(url + '/add_histograms', method='POST', 57 body=data, headers=headers) 58 return response, content 59 60 61# TODO(https://crbug.com/1029452): HACKHACK 62# Remove once we have doubles in the proto and handle -infinity correctly. 63def _ApplyHacks(dicts): 64 for d in dicts: 65 if 'running' in d: 66 def _NoInf(value): 67 if value == float('inf'): 68 return histogram.JS_MAX_VALUE 69 if value == float('-inf'): 70 return -histogram.JS_MAX_VALUE 71 return value 72 d['running'] = [_NoInf(value) for value in d['running']] 73 74 return dicts 75 76 77def _LoadHistogramSetFromProto(options): 78 hs = histogram_set.HistogramSet() 79 with options.input_results_file as f: 80 hs.ImportProto(f.read()) 81 82 return hs 83 84 85def _AddBuildInfo(histograms, options): 86 common_diagnostics = { 87 reserved_infos.MASTERS: options.perf_dashboard_machine_group, 88 reserved_infos.BOTS: options.bot, 89 reserved_infos.POINT_ID: options.commit_position, 90 reserved_infos.BENCHMARKS: options.test_suite, 91 reserved_infos.WEBRTC_REVISIONS: str(options.webrtc_git_hash), 92 reserved_infos.BUILD_URLS: options.build_page_url, 93 } 94 95 for k, v in common_diagnostics.items(): 96 histograms.AddSharedDiagnosticToAllHistograms( 97 k.name, generic_set.GenericSet([v])) 98 99 100def _DumpOutput(histograms, output_file): 101 with output_file: 102 json.dump(_ApplyHacks(histograms.AsDicts()), output_file, indent=4) 103 104 105def UploadToDashboard(options): 106 histograms = _LoadHistogramSetFromProto(options) 107 _AddBuildInfo(histograms, options) 108 109 if options.output_json_file: 110 _DumpOutput(histograms, options.output_json_file) 111 112 oauth_token = _GenerateOauthToken() 113 response, content = _SendHistogramSet( 114 options.dashboard_url, histograms, oauth_token) 115 116 if response.status == 200: 117 print 'Received 200 from dashboard.' 118 return 0 119 else: 120 print('Upload failed with %d: %s\n\n%s' % (response.status, response.reason, 121 content)) 122 return 1 123