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

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

Issue 2455053004: Moving ScheduleNewAnalysis to break the cycle (Closed)
Patch Set: 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 crash.culprit import NullCulprit 15 from crash.culprit import NullCulprit
16 from model import analysis_status 16 from model import analysis_status
17 from model.crash.crash_config import CrashConfig 17 from model.crash.crash_config import CrashConfig
18 18
19 # TODO(http://crbug.com/659346): since most of our unit tests are 19 # TODO(http://crbug.com/659346): since most of our unit tests are
20 # FinditForFracas-specific, wrengr moved them to findit_for_chromecrash_test.py. 20 # FinditForFracas-specific, wrengr moved them to findit_for_chromecrash_test.py.
21 # However, now we're missing coverage for most of this file (due to the 21 # However, now we're missing coverage for most of this file (due to the
22 # buggy way coverage is computed). Need to add a bunch of new unittests 22 # buggy way coverage is computed). Need to add a bunch of new unittests
23 # to get coverage back up. 23 # to get coverage back up.
24 24
25 # TODO: this class depends on ndb stuff, and should therefore move to 25 # TODO: this class depends on ndb stuff, and should therefore move to
26 # cr-culprit-finder/service/predator as part of the big reorganization. 26 # cr-culprit-finder/service/predator as part of the big reorganization.
27 # Also, this class should be renamed to "PredatorApp" (alas, "Azalea" 27 # Also, this class should be renamed to "PredatorApp" (alas, "Azalea"
28 # was renamed to "Predator"). 28 # was renamed to "Predator").
29 class Findit(object): 29 class Findit(object):
30 def __init__(self, repository, pipeline_cls): 30 def __init__(self, repository):
31 """ 31 """
32 Args: 32 Args:
33 repository (Repository): the Git repository for getting CLs to classify. 33 repository (Repository): the Git repository for getting CLs to classify.
34 pipeline_cls (class): the class for constructing pipelines in
35 ScheduleNewAnalysis. This will almost surely be
36 |crash.crash_pipeline.CrashWrapperPipeline|; but we must pass
37 the class in as a parameter in order to break an import cycle.
38 """ 34 """
39 self._repository = repository 35 self._repository = repository
40 # TODO(http://crbug.com/659354): because self.client is volatile, 36 # TODO(http://crbug.com/659354): because self.client is volatile,
41 # we need some way of updating the Azelea instance whenever the 37 # we need some way of updating the Azelea instance whenever the
42 # config changes. How to do that cleanly? 38 # config changes. How to do that cleanly?
43 self._azalea = None 39 self._azalea = None
44 self._stacktrace_parser = None 40 self._stacktrace_parser = None
45 self._pipeline_cls = pipeline_cls
46 41
47 # This is a class method because it should be the same for all 42 # This is a class method because it should be the same for all
48 # instances of this class. We can in fact call class methods on 43 # instances of this class. We can in fact call class methods on
49 # instances (not just on the class itself), so we could in principle 44 # instances (not just on the class itself), so we could in principle
50 # get by with just this method. However, a @classmethod is treated 45 # get by with just this method. However, a @classmethod is treated
51 # syntactically like a method, thus we'd need to have the |()| at the 46 # syntactically like a method, thus we'd need to have the |()| at the
52 # end, unlike for a @property. Thus we have both the class method and 47 # end, unlike for a @property. Thus we have both the class method and
53 # the property, in order to simulate a class property. 48 # the property, in order to simulate a class property.
54 @classmethod 49 @classmethod
55 def _ClientID(cls): # pragma: no cover 50 def _ClientID(cls): # pragma: no cover
(...skipping 130 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 30 matching lines...) Expand all
280 # TODO(http://crbug.com/659359): refactor things so we don't need 231 # TODO(http://crbug.com/659359): refactor things so we don't need
281 # the NullCulprit class. 232 # the NullCulprit class.
282 return NullCulprit() 233 return NullCulprit()
283 234
284 return self._azalea.FindCulprit(CrashReport( 235 return self._azalea.FindCulprit(CrashReport(
285 crashed_version = model.crashed_version, 236 crashed_version = model.crashed_version,
286 signature = model.signature, 237 signature = model.signature,
287 platform = model.platform, 238 platform = model.platform,
288 stacktrace = stacktrace, 239 stacktrace = stacktrace,
289 regression_range = model.regression_range)) 240 regression_range = model.regression_range))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698