Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(874)

Side by Side Diff: appengine/findit/crash/findit.py

Issue 2455053004: Moving ScheduleNewAnalysis to break the cycle (Closed)
Patch Set: rebase Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2016 The Chromium Authors. All rights reserved. 1 # Copyright 2016 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 import copy 5 import copy
6 import logging 6 import logging
7 7
8 from google.appengine.ext import ndb 8 from google.appengine.ext import ndb
9 9
10 from common import appengine_util 10 from common import appengine_util
11 from common import chrome_dependency_fetcher 11 from common import chrome_dependency_fetcher
12 from common import constants 12 from common import constants
13 from common import time_util 13 from common import time_util
14 from crash.crash_report import CrashReport 14 from crash.crash_report import CrashReport
15 from model import analysis_status 15 from model import analysis_status
16 from model.crash.crash_config import CrashConfig 16 from model.crash.crash_config import CrashConfig
17 17
18 # TODO(http://crbug.com/659346): since most of our unit tests are 18 # TODO(http://crbug.com/659346): since most of our unit tests are
19 # FinditForFracas-specific, wrengr moved them to findit_for_chromecrash_test.py. 19 # FinditForFracas-specific, wrengr moved them to findit_for_chromecrash_test.py.
20 # However, now we're missing coverage for most of this file (due to the 20 # However, now we're missing coverage for most of this file (due to the
21 # buggy way coverage is computed). Need to add a bunch of new unittests 21 # buggy way coverage is computed). Need to add a bunch of new unittests
22 # to get coverage back up. 22 # to get coverage back up.
23 23
24 # TODO: this class depends on ndb stuff, and should therefore move to 24 # TODO: this class depends on ndb stuff, and should therefore move to
25 # cr-culprit-finder/service/predator as part of the big reorganization. 25 # cr-culprit-finder/service/predator as part of the big reorganization.
26 # This class should be renamed to avoid confustion between Findit and Predator. 26 # This class should be renamed to avoid confustion between Findit and Predator.
27 # Think of a good name (e.g.'PredatorApp') for this class. 27 # Think of a good name (e.g.'PredatorApp') for this class.
28 class Findit(object): 28 class Findit(object):
29 def __init__(self, repository, pipeline_cls): 29 def __init__(self, repository):
30 """ 30 """
31 Args: 31 Args:
32 repository (Repository): the Git repository for getting CLs to classify. 32 repository (Repository): the Git repository for getting CLs to classify.
33 pipeline_cls (class): the class for constructing pipelines in
34 ScheduleNewAnalysis. This will almost surely be
35 ``crash.crash_pipeline.CrashWrapperPipeline``; but we must pass
36 the class in as a parameter in order to break an import cycle.
37 """ 33 """
38 self._repository = repository 34 self._repository = repository
39 # TODO(http://crbug.com/659354): because self.client is volatile, 35 # TODO(http://crbug.com/659354): because self.client is volatile,
40 # we need some way of updating the Azelea instance whenever the 36 # we need some way of updating the Azelea instance whenever the
41 # config changes. How to do that cleanly? 37 # config changes. How to do that cleanly?
42 self._predator = None 38 self._predator = None
43 self._stacktrace_parser = None 39 self._stacktrace_parser = None
44 self._pipeline_cls = pipeline_cls
45 40
46 # This is a class method because it should be the same for all 41 # This is a class method because it should be the same for all
47 # instances of this class. We can in fact call class methods on 42 # instances of this class. We can in fact call class methods on
48 # instances (not just on the class itself), so we could in principle 43 # instances (not just on the class itself), so we could in principle
49 # get by with just this method. However, a @classmethod is treated 44 # get by with just this method. However, a @classmethod is treated
50 # syntactically like a method, thus we'd need to have the ``()`` at the 45 # syntactically like a method, thus we'd need to have the ``()`` at the
51 # end, unlike for a @property. Thus we have both the class method and 46 # end, unlike for a @property. Thus we have both the class method and
52 # the property, in order to simulate a class property. 47 # the property, in order to simulate a class property.
53 @classmethod 48 @classmethod
54 def _ClientID(cls): # pragma: no cover 49 def _ClientID(cls): # pragma: no cover
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
186 model.regression_range = crash_data.get('regression_range', None) 181 model.regression_range = crash_data.get('regression_range', None)
187 182
188 # Set progress properties. 183 # Set progress properties.
189 model.status = analysis_status.PENDING 184 model.status = analysis_status.PENDING
190 model.requested_time = time_util.GetUTCNow() 185 model.requested_time = time_util.GetUTCNow()
191 186
192 @ndb.transactional 187 @ndb.transactional
193 def _NeedsNewAnalysis(self, crash_data): 188 def _NeedsNewAnalysis(self, crash_data):
194 raise NotImplementedError() 189 raise NotImplementedError()
195 190
196 # TODO(http://crbug.com/659345): try to break the cycle re pipeline_cls.
197 # TODO(http://crbug.com/659346): we don't cover anything after the
198 # call to _NeedsNewAnalysis.
199 def ScheduleNewAnalysis(self, crash_data,
200 queue_name=constants.DEFAULT_QUEUE): # pragma: no cover
201 """Create a pipeline object to perform the analysis, and start it.
202
203 If we can detect that the analysis doesn't need to be performed
204 (e.g., it was already performed, or the ``crash_data`` is empty so
205 there's nothig we can do), then we will skip creating the pipeline
206 at all.
207
208 Args:
209 crash_data (JSON): ??
210 queue_name (??): the name of the AppEngine queue we should start
211 the pipeline on.
212
213 Returns:
214 True if we started the pipeline; False otherwise.
215 """
216 # Check policy and tune arguments if needed.
217 crash_data = self.CheckPolicy(crash_data)
218 if crash_data is None:
219 return False
220
221 # Detect the regression range, and decide if we actually need to
222 # run a new anlaysis or not.
223 if not self._NeedsNewAnalysis(crash_data):
224 return False
225
226 crash_identifiers = crash_data['crash_identifiers']
227 # N.B., we cannot pass ``self`` directly to the _pipeline_cls, because
228 # it is not JSON-serializable (and there's no way to make it such,
229 # since JSON-serializability is defined by JSON-encoders rather than
230 # as methods on the objects being encoded).
231 analysis_pipeline = self._pipeline_cls(self.client_id, crash_identifiers)
232 # Attribute defined outside __init__ - pylint: disable=W0201
233 analysis_pipeline.target = appengine_util.GetTargetNameForModule(
234 constants.CRASH_BACKEND[self.client_id])
235 analysis_pipeline.start(queue_name=queue_name)
236 logging.info('New %s analysis is scheduled for %s', self.client_id,
237 repr(crash_identifiers))
238 return True
239
240 # TODO(wrengr): does the parser actually need the version, signature, 191 # TODO(wrengr): does the parser actually need the version, signature,
241 # and platform? If not, then we should be able to just pass the string 192 # and platform? If not, then we should be able to just pass the string
242 # to be parsed (which would make a lot more sense than passing the 193 # to be parsed (which would make a lot more sense than passing the
243 # whole model). 194 # whole model).
244 # TODO(http://crbug.com/659346): coverage tests for this class, not 195 # TODO(http://crbug.com/659346): coverage tests for this class, not
245 # just for FinditForFracas. 196 # just for FinditForFracas.
246 def ParseStacktrace(self, model): # pragma: no cover 197 def ParseStacktrace(self, model): # pragma: no cover
247 """Parse a CrashAnalysis's ``stack_trace`` string into a Stacktrace object. 198 """Parse a CrashAnalysis's ``stack_trace`` string into a Stacktrace object.
248 199
249 Args: 200 Args:
(...skipping 28 matching lines...) Expand all
278 stacktrace = self.ParseStacktrace(model) 229 stacktrace = self.ParseStacktrace(model)
279 if stacktrace is None: 230 if stacktrace is None:
280 return None 231 return None
281 232
282 return self._predator.FindCulprit(CrashReport( 233 return self._predator.FindCulprit(CrashReport(
283 crashed_version = model.crashed_version, 234 crashed_version = model.crashed_version,
284 signature = model.signature, 235 signature = model.signature,
285 platform = model.platform, 236 platform = model.platform,
286 stacktrace = stacktrace, 237 stacktrace = stacktrace,
287 regression_range = model.regression_range)) 238 regression_range = model.regression_range))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698