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

Side by Side Diff: tools/findit/matchset.py

Issue 421223003: [Findit] Plain objects to represent the returned result from running the algorithm, (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: addressed codereview Created 6 years, 4 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
« tools/findit/blame.py ('K') | « tools/findit/blame.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import logging
6 import re
7 from threading import Lock
8
9 import crash_utils
10
11 LINE_CHANGE_PRIORITY = 1
12 FILE_CHANGE_PRIORITY = 2
13
14
15 class Match(object):
16 """Represents a match entry.
17
18 A match is a CL that is suspected to have caused the crash. A match object
19 contains information about files it changes, their owners, etc.
20
21 Attributes:
22 line_of_crash: The list of lines that caused crash for this CL.
23 function: The list of functions that caused the crash.
24 min_distance: The minimum difference between the lines that CL changed and
25 lines that caused the crash.
26 files: The list of files that the CL changed.
27 file_urls: The list of URLs for the file.
28 owner: The owner of the CL.
29 component: The component that this CL belongs to.
30 stack_frame_indices: For files that caused crash, list of where in the
31 stackframe they occur.
32 rank: The highest priority among the files the CL changes.
33 priorities: For each files, whether it changes the crashed line
34 (priority = 1) or is a simple file change (priority = 2).
35 url: URL of the CL.
36 review_url: The codereview address that reviews this CL.
37 reviewers: The list of people that reviewed this CL.
38 reason: The reason why this CL is suspected.
39 """
40 REVERT_PATTERN = re.compile(r'(revert\w*) r?(\d+)', re.I)
41
42 def __init__(self, revision, component):
43 self.isrevert = False
44 self.revert_of = None
45 self.line_of_crash = []
46 self.function = []
47 self.min_distance = float('inf')
48 self.files = []
49 self.file_urls = []
50 self.owner = revision['author']
51 self.component = component
52 self.stack_frame_indices = []
53 self.rank = float('inf')
54 self.priorities = []
55 self.url = revision['url']
56 self.review_url = 'N/A'
57 self.reviewers = ['N/A']
58 self.reason = None
59
60 def ParseMessage(self, message, codereview_api_url):
61 """Parses the message.
62
63 It checks the message to extract the code review website and list of
64 reviewers, and it also checks if the CL is a revert of another CL.
65
66 Args:
67 message: The message to parse.
68 codereview_api_url: URL to retrieve codereview data from.
69 """
70 for line in message.splitlines():
71 line = line.strip()
72
73 # Check if the line has the code review information.
74 if line.startswith('Review URL: '):
75
76 # Get review number for the code review site from the line.
77 parts = line.split('Review URL: ')
78 self.review_url = parts[1].strip()
79 issue_number = self.review_url.split('/')[-1]
80
81 # Get JSON from the code review site, ignore the line if it fails.
82 url = codereview_api_url % issue_number
83 json_string = crash_utils.GetDataFromURL(url)
84 if not json_string:
85 logging.warning('Failed to retrieve code review information from %s',
86 url)
87 continue
88
89 # Load the JSON from the string, and get the list of reviewers.
90 code_review = crash_utils.LoadJSON(json_string)
91 if code_review:
92 self.reviewers = code_review['reviewers']
93
94 # Check if this CL is a revert of other CL.
95 if line.lower().startswith('revert'):
96 self.isrevert = True
97
98 # Check if the line says what CL this CL is a revert of.
99 revert = self.REVERT_PATTERN.match(line)
100 if revert:
101 self.revert_of = revert.group(2)
102 return
103
104
105 class MatchSet(object):
106 """Represents a set of matches."""
107
108 def __init__(self, codereview_api_url):
109 self.codereview_api_url = codereview_api_url
110 self.matches = {}
111 self.cls_to_ignore = set()
112 self.matches_lock = Lock()
113
114 def GenerateMatchEntry(
115 self, revisions, cl, file_path, file_name, function, component,
116 crashed_lines, stack_frame_indices, file_action, repository_parser):
117 """Generates a match object.
118
119 Args:
120 revisions: The dictionary mapping cl's number to the revision information,
121 as returned from a function ParseRevisions in parser.
122 cl: The SVN revision number or git hash.
123 file_path: The path of the file.
124 file_name: The name of the file that this CL might be the culprit cl.
125 function: The function that caused an crash.
126 component: The component the file is from.
127 crashed_lines: The list of the lines in the file that caused the crash.
128 stack_frame_indices: The list of positions of this file within a stack.
129 file_action: Whether file is modified, added or deleted.
130 repository_parser: The parser object to parse line diff.
131 """
132 # Check if this CL should be avoided.
133 with self.matches_lock:
134 if cl in self.cls_to_ignore:
135 return
136
137 # If this CL is not already identified as suspected, create a new entry.
138 if cl not in self.matches:
139 revision = revisions[cl]
140 match = Match(revision, component)
141 message = revisions[cl]['message']
142 # TODO(jeun): Don't hold lock while issuing http request.
143 match.ParseMessage(message, self.codereview_api_url)
144
145 # If this match is a revert, add to the set of CLs to be avoided.
146 if match.isrevert:
stgao 2014/08/09 19:51:02 |isrevert| -> |is_reverted|?
147 self.cls_to_ignore.add(cl)
148
149 # If this match has info on what CL it is reverted from, add that CL.
150 if match.revert_of:
151 self.cls_to_ignore.add(match.revert_of)
152
153 # Return because we do not need to look at this CL anymore.
154 return
155
156 # Add this CL to the set of matches.
157 self.matches[cl] = match
158
159 # Else, bring in the existing result.
160 else:
161 match = self.matches[cl]
162
163 # Parse line diff information for this file.
164 (diff_url, changed_line_numbers, changed_line_contents) = (
165 repository_parser.ParseLineDiff(file_path, component, file_action, cl))
166
167 # Find the intersection between the lines that this file crashed on and
168 # the changed lines.
169 (line_intersection, stack_frame_index_intersection) = (
170 crash_utils.Intersection(
171 crashed_lines, stack_frame_indices, changed_line_numbers))
172
173 # Find the minimum distance between the changed lines and crashed lines.
174 min_distance = crash_utils.FindMinLineDistance(crashed_lines,
175 changed_line_numbers)
176
177 # Check whether this CL changes the crashed lines or not.
178 if line_intersection:
179 priority = LINE_CHANGE_PRIORITY
180 else:
181 priority = FILE_CHANGE_PRIORITY
182
183 # Add the parsed information to the object.
184 with self.matches_lock:
185 match.line_of_crash.append(line_intersection)
186 match.files.append(file_name)
187
188 # Update the min distance only if it is less than the current one.
189 if min_distance < match.min_distance:
190 match.min_distance = min_distance
191
192 # If this CL does not change the crashed line, all occurrence of this
193 # file in the stack has the same significance.
194 if not stack_frame_index_intersection:
195 stack_frame_index_intersection = stack_frame_indices
196 match.stack_frame_indices.append(stack_frame_index_intersection)
197 match.file_urls.append(diff_url)
198
199 # Only record the highest rank of this CL.
200 if priority < match.rank:
201 match.rank = priority
202 match.priorities.append(priority)
203 match.function.append(function)
204
205 def RemoveReverts(self):
206 """Removes CLs that are revert."""
207 for cl in self.matches:
208 if cl in self.cls_to_ignore:
209 del self.matches[cl]
OLDNEW
« tools/findit/blame.py ('K') | « tools/findit/blame.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698