18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Support for libpfm4 event encoding.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2020 Google LLC.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include "util/cpumap.h"
88c2ecf20Sopenharmony_ci#include "util/debug.h"
98c2ecf20Sopenharmony_ci#include "util/event.h"
108c2ecf20Sopenharmony_ci#include "util/evlist.h"
118c2ecf20Sopenharmony_ci#include "util/evsel.h"
128c2ecf20Sopenharmony_ci#include "util/parse-events.h"
138c2ecf20Sopenharmony_ci#include "util/pmu.h"
148c2ecf20Sopenharmony_ci#include "util/pfm.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <string.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <perfmon/pfmlib_perf_event.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic void libpfm_initialize(void)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	int ret;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	ret = pfm_initialize();
258c2ecf20Sopenharmony_ci	if (ret != PFM_SUCCESS) {
268c2ecf20Sopenharmony_ci		ui__warning("libpfm failed to initialize: %s\n",
278c2ecf20Sopenharmony_ci			pfm_strerror(ret));
288c2ecf20Sopenharmony_ci	}
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ciint parse_libpfm_events_option(const struct option *opt, const char *str,
328c2ecf20Sopenharmony_ci			int unset __maybe_unused)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct evlist *evlist = *(struct evlist **)opt->value;
358c2ecf20Sopenharmony_ci	struct perf_event_attr attr;
368c2ecf20Sopenharmony_ci	struct perf_pmu *pmu;
378c2ecf20Sopenharmony_ci	struct evsel *evsel, *grp_leader = NULL;
388c2ecf20Sopenharmony_ci	char *p, *q, *p_orig;
398c2ecf20Sopenharmony_ci	const char *sep;
408c2ecf20Sopenharmony_ci	int grp_evt = -1;
418c2ecf20Sopenharmony_ci	int ret;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	libpfm_initialize();
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	p_orig = p = strdup(str);
468c2ecf20Sopenharmony_ci	if (!p)
478c2ecf20Sopenharmony_ci		return -1;
488c2ecf20Sopenharmony_ci	/*
498c2ecf20Sopenharmony_ci	 * force loading of the PMU list
508c2ecf20Sopenharmony_ci	 */
518c2ecf20Sopenharmony_ci	perf_pmu__scan(NULL);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	for (q = p; strsep(&p, ",{}"); q = p) {
548c2ecf20Sopenharmony_ci		sep = p ? str + (p - p_orig - 1) : "";
558c2ecf20Sopenharmony_ci		if (*sep == '{') {
568c2ecf20Sopenharmony_ci			if (grp_evt > -1) {
578c2ecf20Sopenharmony_ci				ui__error(
588c2ecf20Sopenharmony_ci					"nested event groups not supported\n");
598c2ecf20Sopenharmony_ci				goto error;
608c2ecf20Sopenharmony_ci			}
618c2ecf20Sopenharmony_ci			grp_evt++;
628c2ecf20Sopenharmony_ci		}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		/* no event */
658c2ecf20Sopenharmony_ci		if (*q == '\0')
668c2ecf20Sopenharmony_ci			continue;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		memset(&attr, 0, sizeof(attr));
698c2ecf20Sopenharmony_ci		event_attr_init(&attr);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci		ret = pfm_get_perf_event_encoding(q, PFM_PLM0|PFM_PLM3,
728c2ecf20Sopenharmony_ci						&attr, NULL, NULL);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci		if (ret != PFM_SUCCESS) {
758c2ecf20Sopenharmony_ci			ui__error("failed to parse event %s : %s\n", str,
768c2ecf20Sopenharmony_ci				  pfm_strerror(ret));
778c2ecf20Sopenharmony_ci			goto error;
788c2ecf20Sopenharmony_ci		}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		pmu = perf_pmu__find_by_type((unsigned int)attr.type);
818c2ecf20Sopenharmony_ci		evsel = parse_events__add_event(evlist->core.nr_entries,
828c2ecf20Sopenharmony_ci						&attr, q, pmu);
838c2ecf20Sopenharmony_ci		if (evsel == NULL)
848c2ecf20Sopenharmony_ci			goto error;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		evsel->is_libpfm_event = true;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci		evlist__add(evlist, evsel);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		if (grp_evt == 0)
918c2ecf20Sopenharmony_ci			grp_leader = evsel;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		if (grp_evt > -1) {
948c2ecf20Sopenharmony_ci			evsel->leader = grp_leader;
958c2ecf20Sopenharmony_ci			grp_leader->core.nr_members++;
968c2ecf20Sopenharmony_ci			grp_evt++;
978c2ecf20Sopenharmony_ci		}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci		if (*sep == '}') {
1008c2ecf20Sopenharmony_ci			if (grp_evt < 0) {
1018c2ecf20Sopenharmony_ci				ui__error(
1028c2ecf20Sopenharmony_ci				   "cannot close a non-existing event group\n");
1038c2ecf20Sopenharmony_ci				goto error;
1048c2ecf20Sopenharmony_ci			}
1058c2ecf20Sopenharmony_ci			evlist->nr_groups++;
1068c2ecf20Sopenharmony_ci			grp_leader = NULL;
1078c2ecf20Sopenharmony_ci			grp_evt = -1;
1088c2ecf20Sopenharmony_ci		}
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_cierror:
1128c2ecf20Sopenharmony_ci	free(p_orig);
1138c2ecf20Sopenharmony_ci	return -1;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic const char *srcs[PFM_ATTR_CTRL_MAX] = {
1178c2ecf20Sopenharmony_ci	[PFM_ATTR_CTRL_UNKNOWN] = "???",
1188c2ecf20Sopenharmony_ci	[PFM_ATTR_CTRL_PMU] = "PMU",
1198c2ecf20Sopenharmony_ci	[PFM_ATTR_CTRL_PERF_EVENT] = "perf_event",
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic void
1238c2ecf20Sopenharmony_ciprint_attr_flags(pfm_event_attr_info_t *info)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	int n = 0;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (info->is_dfl) {
1288c2ecf20Sopenharmony_ci		printf("[default] ");
1298c2ecf20Sopenharmony_ci		n++;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (info->is_precise) {
1338c2ecf20Sopenharmony_ci		printf("[precise] ");
1348c2ecf20Sopenharmony_ci		n++;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (!n)
1388c2ecf20Sopenharmony_ci		printf("- ");
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic void
1428c2ecf20Sopenharmony_ciprint_libpfm_events_detailed(pfm_event_info_t *info, bool long_desc)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	pfm_event_attr_info_t ainfo;
1458c2ecf20Sopenharmony_ci	const char *src;
1468c2ecf20Sopenharmony_ci	int j, ret;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	ainfo.size = sizeof(ainfo);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	printf("  %s\n", info->name);
1518c2ecf20Sopenharmony_ci	printf("    [%s]\n", info->desc);
1528c2ecf20Sopenharmony_ci	if (long_desc) {
1538c2ecf20Sopenharmony_ci		if (info->equiv)
1548c2ecf20Sopenharmony_ci			printf("      Equiv: %s\n", info->equiv);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		printf("      Code  : 0x%"PRIx64"\n", info->code);
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci	pfm_for_each_event_attr(j, info) {
1598c2ecf20Sopenharmony_ci		ret = pfm_get_event_attr_info(info->idx, j,
1608c2ecf20Sopenharmony_ci					      PFM_OS_PERF_EVENT_EXT, &ainfo);
1618c2ecf20Sopenharmony_ci		if (ret != PFM_SUCCESS)
1628c2ecf20Sopenharmony_ci			continue;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci		if (ainfo.type == PFM_ATTR_UMASK) {
1658c2ecf20Sopenharmony_ci			printf("      %s:%s\n", info->name, ainfo.name);
1668c2ecf20Sopenharmony_ci			printf("        [%s]\n", ainfo.desc);
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		if (!long_desc)
1708c2ecf20Sopenharmony_ci			continue;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci		if (ainfo.ctrl >= PFM_ATTR_CTRL_MAX)
1738c2ecf20Sopenharmony_ci			ainfo.ctrl = PFM_ATTR_CTRL_UNKNOWN;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		src = srcs[ainfo.ctrl];
1768c2ecf20Sopenharmony_ci		switch (ainfo.type) {
1778c2ecf20Sopenharmony_ci		case PFM_ATTR_UMASK:
1788c2ecf20Sopenharmony_ci			printf("        Umask : 0x%02"PRIx64" : %s: ",
1798c2ecf20Sopenharmony_ci				ainfo.code, src);
1808c2ecf20Sopenharmony_ci			print_attr_flags(&ainfo);
1818c2ecf20Sopenharmony_ci			putchar('\n');
1828c2ecf20Sopenharmony_ci			break;
1838c2ecf20Sopenharmony_ci		case PFM_ATTR_MOD_BOOL:
1848c2ecf20Sopenharmony_ci			printf("      Modif : %s: [%s] : %s (boolean)\n", src,
1858c2ecf20Sopenharmony_ci				ainfo.name, ainfo.desc);
1868c2ecf20Sopenharmony_ci			break;
1878c2ecf20Sopenharmony_ci		case PFM_ATTR_MOD_INTEGER:
1888c2ecf20Sopenharmony_ci			printf("      Modif : %s: [%s] : %s (integer)\n", src,
1898c2ecf20Sopenharmony_ci				ainfo.name, ainfo.desc);
1908c2ecf20Sopenharmony_ci			break;
1918c2ecf20Sopenharmony_ci		case PFM_ATTR_NONE:
1928c2ecf20Sopenharmony_ci		case PFM_ATTR_RAW_UMASK:
1938c2ecf20Sopenharmony_ci		case PFM_ATTR_MAX:
1948c2ecf20Sopenharmony_ci		default:
1958c2ecf20Sopenharmony_ci			printf("      Attr  : %s: [%s] : %s\n", src,
1968c2ecf20Sopenharmony_ci				ainfo.name, ainfo.desc);
1978c2ecf20Sopenharmony_ci		}
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/*
2028c2ecf20Sopenharmony_ci * list all pmu::event:umask, pmu::event
2038c2ecf20Sopenharmony_ci * printed events may not be all valid combinations of umask for an event
2048c2ecf20Sopenharmony_ci */
2058c2ecf20Sopenharmony_cistatic void
2068c2ecf20Sopenharmony_ciprint_libpfm_events_raw(pfm_pmu_info_t *pinfo, pfm_event_info_t *info)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	pfm_event_attr_info_t ainfo;
2098c2ecf20Sopenharmony_ci	int j, ret;
2108c2ecf20Sopenharmony_ci	bool has_umask = false;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	ainfo.size = sizeof(ainfo);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	pfm_for_each_event_attr(j, info) {
2158c2ecf20Sopenharmony_ci		ret = pfm_get_event_attr_info(info->idx, j,
2168c2ecf20Sopenharmony_ci					      PFM_OS_PERF_EVENT_EXT, &ainfo);
2178c2ecf20Sopenharmony_ci		if (ret != PFM_SUCCESS)
2188c2ecf20Sopenharmony_ci			continue;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		if (ainfo.type != PFM_ATTR_UMASK)
2218c2ecf20Sopenharmony_ci			continue;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		printf("%s::%s:%s\n", pinfo->name, info->name, ainfo.name);
2248c2ecf20Sopenharmony_ci		has_umask = true;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci	if (!has_umask)
2278c2ecf20Sopenharmony_ci		printf("%s::%s\n", pinfo->name, info->name);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_civoid print_libpfm_events(bool name_only, bool long_desc)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	pfm_event_info_t info;
2338c2ecf20Sopenharmony_ci	pfm_pmu_info_t pinfo;
2348c2ecf20Sopenharmony_ci	int i, p, ret;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	libpfm_initialize();
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/* initialize to zero to indicate ABI version */
2398c2ecf20Sopenharmony_ci	info.size  = sizeof(info);
2408c2ecf20Sopenharmony_ci	pinfo.size = sizeof(pinfo);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (!name_only)
2438c2ecf20Sopenharmony_ci		puts("\nList of pre-defined events (to be used in --pfm-events):\n");
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	pfm_for_all_pmus(p) {
2468c2ecf20Sopenharmony_ci		bool printed_pmu = false;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		ret = pfm_get_pmu_info(p, &pinfo);
2498c2ecf20Sopenharmony_ci		if (ret != PFM_SUCCESS)
2508c2ecf20Sopenharmony_ci			continue;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci		/* only print events that are supported by host HW */
2538c2ecf20Sopenharmony_ci		if (!pinfo.is_present)
2548c2ecf20Sopenharmony_ci			continue;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		/* handled by perf directly */
2578c2ecf20Sopenharmony_ci		if (pinfo.pmu == PFM_PMU_PERF_EVENT)
2588c2ecf20Sopenharmony_ci			continue;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		for (i = pinfo.first_event; i != -1;
2618c2ecf20Sopenharmony_ci		     i = pfm_get_event_next(i)) {
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci			ret = pfm_get_event_info(i, PFM_OS_PERF_EVENT_EXT,
2648c2ecf20Sopenharmony_ci						&info);
2658c2ecf20Sopenharmony_ci			if (ret != PFM_SUCCESS)
2668c2ecf20Sopenharmony_ci				continue;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci			if (!name_only && !printed_pmu) {
2698c2ecf20Sopenharmony_ci				printf("%s:\n", pinfo.name);
2708c2ecf20Sopenharmony_ci				printed_pmu = true;
2718c2ecf20Sopenharmony_ci			}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci			if (!name_only)
2748c2ecf20Sopenharmony_ci				print_libpfm_events_detailed(&info, long_desc);
2758c2ecf20Sopenharmony_ci			else
2768c2ecf20Sopenharmony_ci				print_libpfm_events_raw(&pinfo, &info);
2778c2ecf20Sopenharmony_ci		}
2788c2ecf20Sopenharmony_ci		if (!name_only && printed_pmu)
2798c2ecf20Sopenharmony_ci			putchar('\n');
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci}
282