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 18 #define TEMPLATE_ALIAS "%s/bus/event_source/devices/%s/alias" 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 return intel_pt_pmu_default_config(pmu); 34 if (!strcmp(pmu->name, INTEL_BTS_PMU_NAME)) 35 pmu->selectable = true; 36 #endif 37 return NULL; 38 } 39 40 static void pmu_alias__delete(struct pmu_alias *pmu_alias) 41 { 42 if (!pmu_alias) 43 return; 44 45 zfree(&pmu_alias->name); 46 zfree(&pmu_alias->alias); 47 free(pmu_alias); 48 } 49 50 static struct pmu_alias *pmu_alias__new(char *name, char *alias) 51 { 52 struct pmu_alias *pmu_alias = zalloc(sizeof(*pmu_alias)); 53 54 if (pmu_alias) { 55 pmu_alias->name = strdup(name); 56 if (!pmu_alias->name) 57 goto out_delete; 58 59 pmu_alias->alias = strdup(alias); 60 if (!pmu_alias->alias) 61 goto out_delete; 62 } 63 return pmu_alias; 64 65 out_delete: 66 pmu_alias__delete(pmu_alias); 67 return NULL; 68 } 69 70 static int setup_pmu_alias_list(void) 71 { 72 char path[PATH_MAX]; 73 DIR *dir; 74 struct dirent *dent; 75 const char *sysfs = sysfs__mountpoint(); 76 struct pmu_alias *pmu_alias; 77 char buf[MAX_PMU_NAME_LEN]; 78 FILE *file; 79 int ret = -ENOMEM; 80 81 if (!sysfs) 82 return -1; 83 84 snprintf(path, PATH_MAX, 85 "%s" EVENT_SOURCE_DEVICE_PATH, sysfs); 86 87 dir = opendir(path); 88 if (!dir) 89 return -errno; 90 91 while ((dent = readdir(dir))) { 92 if (!strcmp(dent->d_name, ".") || 93 !strcmp(dent->d_name, "..")) 94 continue; 95 96 snprintf(path, PATH_MAX, 97 TEMPLATE_ALIAS, sysfs, dent->d_name); 98 99 if (!file_available(path)) 100 continue; 101 102 file = fopen(path, "r"); 103 if (!file) 104 continue; 105 106 if (!fgets(buf, sizeof(buf), file)) { 107 fclose(file); 108 continue; 109 } 110 111 fclose(file); 112 113 /* Remove the last '\n' */ 114 buf[strlen(buf) - 1] = 0; 115 116 pmu_alias = pmu_alias__new(dent->d_name, buf); 117 if (!pmu_alias) 118 goto close_dir; 119 120 list_add_tail(&pmu_alias->list, &pmu_alias_name_list); 121 } 122 123 ret = 0; 124 125 close_dir: 126 closedir(dir); 127 return ret; 128 } 129 130 static char *__pmu_find_real_name(const char *name) 131 { 132 struct pmu_alias *pmu_alias; 133 134 list_for_each_entry(pmu_alias, &pmu_alias_name_list, list) { 135 if (!strcmp(name, pmu_alias->alias)) 136 return pmu_alias->name; 137 } 138 139 return (char *)name; 140 } 141 142 char *pmu_find_real_name(const char *name) 143 { 144 if (cached_list) 145 return __pmu_find_real_name(name); 146 147 setup_pmu_alias_list(); 148 cached_list = true; 149 150 return __pmu_find_real_name(name); 151 } 152 153 static char *__pmu_find_alias_name(const char *name) 154 { 155 struct pmu_alias *pmu_alias; 156 157 list_for_each_entry(pmu_alias, &pmu_alias_name_list, list) { 158 if (!strcmp(name, pmu_alias->name)) 159 return pmu_alias->alias; 160 } 161 return NULL; 162 } 163 164 char *pmu_find_alias_name(const char *name) 165 { 166 if (cached_list) 167 return __pmu_find_alias_name(name); 168 169 setup_pmu_alias_list(); 170 cached_list = true; 171 172 return __pmu_find_alias_name(name); 173 } 174