perf pmu: Switch to io_dir__readdir

Avoid DIR allocations when scanning sysfs by using io_dir for the
readdir implementation, that allocates about 1kb on the stack.

Acked-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250222061015.303622-4-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
This commit is contained in:
Ian Rogers 2025-02-21 22:10:08 -08:00 committed by Namhyung Kim
parent f7cada5f7e
commit 6a81a3fd9e
2 changed files with 30 additions and 46 deletions

View File

@ -13,6 +13,7 @@
#include <dirent.h>
#include <api/fs/fs.h>
#include <api/io.h>
#include <api/io_dir.h>
#include <locale.h>
#include <fnmatch.h>
#include <math.h>
@ -195,19 +196,17 @@ static void perf_pmu_format__load(const struct perf_pmu *pmu, struct perf_pmu_fo
*/
static int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load)
{
struct dirent *evt_ent;
DIR *format_dir;
struct io_dirent64 *evt_ent;
struct io_dir format_dir;
int ret = 0;
format_dir = fdopendir(dirfd);
if (!format_dir)
return -EINVAL;
io_dir__init(&format_dir, dirfd);
while ((evt_ent = readdir(format_dir)) != NULL) {
while ((evt_ent = io_dir__readdir(&format_dir)) != NULL) {
struct perf_pmu_format *format;
char *name = evt_ent->d_name;
if (!strcmp(name, ".") || !strcmp(name, ".."))
if (io_dir__is_dir(&format_dir, evt_ent))
continue;
format = perf_pmu__new_format(&pmu->format, name);
@ -234,7 +233,7 @@ static int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_lo
}
}
closedir(format_dir);
close(format_dir.dirfd);
return ret;
}
@ -635,14 +634,12 @@ static inline bool pmu_alias_info_file(const char *name)
*/
static int __pmu_aliases_parse(struct perf_pmu *pmu, int events_dir_fd)
{
struct dirent *evt_ent;
DIR *event_dir;
struct io_dirent64 *evt_ent;
struct io_dir event_dir;
event_dir = fdopendir(events_dir_fd);
if (!event_dir)
return -EINVAL;
io_dir__init(&event_dir, events_dir_fd);
while ((evt_ent = readdir(event_dir))) {
while ((evt_ent = io_dir__readdir(&event_dir))) {
char *name = evt_ent->d_name;
int fd;
FILE *file;
@ -674,7 +671,6 @@ static int __pmu_aliases_parse(struct perf_pmu *pmu, int events_dir_fd)
fclose(file);
}
closedir(event_dir);
pmu->sysfs_aliases_loaded = true;
return 0;
}
@ -2221,10 +2217,9 @@ static void perf_pmu__del_caps(struct perf_pmu *pmu)
*/
int perf_pmu__caps_parse(struct perf_pmu *pmu)
{
struct stat st;
char caps_path[PATH_MAX];
DIR *caps_dir;
struct dirent *evt_ent;
struct io_dir caps_dir;
struct io_dirent64 *evt_ent;
int caps_fd;
if (pmu->caps_initialized)
@ -2235,24 +2230,21 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu)
if (!perf_pmu__pathname_scnprintf(caps_path, sizeof(caps_path), pmu->name, "caps"))
return -1;
if (stat(caps_path, &st) < 0) {
caps_fd = open(caps_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY);
if (caps_fd == -1) {
pmu->caps_initialized = true;
return 0; /* no error if caps does not exist */
}
caps_dir = opendir(caps_path);
if (!caps_dir)
return -EINVAL;
io_dir__init(&caps_dir, caps_fd);
caps_fd = dirfd(caps_dir);
while ((evt_ent = readdir(caps_dir)) != NULL) {
while ((evt_ent = io_dir__readdir(&caps_dir)) != NULL) {
char *name = evt_ent->d_name;
char value[128];
FILE *file;
int fd;
if (!strcmp(name, ".") || !strcmp(name, ".."))
if (io_dir__is_dir(&caps_dir, evt_ent))
continue;
fd = openat(caps_fd, name, O_RDONLY);
@ -2274,7 +2266,7 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu)
fclose(file);
}
closedir(caps_dir);
close(caps_fd);
pmu->caps_initialized = true;
return pmu->nr_caps;

View File

@ -3,10 +3,10 @@
#include <linux/list_sort.h>
#include <linux/string.h>
#include <linux/zalloc.h>
#include <api/io_dir.h>
#include <subcmd/pager.h>
#include <sys/types.h>
#include <ctype.h>
#include <dirent.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
@ -235,20 +235,16 @@ static void pmu_read_sysfs(unsigned int to_read_types)
if (to_read_types & (PERF_TOOL_PMU_TYPE_PE_CORE_MASK | PERF_TOOL_PMU_TYPE_PE_OTHER_MASK)) {
int fd = perf_pmu__event_source_devices_fd();
DIR *dir;
struct dirent *dent;
struct io_dir dir;
struct io_dirent64 *dent;
bool core_only = (to_read_types & PERF_TOOL_PMU_TYPE_PE_OTHER_MASK) == 0;
if (fd < 0)
goto skip_pe_pmus;
dir = fdopendir(fd);
if (!dir) {
close(fd);
goto skip_pe_pmus;
}
io_dir__init(&dir, fd);
while ((dent = readdir(dir))) {
while ((dent = io_dir__readdir(&dir)) != NULL) {
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
continue;
if (core_only && !is_pmu_core(dent->d_name))
@ -257,7 +253,7 @@ static void pmu_read_sysfs(unsigned int to_read_types)
perf_pmu__find2(fd, dent->d_name);
}
closedir(dir);
close(fd);
}
skip_pe_pmus:
if ((to_read_types & PERF_TOOL_PMU_TYPE_PE_CORE_MASK) && list_empty(&core_pmus)) {
@ -721,8 +717,8 @@ bool perf_pmus__supports_extended_type(void)
char *perf_pmus__default_pmu_name(void)
{
int fd;
DIR *dir;
struct dirent *dent;
struct io_dir dir;
struct io_dirent64 *dent;
char *result = NULL;
if (!list_empty(&core_pmus))
@ -732,13 +728,9 @@ char *perf_pmus__default_pmu_name(void)
if (fd < 0)
return strdup("cpu");
dir = fdopendir(fd);
if (!dir) {
close(fd);
return strdup("cpu");
}
io_dir__init(&dir, fd);
while ((dent = readdir(dir))) {
while ((dent = io_dir__readdir(&dir)) != NULL) {
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
continue;
if (is_pmu_core(dent->d_name)) {
@ -747,7 +739,7 @@ char *perf_pmus__default_pmu_name(void)
}
}
closedir(dir);
close(fd);
return result ?: strdup("cpu");
}