diff --git a/repos/libports/lib/import/import-libpfm4.mk b/repos/libports/lib/import/import-libpfm4.mk
new file mode 100644
index 0000000000..ba6094e1cf
--- /dev/null
+++ b/repos/libports/lib/import/import-libpfm4.mk
@@ -0,0 +1 @@
+INC_DIR += $(call select_from_ports,libpfm4)/include
\ No newline at end of file
diff --git a/repos/libports/lib/mk/libpfm4.mk b/repos/libports/lib/mk/libpfm4.mk
new file mode 100644
index 0000000000..c998bfad52
--- /dev/null
+++ b/repos/libports/lib/mk/libpfm4.mk
@@ -0,0 +1,204 @@
+LIBPFM4_DIR := $(call select_from_ports,libpfm4)/src/lib/libpfm4
+
+CC_OPT += -D_REENTRANT -fvisibility=hidden
+
+SRC_CC = $(LIBPFM4_DIR)/lib/pfmlib_common.c
+
+# build libpfm only for x86_64 for now
+CONFIG_PFMLIB_ARCH_X86_64=y
+CONFIG_PFMLIB_ARCH_X86=y
+
+CONFIG_PFMLIB_SHARED?=n
+CONFIG_PFMLIB_DEBUG?=y
+CONFIG_PFMLIB_NOPYTHON?=y
+
+#
+# list all library support modules
+#
+ifeq ($(CONFIG_PFMLIB_ARCH_IA64),y)
+INCARCH = $(INC_IA64)
+#SRCS += pfmlib_gen_ia64.c pfmlib_itanium.c pfmlib_itanium2.c pfmlib_montecito.c
+CFLAGS += -DCONFIG_PFMLIB_ARCH_IA64
+endif
+
+ifeq ($(CONFIG_PFMLIB_ARCH_X86),y)
+
+ifeq ($(SYS),Linux)
+SRCS += pfmlib_intel_x86_perf_event.c pfmlib_amd64_perf_event.c \
+ pfmlib_intel_netburst_perf_event.c \
+ pfmlib_intel_snbep_unc_perf_event.c
+endif
+
+INCARCH = $(INC_X86)
+SRCS += pfmlib_amd64.c pfmlib_intel_core.c pfmlib_intel_x86.c \
+ pfmlib_intel_x86_arch.c pfmlib_intel_atom.c \
+ pfmlib_intel_nhm_unc.c pfmlib_intel_nhm.c \
+ pfmlib_intel_wsm.c \
+ pfmlib_intel_snb.c pfmlib_intel_snb_unc.c \
+ pfmlib_intel_ivb.c pfmlib_intel_ivb_unc.c \
+ pfmlib_intel_hsw.c \
+ pfmlib_intel_bdw.c \
+ pfmlib_intel_skl.c \
+ pfmlib_intel_icl.c \
+ pfmlib_intel_spr.c \
+ pfmlib_intel_rapl.c \
+ pfmlib_intel_snbep_unc.c \
+ pfmlib_intel_snbep_unc_cbo.c \
+ pfmlib_intel_snbep_unc_ha.c \
+ pfmlib_intel_snbep_unc_imc.c \
+ pfmlib_intel_snbep_unc_pcu.c \
+ pfmlib_intel_snbep_unc_qpi.c \
+ pfmlib_intel_snbep_unc_ubo.c \
+ pfmlib_intel_snbep_unc_r2pcie.c \
+ pfmlib_intel_snbep_unc_r3qpi.c \
+ pfmlib_intel_ivbep_unc_cbo.c \
+ pfmlib_intel_ivbep_unc_ha.c \
+ pfmlib_intel_ivbep_unc_imc.c \
+ pfmlib_intel_ivbep_unc_pcu.c \
+ pfmlib_intel_ivbep_unc_qpi.c \
+ pfmlib_intel_ivbep_unc_ubo.c \
+ pfmlib_intel_ivbep_unc_r2pcie.c \
+ pfmlib_intel_ivbep_unc_r3qpi.c \
+ pfmlib_intel_ivbep_unc_irp.c \
+ pfmlib_intel_hswep_unc_cbo.c \
+ pfmlib_intel_hswep_unc_ha.c \
+ pfmlib_intel_hswep_unc_imc.c \
+ pfmlib_intel_hswep_unc_pcu.c \
+ pfmlib_intel_hswep_unc_qpi.c \
+ pfmlib_intel_hswep_unc_ubo.c \
+ pfmlib_intel_hswep_unc_r2pcie.c \
+ pfmlib_intel_hswep_unc_r3qpi.c \
+ pfmlib_intel_hswep_unc_irp.c \
+ pfmlib_intel_hswep_unc_sbo.c \
+ pfmlib_intel_bdx_unc_cbo.c \
+ pfmlib_intel_bdx_unc_ubo.c \
+ pfmlib_intel_bdx_unc_sbo.c \
+ pfmlib_intel_bdx_unc_ha.c \
+ pfmlib_intel_bdx_unc_imc.c \
+ pfmlib_intel_bdx_unc_irp.c \
+ pfmlib_intel_bdx_unc_pcu.c \
+ pfmlib_intel_bdx_unc_qpi.c \
+ pfmlib_intel_bdx_unc_r2pcie.c \
+ pfmlib_intel_bdx_unc_r3qpi.c \
+ pfmlib_intel_skx_unc_cha.c \
+ pfmlib_intel_skx_unc_iio.c \
+ pfmlib_intel_skx_unc_imc.c \
+ pfmlib_intel_skx_unc_irp.c \
+ pfmlib_intel_skx_unc_m2m.c \
+ pfmlib_intel_skx_unc_m3upi.c \
+ pfmlib_intel_skx_unc_pcu.c \
+ pfmlib_intel_skx_unc_ubo.c \
+ pfmlib_intel_skx_unc_upi.c \
+ pfmlib_intel_knc.c \
+ pfmlib_intel_slm.c \
+ pfmlib_intel_tmt.c \
+ pfmlib_intel_knl.c \
+ pfmlib_intel_knl_unc_imc.c \
+ pfmlib_intel_knl_unc_edc.c \
+ pfmlib_intel_knl_unc_cha.c \
+ pfmlib_intel_knl_unc_m2pcie.c \
+ pfmlib_intel_glm.c \
+ pfmlib_intel_netburst.c \
+ pfmlib_amd64_k7.c pfmlib_amd64_k8.c pfmlib_amd64_fam10h.c \
+ pfmlib_amd64_fam11h.c pfmlib_amd64_fam12h.c \
+ pfmlib_amd64_fam14h.c pfmlib_amd64_fam15h.c \
+ pfmlib_amd64_fam17h.c pfmlib_amd64_fam16h.c \
+ pfmlib_amd64_fam19h.c pfmlib_amd64_rapl.c \
+ pfmlib_amd64_fam19h_l3.c
+
+CFLAGS += -DCONFIG_PFMLIB_ARCH_X86
+
+ifeq ($(CONFIG_PFMLIB_ARCH_I386),y)
+SRCS += pfmlib_intel_coreduo.c pfmlib_intel_p6.c
+CFLAGS += -DCONFIG_PFMLIB_ARCH_I386
+endif
+
+ifeq ($(CONFIG_PFMLIB_ARCH_X86_64),y)
+CFLAGS += -DCONFIG_PFMLIB_ARCH_X86_64
+endif
+
+endif
+
+ifeq ($(CONFIG_PFMLIB_ARCH_POWERPC),y)
+
+ifeq ($(SYS),Linux)
+SRCS += pfmlib_powerpc_perf_event.c
+endif
+
+INCARCH = $(INC_POWERPC)
+SRCS += pfmlib_powerpc.c pfmlib_power4.c pfmlib_ppc970.c pfmlib_power5.c \
+ pfmlib_power6.c pfmlib_power7.c pfmlib_torrent.c pfmlib_power8.c \
+ pfmlib_power9.c pfmlib_powerpc_nest.c pfmlib_power10.c
+CFLAGS += -DCONFIG_PFMLIB_ARCH_POWERPC
+endif
+
+ifeq ($(CONFIG_PFMLIB_ARCH_S390X),y)
+
+ifeq ($(SYS),Linux)
+SRCS += pfmlib_s390x_perf_event.c
+endif
+
+INCARCH = $(INC_S390X)
+SRCS += pfmlib_s390x_cpumf.c
+CFLAGS += -DCONFIG_PFMLIB_ARCH_S390X
+endif
+
+ifeq ($(CONFIG_PFMLIB_ARCH_SPARC),y)
+
+ifeq ($(SYS),Linux)
+SRCS += pfmlib_sparc_perf_event.c
+endif
+
+INCARCH = $(INC_SPARC)
+SRCS += pfmlib_sparc.c pfmlib_sparc_ultra12.c pfmlib_sparc_ultra3.c pfmlib_sparc_ultra4.c pfmlib_sparc_niagara.c
+CFLAGS += -DCONFIG_PFMLIB_ARCH_SPARC
+endif
+
+ifeq ($(CONFIG_PFMLIB_ARCH_ARM),y)
+
+ifeq ($(SYS),Linux)
+SRCS += pfmlib_arm_perf_event.c
+endif
+
+INCARCH = $(INC_ARM)
+SRCS += pfmlib_arm.c pfmlib_arm_armv7_pmuv1.c pfmlib_arm_armv6.c pfmlib_arm_armv8.c pfmlib_tx2_unc_perf_event.c pfmlib_kunpeng_unc_perf_event.c
+CFLAGS += -DCONFIG_PFMLIB_ARCH_ARM
+endif
+
+ifeq ($(CONFIG_PFMLIB_ARCH_ARM64),y)
+
+ifeq ($(SYS),Linux)
+SRCS += pfmlib_arm_perf_event.c
+endif
+
+INCARCH = $(INC_ARM64)
+SRCS += pfmlib_arm.c pfmlib_arm_armv8.c pfmlib_tx2_unc_perf_event.c pfmlib_kunpeng_unc_perf_event.c
+CFLAGS += -DCONFIG_PFMLIB_ARCH_ARM64
+endif
+
+ifeq ($(CONFIG_PFMLIB_ARCH_MIPS),y)
+
+ifeq ($(SYS),Linux)
+SRCS += pfmlib_mips_perf_event.c
+endif
+
+INCARCH = $(INC_MIPS)
+SRCS += pfmlib_mips.c pfmlib_mips_74k.c
+CFLAGS += -DCONFIG_PFMLIB_ARCH_MIPS
+endif
+
+ifeq ($(CONFIG_PFMLIB_CELL),y)
+INCARCH = $(INC_CELL)
+#SRCS += pfmlib_cell.c
+CFLAGS += -DCONFIG_PFMLIB_CELL
+endif
+
+SRC_CC += $(addprefix $(LIBPFM4_DIR)/lib/,$(SRCS))
+vpath %.c $(LIBPFM4_DIR)/lib
+
+CC_OPT += $(CFLAGS)
+
+INC_DIR += $(LIBPFM4_DIR)/include $(LIBPFM4_DIR)/lib/events
+vpath %.h $(INC_DIR)
+
+LIBS += base libm libc
diff --git a/repos/libports/ports/libpfm4.hash b/repos/libports/ports/libpfm4.hash
new file mode 100644
index 0000000000..6eeb6653d7
--- /dev/null
+++ b/repos/libports/ports/libpfm4.hash
@@ -0,0 +1 @@
+b0ec09148c2be9f4a96203a3d2de4ebed6ce2da0
diff --git a/repos/libports/ports/libpfm4.port b/repos/libports/ports/libpfm4.port
new file mode 100644
index 0000000000..f0d7542ca3
--- /dev/null
+++ b/repos/libports/ports/libpfm4.port
@@ -0,0 +1,13 @@
+LICENSE := PD
+DOWNLOADS := libpfm4.git
+VERSION := git
+
+URL(libpfm4) := https://github.com/wcohen/libpfm4.git
+REV(libpfm4) := 8aaaf1747e96031a47ed6bd9337ff61a21f8cc64
+DIR(libpfm4) := src/lib/libpfm4
+
+DIRS += include
+DIRS += include/perfmon
+
+DIR_CONTENT(include) += src/lib/libpfm4/include/perfmon
+DIR_CONTENT(include/perfmon) += src/lib/libpfm4/include/perfmon/*.h
\ No newline at end of file
diff --git a/repos/libports/recipes/src/libpfm4/api b/repos/libports/recipes/src/libpfm4/api
new file mode 100644
index 0000000000..954b4ab6ae
--- /dev/null
+++ b/repos/libports/recipes/src/libpfm4/api
@@ -0,0 +1 @@
+libpfm4
\ No newline at end of file
diff --git a/repos/libports/recipes/src/libpfm4/content.mk b/repos/libports/recipes/src/libpfm4/content.mk
new file mode 100644
index 0000000000..1301d4f0ba
--- /dev/null
+++ b/repos/libports/recipes/src/libpfm4/content.mk
@@ -0,0 +1,17 @@
+MIRROR_FROM_REP_DIR := lib/mk/libpfm4.mk lib/import/import-libpfm4.mk
+
+content: src/lib/libpfm4 COPYING $(MIRROR_FROM_REP_DIR)
+
+PORT_DIR := $(call port_dir,$(REP_DIR)/ports/libpfm4)
+
+src/lib/libpfm4:
+ mkdir -p $@
+ cp -r $(PORT_DIR)/src/lib/libpfm4/* $@
+ rm -rf $@/.git
+ echo "LIBS = libpfm4" > $@/target.mk
+
+$(MIRROR_FROM_REP_DIR):
+ $(mirror_from_rep_dir)
+
+LICENSE:
+ echo "libpfm license, see src/lib/libpfm4/COPYING" > $@
\ No newline at end of file
diff --git a/repos/libports/recipes/src/libpfm4/used_api b/repos/libports/recipes/src/libpfm4/used_api
new file mode 100644
index 0000000000..186e29c4c6
--- /dev/null
+++ b/repos/libports/recipes/src/libpfm4/used_api
@@ -0,0 +1,3 @@
+base
+libm
+libc
\ No newline at end of file
diff --git a/repos/mml/run/libpfm_test.run b/repos/mml/run/libpfm_test.run
new file mode 100644
index 0000000000..12d1aec044
--- /dev/null
+++ b/repos/mml/run/libpfm_test.run
@@ -0,0 +1,68 @@
+set build_components {
+ core init timer app/libpfm_test
+}
+
+source ${genode_dir}/repos/base/run/platform_drv.inc
+append_platform_drv_build_components
+
+build $build_components
+
+create_boot_directory
+
+set config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+append config {
+
+
+
+ 2022-07-20 14:30
+
+
+
+
+
+
+
+
+}
+
+install_config $config
+
+set boot_modules {
+ core init timer vfs.lib.so ld.lib.so posix.lib.so libc.lib.so libm.lib.so stdcxx.lib.so libpfm_test
+}
+
+append_platform_drv_boot_modules
+
+build_boot_image $boot_modules
+append qemu_args "-nographic "
+
+run_genode_until forever
\ No newline at end of file
diff --git a/repos/mml/src/app/libpfm_test/check_events.c b/repos/mml/src/app/libpfm_test/check_events.c
new file mode 100644
index 0000000000..9edaebd7e1
--- /dev/null
+++ b/repos/mml/src/app/libpfm_test/check_events.c
@@ -0,0 +1,174 @@
+/*
+ * check_events.c - show event encoding
+ *
+ * Copyright (c) 2009 Google, Inc
+ * Contributed by Stephane Eranian
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * This file is part of libpfm, a performance monitoring support library for
+ * applications on Linux.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+int pmu_is_present(pfm_pmu_t p)
+{
+ pfm_pmu_info_t pinfo;
+ int ret;
+
+ memset(&pinfo, 0, sizeof(pinfo));
+ ret = pfm_get_pmu_info(p, &pinfo);
+ return ret == PFM_SUCCESS ? pinfo.is_present : 0;
+}
+
+int main(int argc, const char **argv)
+{
+ pfm_pmu_info_t pinfo;
+ pfm_pmu_encode_arg_t e;
+ const char *arg[3];
+ const char **p;
+ char *fqstr;
+ pfm_event_info_t info;
+ int j, ret;
+ pfm_pmu_t i;
+ int total_supported_events = 0;
+ int total_available_events = 0;
+
+ unsigned long low, high, msr;
+ msr = 0xc0010200;
+
+ asm volatile("rdmsr"
+ : "=a"(low), "=d"(high)
+ : "c"(msr)); /*
+ * Initialize pfm library (required before we can use it)
+ */
+ ret = pfm_initialize();
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot initialize library: %s\n", pfm_strerror(ret));
+
+ memset(&pinfo, 0, sizeof(pinfo));
+ memset(&info, 0, sizeof(info));
+
+ printf("Supported PMU models:\n");
+ for (i = PFM_PMU_NONE; i < PFM_PMU_MAX; i++)
+ {
+ ret = pfm_get_pmu_info(i, &pinfo);
+ if (ret != PFM_SUCCESS)
+ continue;
+
+ printf("\t[%d, %s, \"%s\"]\n", i, pinfo.name, pinfo.desc);
+ }
+
+ printf("Detected PMU models:\n");
+ for (i = PFM_PMU_NONE; i < PFM_PMU_MAX; i++)
+ {
+ ret = pfm_get_pmu_info(i, &pinfo);
+ if (ret != PFM_SUCCESS)
+ continue;
+ if (pinfo.is_present)
+ {
+ printf("\t[%d, %s, \"%s\"]\n", i, pinfo.name, pinfo.desc);
+ total_supported_events += pinfo.nevents;
+ }
+ total_available_events += pinfo.nevents;
+ }
+
+ printf("Total events: %d available, %d supported\n", total_available_events, total_supported_events);
+
+ /*
+ * be nice to user!
+ */
+ if (argc < 2 && pmu_is_present(PFM_PMU_PERF_EVENT))
+ {
+ arg[0] = "PERF_COUNT_HW_CPU_CYCLES";
+ arg[1] = "PERF_COUNT_HW_INSTRUCTIONS";
+ arg[2] = NULL;
+ p = arg;
+ }
+ else
+ {
+ p = argv + 1;
+ }
+
+ if (!*p)
+ errx(1, "you must pass at least one event");
+
+ memset(&e, 0, sizeof(e));
+ while (*p)
+ {
+ /*
+ * extract raw event encoding
+ *
+ * For perf_event encoding, use
+ * #include
+ * and the function:
+ * pfm_get_perf_event_encoding()
+ */
+ fqstr = NULL;
+ e.fstr = &fqstr;
+ ret = pfm_get_os_event_encoding(*p, PFM_PLM0 | PFM_PLM3, PFM_OS_NONE, &e);
+ if (ret != PFM_SUCCESS)
+ {
+ /*
+ * codes is too small for this event
+ * free and let the library resize
+ */
+ if (ret == PFM_ERR_TOOSMALL)
+ {
+ free(e.codes);
+ e.codes = NULL;
+ e.count = 0;
+ free(fqstr);
+ continue;
+ }
+ if (ret == PFM_ERR_NOTFOUND && strstr(*p, "::"))
+ errx(1, "%s: try setting LIBPFM_ENCODE_INACTIVE=1", pfm_strerror(ret));
+ errx(1, "cannot encode event %s: %s", *p, pfm_strerror(ret));
+ }
+ ret = pfm_get_event_info(e.idx, PFM_OS_NONE, &info);
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot get event info: %s", pfm_strerror(ret));
+
+ ret = pfm_get_pmu_info(info.pmu, &pinfo);
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot get PMU info: %s", pfm_strerror(ret));
+
+ printf("Requested Event: %s\n", *p);
+ printf("Actual Event: %s\n", fqstr);
+ printf("PMU : %s\n", pinfo.desc);
+ printf("IDX : %d\n", e.idx);
+ printf("Codes :");
+ for (j = 0; j < e.count; j++)
+ printf(" 0x%" PRIx64, e.codes[j]);
+ putchar('\n');
+
+ free(fqstr);
+ p++;
+ }
+ if (e.codes)
+ free(e.codes);
+ return 0;
+}
\ No newline at end of file
diff --git a/repos/mml/src/app/libpfm_test/showevtinfo.c b/repos/mml/src/app/libpfm_test/showevtinfo.c
new file mode 100644
index 0000000000..3c775c8da0
--- /dev/null
+++ b/repos/mml/src/app/libpfm_test/showevtinfo.c
@@ -0,0 +1,1020 @@
+/*
+ * showevtinfo.c - show event information
+ *
+ * Copyright (c) 2010 Google, Inc
+ * Contributed by Stephane Eranian
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * This file is part of libpfm, a performance monitoring support library for
+ * applications on Linux.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#define MAXBUF 1024
+#define COMBO_MAX 18
+
+static struct
+{
+ int compact;
+ int sort;
+ uint8_t encode;
+ uint8_t combo;
+ uint8_t combo_lim;
+ uint8_t name_only;
+ uint8_t desc;
+ char *csv_sep;
+ pfm_event_info_t efilter;
+ pfm_event_attr_info_t ufilter;
+ pfm_os_t os;
+ uint64_t mask;
+} options;
+
+typedef struct
+{
+ uint64_t code;
+ int idx;
+} code_info_t;
+
+static void show_event_info_compact(pfm_event_info_t *info);
+
+static const char *srcs[PFM_ATTR_CTRL_MAX] = {
+ [PFM_ATTR_CTRL_UNKNOWN] = "???",
+ [PFM_ATTR_CTRL_PMU] = "PMU",
+ [PFM_ATTR_CTRL_PERF_EVENT] = "perf_event",
+};
+
+#ifdef PFMLIB_WINDOWS
+int set_env_var(const char *var, const char *value, int ov)
+{
+ size_t len;
+ char *str;
+ int ret;
+
+ len = strlen(var) + 1 + strlen(value) + 1;
+
+ str = malloc(len);
+ if (!str)
+ return PFM_ERR_NOMEM;
+
+ sprintf(str, "%s=%s", var, value);
+
+ ret = putenv(str);
+
+ free(str);
+
+ return ret ? PFM_ERR_INVAL : PFM_SUCCESS;
+}
+#else
+static inline int
+set_env_var(const char *var, const char *value, int ov)
+{
+ return setenv(var, value, ov);
+}
+#endif
+
+static int
+event_has_pname(char *s)
+{
+ char *p;
+ return (p = strchr(s, ':')) && *(p + 1) == ':';
+}
+
+static int
+print_codes(char *buf, int plm, int max_encoding)
+{
+ uint64_t *codes = NULL;
+ int j, ret, count = 0;
+
+ ret = pfm_get_event_encoding(buf, PFM_PLM0 | PFM_PLM3, NULL, NULL, &codes, &count);
+ if (ret != PFM_SUCCESS)
+ {
+ if (ret == PFM_ERR_NOTFOUND)
+ errx(1, "encoding failed, try setting env variable LIBPFM_ENCODE_INACTIVE=1");
+ return -1;
+ }
+ for (j = 0; j < max_encoding; j++)
+ {
+ if (j < count)
+ printf("0x%" PRIx64, codes[j]);
+ printf("%s", options.csv_sep);
+ }
+ free(codes);
+ return 0;
+}
+
+static int
+check_valid(char *buf, int plm)
+{
+ uint64_t *codes = NULL;
+ int ret, count = 0;
+
+ ret = pfm_get_event_encoding(buf, PFM_PLM0 | PFM_PLM3, NULL, NULL, &codes, &count);
+ if (ret != PFM_SUCCESS)
+ return -1;
+ free(codes);
+ return 0;
+}
+
+static int
+match_ufilters(pfm_event_attr_info_t *info)
+{
+ uint32_t ufilter1 = 0;
+ uint32_t ufilter2 = 0;
+
+ if (options.ufilter.is_dfl)
+ ufilter1 |= 0x1;
+
+ if (info->is_dfl)
+ ufilter2 |= 0x1;
+
+ if (options.ufilter.is_precise)
+ ufilter1 |= 0x2;
+
+ if (info->is_precise)
+ ufilter2 |= 0x2;
+
+ if (!ufilter1)
+ return 1;
+
+ /* at least one filter matches */
+ return ufilter1 & ufilter2;
+}
+
+static int
+match_efilters(pfm_event_info_t *info)
+{
+ pfm_event_attr_info_t ainfo;
+ int n = 0;
+ int i, ret;
+
+ if (options.efilter.is_precise && !info->is_precise)
+ return 0;
+
+ memset(&ainfo, 0, sizeof(ainfo));
+ ainfo.size = sizeof(ainfo);
+
+ pfm_for_each_event_attr(i, info)
+ {
+ ret = pfm_get_event_attr_info(info->idx, i, options.os, &ainfo);
+ if (ret != PFM_SUCCESS)
+ continue;
+ if (match_ufilters(&ainfo))
+ return 1;
+ if (ainfo.type == PFM_ATTR_UMASK)
+ n++;
+ }
+ return n ? 0 : 1;
+}
+
+static void
+show_event_info_combo(pfm_event_info_t *info)
+{
+ pfm_event_attr_info_t *ainfo;
+ pfm_pmu_info_t pinfo;
+ char buf[MAXBUF];
+ size_t len;
+ int numasks = 0;
+ int i, j, ret;
+ uint64_t total, m, u;
+
+ memset(&pinfo, 0, sizeof(pinfo));
+
+ pinfo.size = sizeof(pinfo);
+
+ ret = pfm_get_pmu_info(info->pmu, &pinfo);
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot get PMU info");
+
+ ainfo = calloc(info->nattrs, sizeof(*ainfo));
+ if (!ainfo)
+ err(1, "event %s : ", info->name);
+
+ /*
+ * extract attribute information and count number
+ * of umasks
+ *
+ * we cannot just drop non umasks because we need
+ * to keep attributes in order for the enumeration
+ * of 2^n
+ */
+ pfm_for_each_event_attr(i, info)
+ {
+ ainfo[i].size = sizeof(*ainfo);
+
+ ret = pfm_get_event_attr_info(info->idx, i, options.os, &ainfo[i]);
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot get attribute info: %s", pfm_strerror(ret));
+
+ if (ainfo[i].type == PFM_ATTR_UMASK)
+ numasks++;
+ }
+ if (numasks > options.combo_lim)
+ {
+ warnx("event %s has too many umasks to print all combinations, dropping to simple enumeration", info->name);
+ free(ainfo);
+ show_event_info_compact(info);
+ return;
+ }
+
+ if (numasks)
+ {
+ if (info->nattrs > (int)((sizeof(total) << 3)))
+ {
+ warnx("too many umasks, cannot show all combinations for event %s", info->name);
+ goto end;
+ }
+ total = 1ULL << info->nattrs;
+
+ for (u = 1; u < total; u++)
+ {
+ len = sizeof(buf);
+ len -= snprintf(buf, len, "%s::%s", pinfo.name, info->name);
+ if (len <= 0)
+ {
+ warnx("event name too long%s", info->name);
+ goto end;
+ }
+ for (m = u, j = 0; m; m >>= 1, j++)
+ {
+ if (m & 0x1ULL)
+ {
+ /* we have hit a non umasks attribute, skip */
+ if (ainfo[j].type != PFM_ATTR_UMASK)
+ break;
+
+ if (len < (1 + strlen(ainfo[j].name)))
+ {
+ warnx("umasks combination too long for event %s", buf);
+ break;
+ }
+ strncat(buf, ":", len - 1);
+ buf[len - 1] = '\0';
+ len--;
+ strncat(buf, ainfo[j].name, len - 1);
+ buf[len - 1] = '\0';
+ len -= strlen(ainfo[j].name);
+ }
+ }
+ /* if found a valid umask combination, check encoding */
+ if (m == 0)
+ {
+ if (options.encode)
+ ret = print_codes(buf, PFM_PLM0 | PFM_PLM3, pinfo.max_encoding);
+ else
+ ret = check_valid(buf, PFM_PLM0 | PFM_PLM3);
+ if (!ret)
+ printf("%s\n", buf);
+ }
+ }
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf) - 1, "%s::%s", pinfo.name, info->name);
+ buf[sizeof(buf) - 1] = '\0';
+
+ ret = options.encode ? print_codes(buf, PFM_PLM0 | PFM_PLM3, pinfo.max_encoding) : 0;
+ if (!ret)
+ printf("%s\n", buf);
+ }
+end:
+ free(ainfo);
+}
+
+static void
+show_event_info_compact(pfm_event_info_t *info)
+{
+ pfm_event_attr_info_t ainfo;
+ pfm_pmu_info_t pinfo;
+ char buf[MAXBUF];
+ int i, ret, um = 0;
+
+ memset(&ainfo, 0, sizeof(ainfo));
+ memset(&pinfo, 0, sizeof(pinfo));
+
+ pinfo.size = sizeof(pinfo);
+ ainfo.size = sizeof(ainfo);
+
+ ret = pfm_get_pmu_info(info->pmu, &pinfo);
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot get pmu info: %s", pfm_strerror(ret));
+
+ if (options.name_only)
+ {
+ if (options.encode)
+ printf("0x%-10" PRIx64, info->code);
+ printf("%s\n", info->name);
+ return;
+ }
+ pfm_for_each_event_attr(i, info)
+ {
+ ret = pfm_get_event_attr_info(info->idx, i, options.os, &ainfo);
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot get attribute info: %s", pfm_strerror(ret));
+
+ if (ainfo.type != PFM_ATTR_UMASK)
+ continue;
+
+ if (!match_ufilters(&ainfo))
+ continue;
+
+ snprintf(buf, sizeof(buf) - 1, "%s::%s:%s", pinfo.name, info->name, ainfo.name);
+ buf[sizeof(buf) - 1] = '\0';
+
+ ret = 0;
+ if (options.encode)
+ {
+ ret = print_codes(buf, PFM_PLM0 | PFM_PLM3, pinfo.max_encoding);
+ }
+ if (!ret)
+ {
+ printf("%s", buf);
+ if (options.desc)
+ {
+ printf("%s", options.csv_sep);
+ printf("\"%s. %s.\"", info->desc, ainfo.desc);
+ }
+ putchar('\n');
+ }
+ um++;
+ }
+ if (um == 0)
+ {
+ if (!match_efilters(info))
+ return;
+
+ snprintf(buf, sizeof(buf) - 1, "%s::%s", pinfo.name, info->name);
+ buf[sizeof(buf) - 1] = '\0';
+ if (options.encode)
+ {
+ ret = print_codes(buf, PFM_PLM0 | PFM_PLM3, pinfo.max_encoding);
+ if (ret)
+ return;
+ }
+ printf("%s", buf);
+ if (options.desc)
+ {
+ printf("%s", options.csv_sep);
+ printf("\"%s.\"", info->desc);
+ }
+ putchar('\n');
+ }
+}
+
+int compare_codes(const void *a, const void *b)
+{
+ const code_info_t *aa = a;
+ const code_info_t *bb = b;
+ uint64_t m = options.mask;
+
+ if ((aa->code & m) < (bb->code & m))
+ return -1;
+ if ((aa->code & m) == (bb->code & m))
+ return 0;
+ return 1;
+}
+
+static void
+print_event_flags(pfm_event_info_t *info)
+{
+ int n = 0;
+ int spec = info->is_speculative;
+
+ if (info->is_precise)
+ {
+ printf("[precise] ");
+ n++;
+ }
+
+ if (info->support_hw_smpl)
+ {
+ printf("[hw_smpl] ");
+ n++;
+ }
+
+ if (spec > PFM_EVENT_INFO_SPEC_NA)
+ {
+ printf("[%s] ", spec == PFM_EVENT_INFO_SPEC_TRUE ? "speculative" : "non-speculative");
+ n++;
+ }
+
+ if (!n)
+ printf("None");
+}
+
+static void
+print_attr_flags(pfm_event_attr_info_t *info)
+{
+ int n = 0;
+ int spec = info->is_speculative;
+
+ if (info->is_dfl)
+ {
+ printf("[default] ");
+ n++;
+ }
+
+ if (info->is_precise)
+ {
+ printf("[precise] ");
+ n++;
+ }
+
+ if (info->support_hw_smpl)
+ {
+ printf("[hw_smpl] ");
+ n++;
+ }
+
+ if (spec > PFM_EVENT_INFO_SPEC_NA)
+ {
+ printf("[%s] ", spec == PFM_EVENT_INFO_SPEC_TRUE ? "speculative" : "non-speculative");
+ n++;
+ }
+
+ if (!n)
+ printf("None ");
+}
+
+static void
+show_event_info(pfm_event_info_t *info)
+{
+ pfm_event_attr_info_t ainfo;
+ pfm_pmu_info_t pinfo;
+ int mod = 0, um = 0;
+ int i, ret;
+ const char *src;
+
+ if (options.name_only)
+ {
+ printf("%s\n", info->name);
+ return;
+ }
+
+ memset(&ainfo, 0, sizeof(ainfo));
+ memset(&pinfo, 0, sizeof(pinfo));
+
+ pinfo.size = sizeof(pinfo);
+ ainfo.size = sizeof(ainfo);
+
+ if (!match_efilters(info))
+ return;
+ ret = pfm_get_pmu_info(info->pmu, &pinfo);
+ if (ret)
+ errx(1, "cannot get pmu info: %s", pfm_strerror(ret));
+
+ printf("#-----------------------------\n"
+ "IDX : %d\n"
+ "PMU name : %s (%s)\n"
+ "Name : %s\n"
+ "Equiv : %s\n",
+ info->idx,
+ pinfo.name,
+ pinfo.desc,
+ info->name,
+ info->equiv ? info->equiv : "None");
+
+ printf("Flags : ");
+ print_event_flags(info);
+ putchar('\n');
+
+ printf("Desc : %s\n", info->desc ? info->desc : "no description available");
+ printf("Code : 0x%" PRIx64 "\n", info->code);
+
+ pfm_for_each_event_attr(i, info)
+ {
+ ret = pfm_get_event_attr_info(info->idx, i, options.os, &ainfo);
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot retrieve event %s attribute info: %s", info->name, pfm_strerror(ret));
+
+ if (ainfo.ctrl >= PFM_ATTR_CTRL_MAX)
+ {
+ warnx("event: %s has unsupported attribute source %d", info->name, ainfo.ctrl);
+ ainfo.ctrl = PFM_ATTR_CTRL_UNKNOWN;
+ }
+ src = srcs[ainfo.ctrl];
+ switch (ainfo.type)
+ {
+ case PFM_ATTR_UMASK:
+ if (!match_ufilters(&ainfo))
+ continue;
+
+ printf("Umask-%02u : 0x%02" PRIx64 " : %s : [%s] : ",
+ um,
+ ainfo.code,
+ src,
+ ainfo.name);
+
+ print_attr_flags(&ainfo);
+
+ putchar(':');
+
+ if (ainfo.equiv)
+ printf(" Alias to %s", ainfo.equiv);
+ else
+ printf(" %s", ainfo.desc);
+
+ putchar('\n');
+ um++;
+ break;
+ case PFM_ATTR_MOD_BOOL:
+ printf("Modif-%02u : 0x%02" PRIx64 " : %s : [%s] : %s (boolean)\n", mod, ainfo.code, src, ainfo.name, ainfo.desc);
+ mod++;
+ break;
+ case PFM_ATTR_MOD_INTEGER:
+ printf("Modif-%02u : 0x%02" PRIx64 " : %s : [%s] : %s (integer)\n", mod, ainfo.code, src, ainfo.name, ainfo.desc);
+ mod++;
+ break;
+ default:
+ printf("Attr-%02u : 0x%02" PRIx64 " : %s : [%s] : %s\n", i, ainfo.code, ainfo.name, src, ainfo.desc);
+ }
+ }
+}
+
+static int
+show_info(char *event, regex_t *preg)
+{
+ pfm_pmu_info_t pinfo;
+ pfm_event_info_t info;
+ pfm_pmu_t j;
+ int i, ret, match = 0, pname;
+ size_t len, l = 0;
+ char *fullname = NULL;
+
+ memset(&pinfo, 0, sizeof(pinfo));
+ memset(&info, 0, sizeof(info));
+
+ pinfo.size = sizeof(pinfo);
+ info.size = sizeof(info);
+
+ pname = event_has_pname(event);
+
+ /*
+ * scan all supported events, incl. those
+ * from undetected PMU models
+ */
+ pfm_for_all_pmus(j)
+ {
+
+ ret = pfm_get_pmu_info(j, &pinfo);
+ if (ret != PFM_SUCCESS)
+ continue;
+
+ /* no pmu prefix, just look for detected PMU models */
+ if (!pname && !pinfo.is_present)
+ continue;
+
+ for (i = pinfo.first_event; i != -1; i = pfm_get_event_next(i))
+ {
+ ret = pfm_get_event_info(i, options.os, &info);
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot get event info: %s", pfm_strerror(ret));
+
+ len = strlen(info.name) + strlen(pinfo.name) + 1 + 2;
+ if (len > l)
+ {
+ l = len;
+ fullname = realloc(fullname, l);
+ if (!fullname)
+ err(1, "cannot allocate memory");
+ }
+ sprintf(fullname, "%s::%s", pinfo.name, info.name);
+
+ if (regexec(preg, fullname, 0, NULL, 0) == 0)
+ {
+ if (options.compact)
+ if (options.combo)
+ show_event_info_combo(&info);
+ else
+ show_event_info_compact(&info);
+ else
+ show_event_info(&info);
+ match++;
+ }
+ }
+ }
+ if (fullname)
+ free(fullname);
+
+ return match;
+}
+
+static int
+show_info_sorted(char *event, regex_t *preg)
+{
+ pfm_pmu_info_t pinfo;
+ pfm_event_info_t info;
+ pfm_pmu_t j;
+ int i, ret, n, match = 0;
+ size_t len, l = 0;
+ char *fullname = NULL;
+ code_info_t *codes;
+
+ memset(&pinfo, 0, sizeof(pinfo));
+ memset(&info, 0, sizeof(info));
+
+ pinfo.size = sizeof(pinfo);
+ info.size = sizeof(info);
+
+ pfm_for_all_pmus(j)
+ {
+
+ ret = pfm_get_pmu_info(j, &pinfo);
+ if (ret != PFM_SUCCESS)
+ continue;
+
+ codes = malloc(pinfo.nevents * sizeof(*codes));
+ if (!codes)
+ err(1, "cannot allocate memory\n");
+
+ /* scans all supported events */
+ n = 0;
+ for (i = pinfo.first_event; i != -1; i = pfm_get_event_next(i))
+ {
+
+ ret = pfm_get_event_info(i, options.os, &info);
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot get event info: %s", pfm_strerror(ret));
+
+ if (info.pmu != j)
+ continue;
+
+ codes[n].idx = info.idx;
+ codes[n].code = info.code;
+ n++;
+ }
+ qsort(codes, n, sizeof(*codes), compare_codes);
+ for (i = 0; i < n; i++)
+ {
+ ret = pfm_get_event_info(codes[i].idx, options.os, &info);
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot get event info: %s", pfm_strerror(ret));
+
+ len = strlen(info.name) + strlen(pinfo.name) + 1 + 2;
+ if (len > l)
+ {
+ l = len;
+ fullname = realloc(fullname, l);
+ if (!fullname)
+ err(1, "cannot allocate memory");
+ }
+ sprintf(fullname, "%s::%s", pinfo.name, info.name);
+
+ if (regexec(preg, fullname, 0, NULL, 0) == 0)
+ {
+ if (options.compact)
+ show_event_info_compact(&info);
+ else
+ show_event_info(&info);
+ match++;
+ }
+ }
+ free(codes);
+ }
+ if (fullname)
+ free(fullname);
+
+ return match;
+}
+
+static void
+usage(void)
+{
+ printf("showevtinfo [-L] [-E] [-h] [-s] [-m mask]\n"
+ "-L\t\tlist one event per line (compact mode)\n"
+ "-E\t\tlist one event per line with encoding (compact mode)\n"
+ "-M\t\tdisplay all valid unit masks combination (use with -L or -E)\n"
+ "-h\t\tget help\n"
+ "-s\t\tsort event by PMU and by code based on -m mask\n"
+ "-l\t\tmaximum number of umasks to list all combinations (default: %d)\n"
+ "-F\t\tshow only events and attributes with certain flags (precise,...)\n"
+ "-m mask\t\thexadecimal event code mask, bits to match when sorting\n"
+ "-x sep\t\tuse sep as field separator in compact mode\n"
+ "-D\t\t\tprint event description in compact mode\n"
+ "-O os\t\tshow attributes for the specific operating system\n",
+ COMBO_MAX);
+}
+
+/*
+ * keep: [pmu::]event
+ * drop everything else
+ */
+static void
+drop_event_attributes(char *str)
+{
+ char *p;
+
+ p = strchr(str, ':');
+ if (!p)
+ return;
+
+ str = p + 1;
+ /* keep PMU name */
+ if (*str == ':')
+ str++;
+
+ /* stop string at 1st attribute */
+ p = strchr(str, ':');
+ if (p)
+ *p = '\0';
+}
+
+#define EVENT_FLAGS(n, f, l) \
+ { \
+ .name = n, .ebit = f, .ubit = l \
+ }
+struct attr_flags
+{
+ const char *name;
+ int ebit; /* bit position in pfm_event_info_t.flags, -1 means ignore */
+ int ubit; /* bit position in pfm_event_attr_info_t.flags, -1 means ignore */
+};
+
+static const struct attr_flags event_flags[] = {
+ EVENT_FLAGS("precise", 0, 1),
+ EVENT_FLAGS("pebs", 0, 1),
+ EVENT_FLAGS("default", -1, 0),
+ EVENT_FLAGS("dfl", -1, 0),
+ EVENT_FLAGS(NULL, 0, 0)};
+
+static void
+parse_filters(char *arg)
+{
+ const struct attr_flags *attr;
+ char *p;
+
+ while (arg)
+ {
+ p = strchr(arg, ',');
+ if (p)
+ *p++ = 0;
+
+ for (attr = event_flags; attr->name; attr++)
+ {
+ if (!strcasecmp(attr->name, arg))
+ {
+ switch (attr->ebit)
+ {
+ case 0:
+ options.efilter.is_precise = 1;
+ break;
+ case -1:
+ break;
+ default:
+ errx(1, "unknown event flag %d", attr->ebit);
+ }
+ switch (attr->ubit)
+ {
+ case 0:
+ options.ufilter.is_dfl = 1;
+ break;
+ case 1:
+ options.ufilter.is_precise = 1;
+ break;
+ case -1:
+ break;
+ default:
+ errx(1, "unknown umaks flag %d", attr->ubit);
+ }
+ break;
+ }
+ }
+ arg = p;
+ }
+}
+
+static const struct
+{
+ char *name;
+ pfm_os_t os;
+} supported_oses[] = {
+ {.name = "none", .os = PFM_OS_NONE},
+ {.name = "raw", .os = PFM_OS_NONE},
+ {.name = "pmu", .os = PFM_OS_NONE},
+
+ {.name = "perf", .os = PFM_OS_PERF_EVENT},
+ {.name = "perf_ext", .os = PFM_OS_PERF_EVENT_EXT},
+ {
+ .name = NULL,
+ }};
+
+static const char *pmu_types[] = {
+ "unknown type",
+ "core",
+ "uncore",
+ "OS generic",
+};
+
+static void
+setup_os(char *ostr)
+{
+ int i;
+
+ for (i = 0; supported_oses[i].name; i++)
+ {
+ if (!strcmp(supported_oses[i].name, ostr))
+ {
+ options.os = supported_oses[i].os;
+ return;
+ }
+ }
+ fprintf(stderr, "unknown OS layer %s, choose from:", ostr);
+ for (i = 0; supported_oses[i].name; i++)
+ {
+ if (i)
+ fputc(',', stderr);
+ fprintf(stderr, " %s", supported_oses[i].name);
+ }
+ fputc('\n', stderr);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ static char *argv_all[2] = {".*", NULL};
+ pfm_pmu_info_t pinfo;
+ char *endptr = NULL;
+ char default_sep[2] = "\t";
+ char *ostr = NULL;
+ char **args;
+ pfm_pmu_t i;
+ int match;
+ regex_t preg;
+ int ret, c;
+
+ memset(&pinfo, 0, sizeof(pinfo));
+
+ pinfo.size = sizeof(pinfo);
+
+ while ((c = getopt(argc, argv, "hELsm:MNl:F:x:DO:")) != -1)
+ {
+ switch (c)
+ {
+ case 'L':
+ options.compact = 1;
+ break;
+ case 'F':
+ parse_filters(optarg);
+ break;
+ case 'E':
+ options.compact = 1;
+ options.encode = 1;
+ break;
+ case 'M':
+ options.combo = 1;
+ break;
+ case 'N':
+ options.name_only = 1;
+ break;
+ case 's':
+ options.sort = 1;
+ break;
+ case 'D':
+ options.desc = 1;
+ break;
+ case 'l':
+ options.combo_lim = atoi(optarg);
+ break;
+ case 'x':
+ options.csv_sep = optarg;
+ break;
+ case 'O':
+ ostr = optarg;
+ break;
+ case 'm':
+ options.mask = strtoull(optarg, &endptr, 16);
+ if (*endptr)
+ errx(1, "mask must be in hexadecimal\n");
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ errx(1, "unknown option error");
+ }
+ }
+ /* to allow encoding of events from non detected PMU models */
+ ret = set_env_var("LIBPFM_ENCODE_INACTIVE", "1", 1);
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot force inactive encoding");
+
+ ret = pfm_initialize();
+ if (ret != PFM_SUCCESS)
+ errx(1, "cannot initialize libpfm: %s", pfm_strerror(ret));
+
+ if (options.mask == 0)
+ options.mask = ~0;
+
+ if (optind == argc)
+ {
+ args = argv_all;
+ }
+ else
+ {
+ args = argv + optind;
+ }
+ if (!options.csv_sep)
+ options.csv_sep = default_sep;
+
+ /* avoid combinatorial explosion */
+ if (options.combo_lim == 0)
+ options.combo_lim = COMBO_MAX;
+
+ if (ostr)
+ setup_os(ostr);
+ else
+ options.os = PFM_OS_NONE;
+
+ if (!options.compact)
+ {
+ int total_supported_events = 0;
+ int total_available_events = 0;
+
+ printf("Supported PMU models:\n");
+ pfm_for_all_pmus(i)
+ {
+ ret = pfm_get_pmu_info(i, &pinfo);
+ if (ret != PFM_SUCCESS)
+ continue;
+
+ printf("\t[%d, %s, \"%s\"]\n", i, pinfo.name, pinfo.desc);
+ }
+
+ printf("Detected PMU models:\n");
+ pfm_for_all_pmus(i)
+ {
+ ret = pfm_get_pmu_info(i, &pinfo);
+ if (ret != PFM_SUCCESS)
+ continue;
+
+ if (pinfo.is_present)
+ {
+ if (pinfo.type >= PFM_PMU_TYPE_MAX)
+ pinfo.type = PFM_PMU_TYPE_UNKNOWN;
+
+ printf("\t[%d, %s, \"%s\", %d events, %d max encoding, %d counters, %s PMU]\n",
+ i,
+ pinfo.name,
+ pinfo.desc,
+ pinfo.nevents,
+ pinfo.max_encoding,
+ pinfo.num_cntrs + pinfo.num_fixed_cntrs,
+ pmu_types[pinfo.type]);
+
+ total_supported_events += pinfo.nevents;
+ }
+ total_available_events += pinfo.nevents;
+ }
+ printf("Total events: %d available, %d supported\n", total_available_events, total_supported_events);
+ }
+
+ while (*args)
+ {
+ /* drop umasks and modifiers */
+ drop_event_attributes(*args);
+ if (regcomp(&preg, *args, REG_ICASE))
+ errx(1, "error in regular expression for event \"%s\"", *argv);
+
+ if (options.sort)
+ match = show_info_sorted(*args, &preg);
+ else
+ match = show_info(*args, &preg);
+
+ if (match == 0)
+ errx(1, "event %s not found", *args);
+
+ args++;
+ }
+
+ regfree(&preg);
+
+ pfm_terminate();
+
+ return 0;
+}
diff --git a/repos/mml/src/app/libpfm_test/target.mk b/repos/mml/src/app/libpfm_test/target.mk
new file mode 100644
index 0000000000..5134b51d97
--- /dev/null
+++ b/repos/mml/src/app/libpfm_test/target.mk
@@ -0,0 +1,5 @@
+TARGET = libpfm_test
+SRC_CC = check_events.c
+LIBS += base posix libm libc stdcxx libpfm4
+CC_OPT += -Wno-error -Wno-permissive -fpermissive
+
diff --git a/repos/mml/src/app/thread_test/target.mk b/repos/mml/src/app/thread_test/target.mk
index 79ffb18ea9..55f1259a48 100644
--- a/repos/mml/src/app/thread_test/target.mk
+++ b/repos/mml/src/app/thread_test/target.mk
@@ -1,4 +1,4 @@
TARGET = thread_test
SRC_CC = thread_test.cc
-LIBS += base stdcxx
+LIBS += base libc stdcxx
CXXFLAGS += -Wno-error