summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 57b9e83)
raw | patch | inline | side by side (parent: 57b9e83)
author | Pshyk, SerhiyX <serhiyx.pshyk@intel.com> | |
Wed, 10 May 2017 12:29:05 +0000 (13:29 +0100) | ||
committer | Pshyk, SerhiyX <serhiyx.pshyk@intel.com> | |
Fri, 12 May 2017 13:40:59 +0000 (14:40 +0100) |
The intel_pmu plugin collects information about system performance
counters using kernel Linux perf interface.
Signed-off-by: Serhiy Pshyk <serhiyx.pshyk@intel.com>
counters using kernel Linux perf interface.
Signed-off-by: Serhiy Pshyk <serhiyx.pshyk@intel.com>
Makefile.am | patch | blob | history | |
README | patch | blob | history | |
configure.ac | patch | blob | history | |
contrib/systemd.collectd.service | patch | blob | history | |
src/collectd.conf.in | patch | blob | history | |
src/collectd.conf.pod | patch | blob | history | |
src/intel_pmu.c | [new file with mode: 0644] | patch | blob |
diff --git a/Makefile.am b/Makefile.am
index 1f18d231c213f41d810fc1fcb2e589d35471486e..57641dd41d3e8a5fb237643e22ceee3a9030a037 100644 (file)
--- a/Makefile.am
+++ b/Makefile.am
hugepages_la_LDFLAGS = $(PLUGIN_LDFLAGS)
endif
+if BUILD_PLUGIN_INTEL_PMU
+pkglib_LTLIBRARIES += intel_pmu.la
+intel_pmu_la_SOURCES = src/intel_pmu.c
+intel_pmu_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBJEVENTS_CPPFLAGS)
+intel_pmu_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBJEVENTS_LDFLAGS)
+intel_pmu_la_LIBADD = $(BUILD_WITH_LIBJEVENTS_LIBS)
+endif
+
if BUILD_PLUGIN_INTEL_RDT
pkglib_LTLIBRARIES += intel_rdt.la
intel_rdt_la_SOURCES = src/intel_rdt.c
index 1860281632a786a269fd209d38e2f4417e06bebc..f71dbe76f546cd43651c83c29c7715066238e557 100644 (file)
--- a/README
+++ b/README
hugepages can be found here:
https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt.
+ - intel_pmu
+ The intel_pmu plugin reads performance counters provided by the Linux
+ kernel perf interface. The plugin uses jevents library to resolve named
+ events to perf events and access perf interface.
+
- intel_rdt
The intel_rdt plugin collects information provided by monitoring features
of Intel Resource Director Technology (Intel(R) RDT) like Cache Monitoring
- netapp
Plugin to query performance values from a NetApp storage system using the
- “Manage ONTAP” SDK provided by NetApp.
+ “Manage ONTAP” SDK provided by NetApp.
- netlink
Very detailed Linux network interface and routing statistics. You can get
For querying iptables counters.
<http://netfilter.org/>
+ * libjevents (optional)
+ The jevents library is used by the `intel_pmu' plugin to access the Linux
+ kernel perf interface.
+ Note: the library should be build with -fPIC flag to be linked with
+ intel_pmu shared object correctly.
+ <https://github.com/andikleen/pmu-tools>
+
* libjvm (optional)
Library that encapsulates the `Java Virtual Machine' (JVM). This library is
used by the `java' plugin to execute Java bytecode.
diff --git a/configure.ac b/configure.ac
index 3962fc999a47797f1eb21b282d0e7a630a715b54..af3bcfea4ec313fcb311e61fabd8fa6f1e5b891f 100644 (file)
--- a/configure.ac
+++ b/configure.ac
fi
# }}}
+# --with-libjevents {{{
+with_libjevents_cppflags=""
+with_libjevents_ldflags=""
+AC_ARG_WITH(libjevents, [AS_HELP_STRING([--with-libjevents@<:@=PREFIX@:>@], [Path to libjevents.])],
+[
+ if test "x$withval" != "xno" && test "x$withval" != "xyes"
+ then
+ with_libjevents_cppflags="-I$withval/include"
+ with_libjevents_ldflags="-L$withval/lib"
+ with_libjevents="yes"
+ else
+ with_libjevents="$withval"
+ fi
+],
+[
+ with_libjevents="yes"
+])
+if test "x$with_libjevents" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libjevents_cppflags"
+
+ AC_CHECK_HEADERS(jevents.h, [with_libjevents="yes"], [with_libjevents="no (jevents.h not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libjevents" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ SAVE_LDFLAGS="$LDFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libjevents_cppflags"
+ LDFLAGS="$LDFLAGS $with_libjevents_ldflags"
+
+ AC_CHECK_LIB(jevents, json_events, [with_libjevents="yes"], [with_libjevents="no (Can't find libjevents)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libjevents" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ SAVE_LDFLAGS="$LDFLAGS"
+ SAVE_LIBS="$LIBS"
+ CPPFLAGS="$CPPFLAGS -fPIC"
+ LDFLAGS="$LDFLAGS -shared"
+ LIBS="-ljevents"
+ AC_LINK_IFELSE([AC_LANG_SOURCE(
+ [[
+ #include <stdio.h>
+ #include "jevents.h"
+ void print_cpu(void){
+ printf("%s", get_cpu_str());
+ }
+ ]]
+ )],
+ [with_libjevents="yes"], [with_libjevents="no (could not link to libjevents. Check jevents is compiled with -fPIC.)"])
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
+ LIBS="$SAVE_LIBS"
+fi
+if test "x$with_libjevents" = "xyes"
+then
+ BUILD_WITH_LIBJEVENTS_CPPFLAGS="$with_libjevents_cppflags"
+ BUILD_WITH_LIBJEVENTS_LDFLAGS="$with_libjevents_ldflags"
+ BUILD_WITH_LIBJEVENTS_LIBS="-ljevents"
+ AC_SUBST(BUILD_WITH_LIBJEVENTS_CPPFLAGS)
+ AC_SUBST(BUILD_WITH_LIBJEVENTS_LDFLAGS)
+ AC_SUBST(BUILD_WITH_LIBJEVENTS_LIBS)
+fi
+# }}}
+
# --with-libprotobuf {{{
with_libprotobuf_cppflags=""
with_libprotobuf_ldflags=""
plugin_gps="no"
plugin_grpc="no"
plugin_hugepages="no"
+plugin_intel_pmu="no"
plugin_intel_rdt="no"
plugin_interface="no"
plugin_ipc="no"
AC_PLUGIN([grpc], [$plugin_grpc], [gRPC plugin])
AC_PLUGIN([hddtemp], [yes], [Query hddtempd])
AC_PLUGIN([hugepages], [$plugin_hugepages], [Hugepages statistics])
+AC_PLUGIN([intel_pmu], [$with_libjevents], [Intel performance monitor plugin])
AC_PLUGIN([intel_rdt], [$with_libpqos], [Intel RDT monitor plugin])
AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics])
AC_PLUGIN([ipc], [$plugin_ipc], [IPC statistics])
AC_MSG_RESULT([ libi2c-dev . . . . . $with_libi2c])
AC_MSG_RESULT([ libiokit . . . . . . $with_libiokit])
AC_MSG_RESULT([ libiptc . . . . . . . $with_libiptc])
+AC_MSG_RESULT([ libjevents . . . . . $with_libjevents])
AC_MSG_RESULT([ libjvm . . . . . . . $with_java])
AC_MSG_RESULT([ libkstat . . . . . . $with_kstat])
AC_MSG_RESULT([ libkvm . . . . . . . $with_libkvm])
AC_MSG_RESULT([ grpc . . . . . . . . $enable_grpc])
AC_MSG_RESULT([ hddtemp . . . . . . . $enable_hddtemp])
AC_MSG_RESULT([ hugepages . . . . . . $enable_hugepages])
+AC_MSG_RESULT([ intel_pmu . . . . . . $enable_intel_pmu])
AC_MSG_RESULT([ intel_rdt . . . . . . $enable_intel_rdt])
AC_MSG_RESULT([ interface . . . . . . $enable_interface])
AC_MSG_RESULT([ ipc . . . . . . . . . $enable_ipc])
index 853363d7b63cd9492759581c124ee3898c331b22..efd922a6bf01b00d57081349efef4f412605a7c9 100644 (file)
ProtectSystem=full
ProtectHome=true
+# Some plugins require access to data located in a specified folder, to access
+# this data you'll have to specify the path by using the Environment directive
+# below.
+#
+# Here's a (incomplete) list of the plugins known environment requirements:
+# intel_pmu XDG_CACHE_HOME
+#
+# Example, if you use the intel_pmu plugin and cache is located in the /opt
+# directory:
+# Environment=XDG_CACHE_HOME=/opt
+#
+# By default, the HOME directory is chosen:
+Environment=
+
# A few plugins won't work without some privileges, which you'll have to
# specify using the CapabilityBoundingSet directive below.
#
# dns CAP_NET_RAW
# exec CAP_SETUID CAP_SETGID
# intel_rdt CAP_SYS_RAWIO
+# intel_pmu CAP_SYS_ADMIN
# iptables CAP_NET_ADMIN
# ping CAP_NET_RAW
# smart CAP_SYS_RAWIO
diff --git a/src/collectd.conf.in b/src/collectd.conf.in
index ad9437bb397a6999a78cd8744c58640a4f405f13..bab7bca3226823e14f44684b7b231582fed9a94e 100644 (file)
--- a/src/collectd.conf.in
+++ b/src/collectd.conf.in
#@BUILD_PLUGIN_GRPC_TRUE@LoadPlugin grpc
#@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
#@BUILD_PLUGIN_HUGEPAGES_TRUE@LoadPlugin hugepages
+#@BUILD_PLUGIN_INTEL_PMU_TRUE@LoadPlugin intel_pmu
#@BUILD_PLUGIN_INTEL_RDT_TRUE@LoadPlugin intel_rdt
@BUILD_PLUGIN_INTERFACE_TRUE@@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
#@BUILD_PLUGIN_IPC_TRUE@LoadPlugin ipc
# ValuesPercentage false
#</Plugin>
+#<Plugin intel_pmu>
+# HWCacheEvents true
+# KernelPMUEvents true
+# SWEvents true
+# HWSpecificEvents "L2_RQSTS.CODE_RD_HIT,L2_RQSTS.CODE_RD_MISS"
+#</Plugin>
+
#<Plugin "intel_rdt">
# Cores "0-2"
#</Plugin>
diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod
index 31b5274c3c44745cec95f841590427e2a6944084..95c1b38778378f9fdf43c25656fca38bf26cf47d 100644 (file)
--- a/src/collectd.conf.pod
+++ b/src/collectd.conf.pod
=back
+=head2 Plugin C<intel_pmu>
+
+The I<intel_pmu> plugin collects performance counters data on Intel CPUs using
+Linux perf interface. All events are reported on a per core basis.
+
+B<Synopsis:>
+
+ <Plugin intel_pmu>
+ HWCacheEvents true
+ KernelPMUEvents true
+ SWEvents true
+ HWSpecificEvents "L2_RQSTS.CODE_RD_HIT,L2_RQSTS.CODE_RD_MISS"
+ </Plugin>
+
+B<Options:>
+
+=over 4
+
+=item B<HWCacheEvents> B<false>|B<true>
+
+Enable or disable measuring of hardware CPU cache events:
+ - L1-dcache-loads
+ - L1-dcache-load-misses
+ - L1-dcache-stores
+ - L1-dcache-store-misses
+ - L1-dcache-prefetches
+ - L1-dcache-prefetch-misses
+ - L1-icache-loads
+ - L1-icache-load-misses
+ - L1-icache-prefetches
+ - L1-icache-prefetch-misses
+ - LLC-loads
+ - LLC-load-misses
+ - LLC-stores
+ - LLC-store-misses
+ - LLC-prefetches
+ - LLC-prefetch-misses
+ - dTLB-loads
+ - dTLB-load-misses
+ - dTLB-stores
+ - dTLB-store-misses
+ - dTLB-prefetches
+ - dTLB-prefetch-misses
+ - iTLB-loads
+ - iTLB-load-misses
+ - branch-loads
+ - branch-load-misses
+
+=item B<KernelPMUEvents> B<false>|B<true>
+
+Enable or disable measuring of the following events:
+ - cpu-cycles
+ - instructions
+ - cache-references
+ - cache-misses
+ - branches
+ - branch-misses
+ - bus-cycles
+
+=item B<SWEvents> B<false>|B<true>
+
+Enable or disable measuring of software events provided by kernel:
+ - cpu-clock
+ - task-clock
+ - context-switches
+ - cpu-migrations
+ - page-faults
+ - minor-faults
+ - major-faults
+ - alignment-faults
+ - emulation-faults
+
+=item B<HWSpecificEvents> I<events>
+
+This field is a list of comma separated event names. To be able to monitor all
+Intel CPU specific events JSON event list file should be downloaded.
+Use the pmu-tools event_download.py script for this.
+
+=back
+
=head2 Plugin C<intel_rdt>
The I<intel_rdt> plugin collects information provided by monitoring features of
diff --git a/src/intel_pmu.c b/src/intel_pmu.c
--- /dev/null
+++ b/src/intel_pmu.c
@@ -0,0 +1,522 @@
+/**
+ * collectd - src/intel_pmu.c
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * 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.
+ *
+ * Authors:
+ * Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+
+#include "jevents.h"
+#include "jsession.h"
+
+#define PMU_PLUGIN "intel_pmu"
+
+#define HW_CACHE_READ_ACCESS (((PERF_COUNT_HW_CACHE_OP_READ) << 8) | \
+ ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
+
+#define HW_CACHE_WRITE_ACCESS (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) | \
+ ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
+
+#define HW_CACHE_PREFETCH_ACCESS (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) | \
+ ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
+
+#define HW_CACHE_READ_MISS (((PERF_COUNT_HW_CACHE_OP_READ) << 8) | \
+ ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
+
+#define HW_CACHE_WRITE_MISS (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) | \
+ ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
+
+#define HW_CACHE_PREFETCH_MISS (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) | \
+ ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
+
+struct event_info {
+ char *name;
+ uint64_t config;
+};
+typedef struct event_info event_info_t;
+
+struct intel_pmu_ctx_s {
+ _Bool hw_cache_events;
+ _Bool kernel_pmu_events;
+ _Bool sw_events;
+ char* hw_specific_events;
+ struct eventlist *event_list;
+};
+typedef struct intel_pmu_ctx_s intel_pmu_ctx_t;
+
+event_info_t g_kernel_pmu_events[] = {
+ { .name = "cpu-cycles",
+ .config = PERF_COUNT_HW_CPU_CYCLES },
+ { .name = "instructions",
+ .config = PERF_COUNT_HW_INSTRUCTIONS },
+ { .name = "cache-references",
+ .config = PERF_COUNT_HW_CACHE_REFERENCES },
+ { .name = "cache-misses",
+ .config = PERF_COUNT_HW_CACHE_MISSES },
+ { .name = "branches",
+ .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
+ { .name = "branch-misses",
+ .config = PERF_COUNT_HW_BRANCH_MISSES },
+ { .name = "bus-cycles",
+ .config = PERF_COUNT_HW_BUS_CYCLES },
+};
+
+event_info_t g_hw_cache_events[] = {
+
+ { .name = "L1-dcache-loads",
+ .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_ACCESS) },
+ { .name = "L1-dcache-load-misses",
+ .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_MISS) },
+ { .name = "L1-dcache-stores",
+ .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_ACCESS) },
+ { .name = "L1-dcache-store-misses",
+ .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_MISS) },
+ { .name = "L1-dcache-prefetches",
+ .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_ACCESS) },
+ { .name = "L1-dcache-prefetch-misses",
+ .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_MISS) },
+
+ { .name = "L1-icache-loads",
+ .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_ACCESS) },
+ { .name = "L1-icache-load-misses",
+ .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_MISS) },
+ { .name = "L1-icache-prefetches",
+ .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_ACCESS) },
+ { .name = "L1-icache-prefetch-misses",
+ .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_MISS) },
+
+ { .name = "LLC-loads",
+ .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_ACCESS) },
+ { .name = "LLC-load-misses",
+ .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_MISS) },
+ { .name = "LLC-stores",
+ .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_ACCESS) },
+ { .name = "LLC-store-misses",
+ .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_MISS) },
+ { .name = "LLC-prefetches",
+ .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_ACCESS) },
+ { .name = "LLC-prefetch-misses",
+ .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_MISS) },
+
+ { .name = "dTLB-loads",
+ .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_ACCESS) },
+ { .name = "dTLB-load-misses",
+ .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_MISS) },
+ { .name = "dTLB-stores",
+ .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_ACCESS) },
+ { .name = "dTLB-store-misses",
+ .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_MISS) },
+ { .name = "dTLB-prefetches",
+ .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_ACCESS) },
+ { .name = "dTLB-prefetch-misses",
+ .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_MISS) },
+
+ { .name = "iTLB-loads",
+ .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_ACCESS) },
+ { .name = "iTLB-load-misses",
+ .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_MISS) },
+
+ { .name = "branch-loads",
+ .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_ACCESS) },
+ { .name = "branch-load-misses",
+ .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_MISS) },
+};
+
+event_info_t g_sw_events[] = {
+ { .name = "cpu-clock",
+ .config = PERF_COUNT_SW_CPU_CLOCK },
+
+ { .name = "task-clock",
+ .config = PERF_COUNT_SW_TASK_CLOCK },
+
+ { .name = "context-switches",
+ .config = PERF_COUNT_SW_CONTEXT_SWITCHES },
+
+ { .name = "cpu-migrations",
+ .config = PERF_COUNT_SW_CPU_MIGRATIONS },
+
+ { .name = "page-faults",
+ .config = PERF_COUNT_SW_PAGE_FAULTS },
+
+ { .name = "minor-faults",
+ .config = PERF_COUNT_SW_PAGE_FAULTS_MIN },
+
+ { .name = "major-faults",
+ .config = PERF_COUNT_SW_PAGE_FAULTS_MAJ },
+
+ { .name = "alignment-faults",
+ .config = PERF_COUNT_SW_ALIGNMENT_FAULTS },
+
+ { .name = "emulation-faults",
+ .config = PERF_COUNT_SW_EMULATION_FAULTS },
+};
+
+static intel_pmu_ctx_t g_ctx;
+
+#if COLLECT_DEBUG
+static void pmu_dump_events() {
+
+ DEBUG(PMU_PLUGIN ": Events:");
+
+ struct event *e;
+
+ for (e = g_ctx.event_list->eventlist; e; e = e->next) {
+ DEBUG(PMU_PLUGIN ": event : %s", e->event);
+ DEBUG(PMU_PLUGIN ": group_lead: %d", e->group_leader);
+ DEBUG(PMU_PLUGIN ": end_group : %d", e->end_group);
+ DEBUG(PMU_PLUGIN ": type : 0x%X", e->attr.type);
+ DEBUG(PMU_PLUGIN ": config : 0x%X", (int)e->attr.config);
+ DEBUG(PMU_PLUGIN ": size : %d", e->attr.size);
+ }
+
+ return;
+}
+
+static void pmu_dump_config(void) {
+
+ DEBUG(PMU_PLUGIN ": Config:");
+ DEBUG(PMU_PLUGIN ": hw_cache_events : %d", g_ctx.hw_cache_events);
+ DEBUG(PMU_PLUGIN ": kernel_pmu_events : %d", g_ctx.kernel_pmu_events);
+ DEBUG(PMU_PLUGIN ": sw_events : %d", g_ctx.sw_events);
+ DEBUG(PMU_PLUGIN ": hw_specific_events: %s", g_ctx.hw_specific_events);
+
+ return;
+}
+
+#endif /* COLLECT_DEBUG */
+
+static int pmu_config(oconfig_item_t *ci) {
+ int ret = 0;
+
+ DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("HWCacheEvents", child->key) == 0) {
+ ret = cf_util_get_boolean(child, &g_ctx.hw_cache_events);
+ } else if (strcasecmp("KernelPMUEvents", child->key) == 0) {
+ ret = cf_util_get_boolean(child, &g_ctx.kernel_pmu_events);
+ } else if (strcasecmp("HWSpecificEvents", child->key) == 0) {
+ ret = cf_util_get_string(child, &g_ctx.hw_specific_events);
+ } else if (strcasecmp("SWEvents", child->key) == 0) {
+ ret = cf_util_get_boolean(child, &g_ctx.sw_events);
+ }else {
+ ERROR(PMU_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
+ ret = (-1);
+ }
+
+ if (ret != 0) {
+ DEBUG(PMU_PLUGIN ": %s:%d ret=%d", __FUNCTION__, __LINE__, ret);
+ return ret;
+ }
+ }
+
+#if COLLECT_DEBUG
+ pmu_dump_config();
+#endif
+
+ return (0);
+}
+
+static void pmu_submit_counter(int cpu, char *event, counter_t value) {
+ value_list_t vl = VALUE_LIST_INIT;
+
+ vl.values = &(value_t){.counter = value};
+ vl.values_len = 1;
+
+ sstrncpy(vl.plugin, PMU_PLUGIN, sizeof(vl.plugin));
+ if (cpu == -1) {
+ snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "all");
+ } else {
+ snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%d", cpu);
+ }
+ sstrncpy(vl.type, "counter", sizeof(vl.type));
+ sstrncpy(vl.type_instance, event, sizeof(vl.type_instance));
+
+ plugin_dispatch_values(&vl);
+}
+
+static int pmu_dispatch_data(void) {
+
+ struct event *e;
+
+ for (e = g_ctx.event_list->eventlist; e; e = e->next) {
+ uint64_t all_value = 0;
+ int event_enabled = 0;
+ for (int i = 0; i < g_ctx.event_list->num_cpus; i++) {
+
+ if (e->efd[i].fd < 0)
+ continue;
+
+ event_enabled++;
+
+ uint64_t value = event_scaled_value(e, i);
+ all_value += value;
+
+ /* dispatch per CPU value */
+ pmu_submit_counter(i, e->event, value);
+ }
+
+ if (event_enabled > 0) {
+ DEBUG(PMU_PLUGIN ": %-20s %'10lu", e->event, all_value);
+ /* dispatch all CPU value */
+ pmu_submit_counter(-1, e->event, all_value);
+ }
+ }
+
+ return (0);
+}
+
+static int pmu_read(__attribute__((unused)) user_data_t *ud) {
+ int ret;
+
+ DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
+
+ ret = read_all_events(g_ctx.event_list);
+ if (ret != 0) {
+ DEBUG(PMU_PLUGIN ": Failed to read values of all events.");
+ return (0);
+ }
+
+ ret = pmu_dispatch_data();
+ if (ret != 0) {
+ DEBUG(PMU_PLUGIN ": Failed to dispatch event values.");
+ return (0);
+ }
+
+ return (0);
+}
+
+static int pmu_add_events(struct eventlist *el, uint32_t type,
+ event_info_t *events, int count) {
+
+ for (int i = 0; i < count; i++) {
+ struct event *e = calloc(sizeof(struct event) +
+ sizeof(struct efd) * el->num_cpus, 1);
+ if (e == NULL) {
+ ERROR(PMU_PLUGIN ": Failed to allocate event structure");
+ return (-ENOMEM);
+ }
+
+ e->attr.type = type;
+ e->attr.config = events[i].config;
+ e->attr.size = PERF_ATTR_SIZE_VER0;
+ e->group_leader = false;
+ e->end_group = false;
+ e->next = NULL;
+ if (!el->eventlist)
+ el->eventlist = e;
+ if (el->eventlist_last)
+ el->eventlist_last->next = e;
+ el->eventlist_last = e;
+ e->event = strdup(events[i].name);
+ }
+
+ return (0);
+}
+
+static int pmu_parse_events(struct eventlist *el, char *events) {
+ char *s, *tmp;
+
+ events = strdup(events);
+ if (!events)
+ return -1;
+
+ for (s = strtok_r(events, ",", &tmp);
+ s;
+ s = strtok_r(NULL, ",", &tmp)) {
+ bool group_leader = false, end_group = false;
+ int len;
+
+ if (s[0] == '{') {
+ s++;
+ group_leader = true;
+ } else if (len = strlen(s), len > 0 && s[len - 1] == '}') {
+ s[len - 1] = 0;
+ end_group = true;
+ }
+
+ struct event *e = calloc(sizeof(struct event) +
+ sizeof(struct efd) * el->num_cpus, 1);
+ if (e == NULL) {
+ free(events);
+ return (-ENOMEM);
+ }
+
+ if (resolve_event(s, &e->attr) == 0) {
+ e->group_leader = group_leader;
+ e->end_group = end_group;
+ e->next = NULL;
+ if (!el->eventlist)
+ el->eventlist = e;
+ if (el->eventlist_last)
+ el->eventlist_last->next = e;
+ el->eventlist_last = e;
+ e->event = strdup(s);
+ } else {
+ DEBUG(PMU_PLUGIN ": Cannot resolve %s", s);
+ sfree(e);
+ }
+ }
+
+ free(events);
+
+ return (0);
+}
+
+static void pmu_free_events(struct eventlist *el) {
+
+ if (el == NULL)
+ return;
+
+ struct event *e = el->eventlist;
+
+ while (e) {
+ struct event *next = e->next;
+ sfree(e);
+ e = next;
+ }
+
+ el->eventlist = NULL;
+}
+
+static int pmu_setup_events(struct eventlist *el, bool measure_all,
+ int measure_pid) {
+ struct event *e, *leader = NULL;
+ int ret = -1;
+
+ for (e = el->eventlist; e; e = e->next) {
+
+ for (int i = 0; i < el->num_cpus; i++) {
+ if (setup_event(e, i, leader, measure_all, measure_pid) < 0) {
+ WARNING(PMU_PLUGIN ": perf event '%s' is not available (cpu=%d).",
+ e->event, i);
+ } else {
+ /* success if at least one event was set */
+ ret = 0;
+ }
+ }
+
+ if (e->group_leader)
+ leader = e;
+ if (e->end_group)
+ leader = NULL;
+ }
+
+ return ret;
+}
+
+static int pmu_init(void) {
+ int ret;
+
+ DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
+
+ g_ctx.event_list = alloc_eventlist();
+ if (g_ctx.event_list == NULL) {
+ ERROR(PMU_PLUGIN ": Failed to allocate event list.");
+ return (-ENOMEM);
+ }
+
+ if (g_ctx.hw_cache_events) {
+ ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_HW_CACHE,
+ g_hw_cache_events, STATIC_ARRAY_SIZE(g_hw_cache_events));
+ if (ret != 0) {
+ ERROR(PMU_PLUGIN ": Failed to add hw cache events.");
+ goto init_error;
+ }
+ }
+
+ if (g_ctx.kernel_pmu_events) {
+ ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_HARDWARE,
+ g_kernel_pmu_events, STATIC_ARRAY_SIZE(g_kernel_pmu_events));
+ if (ret != 0) {
+ ERROR(PMU_PLUGIN ": Failed to parse kernel PMU events.");
+ goto init_error;
+ }
+ }
+
+ /* parse events names if config option is present and is not empty */
+ if (g_ctx.hw_specific_events && (strlen(g_ctx.hw_specific_events) != 0)) {
+ ret = pmu_parse_events(g_ctx.event_list, g_ctx.hw_specific_events);
+ if (ret != 0) {
+ ERROR(PMU_PLUGIN ": Failed to parse hw specific events.");
+ goto init_error;
+ }
+ }
+
+ if (g_ctx.sw_events) {
+ ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_SOFTWARE,
+ g_sw_events, STATIC_ARRAY_SIZE(g_sw_events));
+ if (ret != 0) {
+ ERROR(PMU_PLUGIN ": Failed to add software events.");
+ goto init_error;
+ }
+ }
+
+#if COLLECT_DEBUG
+ pmu_dump_events();
+#endif
+
+ if (g_ctx.event_list->eventlist != NULL) {
+ /* measure all processes */
+ ret = pmu_setup_events(g_ctx.event_list, true, -1);
+ if (ret != 0) {
+ ERROR(PMU_PLUGIN ": Failed to setup perf events for the event list.");
+ goto init_error;
+ }
+ } else {
+ WARNING(PMU_PLUGIN
+ ": Events list is empty. No events were setup for monitoring.");
+ }
+
+ return (0);
+
+init_error:
+
+ pmu_free_events(g_ctx.event_list);
+ sfree(g_ctx.event_list);
+ sfree(g_ctx.hw_specific_events);
+
+ return ret;
+}
+
+static int pmu_shutdown(void) {
+
+ DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
+
+ pmu_free_events(g_ctx.event_list);
+ sfree(g_ctx.event_list);
+ sfree(g_ctx.hw_specific_events);
+
+ return (0);
+}
+
+void module_register(void) {
+ plugin_register_init(PMU_PLUGIN, pmu_init);
+ plugin_register_complex_config(PMU_PLUGIN, pmu_config);
+ plugin_register_complex_read(NULL, PMU_PLUGIN, pmu_read, 0, NULL);
+ plugin_register_shutdown(PMU_PLUGIN, pmu_shutdown);
+}