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.

tools lib api: Add io_dir an allocation free readdir alternative

glibc's opendir allocates a minimum of 32kb, when called recursively
for a directory tree the memory consumption can add up - nearly 300kb
during perf start-up when processing modules. Add a stack allocated
variant of readdir sized a little more than 1kb.

As getdents64 may be missing from libc, add support using syscall. As
the system call number maybe missing, add #defines for those.

Note, an earlier version of this patch had a feature test for
getdents64 but there were problems on certains distros where
getdents64 would be #define renamed to getdents breaking the code. The
syscall use was made uncondtional to work around this. There is
context in:
https://lore.kernel.org/lkml/20231207050433.1426834-1-irogers@google.com/

Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250222061015.303622-2-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Ian Rogers and committed by
Namhyung Kim
d118b08f 7e05269b

+105 -1
+1 -1
tools/lib/api/Makefile
··· 95 95 $(call do_install_mkdir,$(libdir_SQ)); \ 96 96 cp -fpR $(LIBFILE) $(DESTDIR)$(libdir_SQ) 97 97 98 - HDRS := cpu.h debug.h io.h 98 + HDRS := cpu.h debug.h io.h io_dir.h 99 99 FD_HDRS := fd/array.h 100 100 FS_HDRS := fs/fs.h fs/tracing_path.h 101 101 INSTALL_HDRS_PFX := $(DESTDIR)$(prefix)/include/api
+104
tools/lib/api/io_dir.h
··· 1 + /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 + /* 3 + * Lightweight directory reading library. 4 + */ 5 + #ifndef __API_IO_DIR__ 6 + #define __API_IO_DIR__ 7 + 8 + #include <dirent.h> 9 + #include <fcntl.h> 10 + #include <stdlib.h> 11 + #include <unistd.h> 12 + #include <sys/stat.h> 13 + #include <sys/syscall.h> 14 + 15 + #if !defined(SYS_getdents64) 16 + #if defined(__x86_64__) || defined(__arm__) 17 + #define SYS_getdents64 217 18 + #elif defined(__i386__) || defined(__s390x__) || defined(__sh__) 19 + #define SYS_getdents64 220 20 + #elif defined(__alpha__) 21 + #define SYS_getdents64 377 22 + #elif defined(__mips__) 23 + #define SYS_getdents64 308 24 + #elif defined(__powerpc64__) || defined(__powerpc__) 25 + #define SYS_getdents64 202 26 + #elif defined(__sparc64__) || defined(__sparc__) 27 + #define SYS_getdents64 154 28 + #elif defined(__xtensa__) 29 + #define SYS_getdents64 60 30 + #else 31 + #define SYS_getdents64 61 32 + #endif 33 + #endif /* !defined(SYS_getdents64) */ 34 + 35 + static inline ssize_t perf_getdents64(int fd, void *dirp, size_t count) 36 + { 37 + #ifdef MEMORY_SANITIZER 38 + memset(dirp, 0, count); 39 + #endif 40 + return syscall(SYS_getdents64, fd, dirp, count); 41 + } 42 + 43 + struct io_dirent64 { 44 + ino64_t d_ino; /* 64-bit inode number */ 45 + off64_t d_off; /* 64-bit offset to next structure */ 46 + unsigned short d_reclen; /* Size of this dirent */ 47 + unsigned char d_type; /* File type */ 48 + char d_name[NAME_MAX + 1]; /* Filename (null-terminated) */ 49 + }; 50 + 51 + struct io_dir { 52 + int dirfd; 53 + ssize_t available_bytes; 54 + struct io_dirent64 *next; 55 + struct io_dirent64 buff[4]; 56 + }; 57 + 58 + static inline void io_dir__init(struct io_dir *iod, int dirfd) 59 + { 60 + iod->dirfd = dirfd; 61 + iod->available_bytes = 0; 62 + } 63 + 64 + static inline void io_dir__rewinddir(struct io_dir *iod) 65 + { 66 + lseek(iod->dirfd, 0, SEEK_SET); 67 + iod->available_bytes = 0; 68 + } 69 + 70 + static inline struct io_dirent64 *io_dir__readdir(struct io_dir *iod) 71 + { 72 + struct io_dirent64 *entry; 73 + 74 + if (iod->available_bytes <= 0) { 75 + ssize_t rc = perf_getdents64(iod->dirfd, iod->buff, sizeof(iod->buff)); 76 + 77 + if (rc <= 0) 78 + return NULL; 79 + iod->available_bytes = rc; 80 + iod->next = iod->buff; 81 + } 82 + entry = iod->next; 83 + iod->next = (struct io_dirent64 *)((char *)entry + entry->d_reclen); 84 + iod->available_bytes -= entry->d_reclen; 85 + return entry; 86 + } 87 + 88 + static inline bool io_dir__is_dir(const struct io_dir *iod, struct io_dirent64 *dent) 89 + { 90 + if (dent->d_type == DT_UNKNOWN) { 91 + struct stat st; 92 + 93 + if (fstatat(iod->dirfd, dent->d_name, &st, /*flags=*/0)) 94 + return false; 95 + 96 + if (S_ISDIR(st.st_mode)) { 97 + dent->d_type = DT_DIR; 98 + return true; 99 + } 100 + } 101 + return dent->d_type == DT_DIR; 102 + } 103 + 104 + #endif /* __API_IO_DIR__ */