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