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