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

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

Issue 2378133004: [Findit] Rerun if the regression range is different. (Closed)
Patch Set: 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
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 from collections import namedtuple 6 from collections import namedtuple
7 7
8 from common import chromium_deps 8 from common import chromium_deps
9 from crash import detect_regression_range 9 from crash import detect_regression_range
10 from crash import findit_for_crash 10 from crash import findit_for_crash
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
90 If nothing is found: { 90 If nothing is found: {
91 'found_suspects': False, 91 'found_suspects': False,
92 } 92 }
93 """ 93 """
94 cls_list = [result.ToDict() for result in self.cls] 94 cls_list = [result.ToDict() for result in self.cls]
95 95
96 # TODO(wrengr): reformulate the JSON stuff so we can drop fields which 96 # TODO(wrengr): reformulate the JSON stuff so we can drop fields which
97 # are empty; so that, in turn, we can get rid of the NullCulprit class. 97 # are empty; so that, in turn, we can get rid of the NullCulprit class.
98 return ( 98 return (
99 { 99 {
100 'found': (bool(self.project) 100 'found': (bool(self.project) or
101 or bool(self.components) 101 bool(self.components) or
102 or bool(cls_list) 102 bool(cls_list) or
103 or bool(self.regression_range)), 103 bool(self.regression_range)),
104 'regression_range': self.regression_range, 104 'regression_range': self.regression_range,
105 'suspected_project': self.project, 105 'suspected_project': self.project,
106 'suspected_components': self.components, 106 'suspected_components': self.components,
107 'suspected_cls': cls_list, 107 'suspected_cls': cls_list,
108 }, 108 },
109 { 109 {
110 'found_suspects': bool(cls_list), 110 'found_suspects': bool(cls_list),
111 'found_project': bool(self.project), 111 'found_project': bool(self.project),
112 'found_components': bool(self.components), 112 'found_components': bool(self.components),
113 'has_regression_range': bool(self.regression_range), 113 'has_regression_range': bool(self.regression_range),
114 'solution': 'core_algorithm', 114 'solution': 'core_algorithm',
115 } 115 }
116 ) 116 )
117 117
118 118
119 # TODO(wrengr): Either (a) reformulate the unit tests so that FindCulprit 119 # TODO(wrengr): Either (a) reformulate the unit tests so that FindCulprit
120 # can, in fact, return None; or else (b) reformulate the JSON stuff so 120 # can, in fact, return None; or else (b) reformulate the JSON stuff so
121 # that the various fields are optional, so that we can just use 121 # that the various fields are optional, so that we can just use
122 # Culprit('', [], [], None) directly. 122 # Culprit('', [], [], None) directly.
123 class NullCulprit(object): 123 class NullCulprit(object):
124 """A helper class so FindCulprit doesn't return None. 124 """A helper class so FindCulprit doesn't return None.
125 125
126 This class is analogous to Culprit(None, [], [], None), except that the 126 This class is analogous to Culprit(None, [], [], None), except that the
127 result of the ToDicts method is more minimalistic.""" 127 result of the ToDicts method is more minimalistic."""
128 128
129 def ToDicts(self): 129 def ToDicts(self):
130 return ( 130 return {'found': False}, {'found_suspects': False,
131 {'found': False}, 131 'has_regression_range': False}
132 {'found_suspects': False,
133 'has_regression_range': False}
134 )
135 132
136 133
137 # TODO(wrengr): better name for this class. Given the "findit_for_*.py" 134 # TODO(wrengr): better name for this class. Given the "findit_for_*.py"
138 # file names, one might suspect that each of those files implements 135 # file names, one might suspect that each of those files implements
139 # something analogous to this file, and hence we could make a superclass 136 # something analogous to this file, and hence we could make a superclass
140 # to factor out the common bits. However, in truth, all those files 137 # to factor out the common bits. However, in truth, all those files
141 # are unrelated and do completely different things. 138 # are unrelated and do completely different things.
142 class FinditForChromeCrash(object): 139 class FinditForChromeCrash(object):
143 """Process crashes from Chrome crash server and find culprits for them. 140 """Process crashes from Chrome crash server and find culprits for them.
144 141
145 Even though this class has only one method, it is helpful because it 142 Even though this class has only one method, it is helpful because it
146 allows us to cache things which should outlive each call to that method. 143 allows us to cache things which should outlive each call to that method.
147 For example, we store a single ComponentClassifier object so that we 144 For example, we store a single ComponentClassifier object so that we
148 only compile the regexes for each Component object once, rather than 145 only compile the regexes for each Component object once, rather than
149 doing so on each call to FindCulprit. In addition, the class lets 146 doing so on each call to FindCulprit. In addition, the class lets
150 us cache various configuration options so that we need not depend 147 us cache various configuration options so that we need not depend
151 on CrashConfig; thereby decoupling the analysis itself from UX concerns 148 on CrashConfig; thereby decoupling the analysis itself from UX concerns
152 about deciding how to run those analyses. 149 about deciding how to run those analyses.
153 """ 150 """
154
155 # TODO(wrengr): remove the dependency on CrashConfig entirely, by 151 # TODO(wrengr): remove the dependency on CrashConfig entirely, by
156 # passing the relevant data as arguments to this constructor. 152 # passing the relevant data as arguments to this constructor.
157 def __init__(self): 153 def __init__(self):
158 crash_config = CrashConfig.Get() 154 crash_config = CrashConfig.Get()
159 component_classifier_config = crash_config.component_classifier 155 component_classifier_config = crash_config.component_classifier
160 156
161 # TODO(wrengr): why are these two different? 157 # TODO(wrengr): why are these two different?
162 component_classifier_top_n = component_classifier_config['top_n'] 158 component_classifier_top_n = component_classifier_config['top_n']
163 self._fracas_top_n = crash_config.fracas.get('top_n', _DEFAULT_TOP_N) 159 self._fracas_top_n = crash_config.fracas.get('top_n', _DEFAULT_TOP_N)
164 160
165 self.component_classifier = ComponentClassifier( 161 self.component_classifier = ComponentClassifier(
166 [Component(component_name, path_regex, function_regex) 162 [Component(component_name, path_regex, function_regex)
167 for path_regex, function_regex, component_name 163 for path_regex, function_regex, component_name
168 in component_classifier_config['path_function_component']], 164 in component_classifier_config['path_function_component']],
169 component_classifier_top_n) 165 component_classifier_top_n)
170 166
171 # TODO(wrengr); fix ProjectClassifier so it doesn't depend on CrashConfig. 167 # TODO(wrengr); fix ProjectClassifier so it doesn't depend on CrashConfig.
172 self.project_classifier = ProjectClassifier() 168 self.project_classifier = ProjectClassifier()
173 169
174
175 # TODO(wrengr): since this is the only method of interest, it would 170 # TODO(wrengr): since this is the only method of interest, it would
176 # be better IMO to rename it to __call__ to reduce verbosity. 171 # be better IMO to rename it to __call__ to reduce verbosity.
177 def FindCulprit(self, signature, platform, stack_trace, crashed_version, 172 def FindCulprit(self, signature, platform, stack_trace, crashed_version,
178 historic_metadata): 173 regression_range):
179 """Finds culprits for a Chrome crash. 174 """Finds culprits for a Chrome crash.
180 175
181 Args: 176 Args:
182 signature (str): The signature of a crash on the Chrome crash server. 177 signature (str): The signature of a crash on the Chrome crash server.
183 platform (str): The platform name, could be 'win', 'mac', 'linux', 178 platform (str): The platform name, could be 'win', 'mac', 'linux',
184 'android', 'ios', etc. 179 'android', 'ios', etc.
185 stack_trace (str): A string containing the stack trace of a crash. 180 stack_trace (str): A string containing the stack trace of a crash.
186 crashed_version (str): The version of Chrome in which the crash occurred. 181 crashed_version (str): The version of Chrome in which the crash occurred.
187 historic_metadata (list): list of dicts mapping from Chrome version to 182 regression_range (list or None): [good_version, bad_revision] or None.
188 historic metadata.
189 183
190 Returns: 184 Returns:
191 A Culprit object. 185 A Culprit object.
192 """ 186 """
193 crash_deps = chromium_deps.GetChromeDependency(crashed_version, platform) 187 crash_deps = chromium_deps.GetChromeDependency(crashed_version, platform)
194 stacktrace = ChromeCrashParser().Parse(stack_trace, crash_deps, signature) 188 stacktrace = ChromeCrashParser().Parse(stack_trace, crash_deps, signature)
195 if not stacktrace: 189 if not stacktrace:
196 logging.warning('Failed to parse the stacktrace %s', stack_trace) 190 logging.warning('Failed to parse the stacktrace %s', stack_trace)
197 # TODO(wrengr): refactor things so we don't need the NullCulprit class. 191 # TODO(wrengr): refactor things so we don't need the NullCulprit class.
198 return NullCulprit() 192 return NullCulprit()
199 193
200 # Get regression deps and crash deps. 194 # Get regression deps and crash deps.
201 regression_deps_rolls = {} 195 regression_deps_rolls = {}
202 regression_range = detect_regression_range.DetectRegressionRange(
203 historic_metadata)
204 if regression_range: 196 if regression_range:
205 last_good_version, first_bad_version = regression_range 197 last_good_version, first_bad_version = regression_range
206 logging.info('Find regression range %s:%s', last_good_version, 198 logging.info('Find regression range %s:%s', last_good_version,
207 first_bad_version) 199 first_bad_version)
208 regression_deps_rolls = chromium_deps.GetDEPSRollsDict( 200 regression_deps_rolls = chromium_deps.GetDEPSRollsDict(
209 last_good_version, first_bad_version, platform) 201 last_good_version, first_bad_version, platform)
210 202
211 suspected_cls = findit_for_crash.FindItForCrash( 203 suspected_cls = findit_for_crash.FindItForCrash(
212 stacktrace, regression_deps_rolls, crash_deps, self._fracas_top_n) 204 stacktrace, regression_deps_rolls, crash_deps, self._fracas_top_n)
213 205
214 crash_stack = stacktrace.crash_stack 206 crash_stack = stacktrace.crash_stack
215 suspected_project = self.project_classifier.Classify( 207 suspected_project = self.project_classifier.Classify(
216 suspected_cls, crash_stack) 208 suspected_cls, crash_stack)
217 209
218 suspected_components = self.component_classifier.Classify( 210 suspected_components = self.component_classifier.Classify(
219 suspected_cls, crash_stack) 211 suspected_cls, crash_stack)
220 212
221 return Culprit(suspected_project, suspected_components, suspected_cls, 213 return Culprit(suspected_project, suspected_components, suspected_cls,
222 regression_range) 214 regression_range)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698