1 // SPDX-License-Identifier: GPL-2.0 2 #include <string.h> 3 #include <stdio.h> 4 #include <sys/types.h> 5 #include <dirent.h> 6 #include <fcntl.h> 7 #include <linux/stddef.h> 8 #include <linux/perf_event.h> 9 #include <linux/zalloc.h> 10 #include <api/fs/fs.h> 11 #include <errno.h> 12 13 #include "../../../util/intel-pt.h" 14 #include "../../../util/intel-bts.h" 15 #include "../../../util/pmu.h" 16 #include "../../../util/fncache.h" 17 #include "../../../util/pmus.h" 18 #include "env.h" 19 20 struct pmu_alias { 21 char *name; 22 char *alias; 23 struct list_head list; 24 }; 25 26 static LIST_HEAD(pmu_alias_name_list); 27 static bool cached_list; 28 29 struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) 30 { 31 #ifdef HAVE_AUXTRACE_SUPPORT 32 if (!strcmp(pmu->name, INTEL_PT_PMU_NAME)) { 33 pmu->auxtrace = true; 34 return intel_pt_pmu_default_config(pmu); 35 } 36 if (!strcmp(pmu->name, INTEL_BTS_PMU_NAME)) { 37 pmu->auxtrace = true; 38 pmu->selectable = true; 39 } 40 #endif 41 return NULL; 42 } 43 44 static void pmu_alias__delete(struct pmu_alias *pmu_alias) 45 { 46 if (!pmu_alias) 47 return; 48 49 zfree(&pmu_alias->name); 50 zfree(&pmu_alias->alias); 51 free(pmu_alias); 52 } 53 54 static struct pmu_alias *pmu_alias__new(char *name, char *alias) 55 { 56 struct pmu_alias *pmu_alias = zalloc(sizeof(*pmu_alias)); 57 58 if (pmu_alias) { 59 pmu_alias->name = strdup(name); 60 if (!pmu_alias->name) 61 goto out_delete; 62 63 pmu_alias->alias = strdup(alias); 64 if (!pmu_alias->alias) 65 goto out_delete; 66 } 67 return pmu_alias; 68 69 out_delete: 70 pmu_alias__delete(pmu_alias); 71 return NULL; 72 } 73 74 static int setup_pmu_alias_list(void) 75 { 76 int fd, dirfd; 77 DIR *dir; 78 struct dirent *dent; 79 struct pmu_alias *pmu_alias; 80 char buf[MAX_PMU_NAME_LEN]; 81 FILE *file; 82 int ret = -ENOMEM; 83 84 dirfd = perf_pmu__event_source_devices_fd(); 85 if (dirfd < 0) 86 return -1; 87 88 dir = fdopendir(dirfd); 89 if (!dir) 90 return -errno; 91 92 while ((dent = readdir(dir))) { 93 if (!strcmp(dent->d_name, ".") || 94 !strcmp(dent->d_name, "..")) 95 continue; 96 97 fd = perf_pmu__pathname_fd(dirfd, dent->d_name, "alias", O_RDONLY); 98 if (fd < 0) 99 continue; 100 101 file = fdopen(fd, "r"); 102 if (!file) 103 continue; 104 105 if (!fgets(buf, sizeof(buf), file)) { 106 fclose(file); 107 continue; 108 } 109 110 fclose(file); 111 112 /* Remove the last '\n' */ 113 buf[strlen(buf) - 1] = 0; 114 115 pmu_alias = pmu_alias__new(dent->d_name, buf); 116 if (!pmu_alias) 117 goto close_dir; 118 119 list_add_tail(&pmu_alias->list, &pmu_alias_name_list); 120 } 121 122 ret = 0; 123 124 close_dir: 125 closedir(dir); 126 return ret; 127 } 128 129 static const char *__pmu_find_real_name(const char *name) 130 { 131 struct pmu_alias *pmu_alias; 132 133 list_for_each_entry(pmu_alias, &pmu_alias_name_list, list) { 134 if (!strcmp(name, pmu_alias->alias)) 135 return pmu_alias->name; 136 } 137 138 return name; 139 } 140 141 const char *pmu_find_real_name(const char *name) 142 { 143 if (cached_list) 144 return __pmu_find_real_name(name); 145 146 setup_pmu_alias_list(); 147 cached_list = true; 148 149 return __pmu_find_real_name(name); 150 } 151 152 static const char *__pmu_find_alias_name(const char *name) 153 { 154 struct pmu_alias *pmu_alias; 155 156 list_for_each_entry(pmu_alias, &pmu_alias_name_list, list) { 157 if (!strcmp(name, pmu_alias->name)) 158 return pmu_alias->alias; 159 } 160 return NULL; 161 } 162 163 const char *pmu_find_alias_name(const char *name) 164 { 165 if (cached_list) 166 return __pmu_find_alias_name(name); 167 168 setup_pmu_alias_list(); 169 cached_list = true; 170 171 return __pmu_find_alias_name(name); 172 } 173 174 int perf_pmus__num_mem_pmus(void) 175 { 176 /* AMD uses IBS OP pmu and not a core PMU for perf mem/c2c */ 177 if (x86__is_amd_cpu()) 178 return 1; 179 180 /* Intel uses core pmus for perf mem/c2c */ 181 return perf_pmus__num_core_pmus(); 182 } 183