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