1
- import re
2
- import os
3
- import sys
4
1
import argparse
2
+ import configparser
3
+ import os
4
+ import re
5
5
import shlex
6
+ import sys
6
7
import types
7
- import configparser
8
8
9
9
import mx
10
10
@@ -48,7 +48,7 @@ def run_bisect_benchmark(suite, bad, good, callback, threshold=None):
48
48
abortOnError = True ,
49
49
).splitlines ()
50
50
if not commits :
51
- sys . exit ("No merge commits found in the range. Did you swap good and bad?" )
51
+ raise RuntimeError ("No merge commits found in the range. Did you swap good and bad?" )
52
52
downstream_suite = get_downstream_suite (suite )
53
53
values = [None ] * len (commits )
54
54
if threshold is None :
@@ -60,7 +60,7 @@ def run_bisect_benchmark(suite, bad, good, callback, threshold=None):
60
60
downstream_good = get_commit (downstream_suite )
61
61
threshold = (values [bad_index ] + values [good_index ]) / 2
62
62
if values [good_index ] * 1.03 > values [bad_index ]:
63
- sys . exit (
63
+ raise RuntimeError (
64
64
"Didn't detect a regression - less that 3% difference between good value "
65
65
"{} and bad value {}" .format (values [good_index ], values [bad_index ])
66
66
)
@@ -118,25 +118,26 @@ def bad_commit(self):
118
118
119
119
def visualize (self , level = 1 ):
120
120
level_marker = '=' * level
121
- print (level_marker , self .repo_name )
121
+ out = [ "{} {}" . format (level_marker , self .repo_name )]
122
122
for index , (commit , value ) in enumerate (zip (self .commits , self .values )):
123
123
if value is not None :
124
- print ("{} {} {:6.6} {}" .format (level_marker , commit , value , get_message (self .suite , commit )))
124
+ out . append ("{} {} {:6.6} s {}" .format (level_marker , commit , value , get_message (self .suite , commit )))
125
125
if self .subresults and index in self .subresults :
126
- self .subresults [index ].visualize (level + 1 )
126
+ out .append (self .subresults [index ].visualize (level + 1 ))
127
+ return '\n ' .join (out )
127
128
128
129
def summarize (self ):
129
130
if self .bad_commit and self .good_commit :
130
131
for subresult in self .subresults .values ():
131
- if subresult .summarize ():
132
- return True
133
- print ( "Detected bad commit in {} repository: \n {} {}"
134
- . format ( self . repo_name , self . bad_commit , get_message ( self . suite , self . bad_commit )))
135
- return True
136
- return False
132
+ sub = subresult .summarize ()
133
+ if sub :
134
+ return sub
135
+ return ( "Detected bad commit in {} repository: \n {} {}"
136
+ . format ( self . repo_name , self . bad_commit , get_message ( self . suite , self . bad_commit )))
137
+ return ''
137
138
138
139
139
- def bisect_benchmark (argv ):
140
+ def _bisect_benchmark (argv , initial_branch , email_to ):
140
141
if 'BISECT_BENCHMARK_CONFIG' in os .environ :
141
142
cp = configparser .ConfigParser ()
142
143
cp .read (os .environ ['BISECT_BENCHMARK_CONFIG' ])
@@ -183,24 +184,67 @@ def benchmark_callback(suite, commit):
183
184
env ['MX_ALT_OUTPUT_ROOT' ] = 'mxbuild-{}' .format (commit )
184
185
retcode = mx .run (shlex .split (args .build_command ), env = env , nonZeroIsFatal = False )
185
186
if retcode :
186
- sys . exit ("Failed to execute the build command for {}" .format (commit ))
187
+ raise RuntimeError ("Failed to execute the build command for {}" .format (commit ))
187
188
output = mx .OutputCapture ()
188
- retcode = mx .run (shlex .split (args .benchmark_command ), env = env , out = mx .TeeOutputCapture (output ), nonZeroIsFatal = False )
189
+ retcode = mx .run (shlex .split (args .benchmark_command ), env = env , out = mx .TeeOutputCapture (output ),
190
+ nonZeroIsFatal = False )
189
191
if retcode :
190
- sys . exit ("Failed to execute benchmark for {}" .format (commit ))
192
+ raise RuntimeError ("Failed to execute benchmark for {}" .format (commit ))
191
193
match = re .search (r'{}.*duration: ([\d.]+)' .format (re .escape (args .benchmark_criterion )), output .data )
192
194
if not match :
193
- sys . exit ("Failed to get result from the benchmark" )
195
+ raise RuntimeError ("Failed to get result from the benchmark" )
194
196
return float (match .group (1 ))
195
197
196
198
bad = get_commit (primary_suite , args .bad )
197
199
good = get_commit (primary_suite , args .good )
198
200
result = run_bisect_benchmark (primary_suite , bad , good , benchmark_callback )
201
+ visualization = result .visualize ()
202
+ summary = result .summarize ()
203
+
199
204
print ()
200
- result .visualize ()
201
- print ()
202
- result .summarize ()
205
+ print (visualization )
203
206
print ()
207
+ print (summary )
204
208
205
209
if 'CI' not in os .environ :
206
- print ("You can rerun a benchmark for a particular commit using:\n MX_ALT_OUTPUT_ROOT=mxbuild-$commit {}" .format (args .benchmark_command ))
210
+ print ("You can rerun a benchmark for a particular commit using:\n MX_ALT_OUTPUT_ROOT=mxbuild-$commit {}" .format (
211
+ args .benchmark_command ))
212
+
213
+ send_email (
214
+ initial_branch ,
215
+ email_to ,
216
+ "Bisection job has finished successfully.\n {}\n " .format (summary )
217
+ + "Note I'm just a script and I don't validate statistical significance of the above result.\n "
218
+ + "Please take a moment to also inspect the detailed results below.\n \n {}\n \n " .format (visualization )
219
+ + os .environ .get ('BUILD_URL' , 'Unknown URL' )
220
+ )
221
+
222
+
223
+ def bisect_benchmark (argv ):
224
+ suite = mx .primary_suite ()
225
+ initial_branch = suite .vc .git_command (suite .vc_dir , ['rev-parse' , '--abbrev-ref' , 'HEAD' ]).strip ()
226
+ email_to = suite .vc .git_command (suite .vc_dir , ['log' , '--format=%cE' , '-n' , '1' ]).strip ()
227
+ try :
228
+ _bisect_benchmark (argv , initial_branch , email_to )
229
+ except Exception :
230
+ send_email (initial_branch , email_to , "Job failed.\n {}" .format (os .environ .get ('BUILD_URL' , 'Unknown URL' )))
231
+ raise
232
+
233
+
234
+ def send_email (initial_branch , email_to , content ):
235
+ if 'BISECT_EMAIL_SMTP_SERVER' in os .environ :
236
+ import smtplib
237
+ from email .message import EmailMessage
238
+
239
+ msg = EmailMessage ()
240
+ msg ['Subject' ] = "Bisection result for {}" .format (initial_branch )
241
+ msg ['From' ] = os .environ ['BISECT_EMAIL_FROM' ]
242
+ validate_to = os .environ ['BISECT_EMAIL_TO_PATTERN' ]
243
+ if not re .match (validate_to , email_to ):
244
+ sys .exit ("Email {} not allowed, aborting sending" .format (email_to ))
245
+ msg ['To' ] = email_to
246
+ msg .set_content (content )
247
+ print (msg )
248
+ smtp = smtplib .SMTP (os .environ ['BISECT_EMAIL_SMTP_SERVER' ])
249
+ smtp .send_message (msg )
250
+ smtp .quit ()
0 commit comments