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,49 @@ 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
- def checkout_enterprise ():
275
- ee_suite = get_suite ('/vm-enterprise' )
276
- mx .run_mx (['checkout-downstream' , 'vm' , 'vm-enterprise' , '--no-fetch' ], suite = ee_suite )
277
-
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' ))
268
+ def checkout (repo_path : Path , commit ):
269
+ GIT .update_to_branch (repo_path , commit )
270
+ suite_dir = SUITE_MAPPING .get (repo_path , repo_path )
271
+ mx .run_mx (['sforceimports' ], suite = str (suite_dir ))
272
+ mx .run_mx (['--env' , 'ce' , 'sforceimports' ], suite = str (VM_DIR ))
282
273
if args .enterprise :
283
- if suite .name != 'vm-enterprise' :
284
- 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' )))
274
+ if repo_path .name != 'graal-enterprise' :
275
+ mx .run_mx (['--quiet' , 'checkout-downstream' , 'vm' , 'vm-enterprise' , '--no-fetch' ],
276
+ suite = str (VM_ENTERPRISE_DIR ))
277
+ mx .run_mx (['--env' , 'ee' , 'sforceimports' ], suite = str (VM_ENTERPRISE_DIR ))
278
+ GIT .update_to_branch (repo_path , commit )
279
+ mx .run_mx (['sforceimports' ], suite = str (suite_dir ))
280
+ debug_str = f"debug: { SUITE .name } ={ get_commit (SUITE .vc_dir )} graal={ get_commit (GRAAL_DIR )} "
292
281
if args .enterprise :
293
- debug_str += " graal-enterprise={}" . format ( get_commit (get_suite ( '/vm-enterprise' )))
282
+ debug_str += f " graal-enterprise={ get_commit (GRAAL_ENTERPRISE_DIR ) } "
294
283
print (debug_str )
295
284
296
- def checkout_and_build_suite (suite , commit ):
297
- checkout_suite (suite , commit )
285
+ def fetch_jdk ():
286
+ import mx_fetchjdk
287
+ if args .enterprise :
288
+ fetch_args = [
289
+ '--configuration' , str (GRAAL_ENTERPRISE_DIR / 'common.json' ),
290
+ '--jdk-binaries' , str (GRAAL_ENTERPRISE_DIR / 'ci' / 'jdk-binaries.json' ),
291
+ 'labsjdk-ee-latest' ,
292
+ ]
293
+ else :
294
+ fetch_args = [
295
+ '--configuration' , str (GRAAL_DIR / 'common.json' ),
296
+ 'labsjdk-ce-latest' ,
297
+ ]
298
+ # Awkward way to suppress the confirmation prompt
299
+ ci = 'CI' in os .environ
300
+ if not ci :
301
+ os .environ ['CI' ] = '1'
302
+ try :
303
+ return mx_fetchjdk .fetch_jdk (fetch_args )
304
+ finally :
305
+ if not ci :
306
+ del os .environ ['CI' ]
307
+
308
+ def checkout_and_build (repo_path , commit ):
309
+ checkout (repo_path , commit )
310
+ os .environ ['JAVA_HOME' ] = fetch_jdk ()
298
311
build_command = shlex .split (args .build_command )
299
312
if not args .no_clean :
300
313
try :
@@ -308,8 +321,8 @@ def checkout_and_build_suite(suite, commit):
308
321
if retcode :
309
322
raise RuntimeError ("Failed to execute the build command for {}" .format (commit ))
310
323
311
- def benchmark_callback (suite , commit , bench_command = args .benchmark_command ):
312
- checkout_and_build_suite ( suite , commit )
324
+ def benchmark_callback (repo_path : Path , commit , bench_command = args .benchmark_command ):
325
+ checkout_and_build ( repo_path , commit )
313
326
retcode = mx .run (shlex .split (bench_command ), nonZeroIsFatal = False )
314
327
if args .benchmark_metric == 'WORKS' :
315
328
return WorksResult (retcode )
@@ -333,9 +346,9 @@ def benchmark_callback(suite, commit, bench_command=args.benchmark_command):
333
346
result_class = HigherIsBetterResult if doc .get ('metric.better' , 'lower' ) == 'higher' else LowerIsBetterResult
334
347
return result_class (doc ['metric.value' ], doc ['metric.unit' ])
335
348
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 )
349
+ bad = get_commit (DIR , args .bad )
350
+ good = get_commit (DIR , args .good )
351
+ result = run_bisect_benchmark (DIR , bad , good , benchmark_callback )
339
352
visualization = result .visualize ()
340
353
summary = result .summarize ()
341
354
@@ -346,23 +359,22 @@ def benchmark_callback(suite, commit, bench_command=args.benchmark_command):
346
359
347
360
if args .rerun_with_commands :
348
361
print ('\n \n Rerunning the good and bad commits with extra benchmark commands:' )
362
+ repo_path = DIR
349
363
current_result = result
350
- current_suite = primary_suite
351
364
while current_result .subresults and current_result .bad_index in current_result .subresults :
352
- downstream_suite = get_downstream_suite ( current_suite )
365
+ downstream_repo_path = DOWNSTREAM_REPO_MAPPING . get ( repo_path )
353
366
next_result = current_result .subresults [current_result .bad_index ]
354
367
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 ) )
368
+ print (f "Next downstream repo { downstream_repo_path . name } does not have both good and bad commits" )
356
369
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 )
370
+ print (f"Recursing to downstream repo: { downstream_repo_path .name } , commit: { current_result .bad_commit } " )
371
+ checkout (downstream_repo_path , current_result .bad_commit )
360
372
current_result = next_result
361
- current_suite = downstream_suite
373
+ repo_path = downstream_repo_path
362
374
for commit in [current_result .good_commit , current_result .bad_commit ]:
363
375
print_line (80 )
364
376
print ("Commit: {}" .format (commit ))
365
- checkout_and_build_suite ( current_suite , commit )
377
+ checkout_and_build ( repo_path , commit )
366
378
for cmd in args .rerun_with_commands .split (";" ):
367
379
print_line (40 )
368
380
mx .run (shlex .split (cmd .strip ()), nonZeroIsFatal = False )
@@ -378,12 +390,14 @@ def benchmark_callback(suite, commit, bench_command=args.benchmark_command):
378
390
379
391
380
392
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 ()
393
+ initial_branch = GIT .git_command (DIR , ['rev-parse' , '--abbrev-ref' , 'HEAD' ]).strip ()
394
+ initial_commit = GIT .git_command (DIR , ['log' , '--format=%s' , '-n' , '1' ]).strip ()
395
+ email_to = GIT .git_command (DIR , ['log' , '--format=%cE' , '-n' , '1' ]).strip ()
385
396
bisect_id = f'{ initial_branch } : { initial_commit } '
386
- _bisect_benchmark (argv , bisect_id , email_to )
397
+ try :
398
+ _bisect_benchmark (argv , bisect_id , email_to )
399
+ finally :
400
+ GIT .update_to_branch (DIR , initial_branch )
387
401
388
402
389
403
def send_email (bisect_id , email_to , content ):
0 commit comments