Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(107)

Side by Side Diff: tools/findit/blame.py

Issue 421223003: [Findit] Plain objects to represent the returned result from running the algorithm, (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: addressed codereview Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tools/findit/matchset.py » ('j') | tools/findit/matchset.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 from threading import Lock, Thread
6
7 import crash_utils
8 import gitparser
9 import svnparser
10 import utils
11
12
13 class Blame(object):
14 """Represents a blame object.
15
16 The object contains blame information for one line of stack, and this
17 information is shown when there are no CLs that change the crashing files.
18 Attributes:
19 content: The content of the line to find the blame for.
20 component: The component this line is in.
21 stack_frame_index: The stack frame index of this file.
22 file_name: The name of the file.
23 line_number: The line that caused a crash.
24 author: The author of this line on the latest revision.
25 crash_revision: The revision that caused the crash.
26 revision: The latest revision of this line before the crash revision.
27 url: The url of this revision.
28 regression: The regression range of the component, if it exists.
29
30 """
31
32 def __init__(self, content, component, stack_frame_index, file_name,
33 line_number, author, crash_revision, revision, url, regression):
34 # Set all the variables from the arguments.
35 self.content = content
36 self.component = component
37 self.stack_frame_index = stack_frame_index
38 self.file = file_name
39 self.line_number = line_number
40 self.author = author
41 self.revision = revision
42 self.url = url
43 self.regression = regression
44 self.distance = float('inf')
45 revision = int(revision)
46
47 # Calculate the distance, where it measures how far the last revision is
48 # from the regression range.
49 if regression:
50 range1 = int(regression[0])
51 range2 = int(regression[1])
52 self.distance = min(abs(range1 - revision), abs(range2 - revision))
53
54 # If the regression is in SVN but it does not have regression info, check
55 # how far the last revision is from crash revision.
56 elif not utils.IsGitHash(crash_revision):
57 self.distance = abs(int(crash_revision) - revision)
58
59
60 class BlameList(object):
61 """Represents a list of blame objects.
62
63 Thread-safe.
64 """
65
66 def __init__(self):
67 self.blame_list = []
68 self.blame_list_lock = Lock()
69
70 def __getitem__(self, index):
71 return self.blame_list[index]
72
73 def AddBlame(self, blame):
74 """Adds blame object to the set."""
75 with self.blame_list_lock:
76 self.blame_list.append(blame)
77
78 def sort(self, key=None):
79 return self.blame_list.sort(key=key)
80
81 def FindBlame(self, callstack, crash_revision_dic, revision_range_dic,
82 url_map, n=10):
83 """Given a stack within a stacktrace, retrieves blame information.
84
85 Only either first 10 or the length of stack, whichever is shorter,
stgao 2014/08/09 19:51:02 10 here is invalid. Same for the in-line comment b
jeun 2014/08/12 18:17:33 Done.
86 results are returned.
87
88 Args:
89 callstack: The list of stack frames.
90 crash_revision_dic: A dictionary that maps component to its crash
91 revision.
92 revision_range_dic: A dictionary that maps component to its revision
93 range.
94 url_map: A map from repository type to urls.
95 n: A number of stack frames to show the blame result for.
96 """
97 # Only return blame information for first 10 frames.
98 stackframes = callstack.GetFirstN(n)
99
100 threads = []
101 # Iterate through frames in stack.
102 for stack_frame in stackframes:
stgao 2014/08/09 19:51:02 Naming is not that consistent. |stackframes| -> |s
jeun 2014/08/12 18:17:33 Done.
103 # If the component this line is from does not have a crash revision,
104 # It is not possible to get blame information so ignore this line.
105 component = stack_frame.component
106 if component not in crash_revision_dic:
107 continue
108
109 crash_revision = crash_revision_dic[component]
110 regression = None
111 is_svn = not utils.IsGitHash(crash_revision)
112
113 # If the revision is in SVN, and if revision information is available,
114 # get it. Not for Git because we cannot calculate the distance.
115 if is_svn:
116 repository_parser = svnparser.SVNParser(url_map['svn'])
117 if revision_range_dic and component in revision_range_dic:
118 regression = revision_range_dic[component]
119 else:
120 repository_parser = gitparser.GitParser(url_map['git'])
121
122 # Generate blame entry, one thread for one entry.
123 blame_thread = Thread(
124 target=self.__GenerateBlameEntry,
125 args=[repository_parser, stack_frame, crash_revision, regression])
126 threads.append(blame_thread)
127 blame_thread.start()
128
129 # Join the results before returning.
130 for blame_thread in threads:
131 blame_thread.join()
132
133 def __GenerateBlameEntry(self, repository_parser, stack_frame,
134 crash_revision, regression):
135 """Generates blame list from the arguments."""
136 stack_frame_index = stack_frame.index
137 component = stack_frame.component
138 file_name = stack_frame.file_name
139 file_path = stack_frame.file_path
140 line = stack_frame.crashed_line_number
141
142 # Parse blame information.
143 parsed_blame_info = repository_parser.ParseBlameInfo(
144 component, file_path, line, crash_revision)
145
146 # If it fails to retrieve information, do not do anything.
147 if not parsed_blame_info:
148 return
149
150 # Create blame object from the parsed info and add it to the list.
151 (content, revision, author, url) = parsed_blame_info
152 blame = Blame(content, component, stack_frame_index, file_name, line,
153 author, crash_revision, revision, url, regression)
154 self.AddBlame(blame)
155
156 def PrettifyBlame(self):
157 """Returns a string representation of blame results.
158
159 Returns:
160 A string representation of blame_list.
161 """
162 blame_list = self.blame_list
163 return_string = []
164 return_string.append('Below are the blame information of the stacktrace.\n')
165 return_string.append('[\n')
166
167 # If blame info is not available, return the message.
168 if not blame_list:
169 return_string.append('No Blame Information Available.\n')
170 return ''.join(return_string)
171
172 # Sort the blame list by its distance, and its position in stack.
173 blame_list.sort(key=lambda blame: (blame.distance,
174 blame.stack_frame_index))
175
176 for blame in blame_list:
177 # If regression information is available, do some filtering.
178 if blame.regression:
179 regressed = blame.regression
180
181 # Discards results that are too far from the regression range.
182 # For example, if regression is 10000:11000, it is very not
183 # likely that a commit from revision 1000 would have caused a crash.
184 if blame.distance > int(regressed[0]) / 4 and blame.distance > int(
185 regressed[1]) / 4:
186 continue
187
188 return_string.append(' {\n')
189 return_string.append(
190 ' suspected_cl: %s\n' % crash_utils.AddHyperlink(
191 blame.revision,
192 blame.url))
193 return_string.append(' component: %s\n' % blame.component)
194 return_string.append(' owner: %s\n' % blame.author)
195 reason = (
196 'The CL changes line %s of file %s from stack %d.' %
197 (blame.line_number, blame.file, blame.stack_frame_index))
198 return_string.append(' reason: %s\n' % reason)
199 if blame.content:
200 return_string.append(' content: %s\n' % blame.content)
201 return_string.append(' }\n')
202
203 return_string.append(']\n')
204 return_string.append('-------------------------------------------\n')
205 return ''.join(return_string)
OLDNEW
« no previous file with comments | « no previous file | tools/findit/matchset.py » ('j') | tools/findit/matchset.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698