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

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 logging 7 import logging
8 import re 8 import re
9 9
10 from crash.component import Component 10 from crash.component import Component
11 from crash.occurrence import RankByOccurrence 11 from crash.occurrence import RankByOccurrence
12 from libs.gitiles.diff import ChangeType 12 from libs.gitiles.diff import ChangeType
13 13
14 14
15 class ComponentClassifier(object): 15 class ComponentClassifier(object):
16 """Determines the component of a crash. 16 """Determines the component of a crash.
17 17
18 For example: ['Blink>DOM', 'Blink>HTML']. 18 For example: ['Blink>DOM', 'Blink>HTML'].
19 """ 19 """
20 20
21 def __init__(self, components, top_n): 21 def __init__(self, components, top_n_frames):
22 """Build a classifier for components. 22 """Build a classifier for components.
23 23
24 Args: 24 Args:
25 components (list of crash.component.Component): the components to 25 components (list of crash.component.Component): the components to
26 check for. 26 check for.
27 top_n (int): how many frames of the callstack to look at""" 27 top_n_frames (int): how many frames of the callstack to look at
28 """
28 super(ComponentClassifier, self).__init__() 29 super(ComponentClassifier, self).__init__()
29 if not components: 30 self.components = [] if components is None else components
wrengr 2017/01/30 19:14:18 Can simplify that to ``self.components = component
Sharu Jiang 2017/01/30 22:23:52 Done.
30 logging.warning('Empty configuration for component classifier.') 31 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 32
64 # TODO(http://crbug.com/657177): return the Component objects 33 # TODO(http://crbug.com/657177): return the Component objects
65 # themselves, rather than strings naming them. 34 # themselves, rather than strings naming them.
66 def Classify(self, suspects, crash_stack): 35 def ClassifyCallStack(self, stack, top_n=2):
67 """Classifies component of a crash. 36 """Classifies component of a crash.
68 37
69 Args: 38 Args:
70 suspects (list of Suspect): Culprit suspects. 39 stack (CallStack): The callstack that caused the crash.
71 crash_stack (CallStack): The callstack that caused the crash. 40 top_n (int): The number of top components for the stack
wrengr 2017/01/30 19:14:18 would help make the code below more legible to cal
Sharu Jiang 2017/01/30 22:23:52 Done.
72 41
73 Returns: 42 Returns:
74 List of top 2 components. 43 List of top 2 components.
wrengr 2017/01/30 19:14:19 Should say returns the top ``top_n`` components
Sharu Jiang 2017/01/30 22:23:52 Done.
75 """ 44 """
76 # If ``suspects`` are available, we use the components from there since 45 def GetComponentFromStackFrame(frame):
wrengr 2017/01/30 19:14:18 Since this doesn't close over any local variables,
Sharu Jiang 2017/01/30 22:23:51 I hide this because I don't want client to use it
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 components = map(GetComponentFromStackFrame,
94 if (file_change_info.change_type == ChangeType.DELETE): 54 stack.frames[:self.top_n_frames])
95 if component.MatchesFile(file_change_info.old_path): 55 return RankByOccurrence(components, top_n) or None
wrengr 2017/01/30 19:14:18 What's the point of returning ``None`` rather than
Sharu Jiang 2017/01/30 22:23:51 Acknowledged.
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:
104 """ Classifies components of a change log. 63 suspects (list of Suspect): Culprit suspects.
wrengr 2017/01/30 19:14:19 missing documentation for ``top_n``
Sharu Jiang 2017/01/30 22:23:51 Done.
64
65 Returns:
66 List of top 2 components.
wrengr 2017/01/30 19:14:19 should say ``top_n`` since not hard-coded to 2
Sharu Jiang 2017/01/30 22:23:51 Done.
67 """
68 components = []
69 for suspect in suspects:
70 components.extend(self.ClassifySuspect(suspect))
wrengr 2017/01/30 19:14:19 Pretty sure this will break whenever ClassifySuspe
Sharu Jiang 2017/01/30 22:23:51 Oops, return [] instead of None.
71
72 return RankByOccurrence(components, top_n,
73 rank_function=lambda x: -len(x)) or None
wrengr 2017/01/30 19:14:19 Again, what's the point of returning ``None`` rath
Sharu Jiang 2017/01/30 22:23:52 Done.
74
75 def ClassifySuspect(self, suspect, top_n=2):
76 """ Classifies components of a suspect.
105 77
106 Args: 78 Args:
107 change_log: a change log 79 suspect (Suspect): a change log
108 top_n: number of components assigned to this change log, default is 2 80 top_n (int): number of components assigned to this suspect, default is 2
109 81
110 Returns: 82 Returns:
111 List of components 83 List of components
112 """ 84 """
113 if not change_log: 85 if not suspect or not suspect.changelog:
114 return None 86 return None
115 87
116 classes = map(self.GetClassFromFileChangeInfo, change_log.touched_files) 88 def GetComponentFromTouchedFile(touched_file):
wrengr 2017/01/30 19:14:19 Again, would be simpler to float this out. Can be
Sharu Jiang 2017/01/30 22:23:52 This one use local variable suspect, so I'd like t
117 return RankByOccurrence(classes, top_n, rank_function=lambda x:-len(x)) 89 """Determine which component is responsible for a touched file."""
90 for component in self.components:
91 if component.MatchesTouchedFile(suspect.dep_path, touched_file):
92 return component.component_name
93
94 return None
95
96 components = map(GetComponentFromTouchedFile,
97 suspect.changelog.touched_files)
98 return RankByOccurrence(components, top_n,
99 rank_function=lambda x: -len(x)) or None
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698