blob: 2be13bef1913887d6f8e1ce88e524fa9bbe04283 [file] [log] [blame]
Edward Lemur32e3d1e2018-07-12 00:54:05 +00001#!/usr/bin/env python
2# Copyright (c) 2018 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Edward Lemurc87d45b2018-07-26 17:43:11 +00006from __future__ import print_function
7
Edward Lemur03d6d112018-10-23 15:17:36 +00008import re
Edward Lemur32e3d1e2018-07-12 00:54:05 +00009import scm
10import subprocess2
11import sys
Edward Lemur03d6d112018-10-23 15:17:36 +000012import urlparse
Edward Lemur32e3d1e2018-07-12 00:54:05 +000013
14from third_party import colorama
15
16
Edward Lemur48836262018-10-18 02:08:06 +000017# Current version of metrics recording.
18# When we add new metrics, the version number will be increased, we display the
19# user what has changed, and ask the user to agree again.
Edward Lemur5a9ff432018-10-30 19:00:22 +000020CURRENT_VERSION = 1
Edward Lemur48836262018-10-18 02:08:06 +000021
Edward Lemur5ba1e9c2018-07-23 18:19:02 +000022APP_URL = 'https://cit-cli-metrics.appspot.com'
23
Edward Lemur48836262018-10-18 02:08:06 +000024EMPTY_LINE = (
25 '* *'
26)
Edward Lemur32e3d1e2018-07-12 00:54:05 +000027NOTICE_COUNTDOWN_HEADER = (
28 '*****************************************************\n'
29 '* METRICS COLLECTION WILL START IN %2d EXECUTIONS *'
30)
31NOTICE_COLLECTION_HEADER = (
32 '*****************************************************\n'
33 '* METRICS COLLECTION IS TAKING PLACE *'
34)
Edward Lemur48836262018-10-18 02:08:06 +000035NOTICE_VERSION_CHANGE_HEADER = (
36 '*****************************************************\n'
Edward Lemur5a9ff432018-10-30 19:00:22 +000037 '* WE ARE COLLECTING ADDITIONAL METRICS *\n'
38 '* *\n'
39 '* Please review the changes and opt-in again. *'
Edward Lemur48836262018-10-18 02:08:06 +000040)
Edward Lemur32e3d1e2018-07-12 00:54:05 +000041NOTICE_FOOTER = (
Edward Lemur32e3d1e2018-07-12 00:54:05 +000042 '* For more information, and for how to disable this *\n'
43 '* message, please see metrics.README.md in your *\n'
Edward Lemur5a9ff432018-10-30 19:00:22 +000044 '* depot_tools checkout or visit *\n'
45 '* https://bit.ly/2ufRS4p. *\n'
Edward Lemur32e3d1e2018-07-12 00:54:05 +000046 '*****************************************************\n'
47)
48
Edward Lemur48836262018-10-18 02:08:06 +000049CHANGE_NOTICE = {
50 # No changes for version 0
51 0: '',
Edward Lemur5a9ff432018-10-30 19:00:22 +000052 1: ('* We want to collect the Git version. *\n'
53 '* We want to collect information about the HTTP *\n'
54 '* requests that depot_tools makes, and the git and *\n'
55 '* cipd commands it executes. *\n'
56 '* *\n'
57 '* We only collect known strings to make sure we *\n'
58 '* don\'t record PII. *')
Edward Lemur48836262018-10-18 02:08:06 +000059}
60
61
Edward Lemur40764b02018-07-20 18:50:29 +000062KNOWN_PROJECT_URLS = {
63 'https://chrome-internal.googlesource.com/chrome/ios_internal',
64 'https://chrome-internal.googlesource.com/infra/infra_internal',
65 'https://chromium.googlesource.com/breakpad/breakpad',
66 'https://chromium.googlesource.com/chromium/src',
67 'https://chromium.googlesource.com/chromium/tools/depot_tools',
68 'https://chromium.googlesource.com/crashpad/crashpad',
69 'https://chromium.googlesource.com/external/gyp',
70 'https://chromium.googlesource.com/external/naclports',
71 'https://chromium.googlesource.com/infra/goma/client',
72 'https://chromium.googlesource.com/infra/infra',
73 'https://chromium.googlesource.com/native_client/',
74 'https://chromium.googlesource.com/syzygy',
75 'https://chromium.googlesource.com/v8/v8',
76 'https://dart.googlesource.com/sdk',
77 'https://pdfium.googlesource.com/pdfium',
78 'https://skia.googlesource.com/buildbot',
79 'https://skia.googlesource.com/skia',
80 'https://webrtc.googlesource.com/src',
81}
82
Edward Lemur03d6d112018-10-23 15:17:36 +000083KNOWN_HTTP_HOSTS = {
84 'chrome-internal-review.googlesource.com',
85 'chromium-review.googlesource.com',
86 'dart-review.googlesource.com',
87 'eu1-mirror-chromium-review.googlesource.com',
88 'pdfium-review.googlesource.com',
89 'skia-review.googlesource.com',
90 'us1-mirror-chromium-review.googlesource.com',
91 'us2-mirror-chromium-review.googlesource.com',
92 'us3-mirror-chromium-review.googlesource.com',
93 'webrtc-review.googlesource.com',
94}
95
96KNOWN_HTTP_METHODS = {
97 'DELETE',
98 'GET',
99 'PATCH',
100 'POST',
101 'PUT',
102}
103
104KNOWN_HTTP_PATHS = {
105 'accounts':
106 re.compile(r'(/a)?/accounts/.*'),
107 'changes':
108 re.compile(r'(/a)?/changes/([^/]+)?$'),
109 'changes/abandon':
110 re.compile(r'(/a)?/changes/.*/abandon'),
111 'changes/comments':
112 re.compile(r'(/a)?/changes/.*/comments'),
113 'changes/detail':
114 re.compile(r'(/a)?/changes/.*/detail'),
115 'changes/edit':
116 re.compile(r'(/a)?/changes/.*/edit'),
117 'changes/message':
118 re.compile(r'(/a)?/changes/.*/message'),
119 'changes/restore':
120 re.compile(r'(/a)?/changes/.*/restore'),
121 'changes/reviewers':
122 re.compile(r'(/a)?/changes/.*/reviewers/.*'),
123 'changes/revisions/commit':
124 re.compile(r'(/a)?/changes/.*/revisions/.*/commit'),
125 'changes/revisions/review':
126 re.compile(r'(/a)?/changes/.*/revisions/.*/review'),
127 'changes/submit':
128 re.compile(r'(/a)?/changes/.*/submit'),
129 'projects/branches':
130 re.compile(r'(/a)?/projects/.*/branches/.*'),
131}
132
133KNOWN_HTTP_ARGS = {
134 'ALL_REVISIONS',
135 'CURRENT_COMMIT',
136 'CURRENT_REVISION',
137 'DETAILED_ACCOUNTS',
138 'LABELS',
139}
140
Edward Lemur861640f2018-10-31 19:45:31 +0000141GIT_VERSION_RE = re.compile(
142 r'git version (\d)\.(\d{0,2})\.(\d{0,2})'
143)
144
Edward Lemur32e3d1e2018-07-12 00:54:05 +0000145
146def get_python_version():
147 """Return the python version in the major.minor.micro format."""
148 return '{v.major}.{v.minor}.{v.micro}'.format(v=sys.version_info)
149
150
Edward Lemur861640f2018-10-31 19:45:31 +0000151def get_git_version():
152 """Return the Git version in the major.minor.micro format."""
153 p = subprocess2.Popen(
154 ['git', '--version'],
155 stdout=subprocess2.PIPE, stderr=subprocess2.PIPE)
156 stdout, _ = p.communicate()
157 match = GIT_VERSION_RE.match(stdout)
158 if not match:
159 return None
160 return '%s.%s.%s' % match.groups()
161
162
Edward Lemur32e3d1e2018-07-12 00:54:05 +0000163def return_code_from_exception(exception):
164 """Returns the exit code that would result of raising the exception."""
165 if exception is None:
166 return 0
167 if isinstance(exception[1], SystemExit):
168 return exception[1].code
169 return 1
170
171
172def seconds_to_weeks(duration):
173 """Transform a |duration| from seconds to weeks approximately.
174
175 Drops the lowest 19 bits of the integer representation, which ammounts to
176 about 6 days.
177 """
178 return int(duration) >> 19
179
180
Edward Lemur03d6d112018-10-23 15:17:36 +0000181def extract_http_metrics(request_uri, method, status, response_time):
182 """Extract metrics from the request URI.
183
184 Extracts the host, path, and arguments from the request URI, and returns them
185 along with the method, status and response time.
186
187 The host, method, path and arguments must be in the KNOWN_HTTP_* constants
188 defined above.
189
190 Arguments are the values of the o= url parameter. In Gerrit, additional fields
191 can be obtained by adding o parameters, each option requires more database
192 lookups and slows down the query response time to the client, so we make an
193 effort to collect them.
194
195 The regex defined in KNOWN_HTTP_PATH_RES are checked against the path, and
196 those that match will be returned.
197 """
198 http_metrics = {
199 'status': status,
200 'response_time': response_time,
201 }
202
203 if method in KNOWN_HTTP_METHODS:
204 http_metrics['method'] = method
205
206 parsed_url = urlparse.urlparse(request_uri)
207
208 if parsed_url.netloc in KNOWN_HTTP_HOSTS:
209 http_metrics['host'] = parsed_url.netloc
210
211 for name, path_re in KNOWN_HTTP_PATHS.iteritems():
212 if path_re.match(parsed_url.path):
213 http_metrics['path'] = name
214 break
215
216 parsed_query = urlparse.parse_qs(parsed_url.query)
217
218 # Collect o-parameters from the request.
219 args = [
220 arg for arg in parsed_query.get('o', [])
221 if arg in KNOWN_HTTP_ARGS
222 ]
223 if args:
224 http_metrics['arguments'] = args
225
226 return http_metrics
227
228
Edward Lemur32e3d1e2018-07-12 00:54:05 +0000229def get_repo_timestamp(path_to_repo):
230 """Get an approximate timestamp for the upstream of |path_to_repo|.
231
232 Returns the top two bits of the timestamp of the HEAD for the upstream of the
233 branch path_to_repo is checked out at.
234 """
235 # Get the upstream for the current branch. If we're not in a branch, fallback
236 # to HEAD.
237 try:
238 upstream = scm.GIT.GetUpstreamBranch(path_to_repo)
239 except subprocess2.CalledProcessError:
240 upstream = 'HEAD'
241
242 # Get the timestamp of the HEAD for the upstream of the current branch.
243 p = subprocess2.Popen(
244 ['git', '-C', path_to_repo, 'log', '-n1', upstream, '--format=%at'],
245 stdout=subprocess2.PIPE, stderr=subprocess2.PIPE)
246 stdout, _ = p.communicate()
247
248 # If there was an error, give up.
249 if p.returncode != 0:
250 return None
251
252 # Get the age of the checkout in weeks.
253 return seconds_to_weeks(stdout.strip())
254
255
256def print_notice(countdown):
257 """Print a notice to let the user know the status of metrics collection."""
258 colorama.init()
Edward Lemur48836262018-10-18 02:08:06 +0000259 print(colorama.Fore.RED + '\033[1m', file=sys.stderr, end='')
Edward Lemur32e3d1e2018-07-12 00:54:05 +0000260 if countdown:
Edward Lemurc87d45b2018-07-26 17:43:11 +0000261 print(NOTICE_COUNTDOWN_HEADER % countdown, file=sys.stderr)
Edward Lemur32e3d1e2018-07-12 00:54:05 +0000262 else:
Edward Lemurc87d45b2018-07-26 17:43:11 +0000263 print(NOTICE_COLLECTION_HEADER, file=sys.stderr)
Edward Lemur48836262018-10-18 02:08:06 +0000264 print(EMPTY_LINE, file=sys.stderr)
Edward Lemurc87d45b2018-07-26 17:43:11 +0000265 print(NOTICE_FOOTER + colorama.Style.RESET_ALL, file=sys.stderr)
Edward Lemur48836262018-10-18 02:08:06 +0000266
267
268def print_version_change(config_version):
269 """Print a notice to let the user know we are collecting more metrics."""
270 colorama.init()
271 print(colorama.Fore.RED + '\033[1m', file=sys.stderr, end='')
272 print(NOTICE_VERSION_CHANGE_HEADER, file=sys.stderr)
273 print(EMPTY_LINE, file=sys.stderr)
274 for version in range(config_version + 1, CURRENT_VERSION + 1):
275 print(CHANGE_NOTICE[version], file=sys.stderr)
276 print(EMPTY_LINE, file=sys.stderr)