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

Unified Diff: appengine/findit/crash/findit.py

Issue 2663063007: [Predator] Switch from anonymous dict to CrashData. (Closed)
Patch Set: Rebase and fix delta test. Created 3 years, 10 months 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 side-by-side diff with in-line comments
Download patch
Index: appengine/findit/crash/findit.py
diff --git a/appengine/findit/crash/findit.py b/appengine/findit/crash/findit.py
index a05e8d2fd7513dee1be6a94203230eb59c313f99..9fb3fbaf480f0479993e58d9b9f0f610cb3a2df7 100644
--- a/appengine/findit/crash/findit.py
+++ b/appengine/findit/crash/findit.py
@@ -13,12 +13,10 @@ from common.chrome_dependency_fetcher import ChromeDependencyFetcher
from common import constants
from crash.component import Component
from crash.component_classifier import ComponentClassifier
-from crash.crash_report import CrashReport
from crash.project import Project
from crash.project_classifier import ProjectClassifier
from libs import time_util
from model import analysis_status
-from model.crash.crash_config import CrashConfig
# TODO(http://crbug.com/659346): since most of our unit tests are
@@ -47,7 +45,6 @@ class Findit(object):
classifiers.
"""
self._get_repository = get_repository
- self._dep_fetcher = ChromeDependencyFetcher(get_repository)
# The top_n is the number of frames we want to check to get project
# classifications.
@@ -58,6 +55,8 @@ class Findit(object):
projects, config.project_classifier['top_n'],
config.project_classifier['non_chromium_project_rank_priority'])
+ # The top_n is the number of frames we want to check to get component
+ # classifications.
components = [Component(component_name, path_regex, function_regex)
for path_regex, function_regex, component_name
in config.component_classifier['path_function_component']]
@@ -66,12 +65,6 @@ class Findit(object):
self._config = config
- # TODO(http://crbug.com/659354): because self.client is volatile,
- # we need some way of updating the Predator instance whenever the
- # config changes. How to do that cleanly?
- self._predator = None
- self._stacktrace_parser = None
-
# This is a class method because it should be the same for all
# instances of this class. We can in fact call class methods on
# instances (not just on the class itself), so we could in principle
@@ -98,6 +91,9 @@ class Findit(object):
cls.__name__)
raise NotImplementedError()
+ def _Predator(self):
+ raise NotImplementedError()
+
@property
def client_id(self):
"""Get the client id from the class of this object.
@@ -105,11 +101,6 @@ class Findit(object):
N.B., this property is static and should not be overridden."""
return self._ClientID()
- # TODO(http://crbug.com/659354): can we remove the dependency on
- # CrashConfig entirely? It'd be better to receive method calls
- # whenever things change, so that we know the change happened (and
- # what in particular changed) so that we can update our internal state
- # as appropriate.
@property
def client_config(self):
"""Get the current value of the client config for the class of this object.
@@ -133,129 +124,107 @@ class Findit(object):
# something like that.
def RenamePlatform(self, platform):
"""Remap the platform to a different one, based on the config."""
- # TODO(katesonia): Remove the default value after adding validity check to
- # config.
- return self.client_config.get('platform_rename', {}).get(platform, platform)
+ return self.client_config['platform_rename'].get(platform, platform)
- def CheckPolicy(self, crash_data): # pylint: disable=W0613
- """Check whether this client supports analyzing the given report.
+ # TODO(http://crbug.com/644476): rename this to something like
+ # _NewAnalysis, since it only does the "allocation" and needs to/will
+ # be followed up with _InitializeAnalysis anyways.
+ def CreateAnalysis(self, crash_identifiers): # pragma: no cover
+ raise NotImplementedError()
- Some clients only support analysis for crashes on certain platforms
- or channels, etc. This method checks to see whether this client can
- analyze the given crash. The default implementation on the Findit
- base class returns None for everything, so that unsupported clients
- reject everything, as expected.
+ def GetAnalysis(self, crash_identifiers): # pragma: no cover
+ """Returns the CrashAnalysis for the ``crash_identifiers``, if one exists.
Args:
- crash_data (JSON): ??
+ crash_identifiers (JSON): key, value pairs to uniquely identify a crash.
Returns:
- If satisfied, we return the ``crash_data`` which may have had some
- fields modified. Otherwise returns None.
+ If a CrashAnalysis ndb.Model already exists for the
+ ``crash_identifiers``, then we return it. Otherwise, returns None.
"""
- return None
+ raise NotImplementedError()
- # TODO(http://crbug.com/644476): rename this to something like
- # _NewAnalysis, since it only does the "allocation" and needs to/will
- # be followed up with _InitializeAnalysis anyways.
- def CreateAnalysis(self, crash_identifiers): # pylint: disable=W0613
- return None
+ def GetCrashData(self, raw_crash_data): # pragma: no cover
+ """Gets ``CrashData`` object for raw json crash data from clients.
- def GetAnalysis(self, crash_identifiers): # pylint: disable=W0613
- """Returns the CrashAnalysis for the ``crash_identifiers``, if one exists.
+ Args:
+ crash_data (JSON): Json input message from clients.
+
+ Returns:
+ Parsed ``CrashData`` object.
+ """
+ raise NotImplementedError()
+
+ def _CheckPolicy(self, crash_data): # pylint: disable=W0613
+ """Checks whether this client supports analyzing the given report.
+
+ Some clients only support analysis for crashes on certain platforms
+ or channels, etc. This method checks to see whether this client can
+ analyze the given crash. The default implementation on the Findit
+ base class returns None for everything, so that unsupported clients
+ reject everything, as expected.
Args:
- crash_identifiers (JSON): ??
+ crash_data (CrashData): Parsed crash data from clients.
Returns:
- If a CrashAnalysis ndb.Model already exists for the
- ``crash_identifiers``, then we return it. Otherwise, returns None.
+ Boolean to indicate whether the crash passed the policy check.
"""
- return None
+ if not self.client_config:
+ logging.info('No configuration of client %s, analysis is not supported',
+ self.client_id)
+ return True
- # TODO(wrengr): this should be a method on CrashAnalysis, not on Findit.
- # TODO(http://crbug.com/659346): coverage tests for this class, not
- # just for FinditForFracas.
- def _InitializeAnalysis(self, model, crash_data): # pragma: no cover
- """(Re)Initialize a CrashAnalysis ndb.Model, but do not ``put()`` it yet.
-
- This method is only ever called from _NeedsNewAnalysis which is only
- ever called from ScheduleNewAnalysis. It is used for filling in the
- fields of a CrashAnalysis ndb.Model for the first time (though it
- can also be used to re-initialize a given CrashAnalysis). Subclasses
- should extend (not override) this to (re)initialize any
- client-specific fields they may have."""
- # Get rid of any previous values there may have been.
- model.Reset()
-
- # Set the version.
- # ``handlers.crash.test.crash_handler_test.testAnalysisScheduled``
- # provides and expects this field to be called 'chrome_version',
- # whereas everyone else (e.g., in ``crash.test.crash_pipeline_test``
- # the tests ``testAnalysisNeededIfNoAnalysisYet``,
- # ``testRunningAnalysisNoSuspectsFound``, ``testRunningAnalysis``,
- # ``testAnalysisNeededIfLastOneFailed``,
- # ``testRunningAnalysisWithSuspectsCls``) expects it to be called
- # 'crashed_version'. The latter is the better/more general name,
- # so the former needs to be changed in order to get rid of this
- # defaulting ugliness.
- model.crashed_version = crash_data.get('crashed_version',
- crash_data.get('chrome_version', None))
-
- # Set (other) common properties.
- model.stack_trace = crash_data['stack_trace']
- model.signature = crash_data['signature']
- model.platform = crash_data['platform']
- # TODO(wrengr): The only reason to have _InitializeAnalysis as a
- # method of the Findit class rather than as a method on CrashAnalysis
- # is so we can assert that crash_data['client_id'] == self.client_id.
- # So, either we should do that, or else we should move this to be
- # a method on CrashAnalysis.
- model.client_id = self.client_id
- model.regression_range = crash_data.get('regression_range', None)
-
- # Set progress properties.
- model.status = analysis_status.PENDING
- model.requested_time = time_util.GetUTCNow()
+ for blacklist_marker in self.client_config['signature_blacklist_markers']:
+ if blacklist_marker in crash_data.signature:
+ logging.info('%s signature is not supported.', blacklist_marker)
+ return False
+
+ return True
@ndb.transactional
- def _NeedsNewAnalysis(self, crash_data):
- raise NotImplementedError()
+ def NeedsNewAnalysis(self, crash_data):
+ """Checks if the crash needs new anlysis.
- # TODO(wrengr): does the parser actually need the version, signature,
- # and platform? If not, then we should be able to just pass the string
- # to be parsed (which would make a lot more sense than passing the
- # whole model).
- # TODO(http://crbug.com/659346): coverage tests for this class, not
- # just for FinditForFracas.
- def ParseStacktrace(self, model): # pragma: no cover
- """Parse a CrashAnalysis's ``stack_trace`` string into a Stacktrace object.
+ If a new analysis needs to be scheduled, initialize a ``CrashAnalysis``
+ model using crash_data and put it in datastore for later culprit
+ analyzing.
Args:
- model (CrashAnalysis): The model containing the stack_trace string
- to be parsed.
+ crash_data (CrashData): Parsed crash data that contains all the
+ information about the crash.
Returns:
- On success, returns a Stacktrace object; on failure, returns None.
+ Boolean of whether a new analysis needs to be scheduled.
"""
- # Use up-to-date ``top_n`` in self.client_config to filter top n frames.
- stacktrace = self._stacktrace_parser.Parse(
- model.stack_trace,
- self._dep_fetcher.GetDependency(model.crashed_version, model.platform),
- model.signature,
- self.client_config.get('top_n'))
- if not stacktrace:
- logging.warning('Failed to parse the stacktrace %s', model.stack_trace)
- return None
-
- return stacktrace
-
- def ProcessResultForPublishing(self, result, key):
+ # Check policy and modify the raw_crash_data as needed.
+ if not self._CheckPolicy(crash_data):
+ logging.info('The analysis of %s is not supported, task will not be '
+ 'scheduled.', str(crash_data.identifiers))
+ return False
+
+ # Rename platform if configured.
+ crash_data.platform = self.RenamePlatform(crash_data.platform)
+
+ model = self.GetAnalysis(crash_data.identifiers)
+ if (model and not model.failed and
+ crash_data.regression_range == (tuple(model.regression_range)
+ if model.regression_range else None)):
+ logging.info('The analysis of %s has already been done.',
+ repr(crash_data.identifiers))
+ return False
+
+ model = model or self.CreateAnalysis(crash_data.identifiers)
+ model.Initialize(crash_data)
+ model.put()
+ return True
+
+ def ProcessResultForPublishing(self, result, key): # pragma: no cover
"""Client specific processing of result data for publishing."""
raise NotImplementedError()
def GetPublishableResult(self, crash_identifiers, analysis):
- """Convert a culprit result into a publishable result for client.
+ """Converts a culprit result into a publishable result for client.
Note, this function must be called by a concrete subclass of CrashAnalysis
which implements the ProcessResultForPublishing method.
@@ -291,15 +260,6 @@ class Findit(object):
# whole model. And/or, we should just inline this there.
# TODO(http://crbug.com/659346): coverage tests for this class, not
# just for FinditForFracas.
- def FindCulprit(self, model): # pragma: no cover
- """Given a CrashAnalysis ndb.Model, return a Culprit."""
- stacktrace = self.ParseStacktrace(model)
- if stacktrace is None:
- return None
-
- return self._predator.FindCulprit(CrashReport(
- crashed_version = model.crashed_version,
- signature = model.signature,
- platform = model.platform,
- stacktrace = stacktrace,
- regression_range = model.regression_range))
+ def FindCulprit(self, crash_report): # pragma: no cover
+ """Given a ``CrashReport``, returns a ``Culprit``."""
+ return self._Predator().FindCulprit(crash_report)
« no previous file with comments | « appengine/findit/crash/crash_report_with_dependencies.py ('k') | appengine/findit/crash/findit_for_chromecrash.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698