xref: /openbmc/linux/tools/perf/util/print-events.c (revision 3def3387)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <dirent.h>
3 #include <errno.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <fcntl.h>
8 #include <sys/param.h>
9 #include <unistd.h>
10 
11 #include <api/fs/tracing_path.h>
12 #include <linux/stddef.h>
13 #include <linux/perf_event.h>
14 #include <linux/zalloc.h>
15 #include <subcmd/pager.h>
16 
17 #include "build-id.h"
18 #include "debug.h"
19 #include "evsel.h"
20 #include "metricgroup.h"
21 #include "parse-events.h"
22 #include "pmu.h"
23 #include "print-events.h"
24 #include "probe-file.h"
25 #include "string2.h"
26 #include "strlist.h"
27 #include "tracepoint.h"
28 #include "pfm.h"
29 #include "pmu-hybrid.h"
30 
31 #define MAX_NAME_LEN 100
32 
33 /** Strings corresponding to enum perf_type_id. */
34 static const char * const event_type_descriptors[] = {
35 	"Hardware event",
36 	"Software event",
37 	"Tracepoint event",
38 	"Hardware cache event",
39 	"Raw hardware event descriptor",
40 	"Hardware breakpoint",
41 };
42 
43 static const struct event_symbol event_symbols_tool[PERF_TOOL_MAX] = {
44 	[PERF_TOOL_DURATION_TIME] = {
45 		.symbol = "duration_time",
46 		.alias  = "",
47 	},
48 	[PERF_TOOL_USER_TIME] = {
49 		.symbol = "user_time",
50 		.alias  = "",
51 	},
52 	[PERF_TOOL_SYSTEM_TIME] = {
53 		.symbol = "system_time",
54 		.alias  = "",
55 	},
56 };
57 
58 /*
59  * Print the events from <debugfs_mount_point>/tracing/events
60  */
61 void print_tracepoint_events(const struct print_callbacks *print_cb __maybe_unused, void *print_state __maybe_unused)
62 {
63 	char *events_path = get_tracing_file("events");
64 	int events_fd = open(events_path, O_PATH);
65 
66 	put_tracing_file(events_path);
67 	if (events_fd < 0) {
68 		printf("Error: failed to open tracing events directory\n");
69 		return;
70 	}
71 
72 #ifdef HAVE_SCANDIRAT_SUPPORT
73 {
74 	struct dirent **sys_namelist = NULL;
75 	int sys_items = tracing_events__scandir_alphasort(&sys_namelist);
76 
77 	for (int i = 0; i < sys_items; i++) {
78 		struct dirent *sys_dirent = sys_namelist[i];
79 		struct dirent **evt_namelist = NULL;
80 		int dir_fd;
81 		int evt_items;
82 
83 		if (sys_dirent->d_type != DT_DIR ||
84 		    !strcmp(sys_dirent->d_name, ".") ||
85 		    !strcmp(sys_dirent->d_name, ".."))
86 			goto next_sys;
87 
88 		dir_fd = openat(events_fd, sys_dirent->d_name, O_PATH);
89 		if (dir_fd < 0)
90 			goto next_sys;
91 
92 		evt_items = scandirat(events_fd, sys_dirent->d_name, &evt_namelist, NULL, alphasort);
93 		for (int j = 0; j < evt_items; j++) {
94 			struct dirent *evt_dirent = evt_namelist[j];
95 			char evt_path[MAXPATHLEN];
96 			int evt_fd;
97 
98 			if (evt_dirent->d_type != DT_DIR ||
99 			    !strcmp(evt_dirent->d_name, ".") ||
100 			    !strcmp(evt_dirent->d_name, ".."))
101 				goto next_evt;
102 
103 			snprintf(evt_path, sizeof(evt_path), "%s/id", evt_dirent->d_name);
104 			evt_fd = openat(dir_fd, evt_path, O_RDONLY);
105 			if (evt_fd < 0)
106 				goto next_evt;
107 			close(evt_fd);
108 
109 			snprintf(evt_path, MAXPATHLEN, "%s:%s",
110 				 sys_dirent->d_name, evt_dirent->d_name);
111 			print_cb->print_event(print_state,
112 					/*topic=*/NULL,
113 					/*pmu_name=*/NULL,
114 					evt_path,
115 					/*event_alias=*/NULL,
116 					/*scale_unit=*/NULL,
117 					/*deprecated=*/false,
118 					"Tracepoint event",
119 					/*desc=*/NULL,
120 					/*long_desc=*/NULL,
121 					/*encoding_desc=*/NULL);
122 next_evt:
123 			free(evt_namelist[j]);
124 		}
125 		close(dir_fd);
126 		free(evt_namelist);
127 next_sys:
128 		free(sys_namelist[i]);
129 	}
130 
131 	free(sys_namelist);
132 }
133 #else
134 	printf("\nWARNING: Your libc doesn't have the scandirat function, please ask its maintainers to implement it.\n"
135 	       "         As a rough fallback, please do 'ls %s' to see the available tracepoint events.\n", events_path);
136 #endif
137 	close(events_fd);
138 }
139 
140 void print_sdt_events(const struct print_callbacks *print_cb, void *print_state)
141 {
142 	struct strlist *bidlist, *sdtlist;
143 	struct str_node *bid_nd, *sdt_name, *next_sdt_name;
144 	const char *last_sdt_name = NULL;
145 
146 	/*
147 	 * The implicitly sorted sdtlist will hold the tracepoint name followed
148 	 * by @<buildid>. If the tracepoint name is unique (determined by
149 	 * looking at the adjacent nodes) the @<buildid> is dropped otherwise
150 	 * the executable path and buildid are added to the name.
151 	 */
152 	sdtlist = strlist__new(NULL, NULL);
153 	if (!sdtlist) {
154 		pr_debug("Failed to allocate new strlist for SDT\n");
155 		return;
156 	}
157 	bidlist = build_id_cache__list_all(true);
158 	if (!bidlist) {
159 		pr_debug("Failed to get buildids: %d\n", errno);
160 		return;
161 	}
162 	strlist__for_each_entry(bid_nd, bidlist) {
163 		struct probe_cache *pcache;
164 		struct probe_cache_entry *ent;
165 
166 		pcache = probe_cache__new(bid_nd->s, NULL);
167 		if (!pcache)
168 			continue;
169 		list_for_each_entry(ent, &pcache->entries, node) {
170 			char buf[1024];
171 
172 			snprintf(buf, sizeof(buf), "%s:%s@%s",
173 				 ent->pev.group, ent->pev.event, bid_nd->s);
174 			strlist__add(sdtlist, buf);
175 		}
176 		probe_cache__delete(pcache);
177 	}
178 	strlist__delete(bidlist);
179 
180 	strlist__for_each_entry(sdt_name, sdtlist) {
181 		bool show_detail = false;
182 		char *bid = strchr(sdt_name->s, '@');
183 		char *evt_name = NULL;
184 
185 		if (bid)
186 			*(bid++) = '\0';
187 
188 		if (last_sdt_name && !strcmp(last_sdt_name, sdt_name->s)) {
189 			show_detail = true;
190 		} else {
191 			next_sdt_name = strlist__next(sdt_name);
192 			if (next_sdt_name) {
193 				char *bid2 = strchr(next_sdt_name->s, '@');
194 
195 				if (bid2)
196 					*bid2 = '\0';
197 				if (strcmp(sdt_name->s, next_sdt_name->s) == 0)
198 					show_detail = true;
199 				if (bid2)
200 					*bid2 = '@';
201 			}
202 		}
203 		last_sdt_name = sdt_name->s;
204 
205 		if (show_detail) {
206 			char *path = build_id_cache__origname(bid);
207 
208 			if (path) {
209 				if (asprintf(&evt_name, "%s@%s(%.12s)", sdt_name->s, path, bid) < 0)
210 					evt_name = NULL;
211 				free(path);
212 			}
213 		}
214 		print_cb->print_event(print_state,
215 				/*topic=*/NULL,
216 				/*pmu_name=*/NULL,
217 				evt_name ?: sdt_name->s,
218 				/*event_alias=*/NULL,
219 				/*deprecated=*/false,
220 				/*scale_unit=*/NULL,
221 				"SDT event",
222 				/*desc=*/NULL,
223 				/*long_desc=*/NULL,
224 				/*encoding_desc=*/NULL);
225 
226 		free(evt_name);
227 	}
228 	strlist__delete(sdtlist);
229 }
230 
231 int print_hwcache_events(const struct print_callbacks *print_cb, void *print_state)
232 {
233 	struct strlist *evt_name_list = strlist__new(NULL, NULL);
234 	struct str_node *nd;
235 
236 	if (!evt_name_list) {
237 		pr_debug("Failed to allocate new strlist for hwcache events\n");
238 		return -ENOMEM;
239 	}
240 	for (int type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
241 		for (int op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
242 			/* skip invalid cache type */
243 			if (!evsel__is_cache_op_valid(type, op))
244 				continue;
245 
246 			for (int i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
247 				struct perf_pmu *pmu = NULL;
248 				char name[64];
249 
250 				__evsel__hw_cache_type_op_res_name(type, op, i, name, sizeof(name));
251 				if (!perf_pmu__has_hybrid()) {
252 					if (is_event_supported(PERF_TYPE_HW_CACHE,
253 							       type | (op << 8) | (i << 16)))
254 						strlist__add(evt_name_list, name);
255 					continue;
256 				}
257 				perf_pmu__for_each_hybrid_pmu(pmu) {
258 					if (is_event_supported(PERF_TYPE_HW_CACHE,
259 					    type | (op << 8) | (i << 16) |
260 					    ((__u64)pmu->type << PERF_PMU_TYPE_SHIFT))) {
261 						char new_name[128];
262 							snprintf(new_name, sizeof(new_name),
263 								 "%s/%s/", pmu->name, name);
264 							strlist__add(evt_name_list, new_name);
265 					}
266 				}
267 			}
268 		}
269 	}
270 
271 	strlist__for_each_entry(nd, evt_name_list) {
272 		print_cb->print_event(print_state,
273 				"cache",
274 				/*pmu_name=*/NULL,
275 				nd->s,
276 				/*event_alias=*/NULL,
277 				/*scale_unit=*/NULL,
278 				/*deprecated=*/false,
279 				event_type_descriptors[PERF_TYPE_HW_CACHE],
280 				/*desc=*/NULL,
281 				/*long_desc=*/NULL,
282 				/*encoding_desc=*/NULL);
283 	}
284 	strlist__delete(evt_name_list);
285 	return 0;
286 }
287 
288 void print_tool_events(const struct print_callbacks *print_cb, void *print_state)
289 {
290 	// Start at 1 because the first enum entry means no tool event.
291 	for (int i = 1; i < PERF_TOOL_MAX; ++i) {
292 		print_cb->print_event(print_state,
293 				"tool",
294 				/*pmu_name=*/NULL,
295 				event_symbols_tool[i].symbol,
296 				event_symbols_tool[i].alias,
297 				/*scale_unit=*/NULL,
298 				/*deprecated=*/false,
299 				"Tool event",
300 				/*desc=*/NULL,
301 				/*long_desc=*/NULL,
302 				/*encoding_desc=*/NULL);
303 	}
304 }
305 
306 void print_symbol_events(const struct print_callbacks *print_cb, void *print_state,
307 			 unsigned int type, const struct event_symbol *syms,
308 			 unsigned int max)
309 {
310 	struct strlist *evt_name_list = strlist__new(NULL, NULL);
311 	struct str_node *nd;
312 
313 	if (!evt_name_list) {
314 		pr_debug("Failed to allocate new strlist for symbol events\n");
315 		return;
316 	}
317 	for (unsigned int i = 0; i < max; i++) {
318 		/*
319 		 * New attr.config still not supported here, the latest
320 		 * example was PERF_COUNT_SW_CGROUP_SWITCHES
321 		 */
322 		if (syms[i].symbol == NULL)
323 			continue;
324 
325 		if (!is_event_supported(type, i))
326 			continue;
327 
328 		if (strlen(syms[i].alias)) {
329 			char name[MAX_NAME_LEN];
330 
331 			snprintf(name, MAX_NAME_LEN, "%s OR %s", syms[i].symbol, syms[i].alias);
332 			strlist__add(evt_name_list, name);
333 		} else
334 			strlist__add(evt_name_list, syms[i].symbol);
335 	}
336 
337 	strlist__for_each_entry(nd, evt_name_list) {
338 		char *alias = strstr(nd->s, " OR ");
339 
340 		if (alias) {
341 			*alias = '\0';
342 			alias += 4;
343 		}
344 		print_cb->print_event(print_state,
345 				/*topic=*/NULL,
346 				/*pmu_name=*/NULL,
347 				nd->s,
348 				alias,
349 				/*scale_unit=*/NULL,
350 				/*deprecated=*/false,
351 				event_type_descriptors[type],
352 				/*desc=*/NULL,
353 				/*long_desc=*/NULL,
354 				/*encoding_desc=*/NULL);
355 	}
356 	strlist__delete(evt_name_list);
357 }
358 
359 /*
360  * Print the help text for the event symbols:
361  */
362 void print_events(const struct print_callbacks *print_cb, void *print_state)
363 {
364 	print_symbol_events(print_cb, print_state, PERF_TYPE_HARDWARE,
365 			event_symbols_hw, PERF_COUNT_HW_MAX);
366 	print_symbol_events(print_cb, print_state, PERF_TYPE_SOFTWARE,
367 			event_symbols_sw, PERF_COUNT_SW_MAX);
368 
369 	print_tool_events(print_cb, print_state);
370 
371 	print_hwcache_events(print_cb, print_state);
372 
373 	print_pmu_events(print_cb, print_state);
374 
375 	print_cb->print_event(print_state,
376 			/*topic=*/NULL,
377 			/*pmu_name=*/NULL,
378 			"rNNN",
379 			/*event_alias=*/NULL,
380 			/*scale_unit=*/NULL,
381 			/*deprecated=*/false,
382 			event_type_descriptors[PERF_TYPE_RAW],
383 			/*desc=*/NULL,
384 			/*long_desc=*/NULL,
385 			/*encoding_desc=*/NULL);
386 
387 	print_cb->print_event(print_state,
388 			/*topic=*/NULL,
389 			/*pmu_name=*/NULL,
390 			"cpu/t1=v1[,t2=v2,t3 ...]/modifier",
391 			/*event_alias=*/NULL,
392 			/*scale_unit=*/NULL,
393 			/*deprecated=*/false,
394 			event_type_descriptors[PERF_TYPE_RAW],
395 			"(see 'man perf-list' on how to encode it)",
396 			/*long_desc=*/NULL,
397 			/*encoding_desc=*/NULL);
398 
399 	print_cb->print_event(print_state,
400 			/*topic=*/NULL,
401 			/*pmu_name=*/NULL,
402 			"mem:<addr>[/len][:access]",
403 			/*scale_unit=*/NULL,
404 			/*event_alias=*/NULL,
405 			/*deprecated=*/false,
406 			event_type_descriptors[PERF_TYPE_BREAKPOINT],
407 			/*desc=*/NULL,
408 			/*long_desc=*/NULL,
409 			/*encoding_desc=*/NULL);
410 
411 	print_tracepoint_events(print_cb, print_state);
412 
413 	print_sdt_events(print_cb, print_state);
414 
415 	metricgroup__print(print_cb, print_state);
416 
417 	print_libpfm_events(print_cb, print_state);
418 }
419