From: Florian Forster Date: Wed, 19 Jul 2017 05:54:49 +0000 (+0200) Subject: Merge remote-tracking branch 'github/pr/2276' X-Git-Tag: collectd-5.8.0~114 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=88bd89f106abd5c0a9f9a80246e31a16c36a3c6d;hp=f10aace6ef2632df58636d965bc43fdd2730243c;p=collectd.git Merge remote-tracking branch 'github/pr/2276' --- diff --git a/AUTHORS b/AUTHORS index d866c700..b99c156a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -285,6 +285,11 @@ Scott Sanders Sebastien Pahl - AMQP plugin. +Serhiy Pshyk + - intel_pmu plugin + - intel_rdt plugin + - snmp_agent plugin + Simon Kuhnle - OpenBSD code for the cpu and memory plugins. diff --git a/Makefile.am b/Makefile.am index f9273990..0ef9e31d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -888,6 +888,14 @@ hugepages_la_SOURCES = src/hugepages.c 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_CPPFLAGS = $(AM_CPPFLAGS) $(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 diff --git a/README b/README index b0b59214..399ae892 100644 --- a/README +++ b/README @@ -140,6 +140,11 @@ Features 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 @@ -240,7 +245,7 @@ Features - 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 @@ -786,6 +791,13 @@ Prerequisites For querying iptables counters. + * 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. + + * 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 35b139c4..62bea50e 100644 --- a/configure.ac +++ b/configure.ac @@ -4429,6 +4429,49 @@ AC_SUBST([BUILD_WITH_LIBPQOS_LDFLAGS]) AC_SUBST([BUILD_WITH_LIBPQOS_LIBS]) # }}} +# --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_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $with_libjevents_ldflags" + + AC_CHECK_LIB([jevents], [json_events], [with_libjevents="yes"], [with_libjevents="no (Can't find libjevents)"]) + + LDFLAGS="$SAVE_LDFLAGS" +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" +fi +AC_SUBST([BUILD_WITH_LIBJEVENTS_CPPFLAGS]) +AC_SUBST([BUILD_WITH_LIBJEVENTS_LDFLAGS]) +AC_SUBST([BUILD_WITH_LIBJEVENTS_LIBS]) +# }}} + # --with-libprotobuf {{{ with_libprotobuf_cppflags="" with_libprotobuf_ldflags="" @@ -6083,6 +6126,7 @@ plugin_fscache="no" plugin_gps="no" plugin_grpc="no" plugin_hugepages="no" +plugin_intel_pmu="no" plugin_intel_rdt="no" plugin_interface="no" plugin_ipc="no" @@ -6491,6 +6535,7 @@ AC_PLUGIN([gps], [$plugin_gps], [GPS plugin]) 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]) @@ -6809,6 +6854,7 @@ AC_MSG_RESULT([ libhiredis . . . . . $with_libhiredis]) 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]) @@ -6907,6 +6953,7 @@ AC_MSG_RESULT([ gps . . . . . . . . . $enable_gps]) 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]) diff --git a/contrib/systemd.collectd.service b/contrib/systemd.collectd.service index 6333d49a..9c037a4e 100644 --- a/contrib/systemd.collectd.service +++ b/contrib/systemd.collectd.service @@ -19,6 +19,7 @@ ProtectHome=true # 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 6b59d4d5..b675dfbc 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -129,6 +129,7 @@ #@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 @@ -645,6 +646,14 @@ # ValuesPercentage false # +# +# ReportHardwareCacheEvents true +# ReportKernelPMUEvents true +# ReportSoftwareEvents true +# EventList "/var/cache/pmu/GenuineIntel-6-2D-core.json" +# HardwareEvents "L2_RQSTS.CODE_RD_HIT,L2_RQSTS.CODE_RD_MISS" "L2_RQSTS.ALL_CODE_RD" +# + # # Cores "0-2" # diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 67ee0a93..2bf1a558 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -3073,6 +3073,92 @@ Defaults to B. =back +=head2 Plugin C + +The I plugin collects performance counters data on Intel CPUs using +Linux perf interface. All events are reported on a per core basis. + +B + + + ReportHardwareCacheEvents true + ReportKernelPMUEvents true + ReportSoftwareEvents true + EventList "/var/cache/pmu/GenuineIntel-6-2D-core.json" + HardwareEvents "L2_RQSTS.CODE_RD_HIT,L2_RQSTS.CODE_RD_MISS" "L2_RQSTS.ALL_CODE_RD" + + +B + +=over 4 + +=item B B|B + +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 B|B + +Enable or disable measuring of the following events: + - cpu-cycles + - instructions + - cache-references + - cache-misses + - branches + - branch-misses + - bus-cycles + +=item B B|B + +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 I + +JSON performance counter event list file name. 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 to download event list for current CPU. + +=item B I + +This field is a list of event names or groups of comma separated event names. +This option requires B option to be configured. + +=back + =head2 Plugin C The I plugin collects information provided by monitoring features of diff --git a/src/intel_pmu.c b/src/intel_pmu.c new file mode 100644 index 00000000..ea7c83f1 --- /dev/null +++ b/src/intel_pmu.c @@ -0,0 +1,566 @@ +/** + * 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 + **/ + +#include "collectd.h" +#include "common.h" + +#include +#include + +#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 event_list_fn[PATH_MAX]; + char **hw_events; + size_t hw_events_count; + 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 : %#x", e->attr.type); + DEBUG(PMU_PLUGIN ": config : %#x", (unsigned)e->attr.config); + DEBUG(PMU_PLUGIN ": size : %d", e->attr.size); + } +} + +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 ": software_events : %d", g_ctx.sw_events); + + for (size_t i = 0; i < g_ctx.hw_events_count; i++) { + DEBUG(PMU_PLUGIN ": hardware_events[%zu]: %s", i, g_ctx.hw_events[i]); + } +} + +#endif /* COLLECT_DEBUG */ + +static int pmu_config_hw_events(oconfig_item_t *ci) { + + if (strcasecmp("HardwareEvents", ci->key) != 0) { + return -EINVAL; + } + + g_ctx.hw_events = calloc(ci->values_num, sizeof(char *)); + if (g_ctx.hw_events == NULL) { + ERROR(PMU_PLUGIN ": Failed to allocate hw events."); + return -ENOMEM; + } + + for (int i = 0; i < ci->values_num; i++) { + if (ci->values[i].type != OCONFIG_TYPE_STRING) { + WARNING(PMU_PLUGIN ": The %s option requires string arguments.", ci->key); + continue; + } + + g_ctx.hw_events[g_ctx.hw_events_count] = strdup(ci->values[i].value.string); + if (g_ctx.hw_events[g_ctx.hw_events_count] == NULL) { + ERROR(PMU_PLUGIN ": Failed to allocate hw events entry."); + return -ENOMEM; + } + + g_ctx.hw_events_count++; + } + + return 0; +} + +static int pmu_config(oconfig_item_t *ci) { + + DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__); + + for (int i = 0; i < ci->children_num; i++) { + int ret = 0; + oconfig_item_t *child = ci->children + i; + + if (strcasecmp("ReportHardwareCacheEvents", child->key) == 0) { + ret = cf_util_get_boolean(child, &g_ctx.hw_cache_events); + } else if (strcasecmp("ReportKernelPMUEvents", child->key) == 0) { + ret = cf_util_get_boolean(child, &g_ctx.kernel_pmu_events); + } else if (strcasecmp("EventList", child->key) == 0) { + ret = cf_util_get_string_buffer(child, g_ctx.event_list_fn, + sizeof(g_ctx.event_list_fn)); + } else if (strcasecmp("HardwareEvents", child->key) == 0) { + ret = pmu_config_hw_events(child); + } else if (strcasecmp("ReportSoftwareEvents", 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) { + ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "all"); + } else { + ssnprintf(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 void 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); + } + } +} + +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) { + ERROR(PMU_PLUGIN ": Failed to read values of all events."); + return ret; + } + + pmu_dispatch_data(); + + return 0; +} + +static int pmu_add_events(struct eventlist *el, uint32_t type, + event_info_t *events, size_t count) { + + for (size_t i = 0; i < count; i++) { + /* Allocate memory for event struct that contains array of efd structs + for all cores */ + 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; + 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_add_hw_events(struct eventlist *el, char **e, size_t count) { + + for (size_t i = 0; i < count; i++) { + + size_t group_events_count = 0; + + char *events = strdup(e[i]); + if (!events) + return -1; + + char *s, *tmp; + for (s = strtok_r(events, ",", &tmp); s; s = strtok_r(NULL, ",", &tmp)) { + + /* Multiple events parsed in one entry */ + if (group_events_count == 1) { + /* Mark previously added event as group leader */ + el->eventlist_last->group_leader = 1; + } + + /* Allocate memory for event struct that contains array of efd structs + for all cores */ + 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->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); + } + + group_events_count++; + } + + /* Multiple events parsed in one entry */ + if (group_events_count > 1) { + /* Mark last added event as group end */ + el->eventlist_last->end_group = 1; + } + + 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 add kernel PMU events."); + goto init_error; + } + } + + /* parse events names if config option is present and is not empty */ + if (g_ctx.hw_events_count) { + + ret = read_events(g_ctx.event_list_fn); + if (ret != 0) { + ERROR(PMU_PLUGIN ": Failed to read event list file '%s'.", + g_ctx.event_list_fn); + return ret; + } + + ret = pmu_add_hw_events(g_ctx.event_list, g_ctx.hw_events, + g_ctx.hw_events_count); + if (ret != 0) { + ERROR(PMU_PLUGIN ": Failed to add hardware 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); + for (size_t i = 0; i < g_ctx.hw_events_count; i++) { + sfree(g_ctx.hw_events[i]); + } + sfree(g_ctx.hw_events); + g_ctx.hw_events_count = 0; + + + 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); + for (size_t i = 0; i < g_ctx.hw_events_count; i++) { + sfree(g_ctx.hw_events[i]); + } + sfree(g_ctx.hw_events); + g_ctx.hw_events_count = 0; + + 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); +}