xref: /linux/tools/perf/arch/x86/util/pmu.c (revision 06ce23ad57c8e378b86ef3f439b2e08bcb5d05eb)
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 struct pmu_alias {
19 	char *name;
20 	char *alias;
21 	struct list_head list;
22 };
23 
24 static LIST_HEAD(pmu_alias_name_list);
25 static bool cached_list;
26 
27 struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
28 {
29 #ifdef HAVE_AUXTRACE_SUPPORT
30 	if (!strcmp(pmu->name, INTEL_PT_PMU_NAME))
31 		return intel_pt_pmu_default_config(pmu);
32 	if (!strcmp(pmu->name, INTEL_BTS_PMU_NAME))
33 		pmu->selectable = true;
34 #endif
35 	return NULL;
36 }
37 
38 static void pmu_alias__delete(struct pmu_alias *pmu_alias)
39 {
40 	if (!pmu_alias)
41 		return;
42 
43 	zfree(&pmu_alias->name);
44 	zfree(&pmu_alias->alias);
45 	free(pmu_alias);
46 }
47 
48 static struct pmu_alias *pmu_alias__new(char *name, char *alias)
49 {
50 	struct pmu_alias *pmu_alias = zalloc(sizeof(*pmu_alias));
51 
52 	if (pmu_alias) {
53 		pmu_alias->name = strdup(name);
54 		if (!pmu_alias->name)
55 			goto out_delete;
56 
57 		pmu_alias->alias = strdup(alias);
58 		if (!pmu_alias->alias)
59 			goto out_delete;
60 	}
61 	return pmu_alias;
62 
63 out_delete:
64 	pmu_alias__delete(pmu_alias);
65 	return NULL;
66 }
67 
68 static int setup_pmu_alias_list(void)
69 {
70 	char path[PATH_MAX];
71 	DIR *dir;
72 	struct dirent *dent;
73 	struct pmu_alias *pmu_alias;
74 	char buf[MAX_PMU_NAME_LEN];
75 	FILE *file;
76 	int ret = -ENOMEM;
77 
78 	if (!perf_pmu__event_source_devices_scnprintf(path, sizeof(path)))
79 		return -1;
80 
81 	dir = opendir(path);
82 	if (!dir)
83 		return -errno;
84 
85 	while ((dent = readdir(dir))) {
86 		if (!strcmp(dent->d_name, ".") ||
87 		    !strcmp(dent->d_name, ".."))
88 			continue;
89 
90 		perf_pmu__pathname_scnprintf(path, sizeof(path), dent->d_name, "alias");
91 		if (!file_available(path))
92 			continue;
93 
94 		file = fopen(path, "r");
95 		if (!file)
96 			continue;
97 
98 		if (!fgets(buf, sizeof(buf), file)) {
99 			fclose(file);
100 			continue;
101 		}
102 
103 		fclose(file);
104 
105 		/* Remove the last '\n' */
106 		buf[strlen(buf) - 1] = 0;
107 
108 		pmu_alias = pmu_alias__new(dent->d_name, buf);
109 		if (!pmu_alias)
110 			goto close_dir;
111 
112 		list_add_tail(&pmu_alias->list, &pmu_alias_name_list);
113 	}
114 
115 	ret = 0;
116 
117 close_dir:
118 	closedir(dir);
119 	return ret;
120 }
121 
122 static char *__pmu_find_real_name(const char *name)
123 {
124 	struct pmu_alias *pmu_alias;
125 
126 	list_for_each_entry(pmu_alias, &pmu_alias_name_list, list) {
127 		if (!strcmp(name, pmu_alias->alias))
128 			return pmu_alias->name;
129 	}
130 
131 	return (char *)name;
132 }
133 
134 char *pmu_find_real_name(const char *name)
135 {
136 	if (cached_list)
137 		return __pmu_find_real_name(name);
138 
139 	setup_pmu_alias_list();
140 	cached_list = true;
141 
142 	return __pmu_find_real_name(name);
143 }
144 
145 static char *__pmu_find_alias_name(const char *name)
146 {
147 	struct pmu_alias *pmu_alias;
148 
149 	list_for_each_entry(pmu_alias, &pmu_alias_name_list, list) {
150 		if (!strcmp(name, pmu_alias->name))
151 			return pmu_alias->alias;
152 	}
153 	return NULL;
154 }
155 
156 char *pmu_find_alias_name(const char *name)
157 {
158 	if (cached_list)
159 		return __pmu_find_alias_name(name);
160 
161 	setup_pmu_alias_list();
162 	cached_list = true;
163 
164 	return __pmu_find_alias_name(name);
165 }
166