public inbox for linux-perf-users@vger.kernel.org
 help / color / mirror / Atom feed
From: Jakub Brnak <jbrnak@redhat.com>
To: acme@kernel.org, acme@redhat.com, linux-perf-users@vger.kernel.org
Cc: namhyung@kernel.org, irogers@google.com, mpetlan@redhat.com
Subject: [PATCH v7 4/7] perf test: Introduce storing logs for shell tests
Date: Thu, 16 Apr 2026 13:14:16 +0200	[thread overview]
Message-ID: <20260416111419.385010-5-jbrnak@redhat.com> (raw)
In-Reply-To: <20260416111419.385010-1-jbrnak@redhat.com>

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>
Signed-off-by: Jakub Brnak <jbrnak@redhat.com>
---
 tools/perf/tests/builtin-test.c  | 133 ++++++++++++++++++++++++++++++-
 tools/perf/tests/tests-scripts.c |   4 +
 tools/perf/tests/tests-scripts.h |   3 +
 3 files changed, 136 insertions(+), 4 deletions(-)

diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 76fc37b440be..306ecfe5e22f 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,91 @@ 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)
+{
+	/* Stop traversal if going too deep */
+	if (ftwbuf->level > 5) {
+		pr_err("Tree traversal reached level %d, stopping.", ftwbuf->level);
+		return 1;
+	}
+
+	/* 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 0;
+		}
+	}
+
+	/* Attempt to remove the file, continue on failure */
+	if (remove(fpath))
+		pr_err("Failed to remove file: %s", fpath);
+
+	return 0;
+}
+
+static bool create_logs(struct test_suite *t, int pass)
+{
+	bool store_logs = t->priv && ((struct shell_test_info *)(t->priv))->store_logs;
+	bool exclusive = false;
+	int tc;
+
+	test_suite__for_each_test_case(t, tc) {
+		if (test_exclusive(t, tc)) {
+			exclusive = true;
+			break;
+		}
+	}
+
+	if (pass == 1 && (!exclusive || sequential || dont_fork)) {
+		/* Sequential and non-exclusive tests run on the first pass. */
+		return store_logs;
+	} else if (pass != 1 && exclusive && !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) >= PATH_MAX) {
+		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 +716,19 @@ 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;
+		size_t tmpdir_sz = num_logged_suites * runs_per_test;
+		char **tmpdir = tmpdir_sz ? calloc(tmpdir_sz, sizeof(*tmpdir)) : NULL;
+		unsigned int logged_suites = 0;
+
+		if (tmpdir_sz && !tmpdir) {
+			pr_err("Out of memory while allocating log directories\n");
+			err = -ENOMEM;
+			goto err_out;
+		}
 
 		for (struct test_suite **t = suites; *t; t++, curr_suite++) {
 			int curr_test_case;
@@ -663,16 +762,37 @@ 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)) {
+					struct shell_test_info *info = (*t)->priv;
+
+					tmpdir[logged_suites] = setup_shell_logs((*t)->desc);
+					/* Couldn't create log dir, skip test suite */
+					if (tmpdir[logged_suites] == NULL) {
+						if (info->has_setup != NO_SETUP)
+							info->has_setup = FAILED_SETUP;
+					} else {
+						logged_suites++;
+						if (info->has_setup == PASSED_SETUP ||
+						    info->has_setup == FAILED_SETUP)
+							info->has_setup = RUN_SETUP;
+					}
+				}
+
 				test_suite__for_each_test_case(*t, curr_test_case) {
 					if (!suite_matched &&
 					    !perf_test__matches(test_description(*t, curr_test_case),
 								curr_suite, argc, argv))
 						continue;
-					err = start_test(*t, curr_suite, curr_test_case,
-							 &child_tests[child_test_num++],
-							 width, pass);
-					if (err)
+				err = start_test(*t, curr_suite, curr_test_case,
+						 &child_tests[child_test_num++],
+						 width, pass);
+					if (err) {
+						for (size_t x = 0; x < logged_suites; x++)
+							free(tmpdir[x]);
+						free(tmpdir);
 						goto err_out;
+					}
 				}
 			}
 		}
@@ -681,6 +801,11 @@ 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);
 		}
+
+		/* Cleanup logs; on err_out logs are intentionally kept for debugging */
+		for (size_t x = 0; x < logged_suites; x++)
+			cleanup_shell_logs(tmpdir[x]);
+		free(tmpdir);
 	}
 err_out:
 	signal(SIGINT, SIG_DFL);
diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c
index 33f1e46267f4..fc5e1b835ecc 100644
--- a/tools/perf/tests/tests-scripts.c
+++ b/tools/perf/tests/tests-scripts.c
@@ -322,6 +322,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;
@@ -506,6 +507,9 @@ static void append_suites_in_dir(int dir_fd,
 			continue;
 		}
 
+		/* Store logs for testsuites in 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


  parent reply	other threads:[~2026-04-16 11:14 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-16 11:14 [PATCH v7 0/7] Introduce structure for shell tests Jakub Brnak
2026-04-16 11:14 ` [PATCH v7 1/7] perf tests: Create a " Jakub Brnak
2026-04-16 11:38   ` sashiko-bot
2026-04-16 11:14 ` [PATCH v7 2/7] perf test: Provide setup for the shell test suite Jakub Brnak
2026-04-16 12:07   ` sashiko-bot
2026-04-16 11:14 ` [PATCH v7 3/7] perf test: Add empty setup for base_probe Jakub Brnak
2026-04-16 11:14 ` Jakub Brnak [this message]
2026-04-16 18:30   ` [PATCH v7 4/7] perf test: Introduce storing logs for shell tests sashiko-bot
2026-04-16 11:14 ` [PATCH v7 5/7] perf test: Format log directories " Jakub Brnak
2026-04-16 18:56   ` sashiko-bot
2026-04-16 11:14 ` [PATCH v7 6/7] perf test: Remove perftool drivers Jakub Brnak
2026-04-16 19:08   ` sashiko-bot
2026-04-16 11:14 ` [PATCH v7 7/7] perf test: Fix relative path for 'stderr-whitelist.txt' Jakub Brnak

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260416111419.385010-5-jbrnak@redhat.com \
    --to=jbrnak@redhat.com \
    --cc=acme@kernel.org \
    --cc=acme@redhat.com \
    --cc=irogers@google.com \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=mpetlan@redhat.com \
    --cc=namhyung@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox