@@ -570,13 +570,19 @@ impl Config {
570
570
let toolchain = & result. searched [ result. found ] ;
571
571
match self . search_perf_builds ( toolchain) {
572
572
Ok ( result) => {
573
+ let bisection = result. bisection ;
573
574
let url = format ! (
574
575
"https://github.com/rust-lang-ci/rust/commit/{}" ,
575
- result . searched[ result . found]
576
+ bisection . searched[ bisection . found]
576
577
)
577
578
. red ( )
578
579
. bold ( ) ;
579
580
eprintln ! ( "Regression in {url}" ) ;
581
+
582
+ // In case the bisected commit has been garbage-collected by github, we show its
583
+ // additional context here.
584
+ let context = & result. toolchain_descriptions [ bisection. found ] ;
585
+ eprintln ! ( "The PR introducing the regression in this rollup is {context}" ) ;
580
586
}
581
587
Err ( e) => {
582
588
eprintln ! ( "ERROR: {e}" ) ;
@@ -1183,7 +1189,7 @@ impl Config {
1183
1189
} )
1184
1190
}
1185
1191
1186
- fn search_perf_builds ( & self , toolchain : & Toolchain ) -> anyhow:: Result < BisectionResult > {
1192
+ fn search_perf_builds ( & self , toolchain : & Toolchain ) -> anyhow:: Result < PerfBisectionResult > {
1187
1193
eprintln ! ( "Attempting to search unrolled perf builds" ) ;
1188
1194
let Toolchain {
1189
1195
spec : ToolchainSpec :: Ci { commit, .. } ,
@@ -1205,13 +1211,19 @@ impl Config {
1205
1211
. filter ( |c| c. user . login == "rust-timer" )
1206
1212
. find ( |c| c. body . contains ( "Perf builds for each rolled up PR" ) )
1207
1213
. context ( "couldn't find perf build comment" ) ?;
1208
- let builds = extract_perf_shas ( & perf_comment. body ) ?;
1209
- let short_sha = builds
1214
+ let context = extract_perf_builds ( & perf_comment. body ) ?;
1215
+ let short_sha = context
1216
+ . builds
1210
1217
. iter ( )
1211
1218
. map ( |sha| sha. chars ( ) . take ( 8 ) . collect ( ) )
1212
1219
. collect :: < Vec < String > > ( ) ;
1213
1220
eprintln ! ( "Found commits {short_sha:?}" ) ;
1214
- self . linear_in_commits ( & builds)
1221
+
1222
+ let bisection = self . linear_in_commits ( & context. builds ) ?;
1223
+ Ok ( PerfBisectionResult {
1224
+ bisection,
1225
+ toolchain_descriptions : context. descriptions ,
1226
+ } )
1215
1227
}
1216
1228
1217
1229
fn linear_in_commits ( & self , commits : & [ & str ] ) -> anyhow:: Result < BisectionResult > {
@@ -1257,6 +1269,16 @@ struct BisectionResult {
1257
1269
dl_spec : DownloadParams ,
1258
1270
}
1259
1271
1272
+ /// The results of a bisection through the unrolled perf builds in a rollup:
1273
+ /// - the regular bisection results
1274
+ /// - a description of the rolled-up PRs for clearer diagnostics, in case the bisected commit
1275
+ /// doesn't exist anymore on github.
1276
+ #[ derive( Clone ) ]
1277
+ struct PerfBisectionResult {
1278
+ bisection : BisectionResult ,
1279
+ toolchain_descriptions : Vec < String > ,
1280
+ }
1281
+
1260
1282
fn main ( ) {
1261
1283
if let Err ( err) = run ( ) {
1262
1284
match err. downcast :: < ExitError > ( ) {
@@ -1270,27 +1292,81 @@ fn main() {
1270
1292
}
1271
1293
}
1272
1294
1273
- /// Extracts the commits posted by the rust-timer bot on rollups, for unrolled perf builds.
1295
+ /// An in-order mapping from perf build SHA to its description.
1296
+ struct PerfBuildsContext < ' a > {
1297
+ builds : Vec < & ' a str > ,
1298
+ descriptions : Vec < String > ,
1299
+ }
1300
+
1301
+ /// Extracts the commits posted by the rust-timer bot on rollups, for unrolled perf builds, with
1302
+ /// their associated context: the PR number and title if available.
1274
1303
///
1275
- /// We're looking for a commit sha , in a comment whose format has changed (and could change in the
1304
+ /// We're looking for a commit SHA , in a comment whose format has changed (and could change in the
1276
1305
/// future), for example:
1277
1306
/// - v1: https://github.com/rust-lang/rust/pull/113014#issuecomment-1605868471
1278
1307
/// - v2, the current: https://github.com/rust-lang/rust/pull/113105#issuecomment-1610393473
1279
1308
///
1280
- /// The sha comes in later columns, so we'll look for a 40-char hex string and give priority to the
1309
+ /// The SHA comes in later columns, so we'll look for a 40-char hex string and give priority to the
1281
1310
/// last we find (to avoid possible conflicts with commits in the PR title column).
1282
- fn extract_perf_shas ( body : & str ) -> anyhow:: Result < Vec < & str > > {
1311
+ ///
1312
+ /// Depending on how recent the perf build commit is, it may have been garbage-collected by github:
1313
+ /// perf-builds are force pushed to the `try-perf` branch, and accessing that commit can
1314
+ /// 404. Therefore, we try to map back from that commit to the rolled-up PR present in the list of
1315
+ /// unrolled builds.
1316
+ fn extract_perf_builds ( body : & str ) -> anyhow:: Result < PerfBuildsContext < ' _ > > {
1317
+ let mut builds = Vec :: new ( ) ;
1318
+ let mut descriptions = Vec :: new ( ) ;
1319
+
1283
1320
let sha_regex = RegexBuilder :: new ( r"([0-9a-f]{40})" )
1284
1321
. case_insensitive ( true )
1285
1322
. build ( ) ?;
1286
- let builds = body
1323
+ for line in body
1287
1324
. lines ( )
1288
- // lines of table with PR builds
1325
+ // Only look at the lines of the unrolled perf builds table.
1289
1326
. filter ( |l| l. starts_with ( "|#" ) )
1290
- // get the last sha we find, to prioritize the 3rd or 2nd columns.
1291
- . filter_map ( |l| sha_regex. find_iter ( l) . last ( ) . and_then ( |m| Some ( m. as_str ( ) ) ) )
1292
- . collect ( ) ;
1293
- Ok ( builds)
1327
+ {
1328
+ // Get the last SHA we find, to prioritize the 3rd or 2nd columns.
1329
+ let sha = sha_regex
1330
+ . find_iter ( line)
1331
+ . last ( )
1332
+ . and_then ( |m| Some ( m. as_str ( ) ) ) ;
1333
+
1334
+ // If we did find one, we try to extract the associated description.
1335
+ let Some ( sha) = sha else { continue } ;
1336
+
1337
+ let mut description = String :: new ( ) ;
1338
+
1339
+ // In v1 and v2, we know that the first column is the PR number.
1340
+ //
1341
+ // In the unlikely event it's missing because of a parsing discrepancy, we don't want to
1342
+ // ignore it, and ask for feedback: we always want to have *some* context per PR, matching
1343
+ // the number of SHAs we found.
1344
+ let Some ( pr) = line. split ( '|' ) . nth ( 1 ) else {
1345
+ bail ! ( "Couldn't get rolled-up PR number for SHA {sha}, please open an issue." ) ;
1346
+ } ;
1347
+
1348
+ description. push_str ( pr) ;
1349
+
1350
+ // The second column could be a link to the commit (which we don't want in the description),
1351
+ // or the PR title (which we want).
1352
+ if let Some ( title) = line. split ( '|' ) . nth ( 2 ) {
1353
+ // For v1, this column would contain the commit, and we won't have the PR title
1354
+ // anywhere. So we try to still give some context for that older format: if the column
1355
+ // contains the SHA, we don't add that to the description.
1356
+ if !title. contains ( sha) {
1357
+ description. push_str ( ": " ) ;
1358
+ description. push_str ( title) ;
1359
+ }
1360
+ }
1361
+
1362
+ builds. push ( sha) ;
1363
+ descriptions. push ( description) ;
1364
+ }
1365
+
1366
+ Ok ( PerfBuildsContext {
1367
+ builds,
1368
+ descriptions,
1369
+ } )
1294
1370
}
1295
1371
1296
1372
#[ cfg( test) ]
@@ -1381,6 +1457,8 @@ mod tests {
1381
1457
1382
1458
In the case of a perf regression, run the following command for each PR you suspect might be the cause: `@rust-timer build $SHA`
1383
1459
<!-- rust-timer: rollup -->" ;
1460
+ let context =
1461
+ extract_perf_builds ( body) . expect ( "extracting perf builds context on v1 format failed" ) ;
1384
1462
assert_eq ! (
1385
1463
vec![
1386
1464
"05b07dad146a6d43ead9bcd1e8bc10cbd017a5f5" ,
@@ -1389,7 +1467,11 @@ In the case of a perf regression, run the following command for each PR you susp
1389
1467
"0ed6ba504649ca1cb2672572b4ab41acfb06c86c" ,
1390
1468
"18e108ab85b78e6966c5b5bdadfd5b8efeadf080" ,
1391
1469
] ,
1392
- extract_perf_shas( body) . expect( "extracting perf builds on v1 format failed" ) ,
1470
+ context. builds,
1471
+ ) ;
1472
+ assert_eq ! (
1473
+ vec![ "#113009" , "#113008" , "#112956" , "#112950" , "#112937" , ] ,
1474
+ context. descriptions,
1393
1475
) ;
1394
1476
}
1395
1477
@@ -1416,6 +1498,8 @@ In the case of a perf regression, run the following command for each PR you susp
1416
1498
1417
1499
In the case of a perf regression, run the following command for each PR you suspect might be the cause: `@rust-timer build $SHA`
1418
1500
<!-- rust-timer: rollup -->" ;
1501
+ let context =
1502
+ extract_perf_builds ( body) . expect ( "extracting perf builds context on v2 format failed" ) ;
1419
1503
assert_eq ! (
1420
1504
vec![
1421
1505
"bbec6d6e413aa144c8b9346da27a0f2af299cbeb" ,
@@ -1427,7 +1511,20 @@ In the case of a perf regression, run the following command for each PR you susp
1427
1511
"0ce4618dbf5810aabb389edd4950c060b6b4d049" ,
1428
1512
"241cd8cd818cdc865cdf02f0c32a40081420b772" ,
1429
1513
] ,
1430
- extract_perf_shas( body) . expect( "extracting perf builds on v2 format failed" ) ,
1514
+ context. builds,
1515
+ ) ;
1516
+ assert_eq ! (
1517
+ vec![
1518
+ "#112207: Add trustzone and virtualization target features for aarch3…" ,
1519
+ "#112454: Make compiletest aware of targets without dynamic linking" ,
1520
+ "#112628: Allow comparing `Box`es with different allocators" ,
1521
+ "#112692: Provide more context for `rustc +nightly -Zunstable-options…" ,
1522
+ "#112972: Make `UnwindAction::Continue` explicit in MIR dump" ,
1523
+ "#113020: Add tests impl via obj unless denied" ,
1524
+ "#113084: Simplify some conditions" ,
1525
+ "#113103: Normalize types when applying uninhabited predicate." ,
1526
+ ] ,
1527
+ context. descriptions,
1431
1528
) ;
1432
1529
}
1433
1530
}
0 commit comments