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

Side by Side Diff: scripts/master/perf_count_notifier.py

Issue 11092069: Fix PerfCountNotifier email notifier used by perf_av bot. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build/
Patch Set: Created 8 years, 1 month 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | scripts/master/unittests/perf_count_notifier_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import re 5 import re
6 6
7 from twisted.python import log 7 from twisted.python import log
8 8
9 from buildbot.status.builder import FAILURE, SUCCESS 9 from buildbot.status.builder import FAILURE, SUCCESS
10 10
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 54
55 def _InitRecentResults(self): 55 def _InitRecentResults(self):
56 """Initializes a new failures history object to store results.""" 56 """Initializes a new failures history object to store results."""
57 self.recent_results = FailuresHistory(expiration_time=_EXPIRATION_TIME, 57 self.recent_results = FailuresHistory(expiration_time=_EXPIRATION_TIME,
58 size_limit=1000) 58 size_limit=1000)
59 59
60 def _InitNewEmailResults(self): 60 def _InitNewEmailResults(self):
61 """Initializes a new email results used by each email sent.""" 61 """Initializes a new email results used by each email sent."""
62 self.new_email_results = {REGRESS: [], IMPROVE: []} 62 self.new_email_results = {REGRESS: [], IMPROVE: []}
63 63
64 def _UpdateResults(self, results): 64 def _UpdateResults(self, builder_name, results):
65 """Updates the results by adding/removing from the history. 65 """Updates the results by adding/removing from the history.
66 66
67 Args: 67 Args:
68 results: List of result tuples, each tuple is of the form 68 results: List of result tuples, each tuple is of the form
69 ('REGRESS|IMPROVE', 'value_name'). 69 ('REGRESS|IMPROVE', 'value_name', 'builder').
70 """ 70 """
71 new_results_ids = [' '.join(result) for result in results] 71 new_results_ids = [' '.join(result) for result in results]
72 # Delete the old results if the new results do not have them. 72 # Delete the old results if the new results do not have them.
73 to_delete = [old_id for old_id in self.recent_results.failures 73 to_delete = [old_id for old_id in self.recent_results.failures
74 if old_id not in new_results_ids] 74 if (old_id not in new_results_ids and
75 old_id.endswith(builder_name))]
75 76
76 for old_id in to_delete: 77 for old_id in to_delete:
77 self._DeleteResult(old_id) 78 self._DeleteResult(old_id)
78 79
79 # Update the new results history 80 # Update the new results history
80 for new_id in results: 81 for new_id in results:
81 self._StoreResult(new_id) 82 self._StoreResult(new_id)
82 83
83 def _StoreResult(self, result): 84 def _StoreResult(self, result):
84 """Stores the result value and removes counter results. 85 """Stores the result value and removes counter results.
85 86
86 Example: if this is a REGRESS result then it is stored and its counter 87 Example: if this is a REGRESS result then it is stored and its counter
87 IMPROVE result, if any, is reset. 88 IMPROVE result, if any, is reset.
88 89
89 Args: 90 Args:
90 result: A tuple of the form ('REGRESS|IMPROVE', 'value_name'). 91 result: A tuple of the form ('REGRESS|IMPROVE', 'value_name', 'builder').
91 """ 92 """
92 self.recent_results.Put(' '.join(result)) 93 self.recent_results.Put(' '.join(result))
93 if result[0] == REGRESS: 94 if result[0] == REGRESS:
94 counter_id = IMPROVE + ' ' + result[1] 95 counter_id = IMPROVE + ' '.join(result[1:])
95 else: 96 else:
96 counter_id = REGRESS + ' ' + result[1] 97 counter_id = REGRESS + ' '.join(result[1:])
97 # Reset counter_id count since this breaks the consecutive count of it. 98 # Reset counter_id count since this breaks the consecutive count of it.
98 self._DeleteResult(counter_id) 99 self._DeleteResult(counter_id)
99 100
100 def _DeleteResult(self, result_id): 101 def _DeleteResult(self, result_id):
101 """Removes the history of results identified by result_id. 102 """Removes the history of results identified by result_id.
102 103
103 Args: 104 Args:
104 result_id: The id of the history entry (see _StoreResult() for details). 105 result_id: The id of the history entry (see _StoreResult() for details).
105 """ 106 """
106 num_results = self.recent_results.GetCount(result_id) 107 num_results = self.recent_results.GetCount(result_id)
107 if num_results > 0: 108 if num_results > 0:
108 # This is a hack into FailuresHistory since it does not allow to delete 109 # This is a hack into FailuresHistory since it does not allow to delete
109 # entries in its history unless they are expired. 110 # entries in its history unless they are expired.
110 # FailuresHistory.failures_count is the total number of entries in the 111 # FailuresHistory.failures_count is the total number of entries in the
111 # history limitted by FailuresHistory.size_limit. 112 # history limitted by FailuresHistory.size_limit.
112 del self.recent_results.failures[result_id] 113 del self.recent_results.failures[result_id]
113 self.recent_results.failures_count -= num_results 114 self.recent_results.failures_count -= num_results
114 115
116 def _DeleteAllForBuild(self, builder_name):
117 """Deletes all results related to a builder."""
118 to_delete = [result for result in self.recent_results.failures
119 if result.endswith(builder_name)]
120 for result in to_delete:
121 self._DeleteResult(result)
122
115 def _IsPerfStep(self, step_status): 123 def _IsPerfStep(self, step_status):
116 """Checks if the step name is one of the defined perf tests names.""" 124 """Checks if the step name is one of the defined perf tests names."""
117 return self.getName(step_status) in self.step_names 125 return self.getName(step_status) in self.step_names
118 126
119 def isInterestingStep(self, build_status, step_status, results): 127 def isInterestingStep(self, build_status, step_status, results):
120 """Ignore the step if it is not one of the perf results steps. 128 """Ignore the step if it is not one of the perf results steps.
121 129
122 Returns: 130 Returns:
123 True: - if a REGRESS|IMPROVE happens consecutive minimum number of times. 131 True: - if a REGRESS|IMPROVE happens consecutive minimum number of times.
124 - if it is not a SUCCESS step and neither REGRESS|IMPROVE. 132 - if it is not a SUCCESS step and neither REGRESS|IMPROVE.
125 False: - if it is a SUCCESS step. 133 False: - if it is a SUCCESS step.
126 - if it is a notification which has already been notified. 134 - if it is a notification which has already been notified.
127 """ 135 """
128 if not self._IsPerfStep(step_status): 136 if not self._IsPerfStep(step_status):
129 return False 137 return False
130 138
131 # In case of exceptions, sometimes results output is empty. 139 # In case of exceptions, sometimes results output is empty.
132 if not results: 140 if not results:
133 results = [FAILURE] 141 results = [FAILURE]
134 142
143 builder_name = build_status.getName()
135 # If it is a success step, i.e. not interesting, then reset counters. 144 # If it is a success step, i.e. not interesting, then reset counters.
136 if results[0] == SUCCESS: 145 if results[0] == SUCCESS:
137 self._InitRecentResults() 146 self._DeleteAllForBuild(builder_name)
138 return False 147 return False
139 148
140 # step_text is similar to: 149 # step_text is similar to:
141 # media_tests_av_perf <div class="BuildResultInfo"> PERF_REGRESS: 150 # media_tests_av_perf <div class="BuildResultInfo"> PERF_REGRESS:
142 # time/t (89.07%) PERF_IMPROVE: fps/video (5.40%) </div> 151 # time/t (89.07%) PERF_IMPROVE: fps/video (5.40%) </div>
143 # 152 #
144 # regex would return tuples of the form: 153 # regex would return tuples of the form:
145 # ('REGRESS', 'time/t') 154 # ('REGRESS', 'time/t', 'linux-rel')
146 # ('IMPROVE', 'fps/video') 155 # ('IMPROVE', 'fps/video', 'win-debug')
156 #
157 # It is important to put the builder name as the last element in the tuple
158 # since it is used to check tests that belong to same builder.
147 step_text = ' '.join(step_status.getText()) 159 step_text = ' '.join(step_status.getText())
148 log.msg('[PerfCountNotifier] Analyzing failure text: %s.' % step_text) 160 log.msg('[PerfCountNotifier] Analyzing failure text: %s.' % step_text)
149 161
150 perf_regress = perf_improve = '' 162 perf_regress = perf_improve = ''
151 perf_results = [] 163 perf_results = []
152
153 if PERF_REGRESS in step_text: 164 if PERF_REGRESS in step_text:
154 perf_regress = step_text[step_text.find(PERF_REGRESS) + len(PERF_REGRESS) 165 perf_regress = step_text[step_text.find(PERF_REGRESS) + len(PERF_REGRESS)
155 + 1: step_text.find(PERF_IMPROVE)] 166 + 1: step_text.find(PERF_IMPROVE)]
156 perf_results.extend([(REGRESS, test_name) for test_name in 167 perf_results.extend([(REGRESS, test_name, builder_name) for test_name in
157 re.findall('(\S+) (?=\(.+\))', perf_regress)]) 168 re.findall('(\S+) (?=\(.+\))', perf_regress)])
158 169
159 if PERF_IMPROVE in step_text: 170 if PERF_IMPROVE in step_text:
160 # Based on log_parser/process_log.py PerformanceChangesAsText() function, 171 # Based on log_parser/process_log.py PerformanceChangesAsText() function,
161 # we assume that PERF_REGRESS (if any) appears before PERF_IMPROVE. 172 # we assume that PERF_REGRESS (if any) appears before PERF_IMPROVE.
162 perf_improve = step_text[step_text.find(PERF_IMPROVE) + len(PERF_IMPROVE) 173 perf_improve = step_text[step_text.find(PERF_IMPROVE) + len(PERF_IMPROVE)
163 + 1:] 174 + 1:]
164 perf_results.extend([(IMPROVE, test_name) for test_name in 175 perf_results.extend([(IMPROVE, test_name, builder_name) for test_name in
165 re.findall('(\S+) (?=\(.+\))', perf_improve)]) 176 re.findall('(\S+) (?=\(.+\))', perf_improve)])
166 177
167 # If there is no regress or improve then this could be warning or exception. 178 # If there is no regress or improve then this could be warning or exception.
168 if not perf_results: 179 if not perf_results:
169 if not self.notifications.GetCount(step_text): 180 if not self.notifications.GetCount(step_text):
170 log.msg('[PerfCountNotifier] Unrecognized step status encountered. ' 181 log.msg('[PerfCountNotifier] Unrecognized step status encountered. '
171 'Reporting status as interesting.') 182 'Reporting status as interesting.')
172 self.notifications.Put(step_text) 183 self.notifications.Put(step_text)
173 return True 184 return True
174 else: 185 else:
175 log.msg('[PerfCountNotifier] This problem has already been notified.') 186 log.msg('[PerfCountNotifier] This problem has already been notified.')
176 return False 187 return False
177 188
178 is_interesting = False 189 is_interesting = False
179 update_list = [] 190 update_list = []
180 for result in perf_results: 191 for result in perf_results:
181 if len(result) != 2: 192 if len(result) != 3:
182 # We expect a tuple similar to ('REGRESS', 'time/t') 193 # We expect a tuple similar to ('REGRESS', 'time/t', 'linux-rel')
183 continue 194 continue
184 result_id = ' '.join(result) 195 result_id = ' '.join(result)
185 update_list.append(result) 196 update_list.append(result)
186 log.msg('[PerfCountNotifier] Result: %s happened %d times in a row.' % 197 log.msg('[PerfCountNotifier] Result: %s happened %d times in a row.' %
187 (result_id, self.recent_results.GetCount(result_id) + 1)) 198 (result_id, self.recent_results.GetCount(result_id) + 1))
188 if self.recent_results.GetCount(result_id) >= self.minimum_count - 1: 199 if self.recent_results.GetCount(result_id) >= self.minimum_count - 1:
189 # This is an interesting result! We got the minimum consecutive count of 200 # This is an interesting result! We got the minimum consecutive count of
190 # this result, however we still need to check if its been notified. 201 # this result, however we still need to check if its been notified.
191 if not self.notifications.GetCount(result_id): 202 if not self.notifications.GetCount(result_id):
192 log.msg('[PerfCountNotifier] Result: %s happened enough consecutive ' 203 log.msg('[PerfCountNotifier] Result: %s happened enough consecutive '
193 'times to be reported.' % result_id) 204 'times to be reported.' % result_id)
194 self.notifications.Put(result_id) 205 self.notifications.Put(result_id)
195 # New results that cause email notifications. 206 # New results that cause email notifications.
196 self.new_email_results[result[0]].append(result[1]) 207 self.new_email_results[result[0]].append(result[1])
197 is_interesting = True 208 is_interesting = True
198 else: 209 else:
199 log.msg('[PerfCountNotifier] Result: %s has already been notified.' % 210 log.msg('[PerfCountNotifier] Result: %s has already been notified.' %
200 result_id) 211 result_id)
201 212
202 self._UpdateResults(update_list) 213 self._UpdateResults(builder_name, update_list)
203 214
204 return is_interesting 215 return is_interesting
205 216
206 def buildMessage(self, builder_name, build_status, results, step_name): 217 def buildMessage(self, builder_name, build_status, results, step_name):
207 """Send an email about this interesting step. 218 """Send an email about this interesting step.
208 219
209 Add the perf regressions/improvements that resulted in this email if any. 220 Add the perf regressions/improvements that resulted in this email if any.
210 """ 221 """
211 original_header = self.status_header 222 original_header = self.status_header
212 msg = '' 223 msg = ''
213 if self.new_email_results[REGRESS]: 224 if self.new_email_results[REGRESS]:
214 msg += '%s: %s.\n' % (PERF_REGRESS, 225 msg += '%s: %s.\n' % (PERF_REGRESS,
215 ', '.join(self.new_email_results[REGRESS])) 226 ', '.join(self.new_email_results[REGRESS]))
216 if self.new_email_results[IMPROVE]: 227 if self.new_email_results[IMPROVE]:
217 msg += '%s: %s.\n' % (PERF_IMPROVE, 228 msg += '%s: %s.\n' % (PERF_IMPROVE,
218 ', '.join(self.new_email_results[IMPROVE])) 229 ', '.join(self.new_email_results[IMPROVE]))
219 if msg: 230 if msg:
220 self.status_header += ('\n\nNew perf results in this email:\n%s' % msg) 231 self.status_header += ('\n\nNew perf results in this email:\n%s' % msg)
221 email_msg = ChromiumNotifier.buildMessage(self, builder_name, build_status, 232 email_msg = ChromiumNotifier.buildMessage(self, builder_name, build_status,
222 results, step_name) 233 results, step_name)
223 # Reset header and notification list. 234 # Reset header and notification list.
224 self.status_header = original_header 235 self.status_header = original_header
225 self._InitNewEmailResults() 236 self._InitNewEmailResults()
226 return email_msg 237 return email_msg
OLDNEW
« no previous file with comments | « no previous file | scripts/master/unittests/perf_count_notifier_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698