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

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

Issue 2414523002: [Findit] Reorganizing findit_for_*.py (Closed)
Patch Set: Fixing call to ScheduleNewAnalysis in handlers/crash/crash_handler.py Created 4 years, 2 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 unified diff | Download patch
OLDNEW
(Empty)
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
3 # found in the LICENSE file.
4
5 import copy
6 import logging
7
8 from google.appengine.ext import ndb
9
10 from common import appengine_util
11 from common import chromium_deps
12 from common import constants
13 from common import time_util
14 from crash.crash_pipeline import CrashWrapperPipeline
15 from crash.crash_report import CrashReport
16 from crash.culprit import NullCulprit
17 from model import analysis_status
18 from model.crash.crash_config import CrashConfig
19 from model.crash.cracas_crash_analysis import CracasCrashAnalysis
20 from model.crash.fracas_crash_analysis import FracasCrashAnalysis
21
22
23 # TODO: this class depends on ndb stuff, and should therefore move to
24 # cr-culprit-finder/service/predator as part of the big reorganization.
25 # Also, this class should be renamed to "PredatorApp" (alas, "Azalea"
26 # was renamed to "Predator").
27 class Findit(object):
28 def __init__(self):
29 # TODO(wrengr): because self.client is volatile, we need some way
30 # of updating the Azelea instance whenever the config changes. How to
31 # do that cleanly?
32 self.azalea = None
33 self.stacktrace_parser = None
34
35 # This is a method rather than an attribute to ensure it can't be
36 # changed. And it's a classmethod rather than a property, since we can
37 # get it directly from the class itself, without needing to allocate
38 # an instance first.
39 @classmethod
40 def ClientID(cls):
41 """Reify the name of this class as a CrashClient id, for serialization."""
42 raise NotImplementedError()
43
44 @property
45 def client_id(self):
46 """Get the client id from the class of this object.
47
48 N.B., this property is static and should not be overridden."""
49 return self.__class__.ClientID()
50
51 # TODO(wrengr): can we remove the dependency on CrashConfig
52 # entirely? It'd be better to receive method calls whenever things
53 # change, so that we know the change happened (and what in particular
54 # changed) so that we can update our internal state as appropriate.
55 @property
56 def config(self):
57 """Get the current value of the client config.
58
59 N.B., this property is volatile and may change asynchronously."""
60 return CrashConfig.Get().GetClientConfig(self.client_id)
61
62 # TODO(wrengr): rename to CanonicalizePlatform or something like that.
63 def RenamePlatform(self, platform):
64 """Remap the platform to a different one, based on the config."""
65 # TODO(katesonia): Remove the default value after adding validity check to
66 # config.
67 return self.config.get('platform_rename', {}).get(platform, platform)
68
69 def CheckPolicy(self, crash_data):
70 """Check whether this client supports analyzing the given report.
71
72 Some clients only support analysis for crashes on certain platforms
73 or channels, etc. This method checks to see whether this client can
74 analyze the given crash.
75
76 Args:
77 crash_data (dict from JSON): ??
78
79 Returns:
80 If satisfied, we return the |crash_data| which may have had some
81 fields modified. Otherwise returns None.
82 """
83 return None
84
85 # TODO(wrengr): rename this to something like _NewAnalysis, since
86 # it only does the "allocation" and needs to/will be followed up with
87 # _InitializeAnalysis anyways.
88 def CreateAnalysis(self, crash_identifiers):
89 return None
90
91 def GetAnalysis(self, crash_identifiers):
92 """Return the CrashAnalysis for the |crash_identifiers|, if one exists.
93
94 Args:
95 crash_identifiers (??): ??
96
97 Returns:
98 If a CrashAnalysis ndb.Model already exists for the
99 |crash_identifiers|, then we return it. Otherwise, returns None.
100 """
101 return None
102
103 # TODO(wrengr): this should be a method on CrashAnalysis, not on Findit.
104 def _InitializeAnalysis(self, model, crash_data):
105 """(Re)Initialize a CrashAnalysis ndb.Model, but do not |put()| it yet.
106
107 This method is only ever called from _NeedsNewAnalysis which is only
108 ever called from ScheduleNewAnalysis. It is used for filling in the
109 fields of a CrashAnalysis ndb.Model for the first time (though it
110 can also be used to re-initialize a given CrashAnalysis). Subclasses
111 should extend (not override) this to (re)initialize any
112 client-specific fields they may have."""
113 # Get rid of any previous values there may have been.
114 model.Reset()
115
116 # Set common properties.
117 model.crashed_version = crash_data['crashed_version']
118 model.stack_trace = crash_data['stack_trace']
119 model.signature = crash_data['signature']
120 model.platform = crash_data['platform']
121 # TODO(wrengr): assert that crash_data['client_id'] == self.client_id
122 model.client_id = self.client_id
123
124 # Set progress properties.
125 model.status = analysis_status.PENDING
126 model.requested_time = time_util.GetUTCNow()
127
128 @ndb.transactional
129 def _NeedsNewAnalysis(self, crash_data):
130 raise NotImplementedError()
131
132 def ScheduleNewAnalysis(self, crash_data, queue_name=constants.DEFAULT_QUEUE):
133 """Schedules an analysis."""
134 # Check policy and tune arguments if needed.
135 crash_data = self.CheckPolicy(crash_data)
136 if crash_data is None:
137 return False
138
139 # Detect the regression range, and decide if we actually need to
140 # run a new anlaysis or not.
141 if not self._NeedsNewAnalysis(crash_data):
142 return False
143
144 crash_identifiers = crash_data['crash_identifiers']
145 analysis_pipeline = CrashWrapperPipeline(self, crash_identifiers)
146 # Attribute defined outside __init__ - pylint: disable=W0201
147 analysis_pipeline.target = appengine_util.GetTargetNameForModule(
148 constants.CRASH_BACKEND[self.client_id])
149 analysis_pipeline.start(queue_name=queue_name)
150 logging.info('New %s analysis is scheduled for %s', self.client_id,
151 repr(crash_identifiers))
152 return True
153
154 # TODO(wrengr): does the parser actually need the version, signature,
155 # and platform? If not, then we should be able to just pass the string
156 # to be parsed (which would make a lot more sense than passing the
157 # whole model).
158 def ParseStacktrace(self, model):
159 """Parse a CrashAnalysis's |stack_trace| string into a Stacktrace object.
160
161 Args:
162 model (CrashAnalysis): The model containing the stack_trace string
163 to be parsed.
164
165 Returns:
166 On success, returns a Stacktrace object; on failure, returns None.
167 """
168 stacktrace = self.stacktrace_parser.Parse(model.stack_trace,
169 chromium_deps.GetChromeDependency(
170 model.crashed_version, model.platform),
171 model.signature)
172 if not stacktrace:
173 logging.warning('Failed to parse the stacktrace %s', model.stack_trace)
174 return None
175
176 return stacktrace
177
178 # TODO(wrengr): This is only called by |CrashAnalysisPipeline.run|;
179 # we should be able to adjust things so that we only need to take in
180 # |crash_identifiers|, or a CrashReport, rather than taking in the
181 # whole model.
182 # TODO(wrengr): as part of inverting crash_pipeline.py wrt
183 # findit.py, this method should probably be the one to create the
184 # |CrashWrapperPipeline| and |CrashAnalysisPipeline| therein, rather
185 # than the other way around.
186 def FindCulprit(self, model):
187 """Given a CrashAnalysis ndb.Model, return a Culprit."""
188 stacktrace = self.ParseStacktrace(model)
189 if stacktrace is None:
190 # TODO(wrengr): refactor things so we don't need the NullCulprit class.
191 return NullCulprit()
192
193 return self.azalea.FindCulprit(CrashReport(
194 crashed_version = model.crashed_version,
195 signature = model.signature,
196 platform = model.platform,
197 stacktrace = stacktrace,
198 regression_range = model.regression_range))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698