• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright (C) 2019 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import argparse
17import json
18import hashlib
19import sys
20
21from config import DB, PROJECT
22from common_utils import req, SCOPES
23'''
24Uploads the performance metrics of the Perfetto tests to StackDriver and
25Firebase.
26
27The expected format of the JSON is as follows:
28{
29  metrics: [
30    {
31      'metric': *metric name*,
32      'value': *metric value*,
33      'unit': *either s (seconds) or b (bytes)*,
34      'tags': {
35        *tag name*: *tag value*,
36        ...
37      },
38      'labels': {
39        *label name*: *label value*,
40        ...
41      }
42    },
43    ...
44  ]
45}
46'''
47
48STACKDRIVER_API = 'https://monitoring.googleapis.com/v3/projects/%s' % PROJECT
49SCOPES.append('https://www.googleapis.com/auth/firebase.database')
50SCOPES.append('https://www.googleapis.com/auth/userinfo.email')
51SCOPES.append('https://www.googleapis.com/auth/monitoring.write')
52
53
54def sha1(obj):
55  hasher = hashlib.sha1()
56  hasher.update(
57      json.dumps(obj, sort_keys=True, separators=(',', ':')).encode('utf-8'))
58  return hasher.hexdigest()
59
60
61def metric_list_to_hash_dict(raw_metrics):
62  metrics = {}
63  for metric in raw_metrics:
64    key = '%s-%s' % (metric['metric'], sha1(metric['tags']))
65    metrics[key] = metric
66  return metrics
67
68
69def create_stackdriver_metrics(ts, metrics):
70  # Chunk up metrics into 100 element chunks to comply with Stackdriver's
71  # restrictions on the number of metrics in a request.
72  metrics_list = list(metrics.values())
73  metric_chunks = [metrics_list[x:x + 100] for x in range(0, len(metrics), 100)]
74  desc_chunks = []
75
76  for chunk in metric_chunks:
77    desc = {'timeSeries': []}
78    for metric in chunk:
79      metric_name = metric['metric']
80      desc['timeSeries'] += [{
81          'metric': {
82              'type':
83                  'custom.googleapis.com/perfetto-ci/perf/%s' % metric_name,
84              'labels':
85                  dict(
86                      list(metric.get('tags', {}).items()) +
87                      list(metric.get('labels', {}).items())),
88          },
89          'resource': {
90              'type': 'global'
91          },
92          'points': [{
93              'interval': {
94                  'endTime': ts
95              },
96              'value': {
97                  'doubleValue': str(metric['value'])
98              }
99          }]
100      }]
101    desc_chunks.append(desc)
102  return desc_chunks
103
104
105def main():
106  parser = argparse.ArgumentParser()
107  parser.add_argument(
108      '--job-id',
109      type=str,
110      required=True,
111      help='The Perfetto CI job ID to tie this upload to')
112  parser.add_argument(
113      'metrics_file', type=str, help='File containing the metrics to upload')
114  args = parser.parse_args()
115
116  with open(args.metrics_file, 'r') as metrics_file:
117    raw_metrics = json.loads(metrics_file.read())
118
119  job = req('GET', '%s/jobs/%s.json' % (DB, args.job_id))
120  ts = job['time_started']
121
122  metrics = metric_list_to_hash_dict(raw_metrics['metrics'])
123  req('PUT', '%s/perf/%s.json' % (DB, args.job_id), body=metrics)
124
125  # Only upload Stackdriver metrics for post-submit runs.
126  git_ref = job['env'].get('PERFETTO_TEST_GIT_REF')
127  if git_ref == 'refs/heads/master':
128    sd_metrics_chunks = create_stackdriver_metrics(ts, metrics)
129    for sd_metrics in sd_metrics_chunks:
130      req('POST', STACKDRIVER_API + '/timeSeries', body=sd_metrics)
131
132  return 0
133
134
135if __name__ == '__main__':
136  sys.exit(main())
137