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

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

Issue 2663063007: [Predator] Switch from anonymous dict to CrashData. (Closed)
Patch Set: Rebase. 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 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 logging 5 import logging
6 6
7 from google.appengine.ext import ndb 7 from google.appengine.ext import ndb
8 8
9 from common import appengine_util 9 from common import appengine_util
10 from common.chrome_dependency_fetcher import ChromeDependencyFetcher
10 from crash import detect_regression_range 11 from crash import detect_regression_range
11 from crash.chromecrash_parser import ChromeCrashParser 12 from crash.chromecrash_parser import ChromeCrashParser
13 from crash.chrome_crash_buffer import ChromeCrashBuffer
12 from crash.component import Component 14 from crash.component import Component
13 from crash.component_classifier import ComponentClassifier 15 from crash.component_classifier import ComponentClassifier
14 from crash.findit import Findit 16 from crash.findit import Findit
15 from crash.loglinear.changelist_classifier import LogLinearChangelistClassifier 17 from crash.loglinear.changelist_classifier import LogLinearChangelistClassifier
16 from crash.loglinear.changelist_features.touch_crashed_file_meta import ( 18 from crash.loglinear.changelist_features.touch_crashed_file_meta import (
17 TouchCrashedFileMetaFeature) 19 TouchCrashedFileMetaFeature)
18 from crash.loglinear.feature import WrapperMetaFeature 20 from crash.loglinear.feature import WrapperMetaFeature
19 from crash.loglinear.weight import MetaWeight 21 from crash.loglinear.weight import MetaWeight
20 from crash.loglinear.weight import Weight 22 from crash.loglinear.weight import Weight
21 from crash.predator import Predator 23 from crash.predator import Predator
22 from crash.project import Project 24 from crash.project import Project
23 from crash.project_classifier import ProjectClassifier 25 from crash.project_classifier import ProjectClassifier
24 from crash.type_enums import CrashClient 26 from crash.type_enums import CrashClient
25 from model.crash.cracas_crash_analysis import CracasCrashAnalysis 27 from model.crash.cracas_crash_analysis import CracasCrashAnalysis
26 from model.crash.crash_config import CrashConfig
27 from model.crash.fracas_crash_analysis import FracasCrashAnalysis 28 from model.crash.fracas_crash_analysis import FracasCrashAnalysis
28 29
29 # TODO(katesonia): Remove the default value after adding validity check to 30 # TODO(katesonia): Remove the default value after adding validity check to
30 # config. 31 # config.
31 _FRACAS_FEEDBACK_URL_TEMPLATE = 'https://%s/crash/fracas-result-feedback?key=%s' 32 _FRACAS_FEEDBACK_URL_TEMPLATE = 'https://%s/crash/fracas-result-feedback?key=%s'
32 33
33 # TODO(wrengr): [Note#1] in many places below we have to do some ugly
34 # defaulting in case crash_data is missing certain keys. If we had
35 # crash_data be a proper class, rather than an anonymous dict, then we
36 # could clean all this up by having the properties themselves do the check
37 # and return the default whenever keys are missing. This would also
38 # let us do things like have regression_range be automatically computed
39 # from historical_metadata (when historical_metadata is provided and
40 # regression_range is not).
41 34
42 35 class FinditForChromeCrash(Findit): # pylint: disable=W0223
43 class FinditForChromeCrash(Findit):
44 """Find culprits for crash reports from the Chrome Crash server.""" 36 """Find culprits for crash reports from the Chrome Crash server."""
45 37
46 @classmethod 38 @classmethod
47 def _ClientID(cls): # pragma: no cover 39 def _ClientID(cls): # pragma: no cover
48 if cls is FinditForChromeCrash: 40 if cls is FinditForChromeCrash:
49 logging.warning('FinditForChromeCrash is abstract, ' 41 logging.warning('FinditForChromeCrash is abstract, '
50 'but someone constructed an instance and called _ClientID') 42 'but someone constructed an instance and called _ClientID')
51 else: 43 else:
52 logging.warning( 44 logging.warning(
53 'FinditForChromeCrash subclass %s forgot to implement _ClientID', 45 'FinditForChromeCrash subclass %s forgot to implement _ClientID',
54 cls.__name__) 46 cls.__name__)
55 raise NotImplementedError() 47 raise NotImplementedError()
56 48
57 # TODO(http://crbug.com/659354): remove the dependency on CrashConfig 49 def __init__(self, get_repository, config):
58 # entirely, by passing the relevant data as arguments to this constructor. 50 super(FinditForChromeCrash, self).__init__(get_repository, config)
59 def __init__(self, get_repository):
60 super(FinditForChromeCrash, self).__init__(get_repository)
61
62 # TODO(http://crbug.com/687670): Move meta weight initial value to config.
63 meta_weight = MetaWeight({ 51 meta_weight = MetaWeight({
64 'TouchCrashedFileMeta': MetaWeight({ 52 'TouchCrashedFileMeta': MetaWeight({
65 'MinDistance': Weight(1.), 53 'MinDistance': Weight(1.),
66 'TopFrameIndex': Weight(1.), 54 'TopFrameIndex': Weight(1.),
67 'TouchCrashedFile': Weight(1.), 55 'TouchCrashedFile': Weight(1.),
68 }) 56 })
69 }) 57 })
70 meta_feature = WrapperMetaFeature( 58 meta_feature = WrapperMetaFeature(
71 [TouchCrashedFileMetaFeature(get_repository)]) 59 [TouchCrashedFileMetaFeature(get_repository)])
72 60
73 project_classifier_config = CrashConfig.Get().project_classifier 61 self._predator = Predator(LogLinearChangelistClassifier(get_repository,
74 projects = [Project(name, path_regexs, function_regexs, host_directories) 62 meta_feature,
75 for name, path_regexs, function_regexs, host_directories 63 meta_weight),
76 in project_classifier_config['project_path_function_hosts']] 64 self._component_classifier,
77 component_classifier_config = CrashConfig.Get().component_classifier 65 self._project_classifier)
78 components = [Component(component_name, path_regex, function_regex)
79 for path_regex, function_regex, component_name
80 in component_classifier_config['path_function_component']]
81 # The top_n is the number of frames we want to check to get component or
82 # project classifications.
83 self._predator = Predator(
84 cl_classifier = LogLinearChangelistClassifier(get_repository,
85 meta_feature,
86 meta_weight),
87 component_classifier = ComponentClassifier(
88 components, component_classifier_config['top_n']),
89 project_classifier = ProjectClassifier(
90 projects, project_classifier_config['top_n'],
91 project_classifier_config['non_chromium_project_rank_priority']))
92 66
93 self._stacktrace_parser = ChromeCrashParser() 67 def _Predator(self): # pragma: no cover
68 return self._predator
94 69
95 def _InitializeAnalysis(self, model, crash_data): 70 def _CheckPolicy(self, crash_buffer):
96 super(FinditForChromeCrash, self)._InitializeAnalysis(model, crash_data) 71 if not super(FinditForChromeCrash, self)._CheckPolicy(crash_buffer):
97 # TODO(wrengr): see Note#1
98 customized_data = crash_data.get('customized_data', {})
99 model.channel = customized_data.get('channel', None)
100 model.historical_metadata = customized_data.get('historical_metadata', [])
101
102 # TODO(wrengr): see Note#1, which would allow us to lift this
103 # implementation to the Findit base class.
104 @ndb.transactional
105 def _NeedsNewAnalysis(self, crash_data):
106 crash_identifiers = crash_data['crash_identifiers']
107 historical_metadata = crash_data['customized_data']['historical_metadata']
108 model = self.GetAnalysis(crash_identifiers)
109 # N.B., for mocking reasons, we must not call DetectRegressionRange
110 # directly, but rather must access it indirectly through the module.
111 new_regression_range = detect_regression_range.DetectRegressionRange(
112 historical_metadata)
113 if (model and not model.failed and
114 new_regression_range == model.regression_range):
115 logging.info('The analysis of %s has already been done.',
116 repr(crash_identifiers))
117 return False 72 return False
118 73
119 if not model: 74 if crash_buffer.platform not in self.client_config[
120 model = self.CreateAnalysis(crash_identifiers) 75 'supported_platform_list_by_channel'].get(crash_buffer.channel, []):
76 # Bail out if either the channel or platform is not supported yet.
77 logging.info('Analysis of channel %s, platform %s is not supported.',
78 crash_buffer.channel, crash_buffer.platform)
79 return False
121 80
122 crash_data['regression_range'] = new_regression_range
123 self._InitializeAnalysis(model, crash_data)
124 model.put()
125 return True 81 return True
126 82
127 def CheckPolicy(self, crash_data): 83 def GetCrashBuffer(self, crash_data):
128 crash_identifiers = crash_data['crash_identifiers'] 84 return ChromeCrashBuffer(crash_data,
129 platform = crash_data['platform'] 85 ChromeDependencyFetcher(self._get_repository),
130 # TODO(wrengr): see Note#1 86 top_n_frames=self.client_config['top_n'])
131 channel = crash_data.get('customized_data', {}).get('channel', None)
132 # TODO(katesonia): Remove the default value after adding validity check to
133 # config.
134 if platform not in self.config.get(
135 'supported_platform_list_by_channel', {}).get(channel, []):
136 # Bail out if either the channel or platform is not supported yet.
137 logging.info('Analysis of channel %s, platform %s is not supported. '
138 'No analysis is scheduled for %s',
139 channel, platform, repr(crash_identifiers))
140 return None
141
142 signature = crash_data['signature']
143 # TODO(wrengr): can this blacklist stuff be lifted to the base class?
144 # TODO(katesonia): Remove the default value after adding validity check to
145 # config.
146 for blacklist_marker in self.config.get('signature_blacklist_markers', []):
147 if blacklist_marker in signature:
148 logging.info('%s signature is not supported. '
149 'No analysis is scheduled for %s', blacklist_marker,
150 repr(crash_identifiers))
151 return None
152
153 # TODO(wrengr): should we clone ``crash_data`` rather than mutating it?
154 crash_data['platform'] = self.RenamePlatform(platform)
155 return crash_data
156
157 def ProcessResultForPublishing(self, result, key): # pragma: no cover.
158 """Client specific processing of result data for publishing."""
159 # This method needs to get overwritten by subclasses FinditForCracas and
160 # FinditForFracas.
161 raise NotImplementedError()
162 87
163 88
164 # TODO(http://crbug.com/659346): we misplaced the coverage tests; find them! 89 # TODO(http://crbug.com/659346): we misplaced the coverage tests; find them!
165 class FinditForCracas(FinditForChromeCrash): # pragma: no cover 90 class FinditForCracas( # pylint: disable=W0223
91 FinditForChromeCrash): # pragma: no cover
92
166 @classmethod 93 @classmethod
167 def _ClientID(cls): 94 def _ClientID(cls):
168 return CrashClient.CRACAS 95 return CrashClient.CRACAS
169 96
170 def CreateAnalysis(self, crash_identifiers): 97 def CreateAnalysis(self, crash_identifiers):
171 # TODO: inline CracasCrashAnalysis.Create stuff here. 98 # TODO: inline CracasCrashAnalysis.Create stuff here.
172 return CracasCrashAnalysis.Create(crash_identifiers) 99 return CracasCrashAnalysis.Create(crash_identifiers)
173 100
174 def GetAnalysis(self, crash_identifiers): 101 def GetAnalysis(self, crash_identifiers):
175 # TODO: inline CracasCrashAnalysis.Get stuff here. 102 # TODO: inline CracasCrashAnalysis.Get stuff here.
176 return CracasCrashAnalysis.Get(crash_identifiers) 103 return CracasCrashAnalysis.Get(crash_identifiers)
177 104
178 def ProcessResultForPublishing(self, result, key): # pragma: no cover. 105 def ProcessResultForPublishing(self, result, key): # pragma: no cover.
179 """Cracas specific processing of result data for publishing.""" 106 """Cracas specific processing of result data for publishing."""
180 # TODO(katesonia) Add feedback page link information to result after 107 # TODO(katesonia) Add feedback page link information to result after
181 # feedback page of Cracas is added. 108 # feedback page of Cracas is added.
182 return result 109 return result
183 110
184 111
185 class FinditForFracas(FinditForChromeCrash): 112 class FinditForFracas(FinditForChromeCrash): # pylint: disable=W0223
186 @classmethod 113 @classmethod
187 def _ClientID(cls): 114 def _ClientID(cls):
188 return CrashClient.FRACAS 115 return CrashClient.FRACAS
189 116
190 def CreateAnalysis(self, crash_identifiers): 117 def CreateAnalysis(self, crash_identifiers):
191 # TODO: inline FracasCrashAnalysis.Create stuff here. 118 # TODO: inline FracasCrashAnalysis.Create stuff here.
192 return FracasCrashAnalysis.Create(crash_identifiers) 119 return FracasCrashAnalysis.Create(crash_identifiers)
193 120
194 def GetAnalysis(self, crash_identifiers): 121 def GetAnalysis(self, crash_identifiers):
195 # TODO: inline FracasCrashAnalysis.Get stuff here. 122 # TODO: inline FracasCrashAnalysis.Get stuff here.
196 return FracasCrashAnalysis.Get(crash_identifiers) 123 return FracasCrashAnalysis.Get(crash_identifiers)
197 124
198 def ProcessResultForPublishing(self, result, key): 125 def ProcessResultForPublishing(self, result, key):
199 """Fracas specific processing of result data for publishing.""" 126 """Fracas specific processing of result data for publishing."""
200 result['feedback_url'] = _FRACAS_FEEDBACK_URL_TEMPLATE % ( 127 result['feedback_url'] = _FRACAS_FEEDBACK_URL_TEMPLATE % (
201 appengine_util.GetDefaultVersionHostname(), key) 128 appengine_util.GetDefaultVersionHostname(), key)
202 return result 129 return result
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698