pmu.c (3324B)
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 20struct pmu_alias { 21 char *name; 22 char *alias; 23 struct list_head list; 24}; 25 26static LIST_HEAD(pmu_alias_name_list); 27static bool cached_list; 28 29struct 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 40static 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 50static 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 65out_delete: 66 pmu_alias__delete(pmu_alias); 67 return NULL; 68} 69 70static 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 125close_dir: 126 closedir(dir); 127 return ret; 128} 129 130static 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 142char *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 153static 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 164char *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}