OLD | NEW |
1 # Copyright 2017 The Chromium Authors. All rights reserved. | 1 # Copyright 2017 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """URL endpoint containing server-side functionality for pinpoint jobs.""" | 5 """URL endpoint containing server-side functionality for pinpoint jobs.""" |
6 | 6 |
7 import json | 7 import json |
8 | 8 |
9 from google.appengine.api import users | 9 from google.appengine.api import users |
10 from google.appengine.ext import ndb | 10 from google.appengine.ext import ndb |
11 | 11 |
12 from dashboard import start_try_job | 12 from dashboard import start_try_job |
13 from dashboard.common import namespaced_stored_object | 13 from dashboard.common import namespaced_stored_object |
14 from dashboard.common import request_handler | 14 from dashboard.common import request_handler |
15 from dashboard.common import utils | 15 from dashboard.common import utils |
16 from dashboard.services import crrev_service | 16 from dashboard.services import crrev_service |
17 from dashboard.services import pinpoint_service | 17 from dashboard.services import pinpoint_service |
18 | 18 |
19 _BOTS_TO_DIMENSIONS = 'bot_dimensions_map' | 19 _BOTS_TO_DIMENSIONS = 'bot_dimensions_map' |
20 _PINPOINT_REPOSITORIES = 'repositories' | 20 _PINPOINT_REPOSITORIES = 'repositories' |
| 21 _ISOLATE_TARGETS = [ |
| 22 'angle_perftests', 'cc_perftests', 'gpu_perftests', |
| 23 'load_library_perf_tests', 'media_perftests', 'net_perftests', |
| 24 'performance_browser_tests', 'telemetry_perf_tests', |
| 25 'telemetry_perf_webview_tests', 'tracing_perftests'] |
21 | 26 |
22 | 27 |
23 class InvalidParamsError(Exception): | 28 class InvalidParamsError(Exception): |
24 pass | 29 pass |
25 | 30 |
26 | 31 |
27 class PinpointNewPrefillRequestHandler(request_handler.RequestHandler): | 32 class PinpointNewPrefillRequestHandler(request_handler.RequestHandler): |
28 def post(self): | 33 def post(self): |
29 story_filter = start_try_job.GuessStoryFilter(self.request.get('test_path')) | 34 story_filter = start_try_job.GuessStoryFilter(self.request.get('test_path')) |
30 self.response.write(json.dumps({'story_filter': story_filter})) | 35 self.response.write(json.dumps({'story_filter': story_filter})) |
31 | 36 |
32 | 37 |
33 class PinpointNewRequestHandler(request_handler.RequestHandler): | 38 class PinpointNewBisectRequestHandler(request_handler.RequestHandler): |
34 def post(self): | 39 def post(self): |
35 job_params = dict( | 40 job_params = dict( |
36 (a, self.request.get(a)) for a in self.request.arguments()) | 41 (a, self.request.get(a)) for a in self.request.arguments()) |
37 | 42 |
38 try: | 43 try: |
39 pinpoint_params = PinpointParamsFromBisectParams(job_params) | 44 pinpoint_params = PinpointParamsFromBisectParams(job_params) |
40 except InvalidParamsError as e: | 45 except InvalidParamsError as e: |
41 self.response.write(json.dumps({'error': e.message})) | 46 self.response.write(json.dumps({'error': e.message})) |
42 return | 47 return |
43 | 48 |
44 self.response.write(json.dumps(pinpoint_service.NewJob(pinpoint_params))) | 49 self.response.write(json.dumps(pinpoint_service.NewJob(pinpoint_params))) |
45 | 50 |
46 | 51 |
| 52 class PinpointNewPerfTryRequestHandler(request_handler.RequestHandler): |
| 53 def post(self): |
| 54 job_params = dict( |
| 55 (a, self.request.get(a)) for a in self.request.arguments()) |
| 56 |
| 57 try: |
| 58 pinpoint_params = PinpointParamsFromPerfTryParams(job_params) |
| 59 except InvalidParamsError as e: |
| 60 self.response.write(json.dumps({'error': e.message})) |
| 61 return |
| 62 |
| 63 self.response.write(json.dumps(pinpoint_service.NewJob(pinpoint_params))) |
| 64 |
| 65 |
47 def ParseMetricParts(test_path_parts): | 66 def ParseMetricParts(test_path_parts): |
48 metric_parts = test_path_parts[3:] | 67 metric_parts = test_path_parts[3:] |
49 | 68 |
50 # Normal test path structure, ie. M/B/S/foo/bar.html | 69 # Normal test path structure, ie. M/B/S/foo/bar.html |
51 if len(metric_parts) == 2: | 70 if len(metric_parts) == 2: |
52 return '', metric_parts[0], metric_parts[1] | 71 return '', metric_parts[0], metric_parts[1] |
53 | 72 |
54 # 3 part structure, so there's a TIR label in there. | 73 # 3 part structure, so there's a TIR label in there. |
55 # ie. M/B/S/timeToFirstMeaningfulPaint_avg/load_tools/load_tools_weather | 74 # ie. M/B/S/timeToFirstMeaningfulPaint_avg/load_tools/load_tools_weather |
56 if len(metric_parts) == 3: | 75 if len(metric_parts) == 3: |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 def ParseTIRLabelChartNameAndTraceName(test_path_parts): | 107 def ParseTIRLabelChartNameAndTraceName(test_path_parts): |
89 """Returns tir_label, chart_name, trace_name from a test path.""" | 108 """Returns tir_label, chart_name, trace_name from a test path.""" |
90 test = ndb.Key('TestMetadata', '/'.join(test_path_parts)).get() | 109 test = ndb.Key('TestMetadata', '/'.join(test_path_parts)).get() |
91 | 110 |
92 tir_label, chart_name, trace_name = ParseMetricParts(test_path_parts) | 111 tir_label, chart_name, trace_name = ParseMetricParts(test_path_parts) |
93 if trace_name and test.unescaped_story_name: | 112 if trace_name and test.unescaped_story_name: |
94 trace_name = test.unescaped_story_name | 113 trace_name = test.unescaped_story_name |
95 return tir_label, chart_name, trace_name | 114 return tir_label, chart_name, trace_name |
96 | 115 |
97 | 116 |
| 117 def _BotDimensionsFromBotName(bot_name): |
| 118 bots_to_dimensions = namespaced_stored_object.Get(_BOTS_TO_DIMENSIONS) |
| 119 dimensions = bots_to_dimensions.get(bot_name) |
| 120 if not dimensions: |
| 121 raise InvalidParamsError('No dimensions for bot %s defined.' % bot_name) |
| 122 return dimensions |
| 123 |
| 124 |
| 125 def PinpointParamsFromPerfTryParams(params): |
| 126 """Takes parameters from Dashboard's pinpoint-perf-job-dialog and returns |
| 127 a dict with parameters for a new Pinpoint job. |
| 128 |
| 129 Args: |
| 130 params: A dict in the following format: |
| 131 { |
| 132 'test_path': Test path for the metric being bisected. |
| 133 'start_commit': Git hash or commit position of earlier revision. |
| 134 'end_commit': Git hash or commit position of later revision. |
| 135 'start_repository': Repository for earlier revision. |
| 136 'end_repository': Repository for later revision. |
| 137 'extra_telemetry_args': Extra args for telemetry. |
| 138 } |
| 139 |
| 140 Returns: |
| 141 A dict of params for passing to Pinpoint to start a job, or a dict with an |
| 142 'error' field. |
| 143 """ |
| 144 if not utils.IsValidSheriffUser(): |
| 145 user = users.get_current_user() |
| 146 raise InvalidParamsError('User "%s" not authorized.' % user) |
| 147 |
| 148 # Pinpoint takes swarming dimensions, so we need to map bot name to those. |
| 149 test_path = params['test_path'] |
| 150 test_path_parts = test_path.split('/') |
| 151 bot_name = test_path_parts[1] |
| 152 suite = test_path_parts[2] |
| 153 |
| 154 dimensions = _BotDimensionsFromBotName(bot_name) |
| 155 |
| 156 # Pinpoint also requires you specify which isolate target to run the |
| 157 # test, so we derive that from the suite name. Eventually, this would |
| 158 # ideally be stored in a SparesDiagnostic but for now we can guess. |
| 159 target = 'telemetry_perf_tests' |
| 160 if suite in _ISOLATE_TARGETS: |
| 161 raise InvalidParamsError('Only telemetry is supported at the moment.') |
| 162 elif 'webview' in bot_name: |
| 163 target = 'telemetry_perf_webview_tests' |
| 164 |
| 165 start_repository = params['start_repository'] |
| 166 end_repository = params['end_repository'] |
| 167 start_commit = params['start_commit'] |
| 168 end_commit = params['end_commit'] |
| 169 |
| 170 start_git_hash = ResolveToGitHash(start_commit, start_repository) |
| 171 end_git_hash = ResolveToGitHash(end_commit, end_repository) |
| 172 |
| 173 supported_repositories = namespaced_stored_object.Get(_PINPOINT_REPOSITORIES) |
| 174 |
| 175 # Bail if it's not a supported repository to bisect on |
| 176 if not start_repository in supported_repositories: |
| 177 raise InvalidParamsError('Invalid repository: %s' % start_repository) |
| 178 if not end_repository in supported_repositories: |
| 179 raise InvalidParamsError('Invalid repository: %s' % end_repository) |
| 180 |
| 181 # Pinpoint only supports chromium at the moment, so just throw up a |
| 182 # different error for now. |
| 183 if start_repository != 'chromium' or end_repository != 'chromium': |
| 184 raise InvalidParamsError('Only chromium perf try jobs supported currently.') |
| 185 |
| 186 extra_telemetry_args = params['extra_telemetry_args'] |
| 187 |
| 188 email = users.get_current_user().email() |
| 189 job_name = 'Job on [%s/%s] for [%s]' % (bot_name, suite, email) |
| 190 |
| 191 browser = start_try_job.GuessBrowserName(bot_name) |
| 192 |
| 193 return { |
| 194 'configuration': bot_name, |
| 195 'browser': browser, |
| 196 'benchmark': suite, |
| 197 'trace': '', |
| 198 'chart': '', |
| 199 'tir_label': '', |
| 200 'story': '', |
| 201 'start_repository': start_repository, |
| 202 'end_repository': end_repository, |
| 203 'start_git_hash': start_git_hash, |
| 204 'end_git_hash': end_git_hash, |
| 205 'extra_telemetry_args': json.dumps(extra_telemetry_args), |
| 206 'bug_id': '', |
| 207 'auto_explore': '0', |
| 208 'target': target, |
| 209 'dimensions': json.dumps(dimensions), |
| 210 'email': email, |
| 211 'name': job_name |
| 212 } |
| 213 |
| 214 |
98 def PinpointParamsFromBisectParams(params): | 215 def PinpointParamsFromBisectParams(params): |
99 """Takes parameters from Dashboard's pinpoint-job-dialog and returns | 216 """Takes parameters from Dashboard's pinpoint-job-dialog and returns |
100 a dict with parameters for a new Pinpoint job. | 217 a dict with parameters for a new Pinpoint job. |
101 | 218 |
102 Args: | 219 Args: |
103 params: A dict in the following format: | 220 params: A dict in the following format: |
104 { | 221 { |
105 'test_path': Test path for the metric being bisected. | 222 'test_path': Test path for the metric being bisected. |
106 'start_git_hash': Git hash of earlier revision. | 223 'start_git_hash': Git hash of earlier revision. |
107 'end_git_hash': Git hash of later revision. | 224 'end_git_hash': Git hash of later revision. |
108 'start_repository': Repository for earlier revision. | 225 'start_repository': Repository for earlier revision. |
109 'end_repository': Repository for later revision. | 226 'end_repository': Repository for later revision. |
110 'bug_id': Associated bug. | 227 'bug_id': Associated bug. |
111 } | 228 } |
112 | 229 |
113 Returns: | 230 Returns: |
114 A dict of params for passing to Pinpoint to start a job, or a dict with an | 231 A dict of params for passing to Pinpoint to start a job, or a dict with an |
115 'error' field. | 232 'error' field. |
116 """ | 233 """ |
117 if not utils.IsValidSheriffUser(): | 234 if not utils.IsValidSheriffUser(): |
118 user = users.get_current_user() | 235 user = users.get_current_user() |
119 raise InvalidParamsError('User "%s" not authorized.' % user) | 236 raise InvalidParamsError('User "%s" not authorized.' % user) |
120 | 237 |
121 bots_to_dimensions = namespaced_stored_object.Get(_BOTS_TO_DIMENSIONS) | |
122 | |
123 # Pinpoint takes swarming dimensions, so we need to map bot name to those. | 238 # Pinpoint takes swarming dimensions, so we need to map bot name to those. |
124 test_path = params['test_path'] | 239 test_path = params['test_path'] |
125 test_path_parts = test_path.split('/') | 240 test_path_parts = test_path.split('/') |
126 bot_name = test_path_parts[1] | 241 bot_name = test_path_parts[1] |
127 suite = test_path_parts[2] | 242 suite = test_path_parts[2] |
128 story_filter = params['story_filter'] | 243 story_filter = params['story_filter'] |
129 | 244 |
130 # If functional bisects are speciied, Pinpoint expects these parameters to be | 245 # If functional bisects are speciied, Pinpoint expects these parameters to be |
131 # empty. | 246 # empty. |
132 bisect_mode = params['bisect_mode'] | 247 bisect_mode = params['bisect_mode'] |
133 if bisect_mode != 'performance' and bisect_mode != 'functional': | 248 if bisect_mode != 'performance' and bisect_mode != 'functional': |
134 raise InvalidParamsError('Invalid bisect mode %s specified.' % bisect_mode) | 249 raise InvalidParamsError('Invalid bisect mode %s specified.' % bisect_mode) |
135 | 250 |
136 tir_label = '' | 251 tir_label = '' |
137 chart_name = '' | 252 chart_name = '' |
138 trace_name = '' | 253 trace_name = '' |
139 if bisect_mode == 'performance': | 254 if bisect_mode == 'performance': |
140 tir_label, chart_name, trace_name = ParseTIRLabelChartNameAndTraceName( | 255 tir_label, chart_name, trace_name = ParseTIRLabelChartNameAndTraceName( |
141 test_path_parts) | 256 test_path_parts) |
142 | 257 |
143 dimensions = bots_to_dimensions.get(bot_name) | 258 dimensions = _BotDimensionsFromBotName(bot_name) |
144 if not dimensions: | |
145 raise InvalidParamsError('No dimensions for bot %s defined.' % bot_name) | |
146 | 259 |
147 # Pinpoint also requires you specify which isolate target to run the | 260 # Pinpoint also requires you specify which isolate target to run the |
148 # test, so we derive that from the suite name. Eventually, this would | 261 # test, so we derive that from the suite name. Eventually, this would |
149 # ideally be stored in a SparesDiagnostic but for now we can guess. | 262 # ideally be stored in a SparesDiagnostic but for now we can guess. |
150 isolate_targets = [ | |
151 'angle_perftests', 'cc_perftests', 'gpu_perftests', | |
152 'load_library_perf_tests', 'media_perftests', 'net_perftests', | |
153 'performance_browser_tests', 'telemetry_perf_tests', | |
154 'telemetry_perf_webview_tests', 'tracing_perftests'] | |
155 | |
156 target = 'telemetry_perf_tests' | 263 target = 'telemetry_perf_tests' |
157 if suite in isolate_targets: | 264 if suite in _ISOLATE_TARGETS: |
158 target = suite | 265 target = suite |
159 elif 'webview' in bot_name: | 266 elif 'webview' in bot_name: |
160 target = 'telemetry_perf_webview_tests' | 267 target = 'telemetry_perf_webview_tests' |
161 | 268 |
162 start_repository = params['start_repository'] | 269 start_repository = params['start_repository'] |
163 end_repository = params['end_repository'] | 270 end_repository = params['end_repository'] |
164 start_commit = params['start_commit'] | 271 start_commit = params['start_commit'] |
165 end_commit = params['end_commit'] | 272 end_commit = params['end_commit'] |
166 | 273 |
167 start_git_hash = ResolveToGitHash(start_commit, start_repository) | 274 start_git_hash = ResolveToGitHash(start_commit, start_repository) |
(...skipping 29 matching lines...) Expand all Loading... |
197 'end_repository': end_repository, | 304 'end_repository': end_repository, |
198 'start_git_hash': start_git_hash, | 305 'start_git_hash': start_git_hash, |
199 'end_git_hash': end_git_hash, | 306 'end_git_hash': end_git_hash, |
200 'bug_id': params['bug_id'], | 307 'bug_id': params['bug_id'], |
201 'auto_explore': '1', | 308 'auto_explore': '1', |
202 'target': target, | 309 'target': target, |
203 'dimensions': json.dumps(dimensions), | 310 'dimensions': json.dumps(dimensions), |
204 'email': email, | 311 'email': email, |
205 'name': job_name | 312 'name': job_name |
206 } | 313 } |
OLD | NEW |