| Index: appengine/findit/crash/crash_report.py
|
| diff --git a/appengine/findit/crash/crash_report.py b/appengine/findit/crash/crash_report.py
|
| index fcae4a253189bd187f2593b523e6b4b2a7541234..2fd96d23cc27314bac2f27982da6cf5302f8722d 100644
|
| --- a/appengine/findit/crash/crash_report.py
|
| +++ b/appengine/findit/crash/crash_report.py
|
| @@ -2,16 +2,35 @@
|
| # Use of this source code is governed by a BSD-style license that can be
|
| # found in the LICENSE file.
|
|
|
| -import logging
|
| +from collections import defaultdict
|
| from collections import namedtuple
|
| +import logging
|
| +
|
| +
|
| +class _FrozenDict(dict):
|
| + """An immutable ``dict`` (or some approximation thereof).
|
|
|
| -from crash.stacktrace import Stacktrace
|
| + The goal of this class is to render a ``dict`` hashable, so
|
| + that it can be used as a key in other ``dict``s, so that in
|
| + turn we can use our ``MemoizedFunction`` on functions taking
|
| + ``CrashReportWithDependencies`` as an argument.
|
| +
|
| + For now, we simply define the ``__hash__`` method and assume clients
|
| + will not try to mutate instances after they have been stored as keys
|
| + in another ``dict``. In the future it may be worth taking further
|
| + steps to make it more difficult for clients to do such mutation.
|
| +
|
| + N.B., the ``__init__`` method will clone the ``dict`` argument. There
|
| + doesn't seem to be an easy way around this.
|
| + """
|
| + def __hash__(self):
|
| + return hash(tuple(sorted(self.items())))
|
|
|
|
|
| -class CrashReport(namedtuple('CrashReport',
|
| - ['crashed_version', 'signature', 'platform', 'stacktrace',
|
| - 'regression_range'])):
|
| - """A reported crash we want to analyze.
|
| +class CrashReport(namedtuple(
|
| + 'CrashReport', ['crashed_version', 'signature', 'platform', 'stacktrace',
|
| + 'regression_range', 'dependencies', 'dependency_rolls'])):
|
| + """A crash report with all information needed for analysis for clients.
|
|
|
| This class comprises the inputs to the Predator library; as distinguished
|
| from the Culprit class, which comprises the outputs/results of Predator's
|
| @@ -19,41 +38,32 @@ class CrashReport(namedtuple('CrashReport',
|
| a single CrashAnalysis(ndb.Model) class, but that's up to them; in
|
| the library we keep inputs and outputs entirely distinct.
|
|
|
| - Args:
|
| + Properties:
|
| crashed_version (str): The version of Chrome in which the crash occurred.
|
| signature (str): The signature of the crash on the Chrome crash server.
|
| - platform (str): The platform name; e.g., 'win', 'mac', 'linux', 'android',
|
| - 'ios', etc.
|
| + platform (str): The platform affected by the crash; e.g., 'win',
|
| + 'mac', 'linux', 'android', 'ios', etc.
|
| stacktrace (Stacktrace): The stacktrace of the crash. N.B., this is
|
| an object generated by parsing the string containing the stack trace;
|
| we do not store the string itself.
|
| - regression_range (pair or None): a pair of the last-good and first-bad
|
| - versions. N.B., because this is an input, it is up to clients
|
| - to call ``DetectRegressionRange`` (or whatever else) in order to
|
| - provide this information. In addition, while this class does
|
| - support storing ``None`` to indicate a missing regression range
|
| - (because the ClusterFuzz client wants that feature), the
|
| - CL-classifier doesn't actually support that so you won't get a
|
| - very good Culprit. The Component- and project-classifiers do still
|
| - return some results at least.
|
| + regression_range (pair of str or None): a pair of ``last_good_version`` and
|
| + ``first_bad_version``. The regression_range can be ``None``.
|
| + dependencies (_FrozenDict): An immutable dict from dependency paths to
|
| + ``Dependency`` objects. The keys are all those deps which are
|
| + used by both the ``crashed_version`` of the code, and at least
|
| + one frame in the ``stacktrace.crash_stack``.
|
| + dependency_rolls (_FrozenDict) An immutable dict from dependency
|
| + paths to ``DependencyRoll`` objects. The keys are all those
|
| + dependencies which (1) occur in the regression range for the
|
| + ``platform`` where the crash occurred, (2) neither add nor delete
|
| + a dependency, and (3) are also keys of ``dependencies``.
|
| """
|
| __slots__ = ()
|
|
|
| def __new__(cls, crashed_version, signature, platform, stacktrace,
|
| - regression_range):
|
| - assert isinstance(stacktrace, Stacktrace), TypeError(
|
| - 'In the fourth argument to CrashReport constructor, '
|
| - 'expected Stacktrace object, but got %s object instead.'
|
| - % stacktrace.__class__.__name__)
|
| -
|
| - if regression_range is None: # pragma: no cover
|
| - logging.warning('CrashReport.__init__: '
|
| - 'Got ``None`` for the regression range.')
|
| - else:
|
| - # We must ensure that the regression range is immutable, both
|
| - # for semantic reasons and so that it is hashable as is required
|
| - # for memoization.
|
| - regression_range = tuple(regression_range)
|
| -
|
| - return super(cls, CrashReport).__new__(
|
| - cls, crashed_version, signature, platform, stacktrace, regression_range)
|
| + regression_range, dependencies, dependency_rolls):
|
| + return super(CrashReport, cls).__new__(
|
| + cls, crashed_version, signature, platform, stacktrace,
|
| + tuple(regression_range) if regression_range else None,
|
| + _FrozenDict(dependencies) if dependencies else {},
|
| + _FrozenDict(dependency_rolls) if dependency_rolls else {})
|
|
|