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 5import re 6 7from threading import Lock 8 9import crash_utils 10 11 12REVIEW_URL_PATTERN = re.compile(r'Review URL:( *)(.*?)/(\d+)') 13 14 15class 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 authors, etc. 20 21 Attributes: 22 is_revert: True if this CL is reverted by other CL. 23 revert_of: If this CL is a revert of some other CL, a revision number/ 24 git hash of that CL. 25 crashed_line_numbers: The list of lines that caused crash for this CL. 26 function_list: The list of functions that caused the crash. 27 min_distance: The minimum distance between the lines that CL changed and 28 lines that caused the crash. 29 changed_files: The list of files that the CL changed. 30 changed_file_urls: The list of URLs for the file. 31 author: The author of the CL. 32 component_name: The name of the component that this CL belongs to. 33 stack_frame_indices: For files that caused crash, list of where in the 34 stackframe they occur. 35 priorities: A list of priorities for each of the changed file. A priority 36 is 1 if the file changes a crashed line, and 2 if it changes 37 the file but not the crashed line. 38 reivision_url: The revision URL of the CL. 39 review_url: The codereview URL that reviews this CL. 40 reviewers: The list of people that reviewed this CL. 41 reason: The reason why this CL is suspected. 42 """ 43 REVERT_PATTERN = re.compile(r'(revert\w*) r?(\d+)', re.I) 44 45 def __init__(self, revision, component_name): 46 self.is_revert = False 47 self.revert_of = None 48 self.message = None 49 self.crashed_line_numbers = [] 50 self.function_list = [] 51 self.min_distance = crash_utils.INFINITY 52 self.min_distance_info = None 53 self.changed_files = [] 54 self.changed_file_urls = [] 55 self.author = revision['author'] 56 self.component_name = component_name 57 self.stack_frame_indices = [] 58 self.priorities = [] 59 self.revision_url = revision['url'] 60 self.review_url = '' 61 self.reviewers = [] 62 self.reason = None 63 64 def ParseMessage(self, message, codereview_api_url): 65 """Parses the message. 66 67 It checks the message to extract the code review website and list of 68 reviewers, and it also checks if the CL is a revert of another CL. 69 70 Args: 71 message: The message to parse. 72 codereview_api_url: URL to retrieve codereview data from. 73 """ 74 self.message = message 75 for line in message.splitlines(): 76 line = line.strip() 77 review_url_line_match = REVIEW_URL_PATTERN.match(line) 78 79 # Check if the line has the code review information. 80 if review_url_line_match: 81 82 # Get review number for the code review site from the line. 83 issue_number = review_url_line_match.group(3) 84 85 # Get JSON from the code review site, ignore the line if it fails. 86 url = codereview_api_url % issue_number 87 json_string = crash_utils.GetDataFromURL(url) 88 if not json_string: 89 continue 90 91 # Load the JSON from the string, and get the list of reviewers. 92 code_review = crash_utils.LoadJSON(json_string) 93 if code_review: 94 self.reviewers = code_review['reviewers'] 95 96 # Check if this CL is a revert of other CL. 97 if line.lower().startswith('revert'): 98 self.is_revert = True 99 100 # Check if the line says what CL this CL is a revert of. 101 revert = self.REVERT_PATTERN.match(line) 102 if revert: 103 self.revert_of = revert.group(2) 104 return 105 106 107class MatchSet(object): 108 """Represents a set of matches. 109 110 Attributes: 111 matches: A map from CL to a match object. 112 cls_to_ignore: A set of CLs to ignore. 113 matches_lock: A lock guarding matches dictionary. 114 """ 115 116 def __init__(self, codereview_api_url): 117 self.codereview_api_url = codereview_api_url 118 self.matches = {} 119 self.cls_to_ignore = set() 120 self.matches_lock = Lock() 121 122 def RemoveRevertedCLs(self): 123 """Removes CLs that are revert.""" 124 for cl in self.matches: 125 if cl in self.cls_to_ignore: 126 del self.matches[cl] 127