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

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

Issue 2657913002: [Predator] Add ``Project`` class and ``ClassifySuspect`` method to project and component classifier (Closed)
Patch Set: Fix nits. 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 from collections import namedtuple 5 from collections import namedtuple
6 from collections import defaultdict 6 from collections import defaultdict
7 import functools
7 import logging 8 import logging
8 import re 9 import re
9 10
10 from crash.component import Component 11 from crash.component import Component
11 from crash.occurrence import RankByOccurrence 12 from crash.occurrence import RankByOccurrence
12 from libs.gitiles.diff import ChangeType 13 from libs.gitiles.diff import ChangeType
13 14
14 15
15 class ComponentClassifier(object): 16 class ComponentClassifier(object):
16 """Determines the component of a crash. 17 """Determines the component of a crash.
17 18
18 For example: ['Blink>DOM', 'Blink>HTML']. 19 For example: ['Blink>DOM', 'Blink>HTML'].
19 """ 20 """
20 21
21 def __init__(self, components, top_n): 22 def __init__(self, components, top_n_frames):
22 """Build a classifier for components. 23 """Build a classifier for components.
23 24
24 Args: 25 Args:
25 components (list of crash.component.Component): the components to 26 components (list of crash.component.Component): the components to
26 check for. 27 check for.
27 top_n (int): how many frames of the callstack to look at""" 28 top_n_frames (int): how many frames of the callstack to look at
29 """
28 super(ComponentClassifier, self).__init__() 30 super(ComponentClassifier, self).__init__()
29 if not components: 31 self.components = [] if components is None else components
30 logging.warning('Empty configuration for component classifier.') 32 self.top_n_frames = top_n_frames
31 components = [] # Ensure self.components is not None
32 self.components = components
33 self.top_n = top_n
34
35 def GetClassFromStackFrame(self, frame):
36 """Determine which component is responsible for this frame."""
37 for component in self.components:
38 if component.MatchesStackFrame(frame):
39 return component.component_name
40
41 return ''
42
43 # TODO(wrengr): refactor this into a method on Suspect which returns
44 # the cannonical frame (and documents why it's the one we return).
45 def GetClassFromSuspect(self, suspect):
46 """Determine which component is responsible for this suspect.
47
48 Note that Findit assumes files that the culprit suspect touched come from
49 the same component.
50 """
51 if suspect.file_to_stack_infos:
52 # file_to_stack_infos is a dict mapping file_path to stack_infos,
53 # where stack_infos is a list of (frame, callstack_priority)
54 # pairs. So ``.values()`` returns a list of the stack_infos in an
55 # arbitrary order; the first ``[0]`` grabs the "first" stack_infos;
56 # the second ``[0]`` grabs the first pair from the list; and
57 # the third ``[0]`` grabs the ``frame`` from the pair.
58 # TODO(wrengr): why is that the right frame to look at?
59 frame = suspect.file_to_stack_infos.values()[0][0][0]
60 return self.GetClassFromStackFrame(frame)
61
62 return ''
63 33
64 # TODO(http://crbug.com/657177): return the Component objects 34 # TODO(http://crbug.com/657177): return the Component objects
65 # themselves, rather than strings naming them. 35 # themselves, rather than strings naming them.
66 def Classify(self, suspects, crash_stack): 36 def ClassifyCallStack(self, crash_stack, top_n=2):
67 """Classifies component of a crash. 37 """Classifies component of a crash.
68 38
69 Args: 39 Args:
70 suspects (list of Suspect): Culprit suspects.
71 crash_stack (CallStack): The callstack that caused the crash. 40 crash_stack (CallStack): The callstack that caused the crash.
72 41
73 Returns: 42 Returns:
74 List of top 2 components. 43 List of top 2 components.
75 """ 44 """
76 # If ``suspects`` are available, we use the components from there since 45 def _GetClassFromStackFrame(frame):
Martin Barbella 2017/01/26 23:59:48 s/Class/Component/ seems a bit clearer to me.
Sharu Jiang 2017/01/27 03:20:42 Done.
77 # they're more reliable than the ones from the ``crash_stack``. 46 """Determine which component is responsible for this frame."""
78 if suspects: 47 for component in self.components:
79 classes = map(self.GetClassFromSuspect, suspects[:self.top_n]) 48 if component.MatchesStackFrame(frame):
80 else: 49 return component.component_name
81 classes = map(self.GetClassFromStackFrame,
82 crash_stack.frames[:self.top_n])
83 50
84 return RankByOccurrence(classes, 2)
85
86 # TODO(ymzhang): use component of new path as default. RENAME might
87 # need to return two (old path new path may have different components)
88 def GetClassFromFileChangeInfo(self, file_change_info):
89 """Determine which component is responsible for a touched file."""
90 if not file_change_info:
91 return None 51 return None
92 52
93 for component in self.components: 53 classes = map(_GetClassFromStackFrame,
Martin Barbella 2017/01/26 23:59:48 Similarly, classes is a bit too generic to be easi
Sharu Jiang 2017/01/27 03:20:42 Done.
94 if (file_change_info.change_type == ChangeType.DELETE): 54 crash_stack.frames[:self.top_n_frames])
95 if component.MatchesFile(file_change_info.old_path): 55 return RankByOccurrence(classes, top_n) or None
96 return component.component_name
97 else:
98 if component.MatchesFile(file_change_info.new_path):
99 return component.component_name
100 56
101 return '' 57 # TODO(http://crbug.com/657177): return the Component objects
58 # themselves, rather than strings naming them.
59 def ClassifySuspects(self, suspects, top_n=2):
60 """Classifies component of a list of suspects.
102 61
103 def ClassifyChangeLog(self, change_log, top_n=2): 62 Args:
63 suspects (list of Suspect): Culprit suspects.
64
65 Returns:
66 List of top 2 components.
67 """
68 classes = []
69 for suspect in suspects:
70 classes.extend(self.ClassifySuspect(suspect))
71
72 return RankByOccurrence(classes, top_n) or None
73
74 def ClassifySuspect(self, suspect, top_n=2):
104 """ Classifies components of a change log. 75 """ Classifies components of a change log.
105 76
106 Args: 77 Args:
107 change_log: a change log 78 change_log: a change log
108 top_n: number of components assigned to this change log, default is 2 79 top_n: number of components assigned to this change log, default is 2
109 80
110 Returns: 81 Returns:
111 List of components 82 List of components
112 """ 83 """
113 if not change_log: 84 if not suspect or not suspect.changelog:
114 return None 85 return None
115 86
116 classes = map(self.GetClassFromFileChangeInfo, change_log.touched_files) 87 # TODO(ymzhang): use component of new path as default. RENAME might
Martin Barbella 2017/01/26 23:59:48 Same concern mentioned on this TODO in the previou
Sharu Jiang 2017/01/27 03:20:42 Done.
117 return RankByOccurrence(classes, top_n, rank_function=lambda x:-len(x)) 88 # need to return two (old path new path may have different components)
89 def _GetClassFromTouchedFile(dep_path, touched_file):
90 """Determine which component is responsible for a touched file."""
91 for component in self.components:
92 if component.MatchesTouchedFile(dep_path, touched_file):
93 return component.component_name
94
95 return None
96
97 get_class = functools.partial(_GetClassFromTouchedFile, suspect.dep_path)
98 classes = map(get_class, suspect.changelog.touched_files)
99 return RankByOccurrence(classes, top_n,
100 rank_function=lambda x:-len(x)) or None
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698