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
perf_pmu__get_default_config(struct perf_pmu * pmu __maybe_unused)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
pmu_alias__delete(struct pmu_alias * pmu_alias)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
pmu_alias__new(char * name,char * alias)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
setup_pmu_alias_list(void)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
__pmu_find_real_name(const char * name)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
pmu_find_real_name(const char * name)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
__pmu_find_alias_name(const char * name)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
pmu_find_alias_name(const char * name)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
perf_pmus__num_mem_pmus(void)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