| // SPDX-License-Identifier: GPL-2.0 |
| #include <string.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <linux/stddef.h> |
| #include <linux/perf_event.h> |
| #include <linux/zalloc.h> |
| #include <api/fs/fs.h> |
| #include <errno.h> |
| |
| #include "../../../util/intel-pt.h" |
| #include "../../../util/intel-bts.h" |
| #include "../../../util/pmu.h" |
| #include "../../../util/fncache.h" |
| |
| #define TEMPLATE_ALIAS "%s/bus/event_source/devices/%s/alias" |
| |
| struct pmu_alias { |
| char *name; |
| char *alias; |
| struct list_head list; |
| }; |
| |
| static LIST_HEAD(pmu_alias_name_list); |
| static bool cached_list; |
| |
| struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) |
| { |
| #ifdef HAVE_AUXTRACE_SUPPORT |
| if (!strcmp(pmu->name, INTEL_PT_PMU_NAME)) |
| return intel_pt_pmu_default_config(pmu); |
| if (!strcmp(pmu->name, INTEL_BTS_PMU_NAME)) |
| pmu->selectable = true; |
| #endif |
| return NULL; |
| } |
| |
| static void pmu_alias__delete(struct pmu_alias *pmu_alias) |
| { |
| if (!pmu_alias) |
| return; |
| |
| zfree(&pmu_alias->name); |
| zfree(&pmu_alias->alias); |
| free(pmu_alias); |
| } |
| |
| static struct pmu_alias *pmu_alias__new(char *name, char *alias) |
| { |
| struct pmu_alias *pmu_alias = zalloc(sizeof(*pmu_alias)); |
| |
| if (pmu_alias) { |
| pmu_alias->name = strdup(name); |
| if (!pmu_alias->name) |
| goto out_delete; |
| |
| pmu_alias->alias = strdup(alias); |
| if (!pmu_alias->alias) |
| goto out_delete; |
| } |
| return pmu_alias; |
| |
| out_delete: |
| pmu_alias__delete(pmu_alias); |
| return NULL; |
| } |
| |
| static int setup_pmu_alias_list(void) |
| { |
| char path[PATH_MAX]; |
| DIR *dir; |
| struct dirent *dent; |
| const char *sysfs = sysfs__mountpoint(); |
| struct pmu_alias *pmu_alias; |
| char buf[MAX_PMU_NAME_LEN]; |
| FILE *file; |
| int ret = -ENOMEM; |
| |
| if (!sysfs) |
| return -1; |
| |
| snprintf(path, PATH_MAX, |
| "%s" EVENT_SOURCE_DEVICE_PATH, sysfs); |
| |
| dir = opendir(path); |
| if (!dir) |
| return -errno; |
| |
| while ((dent = readdir(dir))) { |
| if (!strcmp(dent->d_name, ".") || |
| !strcmp(dent->d_name, "..")) |
| continue; |
| |
| snprintf(path, PATH_MAX, |
| TEMPLATE_ALIAS, sysfs, dent->d_name); |
| |
| if (!file_available(path)) |
| continue; |
| |
| file = fopen(path, "r"); |
| if (!file) |
| continue; |
| |
| if (!fgets(buf, sizeof(buf), file)) { |
| fclose(file); |
| continue; |
| } |
| |
| fclose(file); |
| |
| /* Remove the last '\n' */ |
| buf[strlen(buf) - 1] = 0; |
| |
| pmu_alias = pmu_alias__new(dent->d_name, buf); |
| if (!pmu_alias) |
| goto close_dir; |
| |
| list_add_tail(&pmu_alias->list, &pmu_alias_name_list); |
| } |
| |
| ret = 0; |
| |
| close_dir: |
| closedir(dir); |
| return ret; |
| } |
| |
| static char *__pmu_find_real_name(const char *name) |
| { |
| struct pmu_alias *pmu_alias; |
| |
| list_for_each_entry(pmu_alias, &pmu_alias_name_list, list) { |
| if (!strcmp(name, pmu_alias->alias)) |
| return pmu_alias->name; |
| } |
| |
| return (char *)name; |
| } |
| |
| char *pmu_find_real_name(const char *name) |
| { |
| if (cached_list) |
| return __pmu_find_real_name(name); |
| |
| setup_pmu_alias_list(); |
| cached_list = true; |
| |
| return __pmu_find_real_name(name); |
| } |
| |
| static char *__pmu_find_alias_name(const char *name) |
| { |
| struct pmu_alias *pmu_alias; |
| |
| list_for_each_entry(pmu_alias, &pmu_alias_name_list, list) { |
| if (!strcmp(name, pmu_alias->name)) |
| return pmu_alias->alias; |
| } |
| return NULL; |
| } |
| |
| char *pmu_find_alias_name(const char *name) |
| { |
| if (cached_list) |
| return __pmu_find_alias_name(name); |
| |
| setup_pmu_alias_list(); |
| cached_list = true; |
| |
| return __pmu_find_alias_name(name); |
| } |