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

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, 2 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 | Annotate | Revision Log
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)]
cmp 2012/10/22 18:39:16 Since the conditional spans the line, it would be
shadi1 2012/10/23 00:11:16 Done.
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')
146 # ('IMPROVE', 'fps/video') 155 # ('IMPROVE', 'fps/video')
147 step_text = ' '.join(step_status.getText()) 156 step_text = ' '.join(step_status.getText())
148 log.msg('[PerfCountNotifier] Analyzing failure text: %s.' % step_text) 157 log.msg('[PerfCountNotifier] Analyzing failure text: %s.' % step_text)
149 158
150 perf_regress = perf_improve = '' 159 perf_regress = perf_improve = ''
151 perf_results = [] 160 perf_results = []
152
153 if PERF_REGRESS in step_text: 161 if PERF_REGRESS in step_text:
154 perf_regress = step_text[step_text.find(PERF_REGRESS) + len(PERF_REGRESS) 162 perf_regress = step_text[step_text.find(PERF_REGRESS) + len(PERF_REGRESS)
155 + 1: step_text.find(PERF_IMPROVE)] 163 + 1: step_text.find(PERF_IMPROVE)]
156 perf_results.extend([(REGRESS, test_name) for test_name in 164 perf_results.extend([(REGRESS, test_name, builder_name) for test_name in
157 re.findall('(\S+) (?=\(.+\))', perf_regress)]) 165 re.findall('(\S+) (?=\(.+\))', perf_regress)])
158 166
159 if PERF_IMPROVE in step_text: 167 if PERF_IMPROVE in step_text:
160 # Based on log_parser/process_log.py PerformanceChangesAsText() function, 168 # Based on log_parser/process_log.py PerformanceChangesAsText() function,
161 # we assume that PERF_REGRESS (if any) appears before PERF_IMPROVE. 169 # we assume that PERF_REGRESS (if any) appears before PERF_IMPROVE.
162 perf_improve = step_text[step_text.find(PERF_IMPROVE) + len(PERF_IMPROVE) 170 perf_improve = step_text[step_text.find(PERF_IMPROVE) + len(PERF_IMPROVE)
163 + 1:] 171 + 1:]
164 perf_results.extend([(IMPROVE, test_name) for test_name in 172 perf_results.extend([(IMPROVE, test_name, builder_name) for test_name in
165 re.findall('(\S+) (?=\(.+\))', perf_improve)]) 173 re.findall('(\S+) (?=\(.+\))', perf_improve)])
166 174
167 # If there is no regress or improve then this could be warning or exception. 175 # If there is no regress or improve then this could be warning or exception.
168 if not perf_results: 176 if not perf_results:
169 if not self.notifications.GetCount(step_text): 177 if not self.notifications.GetCount(step_text):
170 log.msg('[PerfCountNotifier] Unrecognized step status encountered. ' 178 log.msg('[PerfCountNotifier] Unrecognized step status encountered. '
171 'Reporting status as interesting.') 179 'Reporting status as interesting.')
172 self.notifications.Put(step_text) 180 self.notifications.Put(step_text)
173 return True 181 return True
174 else: 182 else:
175 log.msg('[PerfCountNotifier] This problem has already been notified.') 183 log.msg('[PerfCountNotifier] This problem has already been notified.')
176 return False 184 return False
177 185
178 is_interesting = False 186 is_interesting = False
179 update_list = [] 187 update_list = []
180 for result in perf_results: 188 for result in perf_results:
181 if len(result) != 2: 189 if len(result) != 3:
182 # We expect a tuple similar to ('REGRESS', 'time/t') 190 # We expect a tuple similar to ('REGRESS', 'time/t', 'linux-rel')
183 continue 191 continue
184 result_id = ' '.join(result) 192 result_id = ' '.join(result)
185 update_list.append(result) 193 update_list.append(result)
186 log.msg('[PerfCountNotifier] Result: %s happened %d times in a row.' % 194 log.msg('[PerfCountNotifier] Result: %s happened %d times in a row.' %
187 (result_id, self.recent_results.GetCount(result_id) + 1)) 195 (result_id, self.recent_results.GetCount(result_id) + 1))
188 if self.recent_results.GetCount(result_id) >= self.minimum_count - 1: 196 if self.recent_results.GetCount(result_id) >= self.minimum_count - 1:
189 # This is an interesting result! We got the minimum consecutive count of 197 # 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. 198 # this result, however we still need to check if its been notified.
191 if not self.notifications.GetCount(result_id): 199 if not self.notifications.GetCount(result_id):
192 log.msg('[PerfCountNotifier] Result: %s happened enough consecutive ' 200 log.msg('[PerfCountNotifier] Result: %s happened enough consecutive '
193 'times to be reported.' % result_id) 201 'times to be reported.' % result_id)
194 self.notifications.Put(result_id) 202 self.notifications.Put(result_id)
195 # New results that cause email notifications. 203 # New results that cause email notifications.
196 self.new_email_results[result[0]].append(result[1]) 204 self.new_email_results[result[0]].append(result[1])
197 is_interesting = True 205 is_interesting = True
198 else: 206 else:
199 log.msg('[PerfCountNotifier] Result: %s has already been notified.' % 207 log.msg('[PerfCountNotifier] Result: %s has already been notified.' %
200 result_id) 208 result_id)
201 209
202 self._UpdateResults(update_list) 210 self._UpdateResults(builder_name, update_list)
203 211
204 return is_interesting 212 return is_interesting
205 213
206 def buildMessage(self, builder_name, build_status, results, step_name): 214 def buildMessage(self, builder_name, build_status, results, step_name):
207 """Send an email about this interesting step. 215 """Send an email about this interesting step.
208 216
209 Add the perf regressions/improvements that resulted in this email if any. 217 Add the perf regressions/improvements that resulted in this email if any.
210 """ 218 """
211 original_header = self.status_header 219 original_header = self.status_header
212 msg = '' 220 msg = ''
213 if self.new_email_results[REGRESS]: 221 if self.new_email_results[REGRESS]:
214 msg += '%s: %s.\n' % (PERF_REGRESS, 222 msg += '%s: %s.\n' % (PERF_REGRESS,
215 ', '.join(self.new_email_results[REGRESS])) 223 ', '.join(self.new_email_results[REGRESS]))
216 if self.new_email_results[IMPROVE]: 224 if self.new_email_results[IMPROVE]:
217 msg += '%s: %s.\n' % (PERF_IMPROVE, 225 msg += '%s: %s.\n' % (PERF_IMPROVE,
218 ', '.join(self.new_email_results[IMPROVE])) 226 ', '.join(self.new_email_results[IMPROVE]))
219 if msg: 227 if msg:
220 self.status_header += ('\n\nNew perf results in this email:\n%s' % msg) 228 self.status_header += ('\n\nNew perf results in this email:\n%s' % msg)
221 email_msg = ChromiumNotifier.buildMessage(self, builder_name, build_status, 229 email_msg = ChromiumNotifier.buildMessage(self, builder_name, build_status,
222 results, step_name) 230 results, step_name)
223 # Reset header and notification list. 231 # Reset header and notification list.
224 self.status_header = original_header 232 self.status_header = original_header
225 self._InitNewEmailResults() 233 self._InitNewEmailResults()
226 return email_msg 234 return email_msg
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698