Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 import re | |
|
stgao
2014/08/04 20:44:57
Add a new line before this one.
jeun
2014/08/06 21:33:04
Done.
| |
| 5 from threading import Lock | |
| 6 import findit_for_crash_utils as findit_utils | |
|
stgao
2014/08/04 20:44:57
seems not up-to-day.
jeun
2014/08/06 21:33:05
Done.
| |
| 7 | |
| 8 LINE_CHANGE_PRIORITY = 1 | |
| 9 FILE_CHANGE_PRIORITY = 2 | |
| 10 pl = Lock() | |
|
stgao
2014/08/04 20:44:57
Please use full name.
jeun
2014/08/06 21:33:05
Done.
| |
| 11 | |
| 12 | |
| 13 class MatchSet(object): | |
| 14 """Represents a set of matches.""" | |
| 15 | |
| 16 def __init__(self, codereview_api_url): | |
| 17 self.codereview_api_url = codereview_api_url | |
| 18 self.matches = {} | |
| 19 self.cls_to_avoid = set() | |
| 20 self.matches_lock = Lock() | |
| 21 | |
| 22 def GenerateMatchEntry( | |
|
stgao
2014/08/04 20:44:58
I think this function is closely-related to the al
jeun
2014/08/06 21:33:05
We can talk about this. I feel like this function
| |
| 23 self, revisions, cl, file_path, file_name, function, component, | |
| 24 crashed_lines, stack_frame_index, file_action, repository_parser): | |
| 25 """Generates a match object. | |
| 26 | |
| 27 A match is a CL that is suspected to have caused the crash. A match object | |
|
stgao
2014/08/04 20:44:57
It might be better to move the Match class up to t
jeun
2014/08/06 21:33:04
Done.
| |
| 28 contains a line that the file crashed, a name of the function, the minimum | |
| 29 distance from the crashed line and the line this file changed, list of the | |
| 30 files that this CL changes, URLs of the files the CL changes, the author of | |
| 31 the CL, the component, list of the stack numbers,the priority of the CL, | |
| 32 review address and reviewers. | |
| 33 | |
| 34 Args: | |
| 35 revisions: a dictionary mapping cl's number to the revision information, | |
| 36 as returned from ParseRevisions | |
|
stgao
2014/08/04 20:44:57
ParseRevisions is unclear here.
jeun
2014/08/06 21:33:04
Done.
| |
| 37 cl: a SVN revision number or git hash | |
| 38 file_path: path of the file | |
| 39 file_name: a name of the file that this CL might be the culprit cl | |
| 40 function: function that caused an crash | |
| 41 component: a component the file is from | |
| 42 crashed_lines: a list of the lines in the file that caused the crash | |
| 43 stack_frame_index: a list of positions of this file within a stack | |
| 44 file_action: whether file is modified, added or deleted | |
| 45 repository_parser: a parser object to parse line diff | |
| 46 """ | |
| 47 # Check if this CL should be avoided | |
| 48 self.matches_lock.acquire() | |
| 49 if cl in self.cls_to_avoid: | |
|
stgao
2014/08/04 20:44:57
avoid -> ignore?
jeun
2014/08/06 21:33:05
Done.
| |
| 50 self.matches_lock.release() | |
| 51 return | |
| 52 | |
| 53 # If this CL is not already identified as suspected, create a new entry. | |
| 54 if cl not in self.matches: | |
| 55 revision = revisions[cl] | |
| 56 match = Match(revision, component) | |
| 57 message = revisions[cl]['message'] | |
| 58 match.ParseMessage(message, self.codereview_api_url) | |
|
stgao
2014/08/04 20:44:58
This function call will be time-consuming as it is
jeun
2014/08/06 21:33:04
Done.
| |
| 59 | |
| 60 # If this match is a revert, add to the set of CLs to be avoided | |
| 61 if match.isrevert: | |
| 62 self.cls_to_avoid.add(cl) | |
| 63 | |
| 64 # If this match has info on what CL it is reverted from, add that CL | |
| 65 if match.revert_of: | |
| 66 self.cls_to_avoid.add(match.revert_of) | |
|
stgao
2014/08/04 20:44:58
What if the cl revert_of was already analyzed and
jeun
2014/08/06 21:33:05
I remove all reverts at the end again.
| |
| 67 | |
| 68 self.matches_lock.release() | |
| 69 return | |
| 70 self.matches[cl] = match | |
| 71 | |
| 72 # Else, bring in the existing result | |
| 73 else: | |
| 74 match = self.matches[cl] | |
| 75 | |
| 76 self.matches_lock.release() | |
|
stgao
2014/08/04 20:44:57
If code above raise an exception, the lock won't b
jeun
2014/08/06 21:33:04
Done.
| |
| 77 | |
| 78 # Parse line diff information for this file | |
| 79 (diff_url, changed_line_numbers, changed_line_contents) = ( | |
| 80 repository_parser.ParseLineDiff(file_path, component, file_action, cl)) | |
| 81 | |
| 82 # Find the intersection between the lines that this file crashed on and | |
| 83 # the changed lines | |
| 84 (line_intersection, stack_frame_index_intersection) = findit_utils.Intersect ion( | |
|
stgao
2014/08/04 20:44:58
style: > 80 chars.
jeun
2014/08/06 21:33:05
Done.
| |
| 85 crashed_lines, stack_frame_index, changed_line_numbers) | |
| 86 | |
| 87 # Find the minimum distance between the changed lines and crashed lines | |
| 88 min_distance = findit_utils.FindMinLineDistance(crashed_lines, | |
| 89 changed_line_numbers) | |
| 90 | |
| 91 # Check whether this CL changes the crashed lines or not | |
| 92 if line_intersection: | |
| 93 priority = LINE_CHANGE_PRIORITY | |
| 94 else: | |
| 95 priority = FILE_CHANGE_PRIORITY | |
| 96 | |
| 97 # Add the parsed information to the object | |
| 98 with self.matches_lock: | |
| 99 match.line_of_crash.append(line_intersection) | |
| 100 match.file.append(file_name) | |
| 101 | |
| 102 # Update the min distance only if it is less than the current one | |
| 103 if min_distance < match.min_distance: | |
| 104 match.min_distance = min_distance | |
| 105 | |
| 106 # If this CL does not change the crashed line, all occurrence of this | |
| 107 # file in the stack has the same significance | |
| 108 if not stack_frame_index_intersection: | |
| 109 stack_frame_index_intersection = stack_frame_index | |
| 110 match.stack_frame_index.append(stack_frame_index_intersection) | |
| 111 match.file_url.append(diff_url) | |
| 112 | |
| 113 # Only record the highest rank of this CL | |
| 114 if priority < match.rank: | |
| 115 match.rank = priority | |
| 116 match.priorities.append(priority) | |
| 117 match.function.append(function) | |
| 118 | |
| 119 def RemoveReverts(self): | |
| 120 """Removes CLs that are revert.""" | |
| 121 for cl in self.matches: | |
| 122 if cl in self.cls_to_avoid: | |
| 123 del self.matches[cl] | |
| 124 | |
| 125 | |
| 126 class Match(object): | |
| 127 """Represents a match entry. | |
| 128 | |
| 129 Attributes: | |
| 130 line_of_crash: list of lines that caused crash for this CL | |
| 131 function: list of functions that caused the crash | |
| 132 min_distance: minimum difference between the lines that CL changed and | |
| 133 lines that caused the crash | |
| 134 file: list of files that the CL changed | |
|
stgao
2014/08/04 20:44:58
files?
jeun
2014/08/06 21:33:05
Done.
| |
| 135 file_url: list of URLs for the file | |
|
stgao
2014/08/04 20:44:57
file_urls?
jeun
2014/08/06 21:33:05
Done.
| |
| 136 owner: owner of the CL | |
| 137 component: component that this CL belongs to | |
| 138 stack_frame_index: for files that caused crash, list of where in the | |
|
stgao
2014/08/04 20:44:57
The name doesn't indicate it is a list.
jeun
2014/08/06 21:33:04
Done.
| |
| 139 stackframe they occur | |
| 140 rank: the highest priority among the files the CL changes | |
|
stgao
2014/08/04 20:44:57
What's the concept of priority here?
Would you min
jeun
2014/08/06 21:33:05
Done.
| |
| 141 priorities: for each files, whether it changes the crashed line or is a | |
| 142 simple file change | |
| 143 url: URL of the CL | |
| 144 review_address: a codereview address that reviews this CL | |
|
stgao
2014/08/04 20:44:57
review_url?
jeun
2014/08/06 21:33:05
Done.
| |
| 145 reviewers: list of people that reviewed this CL | |
| 146 reason: reason why this CL is suspected | |
| 147 """ | |
| 148 REVERT_PATTERN = re.compile(r'(revert\w*) r?(\d+)', re.I) | |
| 149 | |
| 150 def __init__(self, revision, component): | |
| 151 self.isrevert = False | |
| 152 self.revert_of = None | |
| 153 self.line_of_crash = [] | |
| 154 self.function = [] | |
| 155 self.min_distance = float('inf') | |
| 156 self.file = [] | |
| 157 self.file_url = [] | |
| 158 self.owner = revision['author'] | |
| 159 self.component = component | |
| 160 self.stack_frame_index = [] | |
| 161 self.rank = float('inf') | |
| 162 self.priorities = [] | |
| 163 self.url = revision['url'] | |
| 164 self.review_address = 'N/A' | |
| 165 self.reviewers = ['N/A'] | |
| 166 self.reason = None | |
| 167 | |
| 168 def ParseMessage(self, message, codereview_api_url): | |
| 169 """Parses the message. | |
| 170 | |
| 171 It checks the message to extract the code review website and list of | |
| 172 reviewers, and it also checks if the CL is a revert of another CL. | |
| 173 | |
| 174 Args: | |
| 175 message: a message to parse | |
| 176 codereview_api_url: URL to retrieve codereview data from | |
| 177 """ | |
| 178 for line in message.splitlines(): | |
| 179 line = line.strip() | |
| 180 | |
| 181 # Check if the line has the code review information | |
| 182 if line.startswith('Review URL: '): | |
| 183 | |
| 184 # Get review number for the code review site from the line | |
| 185 parts = line.split('Review URL: ') | |
| 186 self.review_address = parts[1].strip() | |
| 187 review_number = self.review_address.split('/')[-1] | |
|
stgao
2014/08/04 20:44:57
Usually, it is called issue number.
jeun
2014/08/06 21:33:04
Done.
| |
| 188 | |
| 189 # Get JSON from the code review site, ignore the line if it fails | |
| 190 url = codereview_api_url % review_number | |
| 191 json_string = findit_utils.GetDataFromURL(url) | |
| 192 if not json_string: | |
| 193 print 'Failed to retrieve code review information from %s' % url | |
| 194 continue | |
| 195 | |
| 196 # Load the JSON from the string, and get the list of reviewers | |
| 197 code_review = findit_utils.LoadJSON(json_string) | |
| 198 if code_review: | |
| 199 self.reviewers = code_review['reviewers'] | |
| 200 | |
| 201 # Check if this CL is a revert of other CL | |
| 202 if line.lower().startswith('revert'): | |
| 203 self.isrevert = True | |
| 204 | |
| 205 # Check if the line says what CL this CL is a revert of | |
| 206 revert = self.REVERT_PATTERN.match(line) | |
| 207 if revert: | |
| 208 self.revert_of = revert.group(2) | |
| 209 return | |
| OLD | NEW |