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 for adding new histograms to the dashboard.""" | 5 """URL endpoint for adding new histograms to the dashboard.""" |
6 | 6 |
7 import json | 7 import json |
8 import sys | 8 import sys |
9 | 9 |
10 from google.appengine.api import taskqueue | 10 from google.appengine.api import taskqueue |
11 from google.appengine.ext import ndb | 11 from google.appengine.ext import ndb |
12 | 12 |
13 from dashboard import add_point_queue | 13 from dashboard import add_point_queue |
14 from dashboard.api import api_request_handler | 14 from dashboard.api import api_request_handler |
15 from dashboard.common import datastore_hooks | 15 from dashboard.common import datastore_hooks |
16 from dashboard.common import stored_object | 16 from dashboard.common import stored_object |
17 from dashboard.models import histogram | 17 from dashboard.models import histogram |
18 from tracing.value import histogram as histogram_module | |
19 from tracing.value import histogram_set | 18 from tracing.value import histogram_set |
20 from tracing.value.diagnostics import diagnostic | 19 from tracing.value.diagnostics import diagnostic |
21 from tracing.value.diagnostics import reserved_infos | 20 from tracing.value.diagnostics import reserved_infos |
22 | 21 |
23 SUITE_LEVEL_SPARSE_DIAGNOSTIC_NAMES = set( | 22 SUITE_LEVEL_SPARSE_DIAGNOSTIC_NAMES = set([ |
24 [reserved_infos.ARCHITECTURES.name, | 23 reserved_infos.ARCHITECTURES.name, |
25 reserved_infos.BUG_COMPONENTS.name, | 24 reserved_infos.BENCHMARKS.name, |
26 reserved_infos.GPUS.name, | 25 reserved_infos.BOTS.name, |
27 reserved_infos.MEMORY_AMOUNTS.name, | 26 reserved_infos.BUG_COMPONENTS.name, |
28 reserved_infos.OS_NAMES.name, | 27 reserved_infos.GPUS.name, |
29 reserved_infos.OS_VERSIONS.name, | 28 reserved_infos.MASTERS.name, |
30 reserved_infos.OWNERS.name, | 29 reserved_infos.MEMORY_AMOUNTS.name, |
31 reserved_infos.PRODUCT_VERSIONS.name]) | 30 reserved_infos.OS_NAMES.name, |
32 # TODO(#3507): Make BuildbotInfo into GenericSet diagnostics and remove all | 31 reserved_infos.OS_VERSIONS.name, |
33 # logic regarding picking diagnostics by type. | 32 reserved_infos.OWNERS.name, |
34 SUITE_LEVEL_SPARSE_DIAGNOSTIC_TYPES = set( | 33 reserved_infos.PRODUCT_VERSIONS.name, |
35 [histogram_module.BuildbotInfo]) | 34 reserved_infos.TAG_MAP.name, |
36 HISTOGRAM_LEVEL_SPARSE_DIAGNOSTIC_TYPES = set( | 35 ]) |
37 [histogram_module.TelemetryInfo]) | 36 |
38 SPARSE_DIAGNOSTIC_TYPES = SUITE_LEVEL_SPARSE_DIAGNOSTIC_TYPES.union( | 37 HISTOGRAM_LEVEL_SPARSE_DIAGNOSTIC_NAMES = set([ |
39 HISTOGRAM_LEVEL_SPARSE_DIAGNOSTIC_TYPES) | 38 reserved_infos.GPUS.name, |
| 39 reserved_infos.MEMORY_AMOUNTS.name, |
| 40 reserved_infos.PRODUCT_VERSIONS.name, |
| 41 reserved_infos.RELATED_NAMES.name, |
| 42 reserved_infos.STORIES.name, |
| 43 reserved_infos.STORYSET_REPEATS.name, |
| 44 reserved_infos.STORY_TAGS.name, |
| 45 ]) |
| 46 |
| 47 SPARSE_DIAGNOSTIC_NAMES = SUITE_LEVEL_SPARSE_DIAGNOSTIC_NAMES.union( |
| 48 HISTOGRAM_LEVEL_SPARSE_DIAGNOSTIC_NAMES) |
40 | 49 |
41 | 50 |
42 TASK_QUEUE_NAME = 'histograms-queue' | 51 TASK_QUEUE_NAME = 'histograms-queue' |
43 | 52 |
44 | 53 |
45 def _CheckRequest(condition, msg): | 54 def _CheckRequest(condition, msg): |
46 if not condition: | 55 if not condition: |
47 raise api_request_handler.BadRequestError(msg) | 56 raise api_request_handler.BadRequestError(msg) |
48 | 57 |
49 | 58 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 for hist in histograms: | 93 for hist in histograms: |
85 for name, diag in hist.diagnostics.iteritems(): | 94 for name, diag in hist.diagnostics.iteritems(): |
86 if name in SUITE_LEVEL_SPARSE_DIAGNOSTIC_NAMES: | 95 if name in SUITE_LEVEL_SPARSE_DIAGNOSTIC_NAMES: |
87 if diagnostic_names_added.get(name) is None: | 96 if diagnostic_names_added.get(name) is None: |
88 diagnostic_names_added[name] = diag.guid | 97 diagnostic_names_added[name] = diag.guid |
89 | 98 |
90 if diagnostic_names_added.get(name) != diag.guid: | 99 if diagnostic_names_added.get(name) != diag.guid: |
91 raise ValueError( | 100 raise ValueError( |
92 name + ' diagnostics must be the same for all histograms') | 101 name + ' diagnostics must be the same for all histograms') |
93 | 102 |
94 if (name in SUITE_LEVEL_SPARSE_DIAGNOSTIC_NAMES or | 103 if name in SUITE_LEVEL_SPARSE_DIAGNOSTIC_NAMES: |
95 type(diag) in SUITE_LEVEL_SPARSE_DIAGNOSTIC_TYPES): | |
96 suite_level_sparse_diagnostic_entities.append( | 104 suite_level_sparse_diagnostic_entities.append( |
97 histogram.SparseDiagnostic( | 105 histogram.SparseDiagnostic( |
98 id=diag.guid, data=diag.AsDict(), test=suite_key, | 106 id=diag.guid, data=diag.AsDict(), test=suite_key, |
99 start_revision=revision, end_revision=sys.maxint, name=name)) | 107 start_revision=revision, end_revision=sys.maxint, name=name)) |
100 | 108 |
101 # TODO(eakuefner): Refactor master/bot computation to happen above this line | 109 # TODO(eakuefner): Refactor master/bot computation to happen above this line |
102 # so that we can replace with a DiagnosticRef rather than a full diagnostic. | 110 # so that we can replace with a DiagnosticRef rather than a full diagnostic. |
103 new_guids_to_old_diagnostics = DeduplicateAndPut( | 111 new_guids_to_old_diagnostics = DeduplicateAndPut( |
104 suite_level_sparse_diagnostic_entities, suite_key, revision) | 112 suite_level_sparse_diagnostic_entities, suite_key, revision) |
105 for new_guid, old_diagnostic in new_guids_to_old_diagnostics.iteritems(): | 113 for new_guid, old_diagnostic in new_guids_to_old_diagnostics.iteritems(): |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
168 | 176 |
169 | 177 |
170 def _IsDifferent(diagnostic_a, diagnostic_b): | 178 def _IsDifferent(diagnostic_a, diagnostic_b): |
171 return (diagnostic.Diagnostic.FromDict(diagnostic_a) != | 179 return (diagnostic.Diagnostic.FromDict(diagnostic_a) != |
172 diagnostic.Diagnostic.FromDict(diagnostic_b)) | 180 diagnostic.Diagnostic.FromDict(diagnostic_b)) |
173 | 181 |
174 | 182 |
175 def FindHistogramLevelSparseDiagnostics(guid, histograms): | 183 def FindHistogramLevelSparseDiagnostics(guid, histograms): |
176 hist = histograms.LookupHistogram(guid) | 184 hist = histograms.LookupHistogram(guid) |
177 diagnostics = [] | 185 diagnostics = [] |
178 for diag in hist.diagnostics.itervalues(): | 186 for name, diag in hist.diagnostics.iteritems(): |
179 if type(diag) in HISTOGRAM_LEVEL_SPARSE_DIAGNOSTIC_TYPES: | 187 if name in HISTOGRAM_LEVEL_SPARSE_DIAGNOSTIC_NAMES: |
180 diagnostics.append(diag) | 188 diagnostics.append(diag) |
181 return diagnostics | 189 return diagnostics |
182 | 190 |
183 | 191 |
184 def GetSuiteKey(histograms): | 192 def GetSuiteKey(histograms): |
185 assert len(histograms) > 0 | 193 assert len(histograms) > 0 |
186 # TODO(eakuefner): Refactor this to coalesce the boilerplate (note that this | 194 # TODO(eakuefner): Refactor this to coalesce the boilerplate (note that this |
187 # is all also being done in add_histograms_queue's post handler) | 195 # is all also being done in add_histograms_queue's post handler) |
188 master, bot, benchmark = _GetMasterBotBenchmarkFromHistogram( | 196 master, bot, benchmark = _GetMasterBotBenchmarkFromHistogram( |
189 histograms.GetFirstHistogram()) | 197 histograms.GetFirstHistogram()) |
190 bot_whitelist = stored_object.Get(add_point_queue.BOT_WHITELIST_KEY) | 198 bot_whitelist = stored_object.Get(add_point_queue.BOT_WHITELIST_KEY) |
191 internal_only = add_point_queue.BotInternalOnly(bot, bot_whitelist) | 199 internal_only = add_point_queue.BotInternalOnly(bot, bot_whitelist) |
192 return add_point_queue.GetOrCreateAncestors( | 200 return add_point_queue.GetOrCreateAncestors( |
193 master, bot, benchmark, internal_only).key | 201 master, bot, benchmark, internal_only).key |
194 | 202 |
195 | 203 |
196 def ComputeTestPath(guid, histograms): | 204 def ComputeTestPath(guid, histograms): |
197 hist = histograms.LookupHistogram(guid) | 205 hist = histograms.LookupHistogram(guid) |
198 suite_path = '%s/%s/%s' % _GetMasterBotBenchmarkFromHistogram(hist) | 206 suite_path = '%s/%s/%s' % _GetMasterBotBenchmarkFromHistogram(hist) |
199 telemetry_info = hist.diagnostics[reserved_infos.TELEMETRY.name] | |
200 story_display_name = telemetry_info.story_display_name | |
201 | |
202 path = '%s/%s' % (suite_path, hist.name) | 207 path = '%s/%s' % (suite_path, hist.name) |
203 | 208 |
204 if story_display_name != '': | 209 story_name = hist.diagnostics.get(reserved_infos.STORIES.name) |
205 path += '/%s' % story_display_name | 210 if story_name and len(story_name) == 1: |
| 211 path += '/' + list(story_name)[0] |
206 | 212 |
207 return path | 213 return path |
208 | 214 |
209 | 215 |
210 def _GetMasterBotBenchmarkFromHistogram(hist): | 216 def _GetMasterBotBenchmarkFromHistogram(hist): |
211 _CheckRequest(reserved_infos.BUILDBOT.name in hist.diagnostics, | |
212 'Histograms must have BuildbotInfo attached') | |
213 buildbot_info = hist.diagnostics[reserved_infos.BUILDBOT.name] | |
214 _CheckRequest( | 217 _CheckRequest( |
215 reserved_infos.TELEMETRY.name in hist.diagnostics, | 218 reserved_infos.MASTERS.name in hist.diagnostics, |
216 'Histograms must have TelemetryInfo attached') | 219 'Histograms must have "%s" diagnostic' % reserved_infos.MASTERS.name) |
217 telemetry_info = hist.diagnostics[reserved_infos.TELEMETRY.name] | 220 master = hist.diagnostics[reserved_infos.MASTERS.name] |
| 221 _CheckRequest( |
| 222 len(master) == 1, |
| 223 'Histograms must have exactly 1 "%s"' % reserved_infos.MASTERS.name) |
| 224 master = list(master)[0] |
218 | 225 |
219 master = buildbot_info.display_master_name | 226 _CheckRequest( |
220 bot = buildbot_info.display_bot_name | 227 reserved_infos.BOTS.name in hist.diagnostics, |
221 benchmark = telemetry_info.benchmark_name | 228 'Histograms must have "%s" diagnostic' % reserved_infos.BOTS.name) |
| 229 bot = hist.diagnostics[reserved_infos.BOTS.name] |
| 230 _CheckRequest( |
| 231 len(bot) == 1, |
| 232 'Histograms must have exactly 1 "%s"' % reserved_infos.BOTS.name) |
| 233 bot = list(bot)[0] |
| 234 |
| 235 _CheckRequest( |
| 236 reserved_infos.BENCHMARKS.name in hist.diagnostics, |
| 237 'Histograms must have "%s" diagnostic' % reserved_infos.BENCHMARKS.name) |
| 238 benchmark = hist.diagnostics[reserved_infos.BENCHMARKS.name] |
| 239 _CheckRequest( |
| 240 len(benchmark) == 1, |
| 241 'Histograms must have exactly 1 "%s"' % reserved_infos.BENCHMARKS.name) |
| 242 benchmark = list(benchmark)[0] |
222 | 243 |
223 return master, bot, benchmark | 244 return master, bot, benchmark |
224 | 245 |
225 | 246 |
226 def ComputeRevision(histograms): | 247 def ComputeRevision(histograms): |
227 _CheckRequest(len(histograms) > 0, 'Must upload at least one histogram') | 248 _CheckRequest(len(histograms) > 0, 'Must upload at least one histogram') |
228 diagnostics = histograms.GetFirstHistogram().diagnostics | 249 diagnostics = histograms.GetFirstHistogram().diagnostics |
229 _CheckRequest(reserved_infos.CHROMIUM_COMMIT_POSITIONS.name in diagnostics, | 250 _CheckRequest(reserved_infos.CHROMIUM_COMMIT_POSITIONS.name in diagnostics, |
230 'Histograms must have Chromium commit position attached') | 251 'Histograms must have Chromium commit position attached') |
231 chromium_commit_position = list(diagnostics[ | 252 chromium_commit_position = list(diagnostics[ |
232 reserved_infos.CHROMIUM_COMMIT_POSITIONS.name]) | 253 reserved_infos.CHROMIUM_COMMIT_POSITIONS.name]) |
233 | 254 |
234 _CheckRequest(len(chromium_commit_position) == 1, | 255 _CheckRequest(len(chromium_commit_position) == 1, |
235 'Chromium commit position must have 1 value') | 256 'Chromium commit position must have 1 value') |
236 | 257 |
237 # TODO(eakuefner): Allow users to specify other types of revisions to be used | 258 # TODO(eakuefner): Allow users to specify other types of revisions to be used |
238 # for computing revisions of dashboard points. See | 259 # for computing revisions of dashboard points. See |
239 # https://github.com/catapult-project/catapult/issues/3623. | 260 # https://github.com/catapult-project/catapult/issues/3623. |
240 return chromium_commit_position[0] | 261 return chromium_commit_position[0] |
241 | 262 |
242 | 263 |
243 def InlineDenseSharedDiagnostics(histograms): | 264 def InlineDenseSharedDiagnostics(histograms): |
244 # TODO(eakuefner): Delete inlined diagnostics from the set | 265 # TODO(eakuefner): Delete inlined diagnostics from the set |
245 for hist in histograms: | 266 for hist in histograms: |
246 diagnostics = hist.diagnostics | 267 diagnostics = hist.diagnostics |
247 for name, diag in diagnostics.iteritems(): | 268 for name, diag in diagnostics.iteritems(): |
248 if (type(diag) not in SPARSE_DIAGNOSTIC_TYPES and | 269 if name not in SPARSE_DIAGNOSTIC_NAMES: |
249 name not in SUITE_LEVEL_SPARSE_DIAGNOSTIC_NAMES): | |
250 diag.Inline() | 270 diag.Inline() |
OLD | NEW |