36
36
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37
37
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38
38
# SOFTWARE.
39
+ import sys
40
+
39
41
import abc
40
42
import argparse
41
43
import json
44
+ import mx
42
45
import os
43
46
import re
44
47
import shlex
45
- import sys
46
48
import types
47
-
48
- import mx
49
+ from pathlib import Path
49
50
50
51
51
52
def print_line (l ):
52
53
print ('=' * l )
53
54
54
55
55
- def get_suite ( name ):
56
- suite_name = name . lstrip ( '/' )
57
- suite = mx . suite ( suite_name , fatalIfMissing = False )
58
- if not suite :
59
- suite = mx . primary_suite (). import_suite ( suite_name , version = None , urlinfos = None , in_subdir = name . startswith ( '/' ))
60
- assert suite
61
- return suite
56
+ SUITE = mx . suite ( 'graalpython' )
57
+ GIT = SUITE . vc
58
+ DIR = Path ( SUITE . vc_dir ). absolute ( )
59
+ GRAAL_DIR = DIR . parent / 'graal'
60
+ VM_DIR = GRAAL_DIR / 'vm'
61
+ GRAAL_ENTERPRISE_DIR = DIR . parent / 'graal-enterprise'
62
+ VM_ENTERPRISE_DIR = GRAAL_ENTERPRISE_DIR / 'vm-enterprise'
62
63
64
+ SUITE_MAPPING = {
65
+ GRAAL_DIR : VM_DIR ,
66
+ GRAAL_ENTERPRISE_DIR : VM_ENTERPRISE_DIR ,
67
+ }
63
68
64
- def get_downstream_suite (suite ):
65
- downstreams = {
66
- 'graalpython-apptests' : 'graalpython' ,
67
- 'graalpython-extensions' : 'graalpython' ,
68
- 'graalpython' : '/vm' ,
69
- 'vm' : '/vm-enterprise' ,
70
- }
71
- downstream = downstreams .get (suite .name )
72
- if downstream :
73
- return get_suite (downstream )
69
+ DOWNSTREAM_REPO_MAPPING = {
70
+ DIR : GRAAL_DIR ,
71
+ GRAAL_DIR : GRAAL_ENTERPRISE_DIR ,
72
+ }
74
73
75
74
76
- def get_commit (suite , ref = 'HEAD' ):
77
- if not suite :
78
- return None
79
- return suite .vc .git_command (suite .vc_dir , ['rev-parse' , ref ], abortOnError = True ).strip ()
75
+ def get_commit (repo_path : Path , ref = 'HEAD' ):
76
+ return GIT .git_command (repo_path , ['rev-parse' , ref ], abortOnError = True ).strip ()
80
77
81
78
82
- def get_message (suite , commit ):
83
- return suite . vc . git_command (suite . vc_dir , ['log' , '--format=%s' , '-n' , '1' , commit ]).strip ()
79
+ def get_message (repo_path : Path , commit ):
80
+ return GIT . git_command (repo_path , ['log' , '--format=%s' , '-n' , '1' , commit ]).strip ()
84
81
85
82
86
- def run_bisect_benchmark (suite , bad , good , callback , good_result = None , bad_result = None ):
87
- git_dir = suite .vc_dir
88
- commits = suite .vc .git_command (
89
- git_dir ,
83
+ def run_bisect_benchmark (repo_path : Path , bad , good , callback , good_result = None , bad_result = None ):
84
+ commits = GIT .git_command (
85
+ repo_path ,
90
86
['log' , '--first-parent' , '--format=format:%H' , '{}^..{}' .format (good , bad )],
91
87
abortOnError = True ,
92
88
).splitlines ()
93
89
if not commits :
94
90
raise RuntimeError ("No merge commits found in the range. Did you swap good and bad?" )
95
- downstream_suite = get_downstream_suite ( suite )
91
+ downstream_repo_path = DOWNSTREAM_REPO_MAPPING . get ( repo_path )
96
92
results = [None ] * len (commits )
97
93
if good_result is None and bad_result is None :
98
94
bad_index = 0
99
95
good_index = len (commits ) - 1
100
- bad_result = results [bad_index ] = callback (suite , bad )
101
- downstream_bad = get_commit (downstream_suite )
102
- good_result = results [good_index ] = callback (suite , good )
103
- downstream_good = get_commit (downstream_suite )
96
+ bad_result = results [bad_index ] = callback (repo_path , bad )
97
+ downstream_bad = get_commit (downstream_repo_path )
98
+ good_result = results [good_index ] = callback (repo_path , good )
99
+ downstream_good = get_commit (downstream_repo_path )
104
100
if not good_result .bound_is_valid (bad_result ):
105
101
raise RuntimeError (
106
102
"Didn't detect a regression: "
@@ -122,25 +118,25 @@ def run_bisect_benchmark(suite, bad, good, callback, good_result=None, bad_resul
122
118
assert good_index - bad_index == 1
123
119
break
124
120
commit = commits [index ]
125
- result = results [index ] = callback (suite , commit )
121
+ result = results [index ] = callback (repo_path , commit )
126
122
if result .is_good (good_result , bad_result ):
127
123
good_index = index
128
- downstream_good = get_commit (downstream_suite )
124
+ downstream_good = get_commit (downstream_repo_path )
129
125
else :
130
126
bad_index = index
131
- downstream_bad = get_commit (downstream_suite )
127
+ downstream_bad = get_commit (downstream_repo_path )
132
128
subresults = {}
133
129
if downstream_bad and downstream_good and downstream_bad != downstream_good :
134
- suite . vc . update_to_branch (suite . vc_dir , commits [good_index ])
135
- subresult = run_bisect_benchmark (downstream_suite , downstream_bad , downstream_good , callback , good_result ,
130
+ GIT . update_to_branch (DIR , commits [good_index ])
131
+ subresult = run_bisect_benchmark (downstream_repo_path , downstream_bad , downstream_good , callback , good_result ,
136
132
bad_result )
137
133
subresults [bad_index ] = subresult
138
- return BisectResult (suite , commits , results , good_index , bad_index , subresults )
134
+ return BisectResult (downstream_repo_path , commits , results , good_index , bad_index , subresults )
139
135
140
136
141
137
class BisectResult :
142
- def __init__ (self , suite , commits , results , good_index , bad_index , dependency_results ):
143
- self .suite = suite
138
+ def __init__ (self , repo_path : Path , commits , results , good_index , bad_index , dependency_results ):
139
+ self .repo_path = repo_path
144
140
self .commits = commits
145
141
self .results = results
146
142
self .good_index = good_index
@@ -149,7 +145,7 @@ def __init__(self, suite, commits, results, good_index, bad_index, dependency_re
149
145
150
146
@property
151
147
def repo_name (self ):
152
- return os . path . basename ( self .suite . vc_dir )
148
+ return self .repo_path . name
153
149
154
150
@property
155
151
def good_commit (self ):
@@ -166,7 +162,7 @@ def visualize(self, level=1):
166
162
out = ["{} {}" .format (level_marker , self .repo_name )]
167
163
for index , (commit , value ) in enumerate (zip (self .commits , self .results )):
168
164
if value is not None :
169
- out .append (f"{ level_marker } { commit } { value } { get_message (self .suite , commit )} " )
165
+ out .append (f"{ level_marker } { commit } { value } { get_message (self .repo_path , commit )} " )
170
166
if self .dependency_results and index in self .dependency_results :
171
167
out .append (self .dependency_results [index ].visualize (level + 1 ))
172
168
return '\n ' .join (out )
@@ -178,7 +174,7 @@ def summarize(self):
178
174
if summary :
179
175
return summary
180
176
return ("Detected bad commit in {} repository:\n {} {}"
181
- .format (self .repo_name , self .bad_commit , get_message (self .suite , self .bad_commit )))
177
+ .format (self .repo_name , self .bad_commit , get_message (self .repo_path , self .bad_commit )))
182
178
return ''
183
179
184
180
@@ -269,32 +265,27 @@ def _bisect_benchmark(argv, bisect_id, email_to):
269
265
parser .add_argument ('--no-clean' , action = 'store_true' , help = "Do not run 'mx clean' between runs" )
270
266
args = parser .parse_args (argv )
271
267
272
- primary_suite = mx .primary_suite ()
273
-
274
268
def checkout_enterprise ():
275
- ee_suite = get_suite ('/vm-enterprise' )
276
- mx .run_mx (['checkout-downstream' , 'vm' , 'vm-enterprise' , '--no-fetch' ], suite = ee_suite )
269
+ mx .run_mx (['checkout-downstream' , 'vm' , 'vm-enterprise' , '--no-fetch' ], suite = str (VM_ENTERPRISE_DIR ))
277
270
278
- def checkout_suite (suite , commit ):
279
- suite .vc .update_to_branch (suite .vc_dir , commit )
280
- mx .run_mx (['sforceimports' ], suite = suite )
281
- mx .run_mx (['--env' , 'ce' , 'sforceimports' ], suite = get_suite ('/vm' ))
271
+ def checkout (repo_path : Path , commit ):
272
+ GIT .update_to_branch (repo_path , commit )
273
+ suite_dir = SUITE_MAPPING .get (repo_path , repo_path )
274
+ mx .run_mx (['sforceimports' ], suite = str (suite_dir ))
275
+ mx .run_mx (['--env' , 'ce' , 'sforceimports' ], suite = str (VM_DIR ))
282
276
if args .enterprise :
283
- if suite .name != 'vm -enterprise' :
277
+ if repo_path .name != 'graal -enterprise' :
284
278
checkout_enterprise ()
285
- # Make sure vm is imported before vm-enterprise
286
- get_suite ('/vm' )
287
- mx .run_mx (['--env' , 'ee' , 'sforceimports' ], suite = get_suite ('/vm-enterprise' ))
288
- suite .vc .update_to_branch (suite .vc_dir , commit )
289
- mx .run_mx (['sforceimports' ], suite = suite )
290
- debug_str = "debug: graalpython={} graal={}" .format (
291
- get_commit (get_suite ('graalpython' )), get_commit (get_suite ('/vm' )))
279
+ mx .run_mx (['--env' , 'ee' , 'sforceimports' ], suite = str (VM_ENTERPRISE_DIR ))
280
+ GIT .update_to_branch (repo_path , commit )
281
+ mx .run_mx (['sforceimports' ], suite = str (suite_dir ))
282
+ debug_str = f"debug: { SUITE .name } ={ get_commit (SUITE .vc_dir )} graal={ get_commit (GRAAL_DIR )} "
292
283
if args .enterprise :
293
- debug_str += " graal-enterprise={}" . format ( get_commit (get_suite ( '/vm-enterprise' )))
284
+ debug_str += f " graal-enterprise={ get_commit (GRAAL_ENTERPRISE_DIR ) } "
294
285
print (debug_str )
295
286
296
- def checkout_and_build_suite ( suite , commit ):
297
- checkout_suite ( suite , commit )
287
+ def checkout_and_build ( repo_path , commit ):
288
+ checkout ( repo_path , commit )
298
289
build_command = shlex .split (args .build_command )
299
290
if not args .no_clean :
300
291
try :
@@ -308,8 +299,8 @@ def checkout_and_build_suite(suite, commit):
308
299
if retcode :
309
300
raise RuntimeError ("Failed to execute the build command for {}" .format (commit ))
310
301
311
- def benchmark_callback (suite , commit , bench_command = args .benchmark_command ):
312
- checkout_and_build_suite ( suite , commit )
302
+ def benchmark_callback (repo_path : Path , commit , bench_command = args .benchmark_command ):
303
+ checkout_and_build ( repo_path , commit )
313
304
retcode = mx .run (shlex .split (bench_command ), nonZeroIsFatal = False )
314
305
if args .benchmark_metric == 'WORKS' :
315
306
return WorksResult (retcode )
@@ -333,9 +324,9 @@ def benchmark_callback(suite, commit, bench_command=args.benchmark_command):
333
324
result_class = HigherIsBetterResult if doc .get ('metric.better' , 'lower' ) == 'higher' else LowerIsBetterResult
334
325
return result_class (doc ['metric.value' ], doc ['metric.unit' ])
335
326
336
- bad = get_commit (primary_suite , args .bad )
337
- good = get_commit (primary_suite , args .good )
338
- result = run_bisect_benchmark (primary_suite , bad , good , benchmark_callback )
327
+ bad = get_commit (DIR , args .bad )
328
+ good = get_commit (DIR , args .good )
329
+ result = run_bisect_benchmark (DIR , bad , good , benchmark_callback )
339
330
visualization = result .visualize ()
340
331
summary = result .summarize ()
341
332
@@ -346,23 +337,22 @@ def benchmark_callback(suite, commit, bench_command=args.benchmark_command):
346
337
347
338
if args .rerun_with_commands :
348
339
print ('\n \n Rerunning the good and bad commits with extra benchmark commands:' )
340
+ repo_path = DIR
349
341
current_result = result
350
- current_suite = primary_suite
351
342
while current_result .subresults and current_result .bad_index in current_result .subresults :
352
- downstream_suite = get_downstream_suite ( current_suite )
343
+ downstream_repo_path = DOWNSTREAM_REPO_MAPPING . get ( repo_path )
353
344
next_result = current_result .subresults [current_result .bad_index ]
354
345
if not next_result .good_commit or not next_result .bad_commit :
355
- print ("Next downstream suite { } does not have both good and bad commits" . format ( downstream_suite . name ) )
346
+ print (f "Next downstream repo { downstream_repo_path . name } does not have both good and bad commits" )
356
347
break
357
- print ("Recursing to downstream suite: {}, commit: {}" .format (downstream_suite .name ,
358
- current_result .bad_commit ))
359
- checkout_suite (current_suite , current_result .bad_commit )
348
+ print (f"Recursing to downstream repo: { downstream_repo_path .name } , commit: { current_result .bad_commit } " )
349
+ checkout (downstream_repo_path , current_result .bad_commit )
360
350
current_result = next_result
361
- current_suite = downstream_suite
351
+ repo_path = downstream_repo_path
362
352
for commit in [current_result .good_commit , current_result .bad_commit ]:
363
353
print_line (80 )
364
354
print ("Commit: {}" .format (commit ))
365
- checkout_and_build_suite ( current_suite , commit )
355
+ checkout_and_build ( repo_path , commit )
366
356
for cmd in args .rerun_with_commands .split (";" ):
367
357
print_line (40 )
368
358
mx .run (shlex .split (cmd .strip ()), nonZeroIsFatal = False )
@@ -378,12 +368,14 @@ def benchmark_callback(suite, commit, bench_command=args.benchmark_command):
378
368
379
369
380
370
def bisect_benchmark (argv ):
381
- suite = mx .primary_suite ()
382
- initial_branch = suite .vc .git_command (suite .vc_dir , ['rev-parse' , '--abbrev-ref' , 'HEAD' ]).strip ()
383
- initial_commit = suite .vc .git_command (suite .vc_dir , ['log' , '--format=%s' , '-n' , '1' ]).strip ()
384
- email_to = suite .vc .git_command (suite .vc_dir , ['log' , '--format=%cE' , '-n' , '1' ]).strip ()
371
+ initial_branch = GIT .git_command (DIR , ['rev-parse' , '--abbrev-ref' , 'HEAD' ]).strip ()
372
+ initial_commit = GIT .git_command (DIR , ['log' , '--format=%s' , '-n' , '1' ]).strip ()
373
+ email_to = GIT .git_command (DIR , ['log' , '--format=%cE' , '-n' , '1' ]).strip ()
385
374
bisect_id = f'{ initial_branch } : { initial_commit } '
386
- _bisect_benchmark (argv , bisect_id , email_to )
375
+ try :
376
+ _bisect_benchmark (argv , bisect_id , email_to )
377
+ finally :
378
+ GIT .update_to_branch (DIR , initial_branch )
387
379
388
380
389
381
def send_email (bisect_id , email_to , content ):
0 commit comments