-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
Ian Rogers
authored and
Namhyung Kim
committed
Feb 24, 2025
1 parent
7e05269
commit d118b08
Showing
2 changed files
with
105 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ | ||
/* | ||
* Lightweight directory reading library. | ||
*/ | ||
#ifndef __API_IO_DIR__ | ||
#define __API_IO_DIR__ | ||
|
||
#include <dirent.h> | ||
#include <fcntl.h> | ||
#include <stdlib.h> | ||
#include <unistd.h> | ||
#include <sys/stat.h> | ||
#include <sys/syscall.h> | ||
|
||
#if !defined(SYS_getdents64) | ||
#if defined(__x86_64__) || defined(__arm__) | ||
#define SYS_getdents64 217 | ||
#elif defined(__i386__) || defined(__s390x__) || defined(__sh__) | ||
#define SYS_getdents64 220 | ||
#elif defined(__alpha__) | ||
#define SYS_getdents64 377 | ||
#elif defined(__mips__) | ||
#define SYS_getdents64 308 | ||
#elif defined(__powerpc64__) || defined(__powerpc__) | ||
#define SYS_getdents64 202 | ||
#elif defined(__sparc64__) || defined(__sparc__) | ||
#define SYS_getdents64 154 | ||
#elif defined(__xtensa__) | ||
#define SYS_getdents64 60 | ||
#else | ||
#define SYS_getdents64 61 | ||
#endif | ||
#endif /* !defined(SYS_getdents64) */ | ||
|
||
static inline ssize_t perf_getdents64(int fd, void *dirp, size_t count) | ||
{ | ||
#ifdef MEMORY_SANITIZER | ||
memset(dirp, 0, count); | ||
#endif | ||
return syscall(SYS_getdents64, fd, dirp, count); | ||
} | ||
|
||
struct io_dirent64 { | ||
ino64_t d_ino; /* 64-bit inode number */ | ||
off64_t d_off; /* 64-bit offset to next structure */ | ||
unsigned short d_reclen; /* Size of this dirent */ | ||
unsigned char d_type; /* File type */ | ||
char d_name[NAME_MAX + 1]; /* Filename (null-terminated) */ | ||
}; | ||
|
||
struct io_dir { | ||
int dirfd; | ||
ssize_t available_bytes; | ||
struct io_dirent64 *next; | ||
struct io_dirent64 buff[4]; | ||
}; | ||
|
||
static inline void io_dir__init(struct io_dir *iod, int dirfd) | ||
{ | ||
iod->dirfd = dirfd; | ||
iod->available_bytes = 0; | ||
} | ||
|
||
static inline void io_dir__rewinddir(struct io_dir *iod) | ||
{ | ||
lseek(iod->dirfd, 0, SEEK_SET); | ||
iod->available_bytes = 0; | ||
} | ||
|
||
static inline struct io_dirent64 *io_dir__readdir(struct io_dir *iod) | ||
{ | ||
struct io_dirent64 *entry; | ||
|
||
if (iod->available_bytes <= 0) { | ||
ssize_t rc = perf_getdents64(iod->dirfd, iod->buff, sizeof(iod->buff)); | ||
|
||
if (rc <= 0) | ||
return NULL; | ||
iod->available_bytes = rc; | ||
iod->next = iod->buff; | ||
} | ||
entry = iod->next; | ||
iod->next = (struct io_dirent64 *)((char *)entry + entry->d_reclen); | ||
iod->available_bytes -= entry->d_reclen; | ||
return entry; | ||
} | ||
|
||
static inline bool io_dir__is_dir(const struct io_dir *iod, struct io_dirent64 *dent) | ||
{ | ||
if (dent->d_type == DT_UNKNOWN) { | ||
struct stat st; | ||
|
||
if (fstatat(iod->dirfd, dent->d_name, &st, /*flags=*/0)) | ||
return false; | ||
|
||
if (S_ISDIR(st.st_mode)) { | ||
dent->d_type = DT_DIR; | ||
return true; | ||
} | ||
} | ||
return dent->d_type == DT_DIR; | ||
} | ||
|
||
#endif /* __API_IO_DIR__ */ |