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