Chromium Code Reviews| Index: appengine/findit/crash/classifier.py |
| diff --git a/appengine/findit/crash/classifier.py b/appengine/findit/crash/classifier.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1697da36d0f99998d69f6b8426b40f34e937ed00 |
| --- /dev/null |
| +++ b/appengine/findit/crash/classifier.py |
| @@ -0,0 +1,87 @@ |
| +# Copyright 2016 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +from collections import namedtuple |
| +from collections import OrderedDict |
| + |
| +from common import constants |
| +from crash.type_enums import CallStackLanguageType |
| +from model.crash.crash_config import CrashConfig |
| + |
| +class ClassInfo(object): |
| + |
| + def __init__(self, value, count, occurrences): |
| + self.value = value |
| + self.count = count |
| + self.occurrences = occurrences |
| + |
| + def __iter__(self): |
|
Martin Barbella
2016/05/05 18:33:41
It doesn't seem intuitive at all that iterating ov
Sharu Jiang
2016/05/10 03:11:02
Right, this is not that needed, deleted it.
|
| + yield self.value |
| + yield self.count |
| + yield self.occurrences |
| + |
| + |
| +def DefaultRankFunction(class_info): |
|
Martin Barbella
2016/05/05 18:33:41
This doesn't feel like reasonable default behavior
Sharu Jiang
2016/05/10 03:11:02
Because there won't be any other classifiers any s
Martin Barbella
2016/05/10 06:00:41
Alright, that makes sense. This should be well-com
Sharu Jiang
2016/05/17 19:27:54
Done.
|
| + """Default rank function to rank classes.""" |
| + # If the top 2 frames are in the same class, give this class highest |
| + # priority. |
| + if 0 in class_info.occurrences and 1 in class_info.occurrences: |
| + return -constants.INFINITY, class_info.occurrences[0] |
| + |
| + return -class_info.count, class_info.occurrences[0] |
| + |
| + |
| +class Classifier(object): |
| + |
| + def GetClassFromStackFrame(self, frame): # pragma: no cover. |
| + raise NotImplementedError() |
| + |
| + def GetClassFromResult(self, result): # pragma: no cover. |
| + raise NotImplementedError() |
| + |
| + def _Classify(self, results, crash_stack, top_n, max_classes, |
| + rank_function=DefaultRankFunction): |
| + """Classifies a crash to a list of classes. |
| + |
| + Args: |
| + results (list of Result): Culprit results. |
| + crash_stack (CallStack): The callstack that caused the crash. |
| + top_n (int): Number of top frames to be considered when classifying. |
| + max_classes (int): Maximal number of classes to return. |
| + rank_function (function): Used to rank classes based on ClassInfos. |
| + |
| + Returns: |
| + A list of classes of this crash. |
| + """ |
| + # Extract the class list from culprit results since it's more reliable. |
| + if results: |
| + class_list = map(self.GetClassFromResult, results[:top_n]) |
| + else: |
| + class_list = map(self.GetClassFromStackFrame, crash_stack[:top_n]) |
| + |
| + def _GetClassInfos(): |
| + """Gets ClassInfo list from class_list, the order remains the same.""" |
| + if not class_list: |
| + return class_list |
| + |
| + infos = OrderedDict() |
| + |
| + for index, data_class in enumerate(class_list): |
|
Martin Barbella
2016/05/05 18:33:41
This block is a bit hard to read.
Sharu Jiang
2016/05/10 03:11:02
The variable name and the class name are not very
|
| + if data_class not in infos: |
| + infos[data_class] = ClassInfo(data_class, 1, [index]) |
| + else: |
| + infos[data_class].count += 1 |
| + infos[data_class].occurrences.append(index) |
| + |
| + return infos.values() |
| + |
| + class_infos = _GetClassInfos() |
| + class_infos = sorted(class_infos, key=rank_function) |
| + |
| + classes, _, _ = zip(*class_infos) |
| + |
| + return classes[:max_classes] |
| + |
| + def Classify(self, results, crash_stack): # pragma: no cover. |
| + raise NotImplementedError() |