xref: /linux/tools/perf/arch/x86/util/pmu.c (revision b37e6b680e3a4fad40d8c7b92cfe9b2806c6248e)
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