]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
perf test: In forked mode add check that fds aren't leaked
authorIan Rogers <irogers@google.com>
Tue, 24 Jun 2025 19:03:25 +0000 (12:03 -0700)
committerNamhyung Kim <namhyung@kernel.org>
Thu, 3 Jul 2025 02:11:13 +0000 (19:11 -0700)
When a test is forked no file descriptors should be open, however,
parent ones may have been inherited - in particular those of the pipes
of other forked child test processes. Add a loop to clean-up/close
those file descriptors prior to running the test. At the end of the
test assert that no additional file descriptors are present as this
would indicate a file descriptor leak.

Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250624190326.2038704-6-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
tools/perf/tests/builtin-test.c

index e242d56523cee1715ba204e49b0f6ba1f01ebf9f..85142dfb3e01c24593e6c7e168398d7ebfe91d3e 100644 (file)
@@ -4,6 +4,7 @@
  *
  * Builtin regression testing command: ever growing number of sanity tests
  */
+#include <ctype.h>
 #include <fcntl.h>
 #include <errno.h>
 #ifdef HAVE_BACKTRACE_SUPPORT
@@ -159,6 +160,71 @@ static struct test_workload *workloads[] = {
 #define test_suite__for_each_test_case(suite, idx)                     \
        for (idx = 0; (suite)->test_cases && (suite)->test_cases[idx].name != NULL; idx++)
 
+static void close_parent_fds(void)
+{
+       DIR *dir = opendir("/proc/self/fd");
+       struct dirent *ent;
+
+       while ((ent = readdir(dir))) {
+               char *end;
+               long fd;
+
+               if (ent->d_type != DT_LNK)
+                       continue;
+
+               if (!isdigit(ent->d_name[0]))
+                       continue;
+
+               fd = strtol(ent->d_name, &end, 10);
+               if (*end)
+                       continue;
+
+               if (fd <= 3 || fd == dirfd(dir))
+                       continue;
+
+               close(fd);
+       }
+       closedir(dir);
+}
+
+static void check_leaks(void)
+{
+       DIR *dir = opendir("/proc/self/fd");
+       struct dirent *ent;
+       int leaks = 0;
+
+       while ((ent = readdir(dir))) {
+               char path[PATH_MAX];
+               char *end;
+               long fd;
+               ssize_t len;
+
+               if (ent->d_type != DT_LNK)
+                       continue;
+
+               if (!isdigit(ent->d_name[0]))
+                       continue;
+
+               fd = strtol(ent->d_name, &end, 10);
+               if (*end)
+                       continue;
+
+               if (fd <= 3 || fd == dirfd(dir))
+                       continue;
+
+               leaks++;
+               len = readlinkat(dirfd(dir), ent->d_name, path, sizeof(path));
+               if (len > 0 && (size_t)len < sizeof(path))
+                       path[len] = '\0';
+               else
+                       strncpy(path, ent->d_name, sizeof(path));
+               pr_err("Leak of file descriptor %s that opened: '%s'\n", ent->d_name, path);
+       }
+       closedir(dir);
+       if (leaks)
+               abort();
+}
+
 static int test_suite__num_test_cases(const struct test_suite *t)
 {
        int num;
@@ -256,6 +322,8 @@ static int run_test_child(struct child_process *process)
        struct child_test *child = container_of(process, struct child_test, process);
        int err;
 
+       close_parent_fds();
+
        err = sigsetjmp(run_test_jmp_buf, 1);
        if (err) {
                /* Received signal. */
@@ -271,6 +339,7 @@ static int run_test_child(struct child_process *process)
        err = test_function(child->test, child->test_case_num)(child->test, child->test_case_num);
        pr_debug("---- end(%d) ----\n", err);
 
+       check_leaks();
 err_out:
        fflush(NULL);
        for (size_t i = 0; i < ARRAY_SIZE(signals); i++)