Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf test: Run parallel tests in two passes

In pass 1 run all tests that succeed when run in parallel. In pass 2
sequentially run all remaining tests that are flagged as
"exclusive". Sequential and dont_fork tests keep to run in pass 1.
Read the exclusive flag from the shell test descriptions, but remove
from display to avoid >100 characters. Add error handling to finish
tests if starting a later test fails. Mark the task-exit test as
exclusive due to issues reported-by James Clark.

Tested-by: James Clark <james.clark@linaro.org>
Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Colin Ian King <colin.i.king@gmail.com>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Weilin Wang <weilin.wang@intel.com>
Cc: Ilya Leoshkevich <iii@linux.ibm.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Dapeng Mi <dapeng1.mi@linux.intel.com>
Cc: Athira Jajeev <atrajeev@linux.vnet.ibm.com>
Cc: Michael Petlan <mpetlan@redhat.com>
Cc: Veronika Molnarova <vmolnaro@redhat.com>
Link: https://lore.kernel.org/r/20241025192109.132482-8-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Ian Rogers and committed by
Namhyung Kim
79e72f38 a6fffc60

+121 -61
+98 -59
tools/perf/tests/builtin-test.c
··· 199 199 return t->test_cases[subtest].run_case; 200 200 } 201 201 202 + static bool test_exclusive(const struct test_suite *t, int subtest) 203 + { 204 + if (subtest <= 0) 205 + return t->test_cases[0].exclusive; 206 + 207 + return t->test_cases[subtest].exclusive; 208 + } 209 + 202 210 static bool perf_test__matches(const char *desc, int curr, int argc, const char *argv[]) 203 211 { 204 212 int i; ··· 250 242 const int signals[] = { 251 243 SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGINT, SIGPIPE, SIGQUIT, SIGSEGV, SIGTERM, 252 244 }; 253 - static struct child_test *child; 245 + struct child_test *child = container_of(process, struct child_test, process); 254 246 int err; 255 247 256 248 err = sigsetjmp(run_test_jmp_buf, 1); ··· 260 252 goto err_out; 261 253 } 262 254 263 - child = container_of(process, struct child_test, process); 264 255 for (size_t i = 0; i < ARRAY_SIZE(signals); i++) 265 256 signal(signals[i], child_test_sig_handler); 266 257 ··· 312 305 return 0; 313 306 } 314 307 315 - static int finish_test(struct child_test **child_tests, int running_test, int child_test_num, 316 - int width) 308 + static void finish_test(struct child_test **child_tests, int running_test, int child_test_num, 309 + int width) 317 310 { 318 311 struct child_test *child_test = child_tests[running_test]; 319 - struct test_suite *t = child_test->test; 320 - int i = child_test->test_num; 321 - int subi = child_test->subtest; 322 - int err = child_test->process.err; 312 + struct test_suite *t; 313 + int i, subi, err; 323 314 bool err_done = false; 324 315 struct strbuf err_output = STRBUF_INIT; 325 316 int last_running = -1; 326 317 int ret; 327 318 319 + if (child_test == NULL) { 320 + /* Test wasn't started. */ 321 + return; 322 + } 323 + t = child_test->test; 324 + i = child_test->test_num; 325 + subi = child_test->subtest; 326 + err = child_test->process.err; 328 327 /* 329 328 * For test suites with subtests, display the suite name ahead of the 330 329 * sub test names. ··· 360 347 int running = 0; 361 348 362 349 for (int y = running_test; y < child_test_num; y++) { 350 + if (child_tests[y] == NULL) 351 + continue; 363 352 if (check_if_command_finished(&child_tests[y]->process) == 0) 364 353 running++; 365 354 } ··· 414 399 print_test_result(t, i, subi, ret, width, /*running=*/0); 415 400 if (err > 0) 416 401 close(err); 417 - return 0; 402 + zfree(&child_tests[running_test]); 418 403 } 419 404 420 405 static int start_test(struct test_suite *test, int i, int subi, struct child_test **child, 421 - int width) 406 + int width, int pass) 422 407 { 423 408 int err; 424 409 425 410 *child = NULL; 426 411 if (dont_fork) { 427 - pr_debug("--- start ---\n"); 428 - err = test_function(test, subi)(test, subi); 429 - pr_debug("---- end ----\n"); 430 - print_test_result(test, i, subi, err, width, /*running=*/0); 412 + if (pass == 1) { 413 + pr_debug("--- start ---\n"); 414 + err = test_function(test, subi)(test, subi); 415 + pr_debug("---- end ----\n"); 416 + print_test_result(test, i, subi, err, width, /*running=*/0); 417 + } 431 418 return 0; 432 419 } 433 - 420 + if (pass == 1 && !sequential && test_exclusive(test, subi)) { 421 + /* When parallel, skip exclusive tests on the first pass. */ 422 + return 0; 423 + } 424 + if (pass != 1 && (sequential || !test_exclusive(test, subi))) { 425 + /* Sequential and non-exclusive tests were run on the first pass. */ 426 + return 0; 427 + } 434 428 *child = zalloc(sizeof(**child)); 435 429 if (!*child) 436 430 return -ENOMEM; ··· 458 434 (*child)->process.err = -1; 459 435 } 460 436 (*child)->process.no_exec_cmd = run_test_child; 461 - err = start_command(&(*child)->process); 462 - if (err || !sequential) 463 - return err; 464 - return finish_test(child, /*running_test=*/0, /*child_test_num=*/1, width); 437 + if (sequential || pass == 2) { 438 + err = start_command(&(*child)->process); 439 + if (err) 440 + return err; 441 + finish_test(child, /*running_test=*/0, /*child_test_num=*/1, width); 442 + return 0; 443 + } 444 + return start_command(&(*child)->process); 465 445 } 466 446 467 447 #define for_each_test(j, k, t) \ ··· 475 447 static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) 476 448 { 477 449 struct test_suite *t; 478 - unsigned int j, k; 479 - int i = 0; 480 450 int width = 0; 451 + unsigned int j, k; 481 452 size_t num_tests = 0; 482 453 struct child_test **child_tests; 483 - int child_test_num = 0; 454 + int err = 0; 484 455 485 456 for_each_test(j, k, t) { 486 457 int len = strlen(test_description(t, -1)); ··· 502 475 if (!child_tests) 503 476 return -ENOMEM; 504 477 505 - for_each_test(j, k, t) { 506 - int curr = i++; 478 + /* 479 + * In parallel mode pass 1 runs non-exclusive tests in parallel, pass 2 480 + * runs the exclusive tests sequentially. In other modes all tests are 481 + * run in pass 1. 482 + */ 483 + for (int pass = 1; pass <= 2; pass++) { 484 + int child_test_num = 0; 485 + int i = 0; 507 486 508 - if (!perf_test__matches(test_description(t, -1), curr, argc, argv)) { 509 - bool skip = true; 487 + for_each_test(j, k, t) { 488 + int curr = i++; 510 489 511 - for (int subi = 0, subn = num_subtests(t); subi < subn; subi++) { 512 - if (perf_test__matches(test_description(t, subi), 513 - curr, argc, argv)) 514 - skip = false; 490 + if (!perf_test__matches(test_description(t, -1), curr, argc, argv)) { 491 + /* 492 + * Test suite shouldn't be run based on 493 + * description. See if subtest should. 494 + */ 495 + bool skip = true; 496 + 497 + for (int subi = 0, subn = num_subtests(t); subi < subn; subi++) { 498 + if (perf_test__matches(test_description(t, subi), 499 + curr, argc, argv)) 500 + skip = false; 501 + } 502 + 503 + if (skip) 504 + continue; 515 505 } 516 506 517 - if (skip) 507 + if (intlist__find(skiplist, i)) { 508 + pr_info("%3d: %-*s:", curr + 1, width, test_description(t, -1)); 509 + color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n"); 518 510 continue; 519 - } 520 - 521 - if (intlist__find(skiplist, i)) { 522 - pr_info("%3d: %-*s:", curr + 1, width, test_description(t, -1)); 523 - color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n"); 524 - continue; 525 - } 526 - 527 - if (!has_subtests(t)) { 528 - int err = start_test(t, curr, -1, &child_tests[child_test_num++], width); 529 - 530 - if (err) { 531 - /* TODO: if !sequential waitpid the already forked children. */ 532 - free(child_tests); 533 - return err; 534 511 } 535 - } else { 536 - for (int subi = 0, subn = num_subtests(t); subi < subn; subi++) { 537 - int err; 538 512 513 + if (!has_subtests(t)) { 514 + err = start_test(t, curr, -1, &child_tests[child_test_num++], 515 + width, pass); 516 + if (err) 517 + goto err_out; 518 + continue; 519 + } 520 + for (int subi = 0, subn = num_subtests(t); subi < subn; subi++) { 539 521 if (!perf_test__matches(test_description(t, subi), 540 522 curr, argc, argv)) 541 523 continue; 542 524 543 525 err = start_test(t, curr, subi, &child_tests[child_test_num++], 544 - width); 526 + width, pass); 545 527 if (err) 546 - return err; 528 + goto err_out; 547 529 } 548 530 } 549 - } 550 - for (i = 0; i < child_test_num; i++) { 551 531 if (!sequential) { 552 - int ret = finish_test(child_tests, i, child_test_num, width); 553 - 554 - if (ret) 555 - return ret; 532 + /* Parallel mode starts tests but doesn't finish them. Do that now. */ 533 + for (size_t x = 0; x < num_tests; x++) 534 + finish_test(child_tests, x, num_tests, width); 556 535 } 557 - free(child_tests[i]); 536 + } 537 + err_out: 538 + if (err) { 539 + pr_err("Internal test harness failure. Completing any started tests:\n:"); 540 + for (size_t x = 0; x < num_tests; x++) 541 + finish_test(child_tests, x, num_tests, width); 558 542 } 559 543 free(child_tests); 560 - return 0; 544 + return err; 561 545 } 562 546 563 547 static int perf_test__list(int argc, const char **argv) ··· 693 655 694 656 symbol_conf.priv_size = sizeof(int); 695 657 symbol_conf.try_vmlinux_path = true; 658 + 696 659 697 660 if (symbol__init(NULL) < 0) 698 661 return -1;
+8 -1
tools/perf/tests/task-exit.c
··· 152 152 return err; 153 153 } 154 154 155 - DEFINE_SUITE("Number of exit events of a simple workload", task_exit); 155 + struct test_case tests__task_exit[] = { 156 + TEST_CASE_EXCLUSIVE("Number of exit events of a simple workload", task_exit), 157 + { .name = NULL, } 158 + }; 159 + struct test_suite suite__task_exit = { 160 + .desc = "Number of exit events of a simple workload", 161 + .test_cases = tests__task_exit, 162 + };
+6 -1
tools/perf/tests/tests-scripts.c
··· 175 175 struct test_suite *test_suite, **result_tmp; 176 176 struct test_case *tests; 177 177 size_t len; 178 + char *exclusive; 178 179 179 180 snprintf(link, sizeof(link), "/proc/%d/fd/%d", getpid(), dir_fd); 180 181 len = readlink(link, filename, sizeof(filename)); ··· 192 191 return; 193 192 } 194 193 tests[0].name = strdup_check(name); 194 + exclusive = strstr(desc, " (exclusive)"); 195 + if (exclusive != NULL) { 196 + tests[0].exclusive = true; 197 + exclusive[0] = '\0'; 198 + } 195 199 tests[0].desc = strdup_check(desc); 196 200 tests[0].run_case = shell_test__run; 197 - 198 201 test_suite = zalloc(sizeof(*test_suite)); 199 202 if (!test_suite) { 200 203 pr_err("Out of memory while building script test suite list\n");
+9
tools/perf/tests/tests.h
··· 36 36 const char *desc; 37 37 const char *skip_reason; 38 38 test_fnptr run_case; 39 + bool exclusive; 39 40 }; 40 41 41 42 struct test_suite { ··· 61 60 .desc = description, \ 62 61 .run_case = test__##_name, \ 63 62 .skip_reason = _reason, \ 63 + } 64 + 65 + #define TEST_CASE_EXCLUSIVE(description, _name) \ 66 + { \ 67 + .name = #_name, \ 68 + .desc = description, \ 69 + .run_case = test__##_name, \ 70 + .exclusive = true, \ 64 71 } 65 72 66 73 #define DEFINE_SUITE(description, _name) \