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

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

Issue 2657913002: [Predator] Add ``Project`` class and ``ClassifySuspect`` method to project and component classifier (Closed)
Patch Set: Fix nits. Created 3 years, 11 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 functools
5 import logging 6 import logging
6 7
7 from crash.occurrence import RankByOccurrence 8 from crash.occurrence import RankByOccurrence
9 from crash.project import Project
8 from crash.type_enums import LanguageType 10 from crash.type_enums import LanguageType
9 from model.crash.crash_config import CrashConfig 11 from model.crash.crash_config import CrashConfig
10 12
11 13
12 class ProjectClassifier(object): 14 class ProjectClassifier(object):
13 """Determines the project of a crash - (project_name, project_path). 15 """Determines the project of a crash - (project_name, project_path).
14 16
15 For example: ('chromium', 'src/'), ('skia', 'src/skia/'), ...etc. 17 For example: ('chromium', 'src/'), ('skia', 'src/skia/'), ...etc.
16 """ 18 """
17 19
18 # TODO(http://crbug.com/657177): remove dependency on CrashConfig. 20 # TODO(http://crbug.com/657177): remove dependency on CrashConfig.
19 def __init__(self): 21 def __init__(self, projects, top_n_frames,
22 non_chromium_project_rank_priority=None):
20 super(ProjectClassifier, self).__init__() 23 super(ProjectClassifier, self).__init__()
21 self.project_classifier_config = CrashConfig.Get().project_classifier 24 self.projects = projects
22 if self.project_classifier_config: 25 self.top_n_frames = top_n_frames
23 self.project_classifier_config['host_directories'].sort( 26 self.non_chromium_project_rank_priority = non_chromium_project_rank_priority
24 key=lambda host: -len(host.split('/')))
25 27
26 # TODO(http://crbug.com/657177): refactor this into a method on Project. 28 @staticmethod
27 def _GetProjectFromDepPath(self, dep_path): 29 def _GetTopClass(classes, rank_function=None):
28 """Returns the project name from a dep path.""" 30 """Gets the highest ranking class among classes."""
29 if not dep_path: 31 projects = RankByOccurrence(classes, 1, rank_function=rank_function)
30 return ''
31 32
32 if dep_path == 'src/': 33 if projects:
33 return 'chromium' 34 return projects[0]
34 35
35 for host_directory in self.project_classifier_config['host_directories']: 36 logging.warning('ProjectClassifier.Classify: no projects found.')
36 if dep_path.startswith(host_directory): 37 return None
37 path = dep_path[len(host_directory):]
38 return 'chromium-%s' % path.split('/')[0].lower()
39 38
40 # Unknown path, return the whole path as project name. 39 def ClassifyCallStack(self, crash_stack):
41 return 'chromium-%s' % '_'.join(dep_path.split('/'))
42
43 # TODO(http://crbug.com/657177): refactor this into Project.MatchesStackFrame.
44 def GetClassFromStackFrame(self, frame):
45 """Determine which project is responsible for this frame."""
46 for marker, name in self.project_classifier_config[
47 'function_marker_to_project_name'].iteritems():
48 if frame.function.startswith(marker):
49 return name
50
51 for marker, name in self.project_classifier_config[
52 'file_path_marker_to_project_name'].iteritems():
53 if marker in frame.file_path or marker in frame.raw_file_path:
54 return name
55
56 return self._GetProjectFromDepPath(frame.dep_path)
57
58 # TODO(wrengr): refactor this into a method on Suspect which returns
59 # the cannonical frame (and documents why it's the one we return).
60 def GetClassFromSuspect(self, suspect):
61 """Determine which project is responsible for this suspect."""
62 if suspect.file_to_stack_infos:
63 # file_to_stack_infos is a dict mapping file_path to stack_infos,
64 # where stack_infos is a list of (frame, callstack_priority)
65 # pairs. So ``.values()`` returns a list of the stack_infos in an
66 # arbitrary order; the first ``[0]`` grabs the "first" stack_infos;
67 # the second ``[0]`` grabs the first pair from the list; and
68 # the third ``[0]`` grabs the ``frame`` from the pair.
69 # TODO(wrengr): why is that the right frame to look at?
70 frame = suspect.file_to_stack_infos.values()[0][0][0]
71 return self.GetClassFromStackFrame(frame)
72
73 return ''
74
75 def Classify(self, suspects, crash_stack):
76 """Classify project of a crash. 40 """Classify project of a crash.
77 41
78 Args: 42 Args:
79 suspects (list of Suspect): culprit suspects. 43 suspects (list of Suspect): culprit suspects.
80 crash_stack (CallStack): the callstack that caused the crash.
81 44
82 Returns: 45 Returns:
83 The name of the most-suspected project; or the empty string on failure. 46 The name of the most-suspected project; or the empty string on failure.
84 """ 47 """
85 if not self.project_classifier_config:
86 logging.warning('ProjectClassifier.Classify: Empty configuration.')
87 return None
88
89 rank_function = None 48 rank_function = None
90 if crash_stack.language_type == LanguageType.JAVA: 49 if crash_stack.language_type == LanguageType.JAVA:
91 def _RankFunctionForJava(occurrence): 50 def _RankFunctionForJava(occurrence):
92 # TODO(wrengr): why are we weighting by the length, instead of 51 # TODO(wrengr): why are we weighting by the length, instead of
93 # the negative length as we do in the DefaultOccurrenceRanging? 52 # the negative length as we do in the DefaultOccurrenceRanging?
94 weight = len(occurrence) 53 weight = len(occurrence)
95 project_name = occurrence.name 54 project_name = occurrence.name
96 if 'chromium' in project_name: 55 if 'chromium' in project_name:
97 index = 0 56 index = 0
98 else: 57 else:
99 index = self.project_classifier_config[ 58 index = self.non_chromium_project_rank_priority[project_name]
100 'non_chromium_project_rank_priority'][project_name]
101 return (weight, index) 59 return (weight, index)
102 60
103 rank_function = _RankFunctionForJava 61 rank_function = _RankFunctionForJava
104 62
105 top_n_frames = self.project_classifier_config['top_n'] 63 # TODO(http://crbug.com/657177): refactor this into
Martin Barbella 2017/01/26 23:59:48 Is there something specific that makes this diffic
Sharu Jiang 2017/01/27 03:20:42 Forgot to remove it, this is what this cl does.
106 # If ``suspects`` are available, we use the projects from there since 64 # Project.MatchesStackFrame.
107 # they're more reliable than the ones from the ``crash_stack``. 65 def _GetClassFromStackFrame(frame):
108 if suspects: 66 """Determine which project is responsible for this frame."""
109 classes = map(self.GetClassFromSuspect, suspects[:top_n_frames]) 67 for project in self.projects:
110 else: 68 if project.MatchesStackFrame(frame):
111 classes = map(self.GetClassFromStackFrame, 69 return project.GetName(frame.dep_path)
112 crash_stack.frames[:top_n_frames])
113 70
114 # Since we're only going to return the highest-ranked class, might 71 return None
115 # as well set ``max_classes`` to 1.
116 projects = RankByOccurrence(classes, 1, rank_function=rank_function)
117 72
118 if projects: 73 classes = map(_GetClassFromStackFrame,
119 return projects[0] 74 crash_stack.frames[:self.top_n_frames])
120 75
121 logging.warning('ProjectClassifier.Classify: no projects found.') 76 return ProjectClassifier._GetTopClass(classes, rank_function=rank_function)
122 return '' 77
78 def ClassifySuspect(self, suspect):
79 """Determine which project is responsible for this frame."""
80 if not suspect or not suspect.changelog:
81 return None
82
83 def _GetClassFromTouchedFile(dep_path, touched_file):
84 for project in self.projects:
85 if project.MatchesTouchedFile(dep_path, touched_file):
86 return project.GetName(dep_path)
87
88 return None
89
90 get_class = functools.partial(_GetClassFromTouchedFile, suspect.dep_path)
91 classes = map(get_class, suspect.changelog.touched_files)
92 return ProjectClassifier._GetTopClass(classes,
93 rank_function=lambda x:-len(x))
94
95 def ClassifySuspects(self, suspects):
96 """Classify project of a crash.
97
98 Args:
99 suspects (list of Suspect): culprit suspects.
100 crash_stack (CallStack): the callstack that caused the crash.
101
102 Returns:
103 The name of the most-suspected project; or the empty string on failure.
104 """
105 classes = map(self.ClassifySuspect, suspects)
106 return ProjectClassifier._GetTopClass(classes)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698