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 			continue;
67 
68 		memset(&attr, 0, sizeof(attr));
69 		event_attr_init(&attr);
70 
71 		ret = pfm_get_perf_event_encoding(q, PFM_PLM0|PFM_PLM3,
72 						&attr, NULL, NULL);
73 
74 		if (ret != PFM_SUCCESS) {
75 			ui__error("failed to parse event %s : %s\n", str,
76 				  pfm_strerror(ret));
77 			goto error;
78 		}
79 
80 		pmu = perf_pmu__find_by_type((unsigned int)attr.type);
81 		evsel = parse_events__add_event(evlist->core.nr_entries,
82 						&attr, q, pmu);
83 		if (evsel == NULL)
84 			goto error;
85 
86 		evsel->is_libpfm_event = true;
87 
88 		evlist__add(evlist, evsel);
89 
90 		if (grp_evt == 0)
91 			grp_leader = evsel;
92 
93 		if (grp_evt > -1) {
94 			evsel->leader = grp_leader;
95 			grp_leader->core.nr_members++;
96 			grp_evt++;
97 		}
98 
99 		if (*sep == '}') {
100 			if (grp_evt < 0) {
101 				ui__error(
102 				   "cannot close a non-existing event group\n");
103 				goto error;
104 			}
105 			evlist->nr_groups++;
106 			grp_leader = NULL;
107 			grp_evt = -1;
108 		}
109 	}
110 	return 0;
111 error:
112 	free(p_orig);
113 	return -1;
114 }
115 
116 static const char *srcs[PFM_ATTR_CTRL_MAX] = {
117 	[PFM_ATTR_CTRL_UNKNOWN] = "???",
118 	[PFM_ATTR_CTRL_PMU] = "PMU",
119 	[PFM_ATTR_CTRL_PERF_EVENT] = "perf_event",
120 };
121 
122 static void
print_attr_flags(pfm_event_attr_info_t * info)123 print_attr_flags(pfm_event_attr_info_t *info)
124 {
125 	int n = 0;
126 
127 	if (info->is_dfl) {
128 		printf("[default] ");
129 		n++;
130 	}
131 
132 	if (info->is_precise) {
133 		printf("[precise] ");
134 		n++;
135 	}
136 
137 	if (!n)
138 		printf("- ");
139 }
140 
141 static void
print_libpfm_events_detailed(pfm_event_info_t * info,bool long_desc)142 print_libpfm_events_detailed(pfm_event_info_t *info, bool long_desc)
143 {
144 	pfm_event_attr_info_t ainfo;
145 	const char *src;
146 	int j, ret;
147 
148 	ainfo.size = sizeof(ainfo);
149 
150 	printf("  %s\n", info->name);
151 	printf("    [%s]\n", info->desc);
152 	if (long_desc) {
153 		if (info->equiv)
154 			printf("      Equiv: %s\n", info->equiv);
155 
156 		printf("      Code  : 0x%"PRIx64"\n", info->code);
157 	}
158 	pfm_for_each_event_attr(j, info) {
159 		ret = pfm_get_event_attr_info(info->idx, j,
160 					      PFM_OS_PERF_EVENT_EXT, &ainfo);
161 		if (ret != PFM_SUCCESS)
162 			continue;
163 
164 		if (ainfo.type == PFM_ATTR_UMASK) {
165 			printf("      %s:%s\n", info->name, ainfo.name);
166 			printf("        [%s]\n", ainfo.desc);
167 		}
168 
169 		if (!long_desc)
170 			continue;
171 
172 		if (ainfo.ctrl >= PFM_ATTR_CTRL_MAX)
173 			ainfo.ctrl = PFM_ATTR_CTRL_UNKNOWN;
174 
175 		src = srcs[ainfo.ctrl];
176 		switch (ainfo.type) {
177 		case PFM_ATTR_UMASK:
178 			printf("        Umask : 0x%02"PRIx64" : %s: ",
179 				ainfo.code, src);
180 			print_attr_flags(&ainfo);
181 			putchar('\n');
182 			break;
183 		case PFM_ATTR_MOD_BOOL:
184 			printf("      Modif : %s: [%s] : %s (boolean)\n", src,
185 				ainfo.name, ainfo.desc);
186 			break;
187 		case PFM_ATTR_MOD_INTEGER:
188 			printf("      Modif : %s: [%s] : %s (integer)\n", src,
189 				ainfo.name, ainfo.desc);
190 			break;
191 		case PFM_ATTR_NONE:
192 		case PFM_ATTR_RAW_UMASK:
193 		case PFM_ATTR_MAX:
194 		default:
195 			printf("      Attr  : %s: [%s] : %s\n", src,
196 				ainfo.name, ainfo.desc);
197 		}
198 	}
199 }
200 
201 /*
202  * list all pmu::event:umask, pmu::event
203  * printed events may not be all valid combinations of umask for an event
204  */
205 static void
print_libpfm_events_raw(pfm_pmu_info_t * pinfo,pfm_event_info_t * info)206 print_libpfm_events_raw(pfm_pmu_info_t *pinfo, pfm_event_info_t *info)
207 {
208 	pfm_event_attr_info_t ainfo;
209 	int j, ret;
210 	bool has_umask = false;
211 
212 	ainfo.size = sizeof(ainfo);
213 
214 	pfm_for_each_event_attr(j, info) {
215 		ret = pfm_get_event_attr_info(info->idx, j,
216 					      PFM_OS_PERF_EVENT_EXT, &ainfo);
217 		if (ret != PFM_SUCCESS)
218 			continue;
219 
220 		if (ainfo.type != PFM_ATTR_UMASK)
221 			continue;
222 
223 		printf("%s::%s:%s\n", pinfo->name, info->name, ainfo.name);
224 		has_umask = true;
225 	}
226 	if (!has_umask)
227 		printf("%s::%s\n", pinfo->name, info->name);
228 }
229 
print_libpfm_events(bool name_only,bool long_desc)230 void print_libpfm_events(bool name_only, bool long_desc)
231 {
232 	pfm_event_info_t info;
233 	pfm_pmu_info_t pinfo;
234 	int i, p, ret;
235 
236 	libpfm_initialize();
237 
238 	/* initialize to zero to indicate ABI version */
239 	info.size  = sizeof(info);
240 	pinfo.size = sizeof(pinfo);
241 
242 	if (!name_only)
243 		puts("\nList of pre-defined events (to be used in --pfm-events):\n");
244 
245 	pfm_for_all_pmus(p) {
246 		bool printed_pmu = false;
247 
248 		ret = pfm_get_pmu_info(p, &pinfo);
249 		if (ret != PFM_SUCCESS)
250 			continue;
251 
252 		/* only print events that are supported by host HW */
253 		if (!pinfo.is_present)
254 			continue;
255 
256 		/* handled by perf directly */
257 		if (pinfo.pmu == PFM_PMU_PERF_EVENT)
258 			continue;
259 
260 		for (i = pinfo.first_event; i != -1;
261 		     i = pfm_get_event_next(i)) {
262 
263 			ret = pfm_get_event_info(i, PFM_OS_PERF_EVENT_EXT,
264 						&info);
265 			if (ret != PFM_SUCCESS)
266 				continue;
267 
268 			if (!name_only && !printed_pmu) {
269 				printf("%s:\n", pinfo.name);
270 				printed_pmu = true;
271 			}
272 
273 			if (!name_only)
274 				print_libpfm_events_detailed(&info, long_desc);
275 			else
276 				print_libpfm_events_raw(&pinfo, &info);
277 		}
278 		if (!name_only && printed_pmu)
279 			putchar('\n');
280 	}
281 }
282