| OLD | NEW |
| 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 Loading... |
| 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), |
| (...skipping 30 matching lines...) Expand all Loading... |
| 144 | 144 |
| 145 Even though this class has only one method, it is helpful because it | 145 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. | 146 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 | 147 For example, we store a single ComponentClassifier object so that we |
| 148 only compile the regexes for each Component object once, rather than | 148 only compile the regexes for each Component object once, rather than |
| 149 doing so on each call to FindCulprit. In addition, the class lets | 149 doing so on each call to FindCulprit. In addition, the class lets |
| 150 us cache various configuration options so that we need not depend | 150 us cache various configuration options so that we need not depend |
| 151 on CrashConfig; thereby decoupling the analysis itself from UX concerns | 151 on CrashConfig; thereby decoupling the analysis itself from UX concerns |
| 152 about deciding how to run those analyses. | 152 about deciding how to run those analyses. |
| 153 """ | 153 """ |
| 154 | |
| 155 # TODO(wrengr): remove the dependency on CrashConfig entirely, by | 154 # TODO(wrengr): remove the dependency on CrashConfig entirely, by |
| 156 # passing the relevant data as arguments to this constructor. | 155 # passing the relevant data as arguments to this constructor. |
| 157 def __init__(self): | 156 def __init__(self): |
| 158 crash_config = CrashConfig.Get() | 157 crash_config = CrashConfig.Get() |
| 159 component_classifier_config = crash_config.component_classifier | 158 component_classifier_config = crash_config.component_classifier |
| 160 | 159 |
| 161 # TODO(wrengr): why are these two different? | 160 # TODO(wrengr): why are these two different? |
| 162 component_classifier_top_n = component_classifier_config['top_n'] | 161 component_classifier_top_n = component_classifier_config['top_n'] |
| 163 self._fracas_top_n = crash_config.fracas.get('top_n', _DEFAULT_TOP_N) | 162 self._fracas_top_n = crash_config.fracas.get('top_n', _DEFAULT_TOP_N) |
| 164 | 163 |
| 165 self.component_classifier = ComponentClassifier( | 164 self.component_classifier = ComponentClassifier( |
| 166 [Component(component_name, path_regex, function_regex) | 165 [Component(component_name, path_regex, function_regex) |
| 167 for path_regex, function_regex, component_name | 166 for path_regex, function_regex, component_name |
| 168 in component_classifier_config['path_function_component']], | 167 in component_classifier_config['path_function_component']], |
| 169 component_classifier_top_n) | 168 component_classifier_top_n) |
| 170 | 169 |
| 171 # TODO(wrengr); fix ProjectClassifier so it doesn't depend on CrashConfig. | 170 # TODO(wrengr); fix ProjectClassifier so it doesn't depend on CrashConfig. |
| 172 self.project_classifier = ProjectClassifier() | 171 self.project_classifier = ProjectClassifier() |
| 173 | 172 |
| 174 | |
| 175 # TODO(wrengr): since this is the only method of interest, it would | 173 # TODO(wrengr): since this is the only method of interest, it would |
| 176 # be better IMO to rename it to __call__ to reduce verbosity. | 174 # be better IMO to rename it to __call__ to reduce verbosity. |
| 177 def FindCulprit(self, signature, platform, stack_trace, crashed_version, | 175 def FindCulprit(self, signature, platform, stack_trace, crashed_version, |
| 178 historic_metadata): | 176 regression_range): |
| 179 """Finds culprits for a Chrome crash. | 177 """Finds culprits for a Chrome crash. |
| 180 | 178 |
| 181 Args: | 179 Args: |
| 182 signature (str): The signature of a crash on the Chrome crash server. | 180 signature (str): The signature of a crash on the Chrome crash server. |
| 183 platform (str): The platform name, could be 'win', 'mac', 'linux', | 181 platform (str): The platform name, could be 'win', 'mac', 'linux', |
| 184 'android', 'ios', etc. | 182 'android', 'ios', etc. |
| 185 stack_trace (str): A string containing the stack trace of a crash. | 183 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. | 184 crashed_version (str): The version of Chrome in which the crash occurred. |
| 187 historic_metadata (list): list of dicts mapping from Chrome version to | 185 regression_range (list or None): [good_version, bad_revision] or None. |
| 188 historic metadata. | |
| 189 | 186 |
| 190 Returns: | 187 Returns: |
| 191 A Culprit object. | 188 A Culprit object. |
| 192 """ | 189 """ |
| 193 crash_deps = chromium_deps.GetChromeDependency(crashed_version, platform) | 190 crash_deps = chromium_deps.GetChromeDependency(crashed_version, platform) |
| 194 stacktrace = ChromeCrashParser().Parse(stack_trace, crash_deps, signature) | 191 stacktrace = ChromeCrashParser().Parse(stack_trace, crash_deps, signature) |
| 195 if not stacktrace: | 192 if not stacktrace: |
| 196 logging.warning('Failed to parse the stacktrace %s', stack_trace) | 193 logging.warning('Failed to parse the stacktrace %s', stack_trace) |
| 197 # TODO(wrengr): refactor things so we don't need the NullCulprit class. | 194 # TODO(wrengr): refactor things so we don't need the NullCulprit class. |
| 198 return NullCulprit() | 195 return NullCulprit() |
| 199 | 196 |
| 200 # Get regression deps and crash deps. | 197 # Get regression deps and crash deps. |
| 201 regression_deps_rolls = {} | 198 regression_deps_rolls = {} |
| 202 regression_range = detect_regression_range.DetectRegressionRange( | |
| 203 historic_metadata) | |
| 204 if regression_range: | 199 if regression_range: |
| 205 last_good_version, first_bad_version = regression_range | 200 last_good_version, first_bad_version = regression_range |
| 206 logging.info('Find regression range %s:%s', last_good_version, | 201 logging.info('Find regression range %s:%s', last_good_version, |
| 207 first_bad_version) | 202 first_bad_version) |
| 208 regression_deps_rolls = chromium_deps.GetDEPSRollsDict( | 203 regression_deps_rolls = chromium_deps.GetDEPSRollsDict( |
| 209 last_good_version, first_bad_version, platform) | 204 last_good_version, first_bad_version, platform) |
| 210 | 205 |
| 211 suspected_cls = findit_for_crash.FindItForCrash( | 206 suspected_cls = findit_for_crash.FindItForCrash( |
| 212 stacktrace, regression_deps_rolls, crash_deps, self._fracas_top_n) | 207 stacktrace, regression_deps_rolls, crash_deps, self._fracas_top_n) |
| 213 | 208 |
| 214 crash_stack = stacktrace.crash_stack | 209 crash_stack = stacktrace.crash_stack |
| 215 suspected_project = self.project_classifier.Classify( | 210 suspected_project = self.project_classifier.Classify( |
| 216 suspected_cls, crash_stack) | 211 suspected_cls, crash_stack) |
| 217 | 212 |
| 218 suspected_components = self.component_classifier.Classify( | 213 suspected_components = self.component_classifier.Classify( |
| 219 suspected_cls, crash_stack) | 214 suspected_cls, crash_stack) |
| 220 | 215 |
| 221 return Culprit(suspected_project, suspected_components, suspected_cls, | 216 return Culprit(suspected_project, suspected_components, suspected_cls, |
| 222 regression_range) | 217 regression_range) |
| OLD | NEW |