aboutsummaryrefslogtreecommitdiff
path: root/infra/ci/worker/perf_metrics_uploader.py
blob: 05e40958138c9db5406fc6719156b8acaa362d6a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env python
# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import json
import hashlib
import sys

from config import DB, PROJECT
from common_utils import req, SCOPES
'''
Uploads the performance metrics of the Perfetto tests to StackDriver and
Firebase.

The expected format of the JSON is as follows:
{
  metrics: [
    {
      'metric': *metric name*,
      'value': *metric value*,
      'unit': *either s (seconds) or b (bytes)*,
      'tags': {
        *tag name*: *tag value*,
        ...
      },
      'labels': {
        *label name*: *label value*,
        ...
      }
    },
    ...
  ]
}
'''

STACKDRIVER_API = 'https://monitoring.googleapis.com/v3/projects/%s' % PROJECT
SCOPES.append('https://www.googleapis.com/auth/firebase.database')
SCOPES.append('https://www.googleapis.com/auth/userinfo.email')
SCOPES.append('https://www.googleapis.com/auth/monitoring.write')


def sha1(obj):
  hasher = hashlib.sha1()
  hasher.update(json.dumps(obj, sort_keys=True, separators=(',', ':')))
  return hasher.hexdigest()


def metric_list_to_hash_dict(raw_metrics):
  metrics = {}
  for metric in raw_metrics:
    key = '%s-%s' % (metric['metric'], sha1(metric['tags']))
    metrics[key] = metric
  return metrics


def create_stackdriver_metrics(ts, metrics):
  desc = {'timeSeries': []}
  for _, metric in metrics.iteritems():
    metric_name = metric['metric']
    desc['timeSeries'] += [{
        'metric': {
            'type':
                'custom.googleapis.com/perfetto-ci/perf/%s' % metric_name,
            'labels':
                dict(
                    list(metric.get('tags', {}).items()) +
                    list(metric.get('labels', {}).items())),
        },
        'resource': {
            'type': 'global'
        },
        'points': [{
            'interval': {
                'endTime': ts
            },
            'value': {
                'doubleValue': str(metric['value'])
            }
        }]
    }]
  return desc


def main():
  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--job-id',
      type=str,
      required=True,
      help='The Perfetto CI job ID to tie this upload to')
  parser.add_argument(
      'metrics_file', type=str, help='File containing the metrics to upload')
  args = parser.parse_args()

  with open(args.metrics_file, 'r') as metrics_file:
    raw_metrics = json.loads(metrics_file.read())

  job = req('GET', '%s/jobs/%s.json' % (DB, args.job_id))
  ts = job['time_started']

  metrics = metric_list_to_hash_dict(raw_metrics['metrics'])
  req('PUT', '%s/perf/%s.json' % (DB, args.job_id), body=metrics)

  # Only upload Stackdriver metrics for post-submit runs.
  git_ref = job['env'].get('PERFETTO_TEST_GIT_REF')
  if git_ref == 'refs/heads/master':
    sd_metrics = create_stackdriver_metrics(ts, metrics)
    req('POST', STACKDRIVER_API + '/timeSeries', body=sd_metrics)

  return 0


if __name__ == '__main__':
  sys.exit(main())