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 | |
5 import json | |
6 import time | |
7 import urllib | |
8 | |
9 | |
10 def NormalizePathLinux(path): | |
stgao
2014/08/01 17:22:20
Could we include this function only in this patch?
jeun
2014/08/06 18:22:59
I am not sure what is the best way of doing this,
| |
11 """Normalizes linux path. | |
12 | |
13 Args: | |
14 path: a string representing a path | |
15 | |
16 Returns: | |
17 A tuple containing a component this path is in (e.g blink, skia, etc) | |
18 and a path in that component's repository | |
19 """ | |
20 normalized_path = path | |
21 # TODO(jeun): integrate with parsing DEPS file | |
stgao
2014/08/01 17:22:20
style issue: comment.
jeun
2014/08/06 18:22:59
Done.
| |
22 if 'WebKit/' in path: | |
23 component = 'blink' | |
24 normalized_path = path.split('WebKit/')[1] | |
25 else: | |
26 component = 'chromium' | |
27 | |
28 if normalized_path.startswith( | |
29 '/b/build/slave/ASAN_Release__symbolized_/build/'): | |
30 normalized_path = normalized_path.split( | |
31 '/b/build/slave/ASAN_Release__symbolized_/build/')[1] | |
32 | |
33 if '../../' in normalized_path: | |
34 normalized_path = normalized_path.split('../../')[1] | |
35 | |
36 if 'src/v8/' in normalized_path: | |
37 component = 'v8' | |
38 normalized_path = normalized_path.split('src/v8/')[1] | |
39 | |
40 if './' in normalized_path: | |
41 normalized_path = normalized_path.split('./')[1] | |
42 | |
43 if not normalized_path.startswith('src/') and ( | |
44 not normalized_path.startswith('Source/')): | |
45 normalized_path = 'src/' + normalized_path | |
46 | |
47 return (component, normalized_path) | |
48 | |
49 | |
50 def SplitRange(regression): | |
51 """Splits a range as retrieved from clusterfuzz. | |
52 | |
53 Args: | |
54 regression: a string in format 'r1234:r5678' | |
55 | |
56 Returns: | |
57 a list containing two numbers represented in string, for example | |
58 ['1234','5678'] | |
59 """ | |
60 temp = regression.split(':') | |
61 | |
62 # If regression information is not available, return none | |
63 if len(temp) != 2: | |
64 return None | |
65 | |
66 start_range = temp[0] | |
67 end_range = temp[1] | |
68 | |
69 # Check if the range starts with r, such as in 'r10000' format | |
70 if start_range.startswith('r'): | |
71 start_range = start_range[1:] | |
72 if end_range.startswith('r'): | |
73 end_range = end_range[1:] | |
74 | |
75 return [start_range, end_range] | |
76 | |
77 | |
78 def LoadJSON(json_string): | |
79 """Loads json object from string, or None. | |
80 | |
81 Args: | |
82 json_string: a string to get object from. | |
83 | |
84 Returns: | |
85 JSON object if the string represents a JSON object, None otherwise | |
86 """ | |
87 try: | |
88 data = json.loads(json_string) | |
89 except ValueError: | |
90 data = None | |
91 return data | |
92 | |
93 | |
94 def GetDataFromURL(url): | |
95 """Retrieves raw data from URL, tries 10 times. | |
96 | |
97 Args: | |
98 url: url to get data from | |
99 | |
100 Returns: | |
101 None if the data retrieval fails, or the raw data. | |
102 """ | |
103 data = None | |
104 for i in range(10): | |
stgao
2014/08/01 17:22:20
For the retry, it might be better to make it as a
jeun
2014/08/06 18:22:59
Done.
| |
105 | |
106 # Retrieves data from URL | |
107 try: | |
108 data = urllib.urlopen(url) | |
109 | |
110 # If retrieval is successful, break from the retry look | |
111 if data: | |
112 break | |
113 | |
114 # If retrieval fails, try after 0.1 second | |
115 except IOError: | |
116 time.sleep(0.1) | |
stgao
2014/08/01 17:22:20
Sleep interval could be parameterized too.
jeun
2014/08/06 18:22:59
Done.
| |
117 continue | |
118 | |
119 # If returned data has something in it, return the content | |
120 if data: | |
121 return data.read() | |
122 else: | |
123 return None | |
124 | |
125 | |
126 def FindMinLineDistance(crashed_line_list, changed_line_numbers): | |
127 """Calculates how far the changed line is from one of the crashes. | |
128 | |
129 Finds the minimum distance between the lines that the file crashed on | |
130 and the lines that the file changed. For example, if the file crashed on | |
131 line 200 and the CL changes line 203,204 and 205, the function returns 3. | |
132 | |
133 Args: | |
134 crashed_line_list: list of lines that the file crashed on | |
135 changed_line_numbers: list of lines that the file changed | |
136 | |
137 Returns: | |
138 the minimum distance. If either of the input lists is empty, | |
139 it returns inf. | |
140 | |
141 """ | |
142 min_distance = float('inf') | |
143 | |
144 for line in crashed_line_list: | |
145 for distance in changed_line_numbers: | |
146 # Find the current distance and update the min if current distance is | |
147 # less than current min | |
148 current_distance = abs(line - distance) | |
149 if current_distance < min_distance: | |
150 min_distance = current_distance | |
151 | |
152 return min_distance | |
153 | |
154 | |
155 def GuessIfSamePath(path1, path2): | |
156 """Guesses if two paths represent same path. | |
157 | |
158 Compares the name of the folders in the path (by split('/')), and checks | |
159 if they match either more than 3 or min of path lengths | |
160 | |
161 Args: | |
162 path1: First path | |
163 path2: Second path to compare | |
164 | |
165 Returns: | |
166 True if it they are thought to be a same path, False otherwise | |
167 """ | |
168 path1 = path1.split('/') | |
169 path2 = path2.split('/') | |
170 | |
171 intersection = set(path1).intersection(set(path2)) | |
172 return len(intersection) >= (min(3, min(len(path1), len(path2)))) | |
173 | |
174 | |
175 def FindMinStackFrameNum(stack_frame_index, priorities): | |
176 """Finds the minimum stack number, from the list of stack numbers. | |
177 | |
178 Args: | |
179 stack_frame_index: a list of list containing stack position | |
180 priorities: a list of of priority for each file | |
181 | |
182 Returns: | |
183 inf if stack_frame_index is empty, minimum stack number otherwise | |
184 """ | |
185 # Get the indexes of the highest priority (or low priority number) | |
186 highest_priority = min(priorities) | |
187 highest_priority_indices = [] | |
188 for i in range(len(priorities)): | |
189 if priorities[i] == highest_priority: | |
190 highest_priority_indices.append(i) | |
191 | |
192 # Gather the list of stack frame numbers for the files that change the | |
193 # crash lines | |
194 flattened = [] | |
195 for i in highest_priority_indices: | |
196 flattened += stack_frame_index[i] | |
197 | |
198 # If no stack frame information is available, return inf. Else, return min | |
199 if not flattened: | |
200 return float('inf') | |
201 else: | |
202 return min(flattened) | |
203 | |
204 | |
205 def AddHyperlink(to_add, link): | |
206 """Returns a string with HTML link tag. | |
207 | |
208 Args: | |
209 to_add: a string to add link | |
210 link: a link to add to the string | |
211 | |
212 Returns: | |
213 a string with hyperlink added | |
214 """ | |
215 return '<a href="%s">%s<\\a>' % (link, to_add) | |
216 | |
217 | |
218 def PrettifyList(l): | |
219 """Returns a string representation of a list of ints. | |
220 | |
221 Args: | |
222 l: an int list to prettify | |
223 Returns: | |
224 a string representation of list | |
225 """ | |
226 return str(l)[1:-1] | |
227 | |
228 | |
229 def PrettifyFiles(file_list): | |
230 """Returns a string representation of a list of file names. | |
231 | |
232 Args: | |
233 file_list: a list of tuple, (file_name, file_url) | |
234 Returns: | |
235 a string representation of file names | |
236 """ | |
237 ret = ['\n'] | |
238 for file_name, file_url in file_list: | |
239 ret.append(' %s\n' % AddHyperlink(file_name, file_url)) | |
240 return ''.join(ret) | |
241 | |
242 | |
243 def Intersection(crashed_line_list, stack_frame_index, changed_line_numbers): | |
244 """Finds the overlap betwee changed lines and crashed lines. | |
245 | |
246 Finds the intersection of the lines that caused the crash and | |
247 lines that the file changes. The intersection looks within 3 lines | |
248 of the line that caused the crash. | |
249 | |
250 Args: | |
251 crashed_line_list: list of lines that the file crashed on | |
252 stack_frame_index: list of positions in stack for each of the lines | |
253 changed_line_numbers: list of lines that the file changed | |
254 | |
255 Returns: | |
256 line_intersection: intersection between crashed_line_list and | |
257 changed_line_numbers | |
258 stack_frame_index_intersection: stack number for each of the intersections | |
259 """ | |
260 line_intersection = [] | |
261 stack_frame_index_intersection = [] | |
262 | |
263 # Iterate through the crashed lines, and its occurence in stack | |
264 for (line, stack_frame_index) in zip(crashed_line_list, stack_frame_index): | |
265 | |
266 # Also check previous 3 lines | |
267 line_minus_n = range(line - 3, line + 1) | |
268 | |
269 for changed_line in changed_line_numbers: | |
270 | |
271 # If a CL does not change crahsed line, check next line | |
272 if changed_line not in line_minus_n: | |
273 continue | |
274 | |
275 # If the changed line is exactly the crashed line, add that line | |
276 if line in changed_line_numbers: | |
277 to_add = line | |
278 | |
279 # If the changed line is within 3 lines of the crashed line, add the line | |
280 else: | |
281 to_add = changed_line | |
282 | |
283 # Avoid adding the same line twiece | |
284 if to_add not in line_intersection: | |
285 line_intersection.append(to_add) | |
286 stack_frame_index_intersection.append(stack_frame_index) | |
287 | |
288 break | |
289 | |
290 return (line_intersection, stack_frame_index_intersection) | |
OLD | NEW |