1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Support for libpfm4 event encoding.
4  *
5  * Copyright 2020 Google LLC.
6  */
7 #include "util/cpumap.h"
8 #include "util/debug.h"
9 #include "util/event.h"
10 #include "util/evlist.h"
11 #include "util/evsel.h"
12 #include "util/parse-events.h"
13 #include "util/pmu.h"
14 #include "util/pfm.h"
15 
16 #include <string.h>
17 #include <linux/kernel.h>
18 #include <perfmon/pfmlib_perf_event.h>
19 
libpfm_initialize(void)20 static void libpfm_initialize(void)
21 {
22 	int ret;
23 
24 	ret = pfm_initialize();
25 	if (ret != PFM_SUCCESS) {
26 		ui__warning("libpfm failed to initialize: %s\n",
27 			pfm_strerror(ret));
28 	}
29 }
30 
parse_libpfm_events_option(const struct option * opt,const char * str,int unset __maybe_unused)31 int parse_libpfm_events_option(const struct option *opt, const char *str,
32 			int unset __maybe_unused)
33 {
34 	struct evlist *evlist = *(struct evlist **)opt->value;
35 	struct perf_event_attr attr;
36 	struct perf_pmu *pmu;
37 	struct evsel *evsel, *grp_leader = NULL;
38 	char *p, *q, *p_orig;
39 	const char *sep;
40 	int grp_evt = -1;
41 	int ret;
42 
43 	libpfm_initialize();
44 
45 	p_orig = p = strdup(str);
46 	if (!p)
47 		return -1;
48 	/*
49 	 * force loading of the PMU list
50 	 */
51 	perf_pmu__scan(NULL);
52 
53 	for (q = p; strsep(&p, ",{}"); q = p) {
54 		sep = p ? str + (p - p_orig - 1) : "";
55 		if (*sep == '{') {
56 			if (grp_evt > -1) {
57 				ui__error(
58 					"nested event groups not supported\n");
59 				goto error;
60 			}
61 			grp_evt++;
62 		}
63 
64 		/* no event */
65 		if (*q == '\0') {
66 			if (*sep == '}') {
67 				if (grp_evt < 0) {
68 					ui__error("cannot close a non-existing event group\n");
69 					goto error;
70 				}
71 				grp_evt--;
72 			}
73 			continue;
74 		}
75 
76 		memset(&attr, 0, sizeof(attr));
77 		event_attr_init(&attr);
78 
79 		ret = pfm_get_perf_event_encoding(q, PFM_PLM0|PFM_PLM3,
80 						&attr, NULL, NULL);
81 
82 		if (ret != PFM_SUCCESS) {
83 			ui__error("failed to parse event %s : %s\n", str,
84 				  pfm_strerror(ret));
85 			goto error;
86 		}
87 
88 		pmu = perf_pmu__find_by_type((unsigned int)attr.type);
89 		evsel = parse_events__add_event(evlist->core.nr_entries,
90 						&attr, q, /*metric_id=*/NULL,
91 						pmu);
92 		if (evsel == NULL)
93 			goto error;
94 
95 		evsel->is_libpfm_event = true;
96 
97 		evlist__add(evlist, evsel);
98 
99 		if (grp_evt == 0)
100 			grp_leader = evsel;
101 
102 		if (grp_evt > -1) {
103 			evsel__set_leader(evsel, grp_leader);
104 			grp_leader->core.nr_members++;
105 			grp_evt++;
106 		}
107 
108 		if (*sep == '}') {
109 			if (grp_evt < 0) {
110 				ui__error(
111 				   "cannot close a non-existing event group\n");
112 				goto error;
113 			}
114 			evlist->core.nr_groups++;
115 			grp_leader = NULL;
116 			grp_evt = -1;
117 		}
118 	}
119 	free(p_orig);
120 	return 0;
121 error:
122 	free(p_orig);
123 	return -1;
124 }
125 
126 static const char *srcs[PFM_ATTR_CTRL_MAX] = {
127 	[PFM_ATTR_CTRL_UNKNOWN] = "???",
128 	[PFM_ATTR_CTRL_PMU] = "PMU",
129 	[PFM_ATTR_CTRL_PERF_EVENT] = "perf_event",
130 };
131 
132 static void
print_attr_flags(pfm_event_attr_info_t * info)133 print_attr_flags(pfm_event_attr_info_t *info)
134 {
135 	int n = 0;
136 
137 	if (info->is_dfl) {
138 		printf("[default] ");
139 		n++;
140 	}
141 
142 	if (info->is_precise) {
143 		printf("[precise] ");
144 		n++;
145 	}
146 
147 	if (!n)
148 		printf("- ");
149 }
150 
151 static void
print_libpfm_events_detailed(pfm_event_info_t * info,bool long_desc)152 print_libpfm_events_detailed(pfm_event_info_t *info, bool long_desc)
153 {
154 	pfm_event_attr_info_t ainfo;
155 	const char *src;
156 	int j, ret;
157 
158 	ainfo.size = sizeof(ainfo);
159 
160 	printf("  %s\n", info->name);
161 	printf("    [%s]\n", info->desc);
162 	if (long_desc) {
163 		if (info->equiv)
164 			printf("      Equiv: %s\n", info->equiv);
165 
166 		printf("      Code  : 0x%"PRIx64"\n", info->code);
167 	}
168 	pfm_for_each_event_attr(j, info) {
169 		ret = pfm_get_event_attr_info(info->idx, j,
170 					      PFM_OS_PERF_EVENT_EXT, &ainfo);
171 		if (ret != PFM_SUCCESS)
172 			continue;
173 
174 		if (ainfo.type == PFM_ATTR_UMASK) {
175 			printf("      %s:%s\n", info->name, ainfo.name);
176 			printf("        [%s]\n", ainfo.desc);
177 		}
178 
179 		if (!long_desc)
180 			continue;
181 
182 		if (ainfo.ctrl >= PFM_ATTR_CTRL_MAX)
183 			ainfo.ctrl = PFM_ATTR_CTRL_UNKNOWN;
184 
185 		src = srcs[ainfo.ctrl];
186 		switch (ainfo.type) {
187 		case PFM_ATTR_UMASK:
188 			printf("        Umask : 0x%02"PRIx64" : %s: ",
189 				ainfo.code, src);
190 			print_attr_flags(&ainfo);
191 			putchar('\n');
192 			break;
193 		case PFM_ATTR_MOD_BOOL:
194 			printf("      Modif : %s: [%s] : %s (boolean)\n", src,
195 				ainfo.name, ainfo.desc);
196 			break;
197 		case PFM_ATTR_MOD_INTEGER:
198 			printf("      Modif : %s: [%s] : %s (integer)\n", src,
199 				ainfo.name, ainfo.desc);
200 			break;
201 		case PFM_ATTR_NONE:
202 		case PFM_ATTR_RAW_UMASK:
203 		case PFM_ATTR_MAX:
204 		default:
205 			printf("      Attr  : %s: [%s] : %s\n", src,
206 				ainfo.name, ainfo.desc);
207 		}
208 	}
209 }
210 
211 /*
212  * list all pmu::event:umask, pmu::event
213  * printed events may not be all valid combinations of umask for an event
214  */
215 static void
print_libpfm_events_raw(pfm_pmu_info_t * pinfo,pfm_event_info_t * info)216 print_libpfm_events_raw(pfm_pmu_info_t *pinfo, pfm_event_info_t *info)
217 {
218 	pfm_event_attr_info_t ainfo;
219 	int j, ret;
220 	bool has_umask = false;
221 
222 	ainfo.size = sizeof(ainfo);
223 
224 	pfm_for_each_event_attr(j, info) {
225 		ret = pfm_get_event_attr_info(info->idx, j,
226 					      PFM_OS_PERF_EVENT_EXT, &ainfo);
227 		if (ret != PFM_SUCCESS)
228 			continue;
229 
230 		if (ainfo.type != PFM_ATTR_UMASK)
231 			continue;
232 
233 		printf("%s::%s:%s\n", pinfo->name, info->name, ainfo.name);
234 		has_umask = true;
235 	}
236 	if (!has_umask)
237 		printf("%s::%s\n", pinfo->name, info->name);
238 }
239 
print_libpfm_events(bool name_only,bool long_desc)240 void print_libpfm_events(bool name_only, bool long_desc)
241 {
242 	pfm_event_info_t info;
243 	pfm_pmu_info_t pinfo;
244 	int i, p, ret;
245 
246 	libpfm_initialize();
247 
248 	/* initialize to zero to indicate ABI version */
249 	info.size  = sizeof(info);
250 	pinfo.size = sizeof(pinfo);
251 
252 	if (!name_only)
253 		puts("\nList of pre-defined events (to be used in --pfm-events):\n");
254 
255 	pfm_for_all_pmus(p) {
256 		bool printed_pmu = false;
257 
258 		ret = pfm_get_pmu_info(p, &pinfo);
259 		if (ret != PFM_SUCCESS)
260 			continue;
261 
262 		/* only print events that are supported by host HW */
263 		if (!pinfo.is_present)
264 			continue;
265 
266 		/* handled by perf directly */
267 		if (pinfo.pmu == PFM_PMU_PERF_EVENT)
268 			continue;
269 
270 		for (i = pinfo.first_event; i != -1;
271 		     i = pfm_get_event_next(i)) {
272 
273 			ret = pfm_get_event_info(i, PFM_OS_PERF_EVENT_EXT,
274 						&info);
275 			if (ret != PFM_SUCCESS)
276 				continue;
277 
278 			if (!name_only && !printed_pmu) {
279 				printf("%s:\n", pinfo.name);
280 				printed_pmu = true;
281 			}
282 
283 			if (!name_only)
284 				print_libpfm_events_detailed(&info, long_desc);
285 			else
286 				print_libpfm_events_raw(&pinfo, &info);
287 		}
288 		if (!name_only && printed_pmu)
289 			putchar('\n');
290 	}
291 }
292