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

perf test: In forked mode add check that fds aren't leaked

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>

authored by

Ian Rogers and committed by
Namhyung Kim
e9846f5e e793e2c0

+69
+69
tools/perf/tests/builtin-test.c
··· 4 4 * 5 5 * Builtin regression testing command: ever growing number of sanity tests 6 6 */ 7 + #include <ctype.h> 7 8 #include <fcntl.h> 8 9 #include <errno.h> 9 10 #ifdef HAVE_BACKTRACE_SUPPORT ··· 160 159 #define test_suite__for_each_test_case(suite, idx) \ 161 160 for (idx = 0; (suite)->test_cases && (suite)->test_cases[idx].name != NULL; idx++) 162 161 162 + static void close_parent_fds(void) 163 + { 164 + DIR *dir = opendir("/proc/self/fd"); 165 + struct dirent *ent; 166 + 167 + while ((ent = readdir(dir))) { 168 + char *end; 169 + long fd; 170 + 171 + if (ent->d_type != DT_LNK) 172 + continue; 173 + 174 + if (!isdigit(ent->d_name[0])) 175 + continue; 176 + 177 + fd = strtol(ent->d_name, &end, 10); 178 + if (*end) 179 + continue; 180 + 181 + if (fd <= 3 || fd == dirfd(dir)) 182 + continue; 183 + 184 + close(fd); 185 + } 186 + closedir(dir); 187 + } 188 + 189 + static void check_leaks(void) 190 + { 191 + DIR *dir = opendir("/proc/self/fd"); 192 + struct dirent *ent; 193 + int leaks = 0; 194 + 195 + while ((ent = readdir(dir))) { 196 + char path[PATH_MAX]; 197 + char *end; 198 + long fd; 199 + ssize_t len; 200 + 201 + if (ent->d_type != DT_LNK) 202 + continue; 203 + 204 + if (!isdigit(ent->d_name[0])) 205 + continue; 206 + 207 + fd = strtol(ent->d_name, &end, 10); 208 + if (*end) 209 + continue; 210 + 211 + if (fd <= 3 || fd == dirfd(dir)) 212 + continue; 213 + 214 + leaks++; 215 + len = readlinkat(dirfd(dir), ent->d_name, path, sizeof(path)); 216 + if (len > 0 && (size_t)len < sizeof(path)) 217 + path[len] = '\0'; 218 + else 219 + strncpy(path, ent->d_name, sizeof(path)); 220 + pr_err("Leak of file descriptor %s that opened: '%s'\n", ent->d_name, path); 221 + } 222 + closedir(dir); 223 + if (leaks) 224 + abort(); 225 + } 226 + 163 227 static int test_suite__num_test_cases(const struct test_suite *t) 164 228 { 165 229 int num; ··· 322 256 struct child_test *child = container_of(process, struct child_test, process); 323 257 int err; 324 258 259 + close_parent_fds(); 260 + 325 261 err = sigsetjmp(run_test_jmp_buf, 1); 326 262 if (err) { 327 263 /* Received signal. */ ··· 339 271 err = test_function(child->test, child->test_case_num)(child->test, child->test_case_num); 340 272 pr_debug("---- end(%d) ----\n", err); 341 273 274 + check_leaks(); 342 275 err_out: 343 276 fflush(NULL); 344 277 for (size_t i = 0; i < ARRAY_SIZE(signals); i++)