* [PATCH v6 1/7] perf tests: Create a structure for shell tests
2026-03-09 15:57 [PATCH v6 0/7] Introduce structure for shell tests Jakub Brnak
@ 2026-03-09 15:57 ` Jakub Brnak
2026-03-10 15:05 ` Ian Rogers
2026-03-09 15:57 ` [PATCH v6 2/7] perf test: Provide setup for the shell test suite Jakub Brnak
` (6 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Jakub Brnak @ 2026-03-09 15:57 UTC (permalink / raw)
To: acme, acme, linux-perf-users; +Cc: namhyung, irogers, mpetlan
The general structure of test suites with test cases has been implemented
for C tests for some time, while shell tests were just all put into a list
without any possible structuring.
Provide the same possibility of test suite structure for shell tests. The
suite is created for each subdirectory located in the 'perf/tests/shell'
directory that contains at least one test script. All of the deeper levels
of subdirectories will be merged with the first level of test cases.
The name of the test suite is the name of the subdirectory, where the test
cases are located. For all of the test scripts that are not in any
subdirectory, a test suite with a single test case is created as it has
been till now.
The new structure of the shell tests for 'perf test list':
77: build id cache operations
78: coresight
78:1: CoreSight / ASM Pure Loop
78:2: CoreSight / Memcpy 16k 10 Threads
78:3: CoreSight / Thread Loop 10 Threads - Check TID
78:4: CoreSight / Thread Loop 2 Threads - Check TID
78:5: CoreSight / Unroll Loop Thread 10
79: daemon operations
80: perf diff tests
Signed-off-by: Michael Petlan <mpetlan@redhat.com>
Co-developed-by: Veronika Molnarova <vmolnaro@redhat.com>
Signed-off-by: Veronika Molnarova <vmolnaro@redhat.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Jakub Brnak <jbrnak@redhat.com>
---
tools/perf/tests/tests-scripts.c | 251 +++++++++++++++++++++++++------
tools/perf/tests/tests-scripts.h | 5 +
2 files changed, 209 insertions(+), 47 deletions(-)
diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c
index f18c4cd337c8..0f167365fb05 100644
--- a/tools/perf/tests/tests-scripts.c
+++ b/tools/perf/tests/tests-scripts.c
@@ -151,14 +151,64 @@ static char *strdup_check(const char *str)
return newstr;
}
-static int shell_test__run(struct test_suite *test, int subtest __maybe_unused)
+/* Check if dirent entry is a directory using file descriptor, handling DT_UNKNOWN case */
+static bool is_directory_fd(int dir_fd, struct dirent *ent)
{
- const char *file = test->priv;
+ struct stat st;
+
+ if (ent->d_type == DT_DIR)
+ return true;
+
+ if (ent->d_type != DT_UNKNOWN)
+ return false;
+
+ /* Need to stat the file to determine if it's a directory */
+ if (fstatat(dir_fd, ent->d_name, &st, 0) != 0)
+ return false;
+
+ return S_ISDIR(st.st_mode);
+}
+
+/* Free the whole structure of test_suite with its test_cases */
+static void free_suite(struct test_suite *suite)
+{
+ if (suite->test_cases) {
+ int num = 0;
+
+ while (suite->test_cases[num].name) { /* Last case has name set to NULL */
+ free((void *) suite->test_cases[num].name);
+ free((void *) suite->test_cases[num].desc);
+ num++;
+ }
+ free(suite->test_cases);
+ }
+ if (suite->desc)
+ free((void *) suite->desc);
+ if (suite->priv) {
+ struct shell_test_info *test_info = suite->priv;
+
+ free((void *) test_info->base_path);
+ free(test_info);
+ }
+
+ free(suite);
+}
+
+static int shell_test__run(struct test_suite *test, int subtest)
+{
+ const char *file;
int err;
char *cmd = NULL;
+ /* Get absolute file path */
+ if (subtest >= 0)
+ file = test->test_cases[subtest].name;
+ else /* Single test case */
+ file = test->test_cases[0].name;
+
if (asprintf(&cmd, "%s%s", file, verbose ? " -v" : "") < 0)
return TEST_FAIL;
+
err = system(cmd);
free(cmd);
if (!err)
@@ -167,65 +217,112 @@ static int shell_test__run(struct test_suite *test, int subtest __maybe_unused)
return WEXITSTATUS(err) == 2 ? TEST_SKIP : TEST_FAIL;
}
-static void append_script(int dir_fd, const char *name, char *desc,
- struct test_suite ***result,
- size_t *result_sz)
+static struct test_suite *prepare_test_suite(int dir_fd)
{
- char filename[PATH_MAX], link[128];
- struct test_suite *test_suite, **result_tmp;
- struct test_case *tests;
+ char dirpath[PATH_MAX], link[128];
ssize_t len;
- char *exclusive;
+ struct test_suite *test_suite = NULL;
+ struct shell_test_info *test_info;
+ /* Get dir absolute path */
snprintf(link, sizeof(link), "/proc/%d/fd/%d", getpid(), dir_fd);
- len = readlink(link, filename, sizeof(filename));
+ len = readlink(link, dirpath, sizeof(dirpath));
if (len < 0) {
pr_err("Failed to readlink %s", link);
- return;
+ return NULL;
}
- filename[len++] = '/';
- strcpy(&filename[len], name);
+ dirpath[len++] = '/';
+ dirpath[len] = '\0';
- tests = calloc(2, sizeof(*tests));
- if (!tests) {
- pr_err("Out of memory while building script test suite list\n");
- return;
- }
- tests[0].name = strdup_check(name);
- exclusive = strstr(desc, " (exclusive)");
- if (exclusive != NULL) {
- tests[0].exclusive = true;
- exclusive[0] = '\0';
- }
- tests[0].desc = strdup_check(desc);
- tests[0].run_case = shell_test__run;
test_suite = zalloc(sizeof(*test_suite));
if (!test_suite) {
pr_err("Out of memory while building script test suite list\n");
- free(tests);
- return;
+ return NULL;
}
- test_suite->desc = desc;
- test_suite->test_cases = tests;
- test_suite->priv = strdup_check(filename);
+
+ test_info = zalloc(sizeof(*test_info));
+ if (!test_info) {
+ pr_err("Out of memory while building script test suite list\n");
+ return NULL;
+ }
+
+ test_info->base_path = strdup_check(dirpath); /* Absolute path to dir */
+
+ test_suite->priv = test_info;
+ test_suite->desc = NULL;
+ test_suite->test_cases = NULL;
+
+ return test_suite;
+}
+
+static void append_suite(struct test_suite ***result,
+ size_t *result_sz, struct test_suite *test_suite)
+{
+ struct test_suite **result_tmp;
+
/* Realloc is good enough, though we could realloc by chunks, not that
* anyone will ever measure performance here */
result_tmp = realloc(*result, (*result_sz + 1) * sizeof(*result_tmp));
if (result_tmp == NULL) {
pr_err("Out of memory while building script test suite list\n");
- free(tests);
- free(test_suite);
+ free_suite(test_suite);
return;
}
+
/* Add file to end and NULL terminate the struct array */
*result = result_tmp;
(*result)[*result_sz] = test_suite;
(*result_sz)++;
}
-static void append_scripts_in_dir(int dir_fd,
- struct test_suite ***result,
- size_t *result_sz)
+static void append_script_to_suite(int dir_fd, const char *name, char *desc,
+ struct test_suite *test_suite, size_t *tc_count)
+{
+ char file_name[PATH_MAX], link[128];
+ struct test_case *tests;
+ size_t len;
+ char *exclusive;
+
+ if (!test_suite)
+ return;
+
+ /* Requires an empty test case at the end */
+ tests = realloc(test_suite->test_cases, (*tc_count + 2) * sizeof(*tests));
+ if (!tests) {
+ pr_err("Out of memory while building script test suite list\n");
+ return;
+ }
+
+ /* Get path to the test script */
+ snprintf(link, sizeof(link), "/proc/%d/fd/%d", getpid(), dir_fd);
+ len = readlink(link, file_name, sizeof(file_name));
+ if (len < 0) {
+ pr_err("Failed to readlink %s", link);
+ return;
+ }
+ file_name[len++] = '/';
+ strcpy(&file_name[len], name);
+
+ /* Get path to the script from base dir */
+ tests[(*tc_count)].name = strdup_check(file_name);
+ tests[(*tc_count)].exclusive = false;
+ exclusive = strstr(desc, " (exclusive)");
+ if (exclusive != NULL) {
+ tests[(*tc_count)].exclusive = true;
+ exclusive[0] = '\0';
+ }
+ tests[(*tc_count)].desc = desc;
+ tests[(*tc_count)].skip_reason = NULL; /* Unused */
+ tests[(*tc_count)++].run_case = shell_test__run;
+
+ tests[(*tc_count)].name = NULL; /* End the test cases */
+
+ test_suite->test_cases = tests;
+}
+
+static void append_scripts_in_subdir(int dir_fd,
+ struct test_suite *suite,
+ size_t *tc_count)
{
struct dirent **entlist;
struct dirent *ent;
@@ -244,22 +341,82 @@ static void append_scripts_in_dir(int dir_fd,
char *desc = shell_test__description(dir_fd, ent->d_name);
if (desc) /* It has a desc line - valid script */
- append_script(dir_fd, ent->d_name, desc, result, result_sz);
+ append_script_to_suite(dir_fd, ent->d_name, desc, suite, tc_count);
continue;
}
- if (ent->d_type != DT_DIR) {
- struct stat st;
-
- if (ent->d_type != DT_UNKNOWN)
- continue;
- fstatat(dir_fd, ent->d_name, &st, 0);
- if (!S_ISDIR(st.st_mode))
- continue;
+
+ if (!is_directory_fd(dir_fd, ent))
+ continue;
+
+ fd = openat(dir_fd, ent->d_name, O_PATH);
+
+ /* Recurse into the dir */
+ append_scripts_in_subdir(fd, suite, tc_count);
+ }
+ for (i = 0; i < n_dirs; i++) /* Clean up */
+ zfree(&entlist[i]);
+ free(entlist);
+}
+
+static void append_suites_in_dir(int dir_fd,
+ struct test_suite ***result,
+ size_t *result_sz)
+{
+ struct dirent **entlist;
+ struct dirent *ent;
+ int n_dirs, i;
+
+ /* List files, sorted by alpha */
+ n_dirs = scandirat(dir_fd, ".", &entlist, NULL, alphasort);
+ if (n_dirs == -1)
+ return;
+ for (i = 0; i < n_dirs && (ent = entlist[i]); i++) {
+ int fd;
+ struct test_suite *test_suite;
+ size_t cases_count = 0;
+
+ if (ent->d_name[0] == '.')
+ continue; /* Skip hidden files */
+ if (is_test_script(dir_fd, ent->d_name)) { /* It's a test */
+ char *desc = shell_test__description(dir_fd, ent->d_name);
+
+ if (desc) { /* It has a desc line - valid script */
+ /* Create a test suite with a single test case */
+ test_suite = prepare_test_suite(dir_fd);
+ append_script_to_suite(dir_fd, ent->d_name, desc,
+ test_suite, &cases_count);
+ test_suite->desc = strdup_check(desc);
+
+ if (cases_count)
+ append_suite(result, result_sz, test_suite);
+ else /* Wasn't able to create the test case */
+ free_suite(test_suite);
+ }
+ continue;
}
+
+ if (!is_directory_fd(dir_fd, ent))
+ continue;
+
if (strncmp(ent->d_name, "base_", 5) == 0)
continue; /* Skip scripts that have a separate driver. */
+
+ /* Scan subdir for test cases*/
fd = openat(dir_fd, ent->d_name, O_PATH);
- append_scripts_in_dir(fd, result, result_sz);
+ test_suite = prepare_test_suite(fd); /* Prepare a testsuite with its path */
+ if (!test_suite)
+ continue;
+
+ append_scripts_in_subdir(fd, test_suite, &cases_count);
+ if (cases_count == 0) {
+ free_suite(test_suite);
+ continue;
+ }
+
+ /* If no setup, set name to the directory */
+ test_suite->desc = strdup_check(ent->d_name);
+
+ append_suite(result, result_sz, test_suite);
close(fd);
}
for (i = 0; i < n_dirs; i++) /* Clean up */
@@ -278,7 +435,7 @@ struct test_suite **create_script_test_suites(void)
* length array.
*/
if (dir_fd >= 0)
- append_scripts_in_dir(dir_fd, &result, &result_sz);
+ append_suites_in_dir(dir_fd, &result, &result_sz);
result_tmp = realloc(result, (result_sz + 1) * sizeof(*result_tmp));
if (result_tmp == NULL) {
diff --git a/tools/perf/tests/tests-scripts.h b/tools/perf/tests/tests-scripts.h
index b553ad26ea17..1b849d4e70f4 100644
--- a/tools/perf/tests/tests-scripts.h
+++ b/tools/perf/tests/tests-scripts.h
@@ -4,6 +4,11 @@
#include "tests.h"
+/* Additional information attached to shell tests */
+struct shell_test_info {
+ const char *base_path;
+};
+
struct test_suite **create_script_test_suites(void);
#endif /* TESTS_SCRIPTS_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v6 1/7] perf tests: Create a structure for shell tests
2026-03-09 15:57 ` [PATCH v6 1/7] perf tests: Create a " Jakub Brnak
@ 2026-03-10 15:05 ` Ian Rogers
0 siblings, 0 replies; 13+ messages in thread
From: Ian Rogers @ 2026-03-10 15:05 UTC (permalink / raw)
To: Jakub Brnak; +Cc: acme, acme, linux-perf-users, namhyung, mpetlan
On Mon, Mar 9, 2026 at 8:57 AM Jakub Brnak <jbrnak@redhat.com> wrote:
>
> The general structure of test suites with test cases has been implemented
> for C tests for some time, while shell tests were just all put into a list
> without any possible structuring.
>
> Provide the same possibility of test suite structure for shell tests. The
> suite is created for each subdirectory located in the 'perf/tests/shell'
> directory that contains at least one test script. All of the deeper levels
> of subdirectories will be merged with the first level of test cases.
> The name of the test suite is the name of the subdirectory, where the test
> cases are located. For all of the test scripts that are not in any
> subdirectory, a test suite with a single test case is created as it has
> been till now.
>
> The new structure of the shell tests for 'perf test list':
> 77: build id cache operations
> 78: coresight
> 78:1: CoreSight / ASM Pure Loop
> 78:2: CoreSight / Memcpy 16k 10 Threads
> 78:3: CoreSight / Thread Loop 10 Threads - Check TID
> 78:4: CoreSight / Thread Loop 2 Threads - Check TID
> 78:5: CoreSight / Unroll Loop Thread 10
> 79: daemon operations
> 80: perf diff tests
>
> Signed-off-by: Michael Petlan <mpetlan@redhat.com>
> Co-developed-by: Veronika Molnarova <vmolnaro@redhat.com>
> Signed-off-by: Veronika Molnarova <vmolnaro@redhat.com>
> Reviewed-by: Ian Rogers <irogers@google.com>
> Signed-off-by: Jakub Brnak <jbrnak@redhat.com>
> ---
> tools/perf/tests/tests-scripts.c | 251 +++++++++++++++++++++++++------
> tools/perf/tests/tests-scripts.h | 5 +
> 2 files changed, 209 insertions(+), 47 deletions(-)
>
> diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c
> index f18c4cd337c8..0f167365fb05 100644
> --- a/tools/perf/tests/tests-scripts.c
> +++ b/tools/perf/tests/tests-scripts.c
> @@ -151,14 +151,64 @@ static char *strdup_check(const char *str)
> return newstr;
> }
>
> -static int shell_test__run(struct test_suite *test, int subtest __maybe_unused)
> +/* Check if dirent entry is a directory using file descriptor, handling DT_UNKNOWN case */
> +static bool is_directory_fd(int dir_fd, struct dirent *ent)
> {
> - const char *file = test->priv;
> + struct stat st;
> +
> + if (ent->d_type == DT_DIR)
> + return true;
> +
> + if (ent->d_type != DT_UNKNOWN)
> + return false;
> +
> + /* Need to stat the file to determine if it's a directory */
> + if (fstatat(dir_fd, ent->d_name, &st, 0) != 0)
> + return false;
> +
> + return S_ISDIR(st.st_mode);
> +}
> +
> +/* Free the whole structure of test_suite with its test_cases */
> +static void free_suite(struct test_suite *suite)
> +{
> + if (suite->test_cases) {
> + int num = 0;
> +
> + while (suite->test_cases[num].name) { /* Last case has name set to NULL */
> + free((void *) suite->test_cases[num].name);
> + free((void *) suite->test_cases[num].desc);
> + num++;
> + }
> + free(suite->test_cases);
> + }
> + if (suite->desc)
> + free((void *) suite->desc);
> + if (suite->priv) {
> + struct shell_test_info *test_info = suite->priv;
> +
> + free((void *) test_info->base_path);
> + free(test_info);
> + }
> +
> + free(suite);
> +}
> +
> +static int shell_test__run(struct test_suite *test, int subtest)
> +{
> + const char *file;
> int err;
> char *cmd = NULL;
>
> + /* Get absolute file path */
> + if (subtest >= 0)
> + file = test->test_cases[subtest].name;
> + else /* Single test case */
> + file = test->test_cases[0].name;
> +
> if (asprintf(&cmd, "%s%s", file, verbose ? " -v" : "") < 0)
> return TEST_FAIL;
> +
> err = system(cmd);
> free(cmd);
> if (!err)
> @@ -167,65 +217,112 @@ static int shell_test__run(struct test_suite *test, int subtest __maybe_unused)
> return WEXITSTATUS(err) == 2 ? TEST_SKIP : TEST_FAIL;
> }
>
> -static void append_script(int dir_fd, const char *name, char *desc,
> - struct test_suite ***result,
> - size_t *result_sz)
> +static struct test_suite *prepare_test_suite(int dir_fd)
> {
> - char filename[PATH_MAX], link[128];
> - struct test_suite *test_suite, **result_tmp;
> - struct test_case *tests;
> + char dirpath[PATH_MAX], link[128];
> ssize_t len;
> - char *exclusive;
> + struct test_suite *test_suite = NULL;
> + struct shell_test_info *test_info;
>
> + /* Get dir absolute path */
> snprintf(link, sizeof(link), "/proc/%d/fd/%d", getpid(), dir_fd);
> - len = readlink(link, filename, sizeof(filename));
> + len = readlink(link, dirpath, sizeof(dirpath));
> if (len < 0) {
> pr_err("Failed to readlink %s", link);
> - return;
> + return NULL;
> }
> - filename[len++] = '/';
> - strcpy(&filename[len], name);
> + dirpath[len++] = '/';
> + dirpath[len] = '\0';
>
> - tests = calloc(2, sizeof(*tests));
> - if (!tests) {
> - pr_err("Out of memory while building script test suite list\n");
> - return;
> - }
> - tests[0].name = strdup_check(name);
> - exclusive = strstr(desc, " (exclusive)");
> - if (exclusive != NULL) {
> - tests[0].exclusive = true;
> - exclusive[0] = '\0';
> - }
> - tests[0].desc = strdup_check(desc);
> - tests[0].run_case = shell_test__run;
> test_suite = zalloc(sizeof(*test_suite));
> if (!test_suite) {
> pr_err("Out of memory while building script test suite list\n");
> - free(tests);
> - return;
> + return NULL;
> }
> - test_suite->desc = desc;
> - test_suite->test_cases = tests;
> - test_suite->priv = strdup_check(filename);
> +
> + test_info = zalloc(sizeof(*test_info));
> + if (!test_info) {
> + pr_err("Out of memory while building script test suite list\n");
> + return NULL;
> + }
> +
> + test_info->base_path = strdup_check(dirpath); /* Absolute path to dir */
> +
> + test_suite->priv = test_info;
> + test_suite->desc = NULL;
> + test_suite->test_cases = NULL;
> +
> + return test_suite;
> +}
> +
> +static void append_suite(struct test_suite ***result,
> + size_t *result_sz, struct test_suite *test_suite)
> +{
> + struct test_suite **result_tmp;
> +
> /* Realloc is good enough, though we could realloc by chunks, not that
> * anyone will ever measure performance here */
> result_tmp = realloc(*result, (*result_sz + 1) * sizeof(*result_tmp));
> if (result_tmp == NULL) {
> pr_err("Out of memory while building script test suite list\n");
> - free(tests);
> - free(test_suite);
> + free_suite(test_suite);
> return;
> }
> +
> /* Add file to end and NULL terminate the struct array */
> *result = result_tmp;
> (*result)[*result_sz] = test_suite;
> (*result_sz)++;
> }
>
> -static void append_scripts_in_dir(int dir_fd,
> - struct test_suite ***result,
> - size_t *result_sz)
> +static void append_script_to_suite(int dir_fd, const char *name, char *desc,
> + struct test_suite *test_suite, size_t *tc_count)
> +{
> + char file_name[PATH_MAX], link[128];
> + struct test_case *tests;
> + size_t len;
> + char *exclusive;
> +
> + if (!test_suite)
> + return;
> +
> + /* Requires an empty test case at the end */
> + tests = realloc(test_suite->test_cases, (*tc_count + 2) * sizeof(*tests));
> + if (!tests) {
> + pr_err("Out of memory while building script test suite list\n");
> + return;
> + }
> +
> + /* Get path to the test script */
> + snprintf(link, sizeof(link), "/proc/%d/fd/%d", getpid(), dir_fd);
> + len = readlink(link, file_name, sizeof(file_name));
> + if (len < 0) {
> + pr_err("Failed to readlink %s", link);
> + return;
> + }
> + file_name[len++] = '/';
> + strcpy(&file_name[len], name);
> +
> + /* Get path to the script from base dir */
> + tests[(*tc_count)].name = strdup_check(file_name);
> + tests[(*tc_count)].exclusive = false;
> + exclusive = strstr(desc, " (exclusive)");
> + if (exclusive != NULL) {
> + tests[(*tc_count)].exclusive = true;
> + exclusive[0] = '\0';
> + }
> + tests[(*tc_count)].desc = desc;
> + tests[(*tc_count)].skip_reason = NULL; /* Unused */
> + tests[(*tc_count)++].run_case = shell_test__run;
> +
> + tests[(*tc_count)].name = NULL; /* End the test cases */
> +
> + test_suite->test_cases = tests;
> +}
> +
> +static void append_scripts_in_subdir(int dir_fd,
> + struct test_suite *suite,
> + size_t *tc_count)
> {
> struct dirent **entlist;
> struct dirent *ent;
> @@ -244,22 +341,82 @@ static void append_scripts_in_dir(int dir_fd,
> char *desc = shell_test__description(dir_fd, ent->d_name);
>
> if (desc) /* It has a desc line - valid script */
> - append_script(dir_fd, ent->d_name, desc, result, result_sz);
> + append_script_to_suite(dir_fd, ent->d_name, desc, suite, tc_count);
> continue;
> }
> - if (ent->d_type != DT_DIR) {
> - struct stat st;
> -
> - if (ent->d_type != DT_UNKNOWN)
> - continue;
> - fstatat(dir_fd, ent->d_name, &st, 0);
> - if (!S_ISDIR(st.st_mode))
> - continue;
> +
> + if (!is_directory_fd(dir_fd, ent))
> + continue;
> +
> + fd = openat(dir_fd, ent->d_name, O_PATH);
> +
> + /* Recurse into the dir */
> + append_scripts_in_subdir(fd, suite, tc_count);
Is there a missed close of fd here?
> + }
> + for (i = 0; i < n_dirs; i++) /* Clean up */
> + zfree(&entlist[i]);
> + free(entlist);
> +}
> +
> +static void append_suites_in_dir(int dir_fd,
> + struct test_suite ***result,
> + size_t *result_sz)
> +{
> + struct dirent **entlist;
> + struct dirent *ent;
> + int n_dirs, i;
> +
> + /* List files, sorted by alpha */
> + n_dirs = scandirat(dir_fd, ".", &entlist, NULL, alphasort);
Is entlist freed?
> + if (n_dirs == -1)
> + return;
> + for (i = 0; i < n_dirs && (ent = entlist[i]); i++) {
> + int fd;
> + struct test_suite *test_suite;
> + size_t cases_count = 0;
> +
> + if (ent->d_name[0] == '.')
> + continue; /* Skip hidden files */
> + if (is_test_script(dir_fd, ent->d_name)) { /* It's a test */
> + char *desc = shell_test__description(dir_fd, ent->d_name);
> +
> + if (desc) { /* It has a desc line - valid script */
> + /* Create a test suite with a single test case */
> + test_suite = prepare_test_suite(dir_fd);
prepare_test_suite will return NULL on error, should there be a check
and early exit if that happens?
> + append_script_to_suite(dir_fd, ent->d_name, desc,
> + test_suite, &cases_count);
> + test_suite->desc = strdup_check(desc);
> +
> + if (cases_count)
> + append_suite(result, result_sz, test_suite);
> + else /* Wasn't able to create the test case */
> + free_suite(test_suite);
> + }
> + continue;
> }
> +
> + if (!is_directory_fd(dir_fd, ent))
> + continue;
> +
> if (strncmp(ent->d_name, "base_", 5) == 0)
> continue; /* Skip scripts that have a separate driver. */
> +
> + /* Scan subdir for test cases*/
> fd = openat(dir_fd, ent->d_name, O_PATH);
> - append_scripts_in_dir(fd, result, result_sz);
> + test_suite = prepare_test_suite(fd); /* Prepare a testsuite with its path */
> + if (!test_suite)
Does the fd need closing before the continue?
> + continue;
> +
> + append_scripts_in_subdir(fd, test_suite, &cases_count);
> + if (cases_count == 0) {
> + free_suite(test_suite);
Similarly, does fd need closing?
Thanks,
Ian
> + continue;
> + }
> +
> + /* If no setup, set name to the directory */
> + test_suite->desc = strdup_check(ent->d_name);
> +
> + append_suite(result, result_sz, test_suite);
> close(fd);
> }
> for (i = 0; i < n_dirs; i++) /* Clean up */
> @@ -278,7 +435,7 @@ struct test_suite **create_script_test_suites(void)
> * length array.
> */
> if (dir_fd >= 0)
> - append_scripts_in_dir(dir_fd, &result, &result_sz);
> + append_suites_in_dir(dir_fd, &result, &result_sz);
>
> result_tmp = realloc(result, (result_sz + 1) * sizeof(*result_tmp));
> if (result_tmp == NULL) {
> diff --git a/tools/perf/tests/tests-scripts.h b/tools/perf/tests/tests-scripts.h
> index b553ad26ea17..1b849d4e70f4 100644
> --- a/tools/perf/tests/tests-scripts.h
> +++ b/tools/perf/tests/tests-scripts.h
> @@ -4,6 +4,11 @@
>
> #include "tests.h"
>
> +/* Additional information attached to shell tests */
> +struct shell_test_info {
> + const char *base_path;
> +};
> +
> struct test_suite **create_script_test_suites(void);
>
> #endif /* TESTS_SCRIPTS_H */
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v6 2/7] perf test: Provide setup for the shell test suite
2026-03-09 15:57 [PATCH v6 0/7] Introduce structure for shell tests Jakub Brnak
2026-03-09 15:57 ` [PATCH v6 1/7] perf tests: Create a " Jakub Brnak
@ 2026-03-09 15:57 ` Jakub Brnak
2026-03-10 15:14 ` Ian Rogers
2026-03-09 15:57 ` [PATCH v6 3/7] perftool-testsuite: Add empty setup for base_probe Jakub Brnak
` (5 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Jakub Brnak @ 2026-03-09 15:57 UTC (permalink / raw)
To: acme, acme, linux-perf-users; +Cc: namhyung, irogers, mpetlan
Some of the perftool-testsuite test cases require a setup to be done
beforehand as may be recording data, setting up cache or restoring sample
rate. The setup file also provides the possibility to set the name of
the test suite, if the name of the directory is not good enough.
Check for the existence of the "setup.sh" script for the shell test
suites and run it before the any of the test cases. If the setup fails,
skip all of the test cases of the test suite as the setup may be
required for the result to be valid.
Signed-off-by: Michael Petlan <mpetlan@redhat.com>
Co-developed-by: Veronika Molnarova <vmolnaro@redhat.com>
Signed-off-by: Veronika Molnarova <vmolnaro@redhat.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Jakub Brnak <jbrnak@redhat.com>
---
tools/perf/tests/builtin-test.c | 30 +++++++++++++++++++-----
tools/perf/tests/tests-scripts.c | 39 +++++++++++++++++++++++++++++---
tools/perf/tests/tests-scripts.h | 11 ++++++++-
tools/perf/tests/tests.h | 8 ++++---
4 files changed, 75 insertions(+), 13 deletions(-)
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 06507066213b..c1515fa8a947 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -264,6 +264,22 @@ static test_fnptr test_function(const struct test_suite *t, int test_case)
return t->test_cases[test_case].run_case;
}
+/* If setup fails, skip all test cases */
+static void check_shell_setup(const struct test_suite *t, int ret)
+{
+ struct shell_test_info *test_info;
+
+ if (!t->priv)
+ return;
+
+ test_info = t->priv;
+
+ if (ret == TEST_SETUP_FAIL)
+ test_info->has_setup = FAILED_SETUP;
+ else if (test_info->has_setup == RUN_SETUP)
+ test_info->has_setup = PASSED_SETUP;
+}
+
static bool test_exclusive(const struct test_suite *t, int test_case)
{
if (test_case <= 0)
@@ -353,10 +369,9 @@ static int run_test_child(struct child_process *process)
return -err;
}
-#define TEST_RUNNING -3
-
-static int print_test_result(struct test_suite *t, int curr_suite, int curr_test_case,
- int result, int width, int running)
+static int print_test_result(struct test_suite *t, int curr_suite,
+ int curr_test_case, int result, int width,
+ int running)
{
if (test_suite__num_test_cases(t) > 1) {
int subw = width > 2 ? width - 2 : width;
@@ -373,7 +388,8 @@ static int print_test_result(struct test_suite *t, int curr_suite, int curr_test
case TEST_OK:
pr_info(" Ok\n");
break;
- case TEST_SKIP: {
+ case TEST_SKIP:
+ case TEST_SETUP_FAIL:{
const char *reason = skip_reason(t, curr_test_case);
if (reason)
@@ -488,6 +504,7 @@ static void finish_test(struct child_test **child_tests, int running_test, int c
}
/* Clean up child process. */
ret = finish_command(&child_test->process);
+ check_shell_setup(t, ret);
if (verbose > 1 || (verbose == 1 && ret == TEST_FAIL))
fprintf(stderr, "%s", err_output.buf);
@@ -510,7 +527,8 @@ static int start_test(struct test_suite *test, int curr_suite, int curr_test_cas
err = test_function(test, curr_test_case)(test, curr_test_case);
pr_debug("---- end ----\n");
print_test_result(test, curr_suite, curr_test_case, err, width,
- /*running=*/0);
+ /*running=*/0);
+ check_shell_setup(test, err);
}
return 0;
}
diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c
index 0f167365fb05..3703ad7b3b75 100644
--- a/tools/perf/tests/tests-scripts.c
+++ b/tools/perf/tests/tests-scripts.c
@@ -138,6 +138,12 @@ static bool is_test_script(int dir_fd, const char *name)
return is_shell_script(dir_fd, name);
}
+/* Filter for scandir */
+static int setup_filter(const struct dirent *entry)
+{
+ return strcmp(entry->d_name, SHELL_SETUP);
+}
+
/* Duplicate a string and fall over and die if we run out of memory */
static char *strdup_check(const char *str)
{
@@ -196,6 +202,7 @@ static void free_suite(struct test_suite *suite)
static int shell_test__run(struct test_suite *test, int subtest)
{
+ struct shell_test_info *test_info = test->priv;
const char *file;
int err;
char *cmd = NULL;
@@ -206,6 +213,23 @@ static int shell_test__run(struct test_suite *test, int subtest)
else /* Single test case */
file = test->test_cases[0].name;
+ /* Run setup if needed */
+ if (test_info->has_setup == RUN_SETUP) {
+ char *setup_script;
+
+ if (asprintf(&setup_script, "%s%s%s", test_info->base_path,
+ SHELL_SETUP, verbose ? " -v" : "") < 0)
+ return TEST_SETUP_FAIL;
+
+ err = system(setup_script);
+ free(setup_script);
+
+ if (err)
+ return TEST_SETUP_FAIL;
+ } else if (test_info->has_setup == FAILED_SETUP) {
+ return TEST_SKIP; /* Skip test suite if setup failed */
+ }
+
if (asprintf(&cmd, "%s%s", file, verbose ? " -v" : "") < 0)
return TEST_FAIL;
@@ -247,6 +271,7 @@ static struct test_suite *prepare_test_suite(int dir_fd)
}
test_info->base_path = strdup_check(dirpath); /* Absolute path to dir */
+ test_info->has_setup = NO_SETUP;
test_suite->priv = test_info;
test_suite->desc = NULL;
@@ -329,7 +354,7 @@ static void append_scripts_in_subdir(int dir_fd,
int n_dirs, i;
/* List files, sorted by alpha */
- n_dirs = scandirat(dir_fd, ".", &entlist, NULL, alphasort);
+ n_dirs = scandirat(dir_fd, ".", &entlist, setup_filter, alphasort);
if (n_dirs == -1)
return;
for (i = 0; i < n_dirs && (ent = entlist[i]); i++) {
@@ -413,8 +438,16 @@ static void append_suites_in_dir(int dir_fd,
continue;
}
- /* If no setup, set name to the directory */
- test_suite->desc = strdup_check(ent->d_name);
+ if (is_test_script(fd, SHELL_SETUP)) { /* Check for setup existence */
+ char *desc = shell_test__description(fd, SHELL_SETUP);
+
+ /* Set the suite name by the setup description */
+ test_suite->desc = desc;
+ ((struct shell_test_info *)(test_suite->priv))->has_setup = RUN_SETUP;
+ } else {
+ /* If no setup, set name to the directory */
+ test_suite->desc = strdup_check(ent->d_name);
+ }
append_suite(result, result_sz, test_suite);
close(fd);
diff --git a/tools/perf/tests/tests-scripts.h b/tools/perf/tests/tests-scripts.h
index 1b849d4e70f4..013031239883 100644
--- a/tools/perf/tests/tests-scripts.h
+++ b/tools/perf/tests/tests-scripts.h
@@ -4,9 +4,18 @@
#include "tests.h"
-/* Additional information attached to shell tests */
+#define SHELL_SETUP "setup.sh"
+
+enum shell_setup {
+ NO_SETUP = 0,
+ RUN_SETUP = 1,
+ FAILED_SETUP = 2,
+ PASSED_SETUP = 3,
+};
+
struct shell_test_info {
const char *base_path;
+ enum shell_setup has_setup;
};
struct test_suite **create_script_test_suites(void);
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index f5f1238d1f7f..aa91859aeeb7 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -6,9 +6,11 @@
#include "util/debug.h"
enum {
- TEST_OK = 0,
- TEST_FAIL = -1,
- TEST_SKIP = -2,
+ TEST_OK = 0,
+ TEST_FAIL = -1,
+ TEST_SKIP = -2,
+ TEST_RUNNING = -3,
+ TEST_SETUP_FAIL = -4,
};
#define TEST_ASSERT_VAL(text, cond) \
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v6 2/7] perf test: Provide setup for the shell test suite
2026-03-09 15:57 ` [PATCH v6 2/7] perf test: Provide setup for the shell test suite Jakub Brnak
@ 2026-03-10 15:14 ` Ian Rogers
0 siblings, 0 replies; 13+ messages in thread
From: Ian Rogers @ 2026-03-10 15:14 UTC (permalink / raw)
To: Jakub Brnak; +Cc: acme, acme, linux-perf-users, namhyung, mpetlan
On Mon, Mar 9, 2026 at 8:57 AM Jakub Brnak <jbrnak@redhat.com> wrote:
>
> Some of the perftool-testsuite test cases require a setup to be done
> beforehand as may be recording data, setting up cache or restoring sample
> rate. The setup file also provides the possibility to set the name of
> the test suite, if the name of the directory is not good enough.
>
> Check for the existence of the "setup.sh" script for the shell test
> suites and run it before the any of the test cases. If the setup fails,
> skip all of the test cases of the test suite as the setup may be
> required for the result to be valid.
>
> Signed-off-by: Michael Petlan <mpetlan@redhat.com>
> Co-developed-by: Veronika Molnarova <vmolnaro@redhat.com>
> Signed-off-by: Veronika Molnarova <vmolnaro@redhat.com>
> Reviewed-by: Ian Rogers <irogers@google.com>
> Signed-off-by: Jakub Brnak <jbrnak@redhat.com>
> ---
> tools/perf/tests/builtin-test.c | 30 +++++++++++++++++++-----
> tools/perf/tests/tests-scripts.c | 39 +++++++++++++++++++++++++++++---
> tools/perf/tests/tests-scripts.h | 11 ++++++++-
> tools/perf/tests/tests.h | 8 ++++---
> 4 files changed, 75 insertions(+), 13 deletions(-)
>
> diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
> index 06507066213b..c1515fa8a947 100644
> --- a/tools/perf/tests/builtin-test.c
> +++ b/tools/perf/tests/builtin-test.c
> @@ -264,6 +264,22 @@ static test_fnptr test_function(const struct test_suite *t, int test_case)
> return t->test_cases[test_case].run_case;
> }
>
> +/* If setup fails, skip all test cases */
> +static void check_shell_setup(const struct test_suite *t, int ret)
> +{
> + struct shell_test_info *test_info;
> +
> + if (!t->priv)
> + return;
> +
> + test_info = t->priv;
> +
> + if (ret == TEST_SETUP_FAIL)
The line above was flagged by AI review:
Does `ret` here contain a positive exit status from the child
process? If `setup.sh` fails, `run_test_child()` returns
`TEST_SETUP_FAIL` (-4) negated, which becomes an exit status of 4.
Since `TEST_SETUP_FAIL` is -4, `ret == TEST_SETUP_FAIL` will evaluate
to false (4 != -4). This might cause the test suite state to
incorrectly transition to `PASSED_SETUP` instead of `FAILED_SETUP`,
skipping the failure path and causing `print_test_result()` to fall
into the default case instead of skipping the test.
> + test_info->has_setup = FAILED_SETUP;
> + else if (test_info->has_setup == RUN_SETUP)
> + test_info->has_setup = PASSED_SETUP;
> +}
> +
> static bool test_exclusive(const struct test_suite *t, int test_case)
> {
> if (test_case <= 0)
> @@ -353,10 +369,9 @@ static int run_test_child(struct child_process *process)
> return -err;
> }
>
> -#define TEST_RUNNING -3
> -
> -static int print_test_result(struct test_suite *t, int curr_suite, int curr_test_case,
> - int result, int width, int running)
> +static int print_test_result(struct test_suite *t, int curr_suite,
> + int curr_test_case, int result, int width,
> + int running)
> {
> if (test_suite__num_test_cases(t) > 1) {
> int subw = width > 2 ? width - 2 : width;
> @@ -373,7 +388,8 @@ static int print_test_result(struct test_suite *t, int curr_suite, int curr_test
> case TEST_OK:
> pr_info(" Ok\n");
> break;
> - case TEST_SKIP: {
> + case TEST_SKIP:
> + case TEST_SETUP_FAIL:{
> const char *reason = skip_reason(t, curr_test_case);
>
> if (reason)
> @@ -488,6 +504,7 @@ static void finish_test(struct child_test **child_tests, int running_test, int c
> }
> /* Clean up child process. */
> ret = finish_command(&child_test->process);
> + check_shell_setup(t, ret);
> if (verbose > 1 || (verbose == 1 && ret == TEST_FAIL))
> fprintf(stderr, "%s", err_output.buf);
>
> @@ -510,7 +527,8 @@ static int start_test(struct test_suite *test, int curr_suite, int curr_test_cas
> err = test_function(test, curr_test_case)(test, curr_test_case);
> pr_debug("---- end ----\n");
> print_test_result(test, curr_suite, curr_test_case, err, width,
> - /*running=*/0);
> + /*running=*/0);
> + check_shell_setup(test, err);
> }
> return 0;
> }
> diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c
> index 0f167365fb05..3703ad7b3b75 100644
> --- a/tools/perf/tests/tests-scripts.c
> +++ b/tools/perf/tests/tests-scripts.c
> @@ -138,6 +138,12 @@ static bool is_test_script(int dir_fd, const char *name)
> return is_shell_script(dir_fd, name);
> }
>
> +/* Filter for scandir */
> +static int setup_filter(const struct dirent *entry)
> +{
> + return strcmp(entry->d_name, SHELL_SETUP);
> +}
> +
> /* Duplicate a string and fall over and die if we run out of memory */
> static char *strdup_check(const char *str)
> {
> @@ -196,6 +202,7 @@ static void free_suite(struct test_suite *suite)
>
> static int shell_test__run(struct test_suite *test, int subtest)
> {
> + struct shell_test_info *test_info = test->priv;
> const char *file;
> int err;
> char *cmd = NULL;
> @@ -206,6 +213,23 @@ static int shell_test__run(struct test_suite *test, int subtest)
> else /* Single test case */
> file = test->test_cases[0].name;
>
> + /* Run setup if needed */
> + if (test_info->has_setup == RUN_SETUP) {
> + char *setup_script;
> +
> + if (asprintf(&setup_script, "%s%s%s", test_info->base_path,
> + SHELL_SETUP, verbose ? " -v" : "") < 0)
> + return TEST_SETUP_FAIL;
> +
> + err = system(setup_script);
If `perf test` is run in parallel mode (`!sequential`), will
`start_test()` fork a child process for each test case immediately?
Since the parent process hasn't executed the setup, all child
processes will inherit `test_info->has_setup == RUN_SETUP` and execute
`setup.sh` concurrently via `system(setup_script)` here. Could this
lead to race conditions if the setup script modifies shared state?
> + free(setup_script);
> +
> + if (err)
> + return TEST_SETUP_FAIL;
> + } else if (test_info->has_setup == FAILED_SETUP) {
> + return TEST_SKIP; /* Skip test suite if setup failed */
> + }
> +
> if (asprintf(&cmd, "%s%s", file, verbose ? " -v" : "") < 0)
> return TEST_FAIL;
>
> @@ -247,6 +271,7 @@ static struct test_suite *prepare_test_suite(int dir_fd)
> }
>
> test_info->base_path = strdup_check(dirpath); /* Absolute path to dir */
> + test_info->has_setup = NO_SETUP;
>
> test_suite->priv = test_info;
> test_suite->desc = NULL;
> @@ -329,7 +354,7 @@ static void append_scripts_in_subdir(int dir_fd,
> int n_dirs, i;
>
> /* List files, sorted by alpha */
> - n_dirs = scandirat(dir_fd, ".", &entlist, NULL, alphasort);
> + n_dirs = scandirat(dir_fd, ".", &entlist, setup_filter, alphasort);
> if (n_dirs == -1)
> return;
> for (i = 0; i < n_dirs && (ent = entlist[i]); i++) {
> @@ -413,8 +438,16 @@ static void append_suites_in_dir(int dir_fd,
> continue;
> }
>
> - /* If no setup, set name to the directory */
> - test_suite->desc = strdup_check(ent->d_name);
> + if (is_test_script(fd, SHELL_SETUP)) { /* Check for setup existence */
> + char *desc = shell_test__description(fd, SHELL_SETUP);
AI review is flagging that desc may be NULL and lead to a SEGV:
Can `shell_test__description()` return NULL if `setup.sh` lacks a
description format? If it does, `test_suite->desc` will be assigned
NULL without falling back to the directory name. Later on, could this
trigger a segmentation fault when `__cmd_test()` passes the
description pointer to `strlen()`?
Thanks,
Ian
> +
> + /* Set the suite name by the setup description */
> + test_suite->desc = desc;
> + ((struct shell_test_info *)(test_suite->priv))->has_setup = RUN_SETUP;
> + } else {
> + /* If no setup, set name to the directory */
> + test_suite->desc = strdup_check(ent->d_name);
> + }
>
> append_suite(result, result_sz, test_suite);
> close(fd);
> diff --git a/tools/perf/tests/tests-scripts.h b/tools/perf/tests/tests-scripts.h
> index 1b849d4e70f4..013031239883 100644
> --- a/tools/perf/tests/tests-scripts.h
> +++ b/tools/perf/tests/tests-scripts.h
> @@ -4,9 +4,18 @@
>
> #include "tests.h"
>
> -/* Additional information attached to shell tests */
> +#define SHELL_SETUP "setup.sh"
> +
> +enum shell_setup {
> + NO_SETUP = 0,
> + RUN_SETUP = 1,
> + FAILED_SETUP = 2,
> + PASSED_SETUP = 3,
> +};
> +
> struct shell_test_info {
> const char *base_path;
> + enum shell_setup has_setup;
> };
>
> struct test_suite **create_script_test_suites(void);
> diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
> index f5f1238d1f7f..aa91859aeeb7 100644
> --- a/tools/perf/tests/tests.h
> +++ b/tools/perf/tests/tests.h
> @@ -6,9 +6,11 @@
> #include "util/debug.h"
>
> enum {
> - TEST_OK = 0,
> - TEST_FAIL = -1,
> - TEST_SKIP = -2,
> + TEST_OK = 0,
> + TEST_FAIL = -1,
> + TEST_SKIP = -2,
> + TEST_RUNNING = -3,
> + TEST_SETUP_FAIL = -4,
> };
>
> #define TEST_ASSERT_VAL(text, cond) \
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v6 3/7] perftool-testsuite: Add empty setup for base_probe
2026-03-09 15:57 [PATCH v6 0/7] Introduce structure for shell tests Jakub Brnak
2026-03-09 15:57 ` [PATCH v6 1/7] perf tests: Create a " Jakub Brnak
2026-03-09 15:57 ` [PATCH v6 2/7] perf test: Provide setup for the shell test suite Jakub Brnak
@ 2026-03-09 15:57 ` Jakub Brnak
2026-03-09 15:57 ` [PATCH v6 4/7] perf test: Introduce storing logs for shell tests Jakub Brnak
` (4 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Jakub Brnak @ 2026-03-09 15:57 UTC (permalink / raw)
To: acme, acme, linux-perf-users; +Cc: namhyung, irogers, mpetlan
Add empty setup to set a proper name for base_probe testsuite, can be
utilized for basic test setup for the future.
Signed-off-by: Michael Petlan <mpetlan@redhat.com>
Co-developed-by: Veronika Molnarova <vmolnaro@redhat.com>
Signed-off-by: Veronika Molnarova <vmolnaro@redhat.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Jakub Brnak <jbrnak@redhat.com>
---
tools/perf/tests/shell/base_probe/setup.sh | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100755 tools/perf/tests/shell/base_probe/setup.sh
diff --git a/tools/perf/tests/shell/base_probe/setup.sh b/tools/perf/tests/shell/base_probe/setup.sh
new file mode 100755
index 000000000000..fbb99325b555
--- /dev/null
+++ b/tools/perf/tests/shell/base_probe/setup.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+# perftool-testsuite :: perf_probe
+# SPDX-License-Identifier: GPL-2.0
+
+#
+# setup.sh of perf probe test
+# Author: Michael Petlan <mpetlan@redhat.com>
+#
+# Description:
+#
+# Setting testsuite name, for future use
+#
+#
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v6 4/7] perf test: Introduce storing logs for shell tests
2026-03-09 15:57 [PATCH v6 0/7] Introduce structure for shell tests Jakub Brnak
` (2 preceding siblings ...)
2026-03-09 15:57 ` [PATCH v6 3/7] perftool-testsuite: Add empty setup for base_probe Jakub Brnak
@ 2026-03-09 15:57 ` Jakub Brnak
2026-03-10 15:22 ` Ian Rogers
2026-03-09 15:57 ` [PATCH v6 5/7] perf test: Format log directories " Jakub Brnak
` (3 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Jakub Brnak @ 2026-03-09 15:57 UTC (permalink / raw)
To: acme, acme, linux-perf-users; +Cc: namhyung, irogers, mpetlan
Create temporary directories for storing log files for shell tests
that could help while debugging. The log files are necessary for
perftool testsuite test cases also. If the variable PERFTEST_KEEP_LOGS
is set keep the logs, else delete them.
Signed-off-by: Michael Petlan <mpetlan@redhat.com>
Co-developed-by: Veronika Molnarova <vmolnaro@redhat.com>
Signed-off-by: Veronika Molnarova <vmolnaro@redhat.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Jakub Brnak <jbrnak@redhat.com>
---
tools/perf/tests/builtin-test.c | 103 +++++++++++++++++++++++++++++++
tools/perf/tests/tests-scripts.c | 4 ++
tools/perf/tests/tests-scripts.h | 3 +
3 files changed, 110 insertions(+)
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index c1515fa8a947..70f4a61357ec 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -6,6 +6,7 @@
*/
#include <ctype.h>
#include <fcntl.h>
+#include <ftw.h>
#include <errno.h>
#ifdef HAVE_BACKTRACE_SUPPORT
#include <execinfo.h>
@@ -51,6 +52,9 @@ static unsigned int runs_per_test = 1;
const char *dso_to_test;
const char *test_objdump_path = "objdump";
+/* Number of logged test suites. */
+size_t num_logged_suites;
+
/*
* List of architecture specific tests. Not a weak symbol as the array length is
* dependent on the initialization, as such GCC with LTO complains of
@@ -288,6 +292,85 @@ static bool test_exclusive(const struct test_suite *t, int test_case)
return t->test_cases[test_case].exclusive;
}
+static int delete_file(const char *fpath, const struct stat *sb __maybe_unused,
+ int typeflag, struct FTW *ftwbuf)
+{
+ int rv = -1;
+
+ /* Stop traversal if going too deep */
+ if (ftwbuf->level > 5) {
+ pr_err("Tree traversal reached level %d, stopping.", ftwbuf->level);
+ return rv;
+ }
+
+ /* Remove only expected directories */
+ if (typeflag == FTW_D || typeflag == FTW_DP) {
+ const char *dirname = fpath + ftwbuf->base;
+
+ if (strcmp(dirname, "logs") && strcmp(dirname, "examples") &&
+ strcmp(dirname, "header_tar") && strncmp(dirname, "perf_", 5)) {
+ pr_err("Unknown directory %s", dirname);
+ return rv;
+ }
+ }
+
+ /* Attempt to remove the file */
+ rv = remove(fpath);
+ if (rv)
+ pr_err("Failed to remove file: %s", fpath);
+
+ return rv;
+}
+
+static bool create_logs(struct test_suite *t, int pass)
+{
+ bool store_logs = t->priv && ((struct shell_test_info *)(t->priv))->store_logs;
+
+ if (pass == 1 && (!test_exclusive(t, 0) || sequential || dont_fork)) {
+ /* Sequential and non-exclusive tests run on the first pass. */
+ return store_logs;
+ } else if (pass != 1 && test_exclusive(t, 0) && !sequential && !dont_fork) {
+ /* Exclusive tests without sequential run on the second pass. */
+ return store_logs;
+ }
+ return false;
+}
+
+static char *setup_shell_logs(const char *name)
+{
+ char template[PATH_MAX];
+ char *temp_dir;
+
+ if (snprintf(template, PATH_MAX, "/tmp/perf_test_%s.XXXXXX", name) < 0) {
+ pr_err("Failed to create log dir template");
+ return NULL; /* Skip the testsuite */
+ }
+
+ temp_dir = mkdtemp(template);
+ if (temp_dir) {
+ setenv("PERFSUITE_RUN_DIR", temp_dir, 1);
+ return strdup(temp_dir);
+ }
+
+ pr_err("Failed to create the temporary directory");
+
+ return NULL; /* Skip the testsuite */
+}
+
+static void cleanup_shell_logs(char *dirname)
+{
+ char *keep_logs = getenv("PERFTEST_KEEP_LOGS");
+
+ /* Check if logs should be kept or do cleanup */
+ if (dirname) {
+ if (!keep_logs || strcmp(keep_logs, "y") != 0)
+ nftw(dirname, delete_file, 8, FTW_DEPTH | FTW_PHYS);
+ free(dirname);
+ }
+
+ unsetenv("PERFSUITE_RUN_DIR");
+}
+
static bool perf_test__matches(const char *desc, int suite_num, int argc, const char *argv[])
{
int i;
@@ -627,9 +710,12 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[],
* runs the exclusive tests sequentially. In other modes all tests are
* run in pass 1.
*/
+
for (int pass = 1; pass <= 2; pass++) {
int child_test_num = 0;
int curr_suite = 0;
+ char *tmpdir[num_logged_suites * runs_per_test];
+ unsigned int logged_suites = 0;
for (struct test_suite **t = suites; *t; t++, curr_suite++) {
int curr_test_case;
@@ -663,6 +749,18 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[],
}
for (unsigned int run = 0; run < runs_per_test; run++) {
+ /* Setup temporary log directories for shell test suites */
+ if (create_logs(*t, pass)) {
+ tmpdir[logged_suites] = setup_shell_logs((*t)->desc);
+ /* Couldn't create log dir, skip test suite */
+ if (tmpdir[logged_suites] == NULL) {
+ ((struct shell_test_info *)((*t)->priv))->has_setup
+ = FAILED_SETUP;
+ } else {
+ logged_suites++;
+ }
+ }
+
test_suite__for_each_test_case(*t, curr_test_case) {
if (!suite_matched &&
!perf_test__matches(test_description(*t, curr_test_case),
@@ -681,6 +779,9 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[],
for (size_t x = 0; x < num_tests; x++)
finish_test(child_tests, x, num_tests, width);
}
+
+ for (size_t x = 0; x < logged_suites; x++)
+ cleanup_shell_logs(tmpdir[x]);
}
err_out:
signal(SIGINT, SIG_DFL);
@@ -750,6 +851,8 @@ static int perf_test__config(const char *var, const char *value,
return 0;
}
+
+
static struct test_suite **build_suites(void)
{
/*
diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c
index 3703ad7b3b75..c45b41b5bf07 100644
--- a/tools/perf/tests/tests-scripts.c
+++ b/tools/perf/tests/tests-scripts.c
@@ -272,6 +272,7 @@ static struct test_suite *prepare_test_suite(int dir_fd)
test_info->base_path = strdup_check(dirpath); /* Absolute path to dir */
test_info->has_setup = NO_SETUP;
+ test_info->store_logs = false;
test_suite->priv = test_info;
test_suite->desc = NULL;
@@ -438,6 +439,9 @@ static void append_suites_in_dir(int dir_fd,
continue;
}
+ /* Store logs for testsuite is sub-directories */
+ ((struct shell_test_info *)(test_suite->priv))->store_logs = true;
+ num_logged_suites++;
if (is_test_script(fd, SHELL_SETUP)) { /* Check for setup existence */
char *desc = shell_test__description(fd, SHELL_SETUP);
diff --git a/tools/perf/tests/tests-scripts.h b/tools/perf/tests/tests-scripts.h
index 013031239883..1dd91f1444e3 100644
--- a/tools/perf/tests/tests-scripts.h
+++ b/tools/perf/tests/tests-scripts.h
@@ -16,8 +16,11 @@ enum shell_setup {
struct shell_test_info {
const char *base_path;
enum shell_setup has_setup;
+ bool store_logs;
};
struct test_suite **create_script_test_suites(void);
+extern size_t num_logged_suites;
+
#endif /* TESTS_SCRIPTS_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v6 4/7] perf test: Introduce storing logs for shell tests
2026-03-09 15:57 ` [PATCH v6 4/7] perf test: Introduce storing logs for shell tests Jakub Brnak
@ 2026-03-10 15:22 ` Ian Rogers
0 siblings, 0 replies; 13+ messages in thread
From: Ian Rogers @ 2026-03-10 15:22 UTC (permalink / raw)
To: Jakub Brnak; +Cc: acme, acme, linux-perf-users, namhyung, mpetlan
On Mon, Mar 9, 2026 at 8:57 AM Jakub Brnak <jbrnak@redhat.com> wrote:
>
> Create temporary directories for storing log files for shell tests
> that could help while debugging. The log files are necessary for
> perftool testsuite test cases also. If the variable PERFTEST_KEEP_LOGS
> is set keep the logs, else delete them.
>
> Signed-off-by: Michael Petlan <mpetlan@redhat.com>
> Co-developed-by: Veronika Molnarova <vmolnaro@redhat.com>
> Signed-off-by: Veronika Molnarova <vmolnaro@redhat.com>
> Reviewed-by: Ian Rogers <irogers@google.com>
> Signed-off-by: Jakub Brnak <jbrnak@redhat.com>
> ---
> tools/perf/tests/builtin-test.c | 103 +++++++++++++++++++++++++++++++
> tools/perf/tests/tests-scripts.c | 4 ++
> tools/perf/tests/tests-scripts.h | 3 +
> 3 files changed, 110 insertions(+)
>
> diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
> index c1515fa8a947..70f4a61357ec 100644
> --- a/tools/perf/tests/builtin-test.c
> +++ b/tools/perf/tests/builtin-test.c
> @@ -6,6 +6,7 @@
> */
> #include <ctype.h>
> #include <fcntl.h>
> +#include <ftw.h>
> #include <errno.h>
> #ifdef HAVE_BACKTRACE_SUPPORT
> #include <execinfo.h>
> @@ -51,6 +52,9 @@ static unsigned int runs_per_test = 1;
> const char *dso_to_test;
> const char *test_objdump_path = "objdump";
>
> +/* Number of logged test suites. */
> +size_t num_logged_suites;
> +
> /*
> * List of architecture specific tests. Not a weak symbol as the array length is
> * dependent on the initialization, as such GCC with LTO complains of
> @@ -288,6 +292,85 @@ static bool test_exclusive(const struct test_suite *t, int test_case)
> return t->test_cases[test_case].exclusive;
> }
>
> +static int delete_file(const char *fpath, const struct stat *sb __maybe_unused,
> + int typeflag, struct FTW *ftwbuf)
> +{
> + int rv = -1;
> +
> + /* Stop traversal if going too deep */
> + if (ftwbuf->level > 5) {
> + pr_err("Tree traversal reached level %d, stopping.", ftwbuf->level);
> + return rv;
> + }
> +
> + /* Remove only expected directories */
> + if (typeflag == FTW_D || typeflag == FTW_DP) {
> + const char *dirname = fpath + ftwbuf->base;
> +
> + if (strcmp(dirname, "logs") && strcmp(dirname, "examples") &&
> + strcmp(dirname, "header_tar") && strncmp(dirname, "perf_", 5)) {
> + pr_err("Unknown directory %s", dirname);
> + return rv;
> + }
> + }
> +
> + /* Attempt to remove the file */
> + rv = remove(fpath);
> + if (rv)
> + pr_err("Failed to remove file: %s", fpath);
> +
> + return rv;
Returning non-zero here will terminate the nftw, would it make more
sense to always return 0 say as many files are cleaned up as possible?
> +}
> +
> +static bool create_logs(struct test_suite *t, int pass)
> +{
> + bool store_logs = t->priv && ((struct shell_test_info *)(t->priv))->store_logs;
> +
> + if (pass == 1 && (!test_exclusive(t, 0) || sequential || dont_fork)) {
> + /* Sequential and non-exclusive tests run on the first pass. */
> + return store_logs;
> + } else if (pass != 1 && test_exclusive(t, 0) && !sequential && !dont_fork) {
> + /* Exclusive tests without sequential run on the second pass. */
> + return store_logs;
> + }
> + return false;
> +}
> +
> +static char *setup_shell_logs(const char *name)
> +{
> + char template[PATH_MAX];
> + char *temp_dir;
> +
> + if (snprintf(template, PATH_MAX, "/tmp/perf_test_%s.XXXXXX", name) < 0) {
> + pr_err("Failed to create log dir template");
> + return NULL; /* Skip the testsuite */
> + }
> +
> + temp_dir = mkdtemp(template);
> + if (temp_dir) {
> + setenv("PERFSUITE_RUN_DIR", temp_dir, 1);
> + return strdup(temp_dir);
> + }
> +
> + pr_err("Failed to create the temporary directory");
> +
> + return NULL; /* Skip the testsuite */
> +}
> +
> +static void cleanup_shell_logs(char *dirname)
> +{
> + char *keep_logs = getenv("PERFTEST_KEEP_LOGS");
> +
> + /* Check if logs should be kept or do cleanup */
> + if (dirname) {
> + if (!keep_logs || strcmp(keep_logs, "y") != 0)
> + nftw(dirname, delete_file, 8, FTW_DEPTH | FTW_PHYS);
> + free(dirname);
> + }
> +
> + unsetenv("PERFSUITE_RUN_DIR");
> +}
> +
> static bool perf_test__matches(const char *desc, int suite_num, int argc, const char *argv[])
> {
> int i;
> @@ -627,9 +710,12 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[],
> * runs the exclusive tests sequentially. In other modes all tests are
> * run in pass 1.
> */
> +
> for (int pass = 1; pass <= 2; pass++) {
> int child_test_num = 0;
> int curr_suite = 0;
> + char *tmpdir[num_logged_suites * runs_per_test];
Could we avoid a VLA with a calloc here? It is test code so I'd prefer
correctness over performance.
> + unsigned int logged_suites = 0;
>
> for (struct test_suite **t = suites; *t; t++, curr_suite++) {
> int curr_test_case;
> @@ -663,6 +749,18 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[],
> }
>
> for (unsigned int run = 0; run < runs_per_test; run++) {
> + /* Setup temporary log directories for shell test suites */
> + if (create_logs(*t, pass)) {
> + tmpdir[logged_suites] = setup_shell_logs((*t)->desc);
> + /* Couldn't create log dir, skip test suite */
> + if (tmpdir[logged_suites] == NULL) {
> + ((struct shell_test_info *)((*t)->priv))->has_setup
> + = FAILED_SETUP;
> + } else {
> + logged_suites++;
> + }
> + }
> +
> test_suite__for_each_test_case(*t, curr_test_case) {
> if (!suite_matched &&
> !perf_test__matches(test_description(*t, curr_test_case),
> @@ -681,6 +779,9 @@ static int __cmd_test(struct test_suite **suites, int argc, const char *argv[],
> for (size_t x = 0; x < num_tests; x++)
> finish_test(child_tests, x, num_tests, width);
> }
> +
> + for (size_t x = 0; x < logged_suites; x++)
> + cleanup_shell_logs(tmpdir[x]);
Perhaps it is worth commenting that leaving/leaking the logs in the
event of an error is deliberate?
Thanks,
Ian
> }
> err_out:
> signal(SIGINT, SIG_DFL);
> @@ -750,6 +851,8 @@ static int perf_test__config(const char *var, const char *value,
> return 0;
> }
>
> +
> +
> static struct test_suite **build_suites(void)
> {
> /*
> diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c
> index 3703ad7b3b75..c45b41b5bf07 100644
> --- a/tools/perf/tests/tests-scripts.c
> +++ b/tools/perf/tests/tests-scripts.c
> @@ -272,6 +272,7 @@ static struct test_suite *prepare_test_suite(int dir_fd)
>
> test_info->base_path = strdup_check(dirpath); /* Absolute path to dir */
> test_info->has_setup = NO_SETUP;
> + test_info->store_logs = false;
>
> test_suite->priv = test_info;
> test_suite->desc = NULL;
> @@ -438,6 +439,9 @@ static void append_suites_in_dir(int dir_fd,
> continue;
> }
>
> + /* Store logs for testsuite is sub-directories */
> + ((struct shell_test_info *)(test_suite->priv))->store_logs = true;
> + num_logged_suites++;
> if (is_test_script(fd, SHELL_SETUP)) { /* Check for setup existence */
> char *desc = shell_test__description(fd, SHELL_SETUP);
>
> diff --git a/tools/perf/tests/tests-scripts.h b/tools/perf/tests/tests-scripts.h
> index 013031239883..1dd91f1444e3 100644
> --- a/tools/perf/tests/tests-scripts.h
> +++ b/tools/perf/tests/tests-scripts.h
> @@ -16,8 +16,11 @@ enum shell_setup {
> struct shell_test_info {
> const char *base_path;
> enum shell_setup has_setup;
> + bool store_logs;
> };
>
> struct test_suite **create_script_test_suites(void);
>
> +extern size_t num_logged_suites;
> +
> #endif /* TESTS_SCRIPTS_H */
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v6 5/7] perf test: Format log directories for shell tests
2026-03-09 15:57 [PATCH v6 0/7] Introduce structure for shell tests Jakub Brnak
` (3 preceding siblings ...)
2026-03-09 15:57 ` [PATCH v6 4/7] perf test: Introduce storing logs for shell tests Jakub Brnak
@ 2026-03-09 15:57 ` Jakub Brnak
2026-03-10 15:24 ` Ian Rogers
2026-03-09 15:57 ` [PATCH v6 6/7] perf test: Remove perftool drivers Jakub Brnak
` (2 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Jakub Brnak @ 2026-03-09 15:57 UTC (permalink / raw)
To: acme, acme, linux-perf-users; +Cc: namhyung, irogers, mpetlan
The name of the log directory can be taken from the test suite
description, which possibly could contain whitespace characters. This
can cause further issues if the name is not quoted correctly.
Replace the whitespace characters with an underscore to prevent the
possible issues caused by the name splitting.
Signed-off-by: Michael Petlan <mpetlan@redhat.com>
Co-developed-by: Veronika Molnarova <vmolnaro@redhat.com>
Signed-off-by: Veronika Molnarova <vmolnaro@redhat.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Jakub Brnak <jbrnak@redhat.com>
---
tools/perf/tests/builtin-test.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 70f4a61357ec..cbfae7dcf2b6 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -292,6 +292,7 @@ static bool test_exclusive(const struct test_suite *t, int test_case)
return t->test_cases[test_case].exclusive;
}
+
static int delete_file(const char *fpath, const struct stat *sb __maybe_unused,
int typeflag, struct FTW *ftwbuf)
{
@@ -340,12 +341,19 @@ static char *setup_shell_logs(const char *name)
{
char template[PATH_MAX];
char *temp_dir;
+ size_t i;
if (snprintf(template, PATH_MAX, "/tmp/perf_test_%s.XXXXXX", name) < 0) {
pr_err("Failed to create log dir template");
return NULL; /* Skip the testsuite */
}
+ /* Replace non-alphanumeric characters with _ in the name part */
+ for (i = 15; template[i] != '.' && template[i] != '\0'; i++) {
+ if (!isalnum((unsigned char)template[i]))
+ template[i] = '_';
+ }
+
temp_dir = mkdtemp(template);
if (temp_dir) {
setenv("PERFSUITE_RUN_DIR", temp_dir, 1);
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v6 5/7] perf test: Format log directories for shell tests
2026-03-09 15:57 ` [PATCH v6 5/7] perf test: Format log directories " Jakub Brnak
@ 2026-03-10 15:24 ` Ian Rogers
0 siblings, 0 replies; 13+ messages in thread
From: Ian Rogers @ 2026-03-10 15:24 UTC (permalink / raw)
To: Jakub Brnak; +Cc: acme, acme, linux-perf-users, namhyung, mpetlan
On Mon, Mar 9, 2026 at 8:57 AM Jakub Brnak <jbrnak@redhat.com> wrote:
>
> The name of the log directory can be taken from the test suite
> description, which possibly could contain whitespace characters. This
> can cause further issues if the name is not quoted correctly.
>
> Replace the whitespace characters with an underscore to prevent the
> possible issues caused by the name splitting.
>
> Signed-off-by: Michael Petlan <mpetlan@redhat.com>
> Co-developed-by: Veronika Molnarova <vmolnaro@redhat.com>
> Signed-off-by: Veronika Molnarova <vmolnaro@redhat.com>
> Reviewed-by: Ian Rogers <irogers@google.com>
> Signed-off-by: Jakub Brnak <jbrnak@redhat.com>
> ---
> tools/perf/tests/builtin-test.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
> index 70f4a61357ec..cbfae7dcf2b6 100644
> --- a/tools/perf/tests/builtin-test.c
> +++ b/tools/perf/tests/builtin-test.c
> @@ -292,6 +292,7 @@ static bool test_exclusive(const struct test_suite *t, int test_case)
> return t->test_cases[test_case].exclusive;
> }
>
> +
> static int delete_file(const char *fpath, const struct stat *sb __maybe_unused,
> int typeflag, struct FTW *ftwbuf)
> {
> @@ -340,12 +341,19 @@ static char *setup_shell_logs(const char *name)
> {
> char template[PATH_MAX];
> char *temp_dir;
> + size_t i;
>
> if (snprintf(template, PATH_MAX, "/tmp/perf_test_%s.XXXXXX", name) < 0) {
> pr_err("Failed to create log dir template");
> return NULL; /* Skip the testsuite */
> }
>
> + /* Replace non-alphanumeric characters with _ in the name part */
> + for (i = 15; template[i] != '.' && template[i] != '\0'; i++) {
The AI review mentions:
Will this loop terminate prematurely if the test description itself
contains a dot? If the test name is something like "test v1.0
execution", the loop would abort at the dot, leaving the subsequent
spaces unmodified. Should it instead bound the loop by the length of
the string minus the ".XXXXXX" suffix?
Thanks,
Ian
> + if (!isalnum((unsigned char)template[i]))
> + template[i] = '_';
> + }
> +
> temp_dir = mkdtemp(template);
> if (temp_dir) {
> setenv("PERFSUITE_RUN_DIR", temp_dir, 1);
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v6 6/7] perf test: Remove perftool drivers
2026-03-09 15:57 [PATCH v6 0/7] Introduce structure for shell tests Jakub Brnak
` (4 preceding siblings ...)
2026-03-09 15:57 ` [PATCH v6 5/7] perf test: Format log directories " Jakub Brnak
@ 2026-03-09 15:57 ` Jakub Brnak
2026-03-09 15:57 ` [PATCH v6 7/7] perf test: Fix relative path for 'stderr-whitelist.txt' Jakub Brnak
2026-03-12 18:26 ` [PATCH v6 0/7] Introduce structure for shell tests Namhyung Kim
7 siblings, 0 replies; 13+ messages in thread
From: Jakub Brnak @ 2026-03-09 15:57 UTC (permalink / raw)
To: acme, acme, linux-perf-users; +Cc: namhyung, irogers, mpetlan
The perf now provides all of the features required for running the
perftool test cases, such as creating log directories, running
setup scripts and the tests are structured by the base_ directories.
Remove the drivers as they are no longer necessary together with
the condition of skipping the base_ directories and run the
test cases by the default perf test structure.
Signed-off-by: Michael Petlan <mpetlan@redhat.com>
Co-developed-by: Veronika Molnarova <vmolnaro@redhat.com>
Signed-off-by: Veronika Molnarova <vmolnaro@redhat.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Jakub Brnak <jbrnak@redhat.com>
---
.../tests/shell/perftool-testsuite_probe.sh | 24 -------------------
.../tests/shell/perftool-testsuite_report.sh | 23 ------------------
tools/perf/tests/tests-scripts.c | 3 ---
3 files changed, 50 deletions(-)
delete mode 100755 tools/perf/tests/shell/perftool-testsuite_probe.sh
delete mode 100755 tools/perf/tests/shell/perftool-testsuite_report.sh
diff --git a/tools/perf/tests/shell/perftool-testsuite_probe.sh b/tools/perf/tests/shell/perftool-testsuite_probe.sh
deleted file mode 100755
index 3863df16c19b..000000000000
--- a/tools/perf/tests/shell/perftool-testsuite_probe.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-# perftool-testsuite_probe (exclusive)
-# SPDX-License-Identifier: GPL-2.0
-
-[ "$(id -u)" = 0 ] || exit 2
-test -d "$(dirname "$0")/base_probe" || exit 2
-cd "$(dirname "$0")/base_probe" || exit 2
-status=0
-
-PERFSUITE_RUN_DIR=$(mktemp -d /tmp/"$(basename "$0" .sh)".XXX)
-export PERFSUITE_RUN_DIR
-
-for testcase in setup.sh test_*; do # skip setup.sh if not present or not executable
- test -x "$testcase" || continue
- ./"$testcase"
- (( status += $? ))
-done
-
-if ! [ "$PERFTEST_KEEP_LOGS" = "y" ]; then
- rm -rf "$PERFSUITE_RUN_DIR"
-fi
-
-test $status -ne 0 && exit 1
-exit 0
diff --git a/tools/perf/tests/shell/perftool-testsuite_report.sh b/tools/perf/tests/shell/perftool-testsuite_report.sh
deleted file mode 100755
index a8cf75b4e77e..000000000000
--- a/tools/perf/tests/shell/perftool-testsuite_report.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-# perftool-testsuite_report (exclusive)
-# SPDX-License-Identifier: GPL-2.0
-
-test -d "$(dirname "$0")/base_report" || exit 2
-cd "$(dirname "$0")/base_report" || exit 2
-status=0
-
-PERFSUITE_RUN_DIR=$(mktemp -d /tmp/"$(basename "$0" .sh)".XXX)
-export PERFSUITE_RUN_DIR
-
-for testcase in setup.sh test_*; do # skip setup.sh if not present or not executable
- test -x "$testcase" || continue
- ./"$testcase"
- (( status += $? ))
-done
-
-if ! [ "$PERFTEST_KEEP_LOGS" = "y" ]; then
- rm -rf "$PERFSUITE_RUN_DIR"
-fi
-
-test $status -ne 0 && exit 1
-exit 0
diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c
index c45b41b5bf07..b7a125e6e7f9 100644
--- a/tools/perf/tests/tests-scripts.c
+++ b/tools/perf/tests/tests-scripts.c
@@ -424,9 +424,6 @@ static void append_suites_in_dir(int dir_fd,
if (!is_directory_fd(dir_fd, ent))
continue;
- if (strncmp(ent->d_name, "base_", 5) == 0)
- continue; /* Skip scripts that have a separate driver. */
-
/* Scan subdir for test cases*/
fd = openat(dir_fd, ent->d_name, O_PATH);
test_suite = prepare_test_suite(fd); /* Prepare a testsuite with its path */
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v6 7/7] perf test: Fix relative path for 'stderr-whitelist.txt'
2026-03-09 15:57 [PATCH v6 0/7] Introduce structure for shell tests Jakub Brnak
` (5 preceding siblings ...)
2026-03-09 15:57 ` [PATCH v6 6/7] perf test: Remove perftool drivers Jakub Brnak
@ 2026-03-09 15:57 ` Jakub Brnak
2026-03-12 18:26 ` [PATCH v6 0/7] Introduce structure for shell tests Namhyung Kim
7 siblings, 0 replies; 13+ messages in thread
From: Jakub Brnak @ 2026-03-09 15:57 UTC (permalink / raw)
To: acme, acme, linux-perf-users; +Cc: namhyung, irogers, mpetlan
The test scripts for 'perf report' profiles were failing to locate
the 'stderr-whitelist.txt' file.
The path was specified simply as "stderr-whitelist.txt", which relies on
the current working directory when the script runs.
This patch updates the path to use the relative directory variable
'$DIR_PATH' (which points to the current directory of the test script),
ensuring the correct file is found regardless of the current working
directory.
Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Jakub Brnak <jbrnak@redhat.com>
---
tools/perf/tests/shell/base_report/test_basic.sh | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/perf/tests/shell/base_report/test_basic.sh b/tools/perf/tests/shell/base_report/test_basic.sh
index 0dfe7e5fd1ca..b42726655d20 100755
--- a/tools/perf/tests/shell/base_report/test_basic.sh
+++ b/tools/perf/tests/shell/base_report/test_basic.sh
@@ -240,7 +240,7 @@ PERF_EXIT_CODE=$?
"# Overhead Latency Command" < $LOGS_DIR/latency_default.log
CHECK_EXIT_CODE=$?
"$DIR_PATH/../common/check_errors_whitelisted.pl" \
- "stderr-whitelist.txt" < $LOGS_DIR/latency_default.err
+ "$DIR_PATH/stderr-whitelist.txt" < $LOGS_DIR/latency_default.err
(( CHECK_EXIT_CODE += $? ))
print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "default report for latency profile"
@@ -255,7 +255,7 @@ PERF_EXIT_CODE=$?
"# Latency Overhead Command" < $LOGS_DIR/latency_latency.log
CHECK_EXIT_CODE=$?
"$DIR_PATH/../common/check_errors_whitelisted.pl" \
- "stderr-whitelist.txt" < $LOGS_DIR/latency_latency.err
+ "$DIR_PATH/stderr-whitelist.txt" < $LOGS_DIR/latency_latency.err
(( CHECK_EXIT_CODE += $? ))
print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "latency report for latency profile"
@@ -271,7 +271,7 @@ PERF_EXIT_CODE=$?
< $LOGS_DIR/parallelism_hierarchy.log
CHECK_EXIT_CODE=$?
"$DIR_PATH/../common/check_errors_whitelisted.pl" \
- "stderr-whitelist.txt" < $LOGS_DIR/parallelism_hierarchy.err
+ "$DIR_PATH/stderr-whitelist.txt" < $LOGS_DIR/parallelism_hierarchy.err
(( CHECK_EXIT_CODE += $? ))
print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "parallelism histogram"
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v6 0/7] Introduce structure for shell tests
2026-03-09 15:57 [PATCH v6 0/7] Introduce structure for shell tests Jakub Brnak
` (6 preceding siblings ...)
2026-03-09 15:57 ` [PATCH v6 7/7] perf test: Fix relative path for 'stderr-whitelist.txt' Jakub Brnak
@ 2026-03-12 18:26 ` Namhyung Kim
7 siblings, 0 replies; 13+ messages in thread
From: Namhyung Kim @ 2026-03-12 18:26 UTC (permalink / raw)
To: Jakub Brnak; +Cc: acme, acme, linux-perf-users, irogers, mpetlan
Hello,
On Mon, Mar 09, 2026 at 04:57:21PM +0100, Jakub Brnak wrote:
> Hi,
>
> Sending the next revision of patches for perftool-testsuite structure
> setup which incorporates the changes in Patch 4 suggested by Ian.
Can you please add a documentation for the directory structure, setup,
log and other details?
Thanks,
Namhyung
>
> Thanks,
> Jakub
>
> Link to v5: https://lore.kernel.org/linux-perf-users/20251125155648.197527-1-jbrnak@redhat.com/
>
> Changes since v5:
> Patch 4: fixed log deletion logic to ensure logs are only removed after
> all tests (including those executed in parallel) have fully completed
>
> Jakub Brnak (7):
> perf tests: Create a structure for shell tests
> perf test: Provide setup for the shell test suite
> perftool-testsuite: Add empty setup for base_probe
> perf test: Introduce storing logs for shell tests
> perf test: Format log directories for shell tests
> perf test: Remove perftool drivers
> perf test: Fix relative path for 'stderr-whitelist.txt'
>
> tools/perf/tests/builtin-test.c | 141 ++++++++-
> tools/perf/tests/shell/base_probe/setup.sh | 13 +
> .../tests/shell/base_report/test_basic.sh | 6 +-
> .../tests/shell/perftool-testsuite_probe.sh | 24 --
> .../tests/shell/perftool-testsuite_report.sh | 23 --
> tools/perf/tests/tests-scripts.c | 289 +++++++++++++++---
> tools/perf/tests/tests-scripts.h | 17 ++
> tools/perf/tests/tests.h | 8 +-
> 8 files changed, 413 insertions(+), 108 deletions(-)
> create mode 100755 tools/perf/tests/shell/base_probe/setup.sh
> delete mode 100755 tools/perf/tests/shell/perftool-testsuite_probe.sh
> delete mode 100755 tools/perf/tests/shell/perftool-testsuite_report.sh
>
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 13+ messages in thread