Merge branch 'collectd-5.8'
authorPavel Rochnyack <pavel2000@ngs.ru>
Sun, 20 May 2018 15:51:49 +0000 (22:51 +0700)
committerPavel Rochnyack <pavel2000@ngs.ru>
Sun, 20 May 2018 15:51:49 +0000 (22:51 +0700)
1  2 
Makefile.am
src/collectd.conf.in
src/collectd.conf.pod
src/intel_pmu.c
src/intel_rdt.c

diff --combined Makefile.am
@@@ -61,7 -61,6 +61,7 @@@ EXTRA_DIST = 
        src/types.db \
        src/types.db.pod \
        src/valgrind.FreeBSD.suppress \
 +      src/valgrind.suppress \
        testwrapper.sh \
        version-gen.sh
  
@@@ -142,7 -141,8 +142,8 @@@ check_PROGRAMS = 
        test_utils_subst \
        test_utils_time \
        test_utils_vl_lookup \
-       test_libcollectd_network_parse
+       test_libcollectd_network_parse \
+       test_utils_config_cores
  
  
  TESTS = $(check_PROGRAMS)
@@@ -327,6 -327,11 +328,11 @@@ test_utils_subst_SOURCES = 
        src/daemon/utils_subst.h
  test_utils_subst_LDADD = libplugin_mock.la
  
+ test_utils_config_cores_SOURCES = \
+       src/utils_config_cores_test.c \
+       src/testing.h
+ test_utils_config_cores_LDADD = libplugin_mock.la
  libavltree_la_SOURCES = \
        src/daemon/utils_avltree.c \
        src/daemon/utils_avltree.h
@@@ -484,8 -489,7 +490,8 @@@ libcollectdclient_la_SOURCES = 
        src/libcollectdclient/network.c \
        src/libcollectdclient/network_buffer.c \
        src/libcollectdclient/network_parse.c \
 -      src/libcollectdclient/server.c
 +      src/libcollectdclient/server.c \
 +      src/libcollectdclient/collectd/stdendian.h
  libcollectdclient_la_CPPFLAGS = \
        $(AM_CPPFLAGS) \
        -I$(srcdir)/src/libcollectdclient \
@@@ -594,7 -598,7 +600,7 @@@ if BUILD_PLUGIN_BAROMETE
  pkglib_LTLIBRARIES += barometer.la
  barometer_la_SOURCES = src/barometer.c
  barometer_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 -barometer_la_LIBADD = -lm
 +barometer_la_LIBADD = -lm $(BUILD_WITH_LIBI2C_LIBS)
  endif
  
  if BUILD_PLUGIN_BATTERY
@@@ -915,7 -919,10 +921,10 @@@ endi
  
  if BUILD_PLUGIN_INTEL_PMU
  pkglib_LTLIBRARIES += intel_pmu.la
- intel_pmu_la_SOURCES = src/intel_pmu.c
+ intel_pmu_la_SOURCES = \
+       src/intel_pmu.c \
+       src/utils_config_cores.h \
+       src/utils_config_cores.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)
@@@ -923,7 -930,10 +932,10 @@@ endi
  
  if BUILD_PLUGIN_INTEL_RDT
  pkglib_LTLIBRARIES += intel_rdt.la
- intel_rdt_la_SOURCES = src/intel_rdt.c
+ intel_rdt_la_SOURCES = \
+       src/intel_rdt.c \
+       src/utils_config_cores.h \
+       src/utils_config_cores.c
  intel_rdt_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBPQOS_CPPFLAGS)
  intel_rdt_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBPQOS_LDFLAGS)
  intel_rdt_la_LIBADD = $(BUILD_WITH_LIBPQOS_LIBS)
@@@ -1424,28 -1434,14 +1436,28 @@@ python_la_CPPFLAGS = $(AM_CPPFLAGS) $(L
  python_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBPYTHON_LDFLAGS)
  endif
  
 +if HAVE_LIBMNL
 +noinst_LTLIBRARIES += libtaskstats.la
 +libtaskstats_la_SOURCES = \
 +      src/utils_taskstats.c \
 +      src/utils_taskstats.h
 +libtaskstats_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS)
 +libtaskstats_la_LIBADD = $(BUILD_WITH_LIBMNL_LIBS)
 +endif
 +
  if BUILD_PLUGIN_PROCESSES
  pkglib_LTLIBRARIES += processes.la
  processes_la_SOURCES = src/processes.c
 +processes_la_CPPFLAGS = $(AM_CPPFLAGS)
  processes_la_LDFLAGS = $(PLUGIN_LDFLAGS)
  processes_la_LIBADD =
  if BUILD_WITH_LIBKVM_GETPROCS
  processes_la_LIBADD += -lkvm
  endif
 +if HAVE_LIBMNL
 +processes_la_CPPFLAGS += -DHAVE_LIBTASKSTATS=1
 +processes_la_LIBADD += libtaskstats.la
 +endif
  endif
  
  if BUILD_PLUGIN_PROTOCOLS
@@@ -1760,15 -1756,18 +1772,15 @@@ virt_la_CFLAGS = $(AM_CFLAGS) 
  virt_la_LDFLAGS = $(PLUGIN_LDFLAGS)
  virt_la_LIBADD = libignorelist.la $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
  
 -# TODO: enable once we support only modern libvirts which depends on libnl-3
 -# the libvirt on wheezy is linked in libnl v1, and there is a small leak here,
 -# triggered by the library initialization. There are no means to avoid it,
 -# and libvirt switched to libnl3 anyway
 -#test_plugin_virt_SOURCES = src/virt_test.c
 -#test_plugin_virt_CPPFLAGS = $(AM_CPPFLAGS) \
 -#     $(BUILD_WITH_LIBVIRT_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
 -#test_plugin_virt_LDFLAGS = $(PLUGIN_LDFLAGS)
 -#test_plugin_virt_LDADD = libplugin_mock.la \
 -#     $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
 -#check_PROGRAMS += test_plugin_virt
 -#TESTS += test_plugin_virt
 +test_plugin_virt_SOURCES = src/virt_test.c
 +test_plugin_virt_CPPFLAGS = $(AM_CPPFLAGS) \
 +      $(BUILD_WITH_LIBVIRT_CPPFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
 +test_plugin_virt_LDFLAGS = $(PLUGIN_LDFLAGS) \
 +      $(BUILD_WITH_LIBVIRT_LDFLAGS) $(BUILD_WITH_LIBXML2_LDFLAGS)
 +test_plugin_virt_LDADD = libplugin_mock.la \
 +      $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
 +check_PROGRAMS += test_plugin_virt
 +TESTS += test_plugin_virt
  endif
  
  if BUILD_PLUGIN_VMEM
diff --combined src/collectd.conf.in
  #             SSLCACertificateFile "/path/to/root.pem"
  #             SSLCertificateFile "/path/to/client.pem"
  #             SSLCertificateKeyFile "/path/to/client.key"
 +#             VerifyPeer true
  #     </Listen>
  #</Plugin>
  
  #    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-3]"
  #</Plugin>
  
  #<Plugin "intel_rdt">
  #     Timeout 0.9
  #     TTL 255
  #     SourceAddress "1.2.3.4"
 +#     AddressFamily "any"
  #     Device "eth0"
  #     MaxMissed -1
  #</Plugin>
  #     CollectFileDescriptor true
  #     CollectContextSwitch true
  #     CollectMemoryMaps true
 +#     CollectDelayAccounting false
  #     Process "name"
  #     ProcessMatch "name" "regex"
  #     <Process "collectd">
  #             CollectFileDescriptor false
  #             CollectContextSwitch false
 +#             CollectDelayAccounting true
  #     </Process>
  #     <ProcessMatch "name" "regex">
  #             CollectFileDescriptor false
  #     PluginInstanceFormat name
  #     Instances 1
  #     ExtraStats "cpu_util disk disk_err domain_state fs_info job_stats_background pcpu perf vcpupin"
 +#     PersistentNotification false
  #</Plugin>
  
  #<Plugin vmem>
diff --combined src/collectd.conf.pod
@@@ -3094,13 -3094,6 +3094,13 @@@ Whether to enable SSL for incoming conn
  Filenames specifying SSL certificate and key material to be used with SSL
  connections.
  
 +=item B<VerifyPeer> B<true>|B<false>
 +
 +When enabled, a valid client certificate is required to connect to the server.
 +When disabled, a client certifiacte is not requested and any unsolicited client
 +certificate is accepted.
 +Enabled by default.
 +
  =back
  
  =back
@@@ -3182,6 -3175,7 +3182,7 @@@ B<Synopsis:
      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-3" "4,6" "[12-15]"
    </Plugin>
  
  B<Options:>
@@@ -3253,6 -3247,23 +3254,23 @@@ event_download.py script to download ev
  This field is a list of event names or groups of comma separated event names.
  This option requires B<EventList> option to be configured.
  
+ =item B<Cores> I<cores groups>
+ All events are reported on a per core basis. Monitoring of the events can be
+ configured for a group of cores (aggregated statistics). This field defines
+ groups of cores on which to monitor supported events. The field is represented
+ as list of strings with core group values. Each string represents a list of
+ cores in a group. If a group is enclosed in square brackets each core is added
+ individually to a separate group (that is statistics are not aggregated).
+ Allowed formats are:
+     0,1,2,3
+     0-10,20-18
+     1,3,5-8,10,0x10-12
+     [4-15,32-63]
+ If an empty string is provided as value for this field default cores
+ configuration is applied - that is separate group is created for each core.
  =back
  
  =head2 Plugin C<intel_rdt>
@@@ -4128,19 -4139,11 +4146,19 @@@ Configures the base register to read fr
  B<RegisterType> has been set to B<Uint32> or B<Float>, this and the next
  register will be read (the register number is increased by one).
  
 -=item B<RegisterType> B<Int16>|B<Int32>|B<Uint16>|B<Uint32>|B<Float>
 +=item B<RegisterType> B<Int16>|B<Int32>|B<Uint16>|B<Uint32>|B<Float>|B<Int32LE>|B<Uint32LE>|B<FloatLE>
  
 -Specifies what kind of data is returned by the device. If the type is B<Int32>,
 -B<Uint32> or B<Float>, two 16E<nbsp>bit registers will be read and the data is
 -combined into one value. Defaults to B<Uint16>.
 +Specifies what kind of data is returned by the device. This defaults to
 +B<Uint16>.  If the type is B<Int32>, B<Int32LE>, B<Uint32>, B<Uint32LE>,
 +B<Float> or B<FloatLE>, two 16E<nbsp>bit registers at B<RegisterBase>
 +and B<RegisterBase+1> will be read and the data is combined into one
 +32E<nbsp>value. For B<Int32>, B<Uint32> and B<Float> the most significant
 +16E<nbsp>bits are in the register at B<RegisterBase> and the least
 +significant 16E<nbsp>bits are in the register at B<RegisterBase+1>.
 +For B<Int32LE>, B<Uint32LE>, or B<Float32LE>, the high and low order
 +registers are swapped with the most significant 16E<nbsp>bits in
 +the B<RegisterBase+1> and the least significant 16E<nbsp>bits in
 +B<RegisterBase>.
  
  =item B<RegisterCmd> B<ReadHolding>|B<ReadInput>
  
@@@ -6185,11 -6188,6 +6203,11 @@@ long string is used so that the packet 
  Sets the source address to use. I<host> may either be a numerical network
  address or a network hostname.
  
 +=item B<AddressFamily> I<af>
 +
 +Sets the address family to use. I<af> may be "any", "ipv4" or "ipv6". This
 +option will be ignored if you set a B<SourceAddress>.
 +
  =item B<Device> I<name>
  
  Sets the outgoing network device to be used. I<name> has to specify an
@@@ -6862,21 -6860,18 +6880,21 @@@ The statistics collected for matched pr
   - number of memory mapped files (under Linux)
   - io data (where available)
   - context switches (under Linux)
 - - minor and major pagefaults.
 + - minor and major pagefaults
 + - Delay Accounting information (Linux only, requires libmnl)
  
  B<Synopsis:>
  
   <Plugin processes>
 -   CollectFileDescriptor true
 -   CollectContextSwitch true
 +   CollectFileDescriptor  true
 +   CollectContextSwitch   true
 +   CollectDelayAccounting false
     Process "name"
     ProcessMatch "name" "regex"
     <Process "collectd">
 -     CollectFileDescriptor false
 -     CollectContextSwitch false
 +     CollectFileDescriptor  false
 +     CollectContextSwitch   false
 +     CollectDelayAccounting true
     </Process>
     <ProcessMatch "name" "regex">
       CollectFileDescriptor false
@@@ -6906,18 -6901,6 +6924,18 @@@ I<name> must not contain slashes
  Collect the number of context switches for matched processes.
  Disabled by default.
  
 +=item B<CollectDelayAccounting> I<Boolean>
 +
 +If enabled, collect Linux Delay Accounding information for matching processes.
 +Delay Accounting provides the time processes wait for the CPU to become
 +available, for I/O operations to finish, for pages to be swapped in and for
 +freed pages to be reclaimed. The metrics are reported as "seconds per second"
 +using the C<delay_rate> type, e.g. C<delay_rate-delay-cpu>.
 +Disabled by default.
 +
 +This option is only available on Linux, requires the C<libmnl> library and
 +requires the C<CAP_NET_ADMIN> capability at runtime.
 +
  =item B<CollectFileDescriptor> I<Boolean>
  
  Collect number of file descriptors of matched processes.
@@@ -6931,12 -6914,9 +6949,12 @@@ the Linux kernel
  
  =back
  
 -Options B<CollectContextSwitch> and B<CollectFileDescriptor> may be used inside
 -B<Process> and B<ProcessMatch> blocks - then they affect corresponding match
 -only. Otherwise they set the default value for subsequent matches.
 +The B<CollectContextSwitch>, B<CollectDelayAccounting>,
 +B<CollectFileDescriptor> and B<CollectMemoryMaps> options may be used inside
 +B<Process> and B<ProcessMatch> blocks. When used there, these options affect
 +reporting the corresponding processes only. Outside of B<Process> and
 +B<ProcessMatch> blocks these options set the default value for subsequent
 +matches.
  
  =head2 Plugin C<protocols>
  
@@@ -8923,12 -8903,6 +8941,12 @@@ B<Note>: I<perf> metrics can't be colle
  
  =back
  
 +=item B<PersistentNotification> B<true>|B<false>
 +Override default configuration to only send notifications when there is a change
 +in the lifecycle state of a domain. When set to true notifications will be sent
 +for every read cycle. Default is false. Does not affect the stats being
 +dispatched.
 +
  =back
  
  =head2 Plugin C<vmem>
diff --combined src/intel_pmu.c
@@@ -1,7 -1,7 +1,7 @@@
  /**
   * collectd - src/intel_pmu.c
   *
-  * Copyright(c) 2017 Intel Corporation. All rights reserved.
+  * Copyright(c) 2017-2018 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
   *
   * Authors:
   *   Serhiy Pshyk <serhiyx.pshyk@intel.com>
+  *   Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
   **/
  
  #include "collectd.h"
  #include "common.h"
  
+ #include "utils_config_cores.h"
  #include <jevents.h>
  #include <jsession.h>
  
@@@ -64,12 -67,13 +67,13 @@@ struct event_info 
  typedef struct event_info event_info_t;
  
  struct intel_pmu_ctx_s {
 -  _Bool hw_cache_events;
 -  _Bool kernel_pmu_events;
 -  _Bool sw_events;
 +  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;
+   core_groups_list_t cores;
    struct eventlist *event_list;
  };
  typedef struct intel_pmu_ctx_s intel_pmu_ctx_t;
@@@ -192,13 -196,65 +196,66 @@@ static void pmu_dump_config(void) 
    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]);
 +    DEBUG(PMU_PLUGIN ":   hardware_events[%" PRIsz "]: %s", i,
 +          g_ctx.hw_events[i]);
    }
  }
  
+ static void pmu_dump_cgroups(void) {
+   DEBUG(PMU_PLUGIN ": Core groups:");
+   for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
+     core_group_t *cgroup = g_ctx.cores.cgroups + i;
+     const size_t cores_size = cgroup->num_cores * 4 + 1;
+     char *cores = calloc(cores_size, sizeof(*cores));
+     if (cores == NULL) {
+       DEBUG(PMU_PLUGIN ": Failed to allocate string to list cores.");
+       return;
+     }
+     for (size_t j = 0; j < cgroup->num_cores; j++)
+       if (snprintf(cores + strlen(cores), cores_size - strlen(cores), " %d",
+                    cgroup->cores[j]) < 0) {
+         DEBUG(PMU_PLUGIN ": Failed to write list of cores to string.");
+         sfree(cores);
+         return;
+       }
+     DEBUG(PMU_PLUGIN ":   group[%" PRIsz "]", i);
+     DEBUG(PMU_PLUGIN ":     description: %s", cgroup->desc);
+     DEBUG(PMU_PLUGIN ":     cores count: %" PRIsz, cgroup->num_cores);
+     DEBUG(PMU_PLUGIN ":     cores      :%s", cores);
+     sfree(cores);
+   }
+ }
  #endif /* COLLECT_DEBUG */
  
+ static int pmu_validate_cgroups(core_group_t *cgroups, size_t len,
+                                 int max_cores) {
+   /* i - group index, j - core index */
+   for (size_t i = 0; i < len; i++) {
+     for (size_t j = 0; j < cgroups[i].num_cores; j++) {
+       int core = (int)cgroups[i].cores[j];
+       /* Core index cannot exceed number of cores in system,
+          note that max_cores include both online and offline CPUs. */
+       if (core >= max_cores) {
+         ERROR(PMU_PLUGIN ": Core %d is not valid, max core index: %d.", core,
+               max_cores - 1);
+         return -1;
+       }
+     }
+     /* Check if cores are set in remaining groups */
+     for (size_t k = i + 1; k < len; k++)
+       if (config_cores_cmp_cgroups(&cgroups[i], &cgroups[k]) != 0) {
+         ERROR(PMU_PLUGIN ": Same cores cannot be set in different groups.");
+         return -1;
+       }
+   }
+   return 0;
+ }
  static int pmu_config_hw_events(oconfig_item_t *ci) {
  
    if (strcasecmp("HardwareEvents", ci->key) != 0) {
@@@ -253,6 -309,8 +310,8 @@@ static int pmu_config(oconfig_item_t *c
        ret = pmu_config_hw_events(child);
      } else if (strcasecmp("ReportSoftwareEvents", child->key) == 0) {
        ret = cf_util_get_boolean(child, &g_ctx.sw_events);
+     } else if (strcasecmp("Cores", child->key) == 0) {
+       ret = config_cores_parse(child, &g_ctx.cores);
      } else {
        ERROR(PMU_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
        ret = -1;
    return 0;
  }
  
- static void pmu_submit_counter(int cpu, char *event, counter_t value,
-                                meta_data_t *meta) {
+ static void pmu_submit_counter(const char *cgroup, const char *event,
+                                counter_t value, meta_data_t *meta) {
    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 {
+   sstrncpy(vl.plugin_instance, cgroup, sizeof(vl.plugin_instance));
+   if (meta)
      vl.meta = meta;
-     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));
  
@@@ -317,49 -372,65 +373,65 @@@ 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++;
-       /* If there are more events than counters, the kernel uses time
-        * multiplexing. With multiplexing, at the end of the run,
-        * the counter is scaled basing on total time enabled vs time running.
-        * final_count = raw_count * time_enabled/time_running
-        */
-       uint64_t value = event_scaled_value(e, i);
-       all_value += value;
-       /* get meta data with information about scaling */
-       meta_data_t *meta = pmu_meta_data_create(&e->efd[i]);
-       /* dispatch per CPU value */
-       pmu_submit_counter(i, e->event, value, meta);
-       meta_data_destroy(meta);
-     }
+     for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
+       core_group_t *cgroup = g_ctx.cores.cgroups + i;
+       uint64_t cgroup_value = 0;
+       int event_enabled_cgroup = 0;
+       meta_data_t *meta = NULL;
+       for (size_t j = 0; j < cgroup->num_cores; j++) {
+         int core = (int)cgroup->cores[j];
+         if (e->efd[core].fd < 0)
+           continue;
+         event_enabled_cgroup++;
+         /* If there are more events than counters, the kernel uses time
+          * multiplexing. With multiplexing, at the end of the run,
+          * the counter is scaled basing on total time enabled vs time running.
+          * final_count = raw_count * time_enabled/time_running
+          */
+         uint64_t value = event_scaled_value(e, core);
+         cgroup_value += value;
+         /* get meta data with information about scaling */
+         if (cgroup->num_cores == 1)
+           meta = pmu_meta_data_create(&e->efd[core]);
+       }
  
-     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, NULL);
+       if (event_enabled_cgroup > 0) {
+         DEBUG(PMU_PLUGIN ": %s/%s = %lu", e->event, cgroup->desc, cgroup_value);
+         /* dispatch per core group value */
+         pmu_submit_counter(cgroup->desc, e->event, cgroup_value, meta);
+         meta_data_destroy(meta);
+       }
      }
    }
  }
  
  static int pmu_read(__attribute__((unused)) user_data_t *ud) {
    int ret;
+   struct event *e;
  
    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;
+   /* read all events only for configured cores */
+   for (e = g_ctx.event_list->eventlist; e; e = e->next) {
+     for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
+       core_group_t *cgroup = g_ctx.cores.cgroups + i;
+       for (size_t j = 0; j < cgroup->num_cores; j++) {
+         int core = (int)cgroup->cores[j];
+         if (e->efd[core].fd < 0)
+           continue;
+         ret = read_event(e, core);
+         if (ret != 0) {
+           ERROR(PMU_PLUGIN ": Failed to read value of %s/%d event.", e->event,
+                 core);
+           return ret;
+         }
+       }
+     }
    }
  
    pmu_dispatch_data();
@@@ -404,7 -475,7 +476,7 @@@ static int pmu_add_hw_events(struct eve
      if (!events)
        return -1;
  
-     char *s, *tmp;
+     char *s, *tmp = NULL;
      for (s = strtok_r(events, ",", &tmp); s; s = strtok_r(NULL, ",", &tmp)) {
  
        /* Allocate memory for event struct that contains array of efd structs
@@@ -460,6 -531,7 +532,7 @@@ static void pmu_free_events(struct even
  
    while (e) {
      struct event *next = e->next;
+     sfree(e->event);
      sfree(e);
      e = next;
    }
@@@ -474,13 -546,18 +547,18 @@@ static int pmu_setup_events(struct even
  
    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;
+     for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
+       core_group_t *cgroup = g_ctx.cores.cgroups + i;
+       for (size_t j = 0; j < cgroup->num_cores; j++) {
+         int core = (int)cgroup->cores[j];
+         if (setup_event(e, core, leader, measure_all, measure_pid) < 0) {
+           WARNING(PMU_PLUGIN ": perf event '%s' is not available (cpu=%d).",
+                   e->event, core);
+         } else {
+           /* success if at least one event was set */
+           ret = 0;
+         }
        }
      }
  
@@@ -504,6 -581,24 +582,24 @@@ static int pmu_init(void) 
      return -ENOMEM;
    }
  
+   if (g_ctx.cores.num_cgroups == 0) {
+     ret = config_cores_default(g_ctx.event_list->num_cpus, &g_ctx.cores);
+     if (ret != 0) {
+       ERROR(PMU_PLUGIN ": Failed to set default core groups.");
+       goto init_error;
+     }
+   } else {
+     ret = pmu_validate_cgroups(g_ctx.cores.cgroups, g_ctx.cores.num_cgroups,
+                                g_ctx.event_list->num_cpus);
+     if (ret != 0) {
+       ERROR(PMU_PLUGIN ": Invalid core groups configuration.");
+       goto init_error;
+     }
+   }
+ #if COLLECT_DEBUG
+   pmu_dump_cgroups();
+ #endif
    if (g_ctx.hw_cache_events) {
      ret =
          pmu_add_events(g_ctx.event_list, PERF_TYPE_HW_CACHE, g_hw_cache_events,
@@@ -579,6 -674,8 +675,8 @@@ init_error
    sfree(g_ctx.hw_events);
    g_ctx.hw_events_count = 0;
  
+   config_cores_cleanup(&g_ctx.cores);
    return ret;
  }
  
@@@ -594,6 -691,8 +692,8 @@@ static int pmu_shutdown(void) 
    sfree(g_ctx.hw_events);
    g_ctx.hw_events_count = 0;
  
+   config_cores_cleanup(&g_ctx.cores);
    return 0;
  }
  
diff --combined src/intel_rdt.c
@@@ -1,7 -1,7 +1,7 @@@
  /**
   * collectd - src/intel_rdt.c
   *
-  * Copyright(c) 2016 Intel Corporation. All rights reserved.
+  * Copyright(c) 2016-2018 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
   *   Serhiy Pshyk <serhiyx.pshyk@intel.com>
   **/
  
--#include "collectd.h"
  #include "common.h"
 -
+ #include "utils_config_cores.h"
++#include "collectd.h"
  
  #include <pqos.h>
  
@@@ -41,16 -43,9 +42,9 @@@ typedef enum 
    CONFIGURATION_ERROR,
  } rdt_config_status;
  
- struct rdt_core_group_s {
-   char *desc;
-   size_t num_cores;
-   unsigned *cores;
-   enum pqos_mon_event events;
- };
- typedef struct rdt_core_group_s rdt_core_group_t;
  struct rdt_ctx_s {
-   rdt_core_group_t cgroups[RDT_MAX_CORES];
+   core_groups_list_t cores;
+   enum pqos_mon_event events[RDT_MAX_CORES];
    struct pqos_mon_data *pgroups[RDT_MAX_CORES];
    size_t num_groups;
    const struct pqos_cpuinfo *pqos_cpu;
  };
  typedef struct rdt_ctx_s rdt_ctx_t;
  
 -static rdt_ctx_t *g_rdt = NULL;
 +static rdt_ctx_t *g_rdt;
  
  static rdt_config_status g_state = UNKNOWN;
  
- static int isdup(const uint64_t *nums, size_t size, uint64_t val) {
-   for (size_t i = 0; i < size; i++)
-     if (nums[i] == val)
-       return 1;
-   return 0;
- }
- static int strtouint64(const char *s, uint64_t *n) {
-   char *endptr = NULL;
-   assert(s != NULL);
-   assert(n != NULL);
-   *n = strtoull(s, &endptr, 0);
-   if (!(*s != '\0' && *endptr == '\0')) {
-     DEBUG(RDT_PLUGIN ": Error converting '%s' to unsigned number.", s);
-     return -EINVAL;
-   }
-   return 0;
- }
- /*
-  * NAME
-  *   strlisttonums
-  *
-  * DESCRIPTION
-  *   Converts string of characters representing list of numbers into array of
-  *   numbers. Allowed formats are:
-  *     0,1,2,3
-  *     0-10,20-18
-  *     1,3,5-8,10,0x10-12
-  *
-  *   Numbers can be in decimal or hexadecimal format.
-  *
-  * PARAMETERS
-  *   `s'         String representing list of unsigned numbers.
-  *   `nums'      Array to put converted numeric values into.
-  *   `max'       Maximum number of elements that nums can accommodate.
-  *
-  * RETURN VALUE
-  *    Number of elements placed into nums.
-  */
- static size_t strlisttonums(char *s, uint64_t *nums, size_t max) {
-   int ret;
-   size_t index = 0;
-   char *saveptr = NULL;
-   if (s == NULL || nums == NULL || max == 0)
-     return index;
-   for (;;) {
-     char *p = NULL;
-     char *token = NULL;
-     token = strtok_r(s, ",", &saveptr);
-     if (token == NULL)
-       break;
-     s = NULL;
-     while (isspace(*token))
-       token++;
-     if (*token == '\0')
-       continue;
-     p = strchr(token, '-');
-     if (p != NULL) {
-       uint64_t n, start, end;
-       *p = '\0';
-       ret = strtouint64(token, &start);
-       if (ret < 0)
-         return 0;
-       ret = strtouint64(p + 1, &end);
-       if (ret < 0)
-         return 0;
-       if (start > end) {
-         return 0;
-       }
-       for (n = start; n <= end; n++) {
-         if (!(isdup(nums, index, n))) {
-           nums[index] = n;
-           index++;
-         }
-         if (index >= max)
-           return index;
-       }
-     } else {
-       uint64_t val;
-       ret = strtouint64(token, &val);
-       if (ret < 0)
-         return 0;
-       if (!(isdup(nums, index, val))) {
-         nums[index] = val;
-         index++;
-       }
-       if (index >= max)
-         return index;
-     }
-   }
-   return index;
- }
- /*
-  * NAME
-  *   cgroup_cmp
-  *
-  * DESCRIPTION
-  *   Function to compare cores in 2 core groups.
-  *
-  * PARAMETERS
-  *   `cg_a'      Pointer to core group a.
-  *   `cg_b'      Pointer to core group b.
-  *
-  * RETURN VALUE
-  *    1 if both groups contain the same cores
-  *    0 if none of their cores match
-  *    -1 if some but not all cores match
-  */
- static int cgroup_cmp(const rdt_core_group_t *cg_a,
-                       const rdt_core_group_t *cg_b) {
-   int found = 0;
-   assert(cg_a != NULL);
-   assert(cg_b != NULL);
-   const int sz_a = cg_a->num_cores;
-   const int sz_b = cg_b->num_cores;
-   const unsigned *tab_a = cg_a->cores;
-   const unsigned *tab_b = cg_b->cores;
-   for (int i = 0; i < sz_a; i++) {
-     for (int j = 0; j < sz_b; j++)
-       if (tab_a[i] == tab_b[j])
-         found++;
-   }
-   /* if no cores are the same */
-   if (!found)
-     return 0;
-   /* if group contains same cores */
-   if (sz_a == sz_b && sz_b == found)
-     return 1;
-   /* if not all cores are the same */
-   return -1;
- }
- static int cgroup_set(rdt_core_group_t *cg, char *desc, uint64_t *cores,
-                       size_t num_cores) {
-   assert(cg != NULL);
-   assert(desc != NULL);
-   assert(cores != NULL);
-   assert(num_cores > 0);
-   cg->cores = calloc(num_cores, sizeof(unsigned));
-   if (cg->cores == NULL) {
-     ERROR(RDT_PLUGIN ": Error allocating core group table");
-     return -ENOMEM;
-   }
-   cg->num_cores = num_cores;
-   cg->desc = strdup(desc);
-   if (cg->desc == NULL) {
-     ERROR(RDT_PLUGIN ": Error allocating core group description");
-     sfree(cg->cores);
-     return -ENOMEM;
-   }
-   for (size_t i = 0; i < num_cores; i++)
-     cg->cores[i] = (unsigned)cores[i];
-   return 0;
- }
- /*
-  * NAME
-  *   oconfig_to_cgroups
-  *
-  * DESCRIPTION
-  *   Function to set the descriptions and cores for each core group.
-  *   Takes a config option containing list of strings that are used to set
-  *   core group values.
-  *
-  * PARAMETERS
-  *   `item'        Config option containing core groups.
-  *   `groups'      Table of core groups to set values in.
-  *   `max_groups'  Maximum number of core groups allowed.
-  *
-  * RETURN VALUE
-  *   On success, the number of core groups set up. On error, appropriate
-  *   negative error value.
-  */
- static int oconfig_to_cgroups(oconfig_item_t *item, rdt_core_group_t *groups,
-                               size_t max_groups) {
-   int index = 0;
-   assert(groups != NULL);
-   assert(max_groups > 0);
-   assert(item != NULL);
-   for (int j = 0; j < item->values_num; j++) {
-     int ret;
-     size_t n;
-     uint64_t cores[RDT_MAX_CORES] = {0};
-     char value[DATA_MAX_NAME_LEN];
-     if ((item->values[j].value.string == NULL) ||
-         (strlen(item->values[j].value.string) == 0))
-       continue;
-     sstrncpy(value, item->values[j].value.string, sizeof(value));
-     n = strlisttonums(value, cores, STATIC_ARRAY_SIZE(cores));
-     if (n == 0) {
-       ERROR(RDT_PLUGIN ": Error parsing core group (%s)",
-             item->values[j].value.string);
-       return -EINVAL;
-     }
-     /* set core group info */
-     ret = cgroup_set(&groups[index], item->values[j].value.string, cores, n);
-     if (ret < 0)
-       return ret;
-     index++;
-     if (index >= max_groups) {
-       WARNING(RDT_PLUGIN ": Too many core groups configured");
-       return index;
-     }
-   }
-   return index;
- }
  #if COLLECT_DEBUG
  static void rdt_dump_cgroups(void) {
    char cores[RDT_MAX_CORES * 4];
      return;
  
    DEBUG(RDT_PLUGIN ": Core Groups Dump");
 -  DEBUG(RDT_PLUGIN ":  groups count: %zu", g_rdt->num_groups);
 +  DEBUG(RDT_PLUGIN ":  groups count: %" PRIsz, g_rdt->num_groups);
  
    for (int i = 0; i < g_rdt->num_groups; i++) {
+     core_group_t *cgroup = g_rdt->cores.cgroups + i;
  
      memset(cores, 0, sizeof(cores));
-     for (int j = 0; j < g_rdt->cgroups[i].num_cores; j++) {
+     for (int j = 0; j < cgroup->num_cores; j++) {
        snprintf(cores + strlen(cores), sizeof(cores) - strlen(cores) - 1, " %d",
-                g_rdt->cgroups[i].cores[j]);
+                cgroup->cores[j]);
      }
  
      DEBUG(RDT_PLUGIN ":  group[%d]:", i);
-     DEBUG(RDT_PLUGIN ":    description: %s", g_rdt->cgroups[i].desc);
+     DEBUG(RDT_PLUGIN ":    description: %s", cgroup->desc);
      DEBUG(RDT_PLUGIN ":    cores: %s", cores);
-     DEBUG(RDT_PLUGIN ":    events: 0x%X", g_rdt->cgroups[i].events);
+     DEBUG(RDT_PLUGIN ":    events: 0x%X", g_rdt->events[i]);
    }
  
    return;
@@@ -350,40 -109,54 +108,54 @@@ static void rdt_dump_data(void) 
      double mbr = bytes_to_mb(pv->mbm_remote_delta);
      double mbl = bytes_to_mb(pv->mbm_local_delta);
  
-     DEBUG(" [%s] %8u %10.1f %10.1f %10.1f", g_rdt->cgroups[i].desc,
+     DEBUG(" [%s] %8u %10.1f %10.1f %10.1f", g_rdt->cores.cgroups[i].desc,
            g_rdt->pgroups[i]->poll_ctx[0].rmid, llc, mbl, mbr);
    }
  }
  #endif /* COLLECT_DEBUG */
  
  static void rdt_free_cgroups(void) {
+   config_cores_cleanup(&g_rdt->cores);
    for (int i = 0; i < RDT_MAX_CORES; i++) {
-     sfree(g_rdt->cgroups[i].desc);
-     sfree(g_rdt->cgroups[i].cores);
-     g_rdt->cgroups[i].num_cores = 0;
      sfree(g_rdt->pgroups[i]);
    }
  }
  
  static int rdt_default_cgroups(void) {
-   int ret;
+   unsigned num_cores = g_rdt->pqos_cpu->num_cores;
+   g_rdt->cores.cgroups = calloc(num_cores, sizeof(*(g_rdt->cores.cgroups)));
+   if (g_rdt->cores.cgroups == NULL) {
+     ERROR(RDT_PLUGIN ": Error allocating core groups array");
+     return -ENOMEM;
+   }
+   g_rdt->cores.num_cgroups = num_cores;
  
    /* configure each core in separate group */
-   for (unsigned i = 0; i < g_rdt->pqos_cpu->num_cores; i++) {
+   for (unsigned i = 0; i < num_cores; i++) {
+     core_group_t *cgroup = g_rdt->cores.cgroups + i;
      char desc[DATA_MAX_NAME_LEN];
-     uint64_t core = i;
-     snprintf(desc, sizeof(desc), "%d", g_rdt->pqos_cpu->cores[i].lcore);
  
      /* set core group info */
-     ret = cgroup_set(&g_rdt->cgroups[i], desc, &core, 1);
-     if (ret < 0)
-       return ret;
+     cgroup->cores = calloc(1, sizeof(*(cgroup->cores)));
+     if (cgroup->cores == NULL) {
+       ERROR(RDT_PLUGIN ": Error allocating cores array");
+       rdt_free_cgroups();
+       return -ENOMEM;
+     }
+     cgroup->num_cores = 1;
+     cgroup->cores[0] = i;
+     snprintf(desc, sizeof(desc), "%d", g_rdt->pqos_cpu->cores[i].lcore);
+     cgroup->desc = strdup(desc);
+     if (cgroup->desc == NULL) {
+       ERROR(RDT_PLUGIN ": Error allocating core group description");
+       rdt_free_cgroups();
+       return -ENOMEM;
+     }
    }
  
-   return g_rdt->pqos_cpu->num_cores;
+   return num_cores;
  }
  
  static int rdt_is_core_id_valid(int core_id) {
  }
  
  static int rdt_config_cgroups(oconfig_item_t *item) {
-   int n = 0;
+   size_t n = 0;
    enum pqos_mon_event events = 0;
  
-   if (item == NULL) {
-     DEBUG(RDT_PLUGIN ": cgroups_config: Invalid argument.");
-     return -EINVAL;
-   }
-   DEBUG(RDT_PLUGIN ": Core groups [%d]:", item->values_num);
-   for (int j = 0; j < item->values_num; j++) {
-     if (item->values[j].type != OCONFIG_TYPE_STRING) {
-       ERROR(RDT_PLUGIN ": given core group value is not a string [idx=%d]", j);
-       return -EINVAL;
-     }
-     DEBUG(RDT_PLUGIN ":  [%d]: %s", j, item->values[j].value.string);
-   }
-   n = oconfig_to_cgroups(item, g_rdt->cgroups, g_rdt->pqos_cpu->num_cores);
-   if (n < 0) {
+   if (config_cores_parse(item, &g_rdt->cores) < 0) {
      rdt_free_cgroups();
      ERROR(RDT_PLUGIN ": Error parsing core groups configuration.");
      return -EINVAL;
    }
+   n = g_rdt->cores.num_cgroups;
  
    /* validate configured core id values */
-   for (int group_idx = 0; group_idx < n; group_idx++) {
-     for (int core_idx = 0; core_idx < g_rdt->cgroups[group_idx].num_cores;
-          core_idx++) {
-       if (!rdt_is_core_id_valid(g_rdt->cgroups[group_idx].cores[core_idx])) {
+   for (size_t group_idx = 0; group_idx < n; group_idx++) {
+     core_group_t *cgroup = g_rdt->cores.cgroups + group_idx;
+     for (size_t core_idx = 0; core_idx < cgroup->num_cores; core_idx++) {
+       if (!rdt_is_core_id_valid((int)cgroup->cores[core_idx])) {
          ERROR(RDT_PLUGIN ": Core group '%s' contains invalid core id '%d'",
-               g_rdt->cgroups[group_idx].desc,
-               (int)g_rdt->cgroups[group_idx].cores[core_idx]);
+               cgroup->desc, (int)cgroup->cores[core_idx]);
          rdt_free_cgroups();
          return -EINVAL;
        }
  
    if (n == 0) {
      /* create default core groups if "Cores" config option is empty */
-     n = rdt_default_cgroups();
-     if (n < 0) {
+     int ret = rdt_default_cgroups();
+     if (ret < 0) {
        rdt_free_cgroups();
        ERROR(RDT_PLUGIN ": Error creating default core groups configuration.");
-       return n;
+       return ret;
      }
+     n = (size_t)ret;
      INFO(RDT_PLUGIN
           ": No core groups configured. Default core groups created.");
    }
    DEBUG(RDT_PLUGIN ": Available events to monitor: %#x", events);
  
    g_rdt->num_groups = n;
-   for (int i = 0; i < n; i++) {
-     for (int j = 0; j < i; j++) {
+   for (size_t i = 0; i < n; i++) {
+     for (size_t j = 0; j < i; j++) {
        int found = 0;
-       found = cgroup_cmp(&g_rdt->cgroups[j], &g_rdt->cgroups[i]);
+       found = config_cores_cmp_cgroups(&g_rdt->cores.cgroups[j],
+                                        &g_rdt->cores.cgroups[i]);
        if (found != 0) {
          rdt_free_cgroups();
          ERROR(RDT_PLUGIN ": Cannot monitor same cores in different groups.");
        }
      }
  
-     g_rdt->cgroups[i].events = events;
+     g_rdt->events[i] = events;
      g_rdt->pgroups[i] = calloc(1, sizeof(*g_rdt->pgroups[i]));
      if (g_rdt->pgroups[i] == NULL) {
        rdt_free_cgroups();
@@@ -628,6 -388,8 +387,8 @@@ static int rdt_read(__attribute__((unus
  #endif /* COLLECT_DEBUG */
  
    for (int i = 0; i < g_rdt->num_groups; i++) {
+     core_group_t *cgroup = g_rdt->cores.cgroups + i;
      enum pqos_mon_event mbm_events =
          (PQOS_MON_EVENT_LMEM_BW | PQOS_MON_EVENT_TMEM_BW |
           PQOS_MON_EVENT_RMEM_BW);
  
      /* Submit only monitored events data */
  
-     if (g_rdt->cgroups[i].events & PQOS_MON_EVENT_L3_OCCUP)
-       rdt_submit_gauge(g_rdt->cgroups[i].desc, "bytes", "llc", pv->llc);
+     if (g_rdt->events[i] & PQOS_MON_EVENT_L3_OCCUP)
+       rdt_submit_gauge(cgroup->desc, "bytes", "llc", pv->llc);
  
-     if (g_rdt->cgroups[i].events & PQOS_PERF_EVENT_IPC)
-       rdt_submit_gauge(g_rdt->cgroups[i].desc, "ipc", NULL, pv->ipc);
+     if (g_rdt->events[i] & PQOS_PERF_EVENT_IPC)
+       rdt_submit_gauge(cgroup->desc, "ipc", NULL, pv->ipc);
  
-     if (g_rdt->cgroups[i].events & mbm_events) {
-       rdt_submit_derive(g_rdt->cgroups[i].desc, "memory_bandwidth", "local",
+     if (g_rdt->events[i] & mbm_events) {
+       rdt_submit_derive(cgroup->desc, "memory_bandwidth", "local",
                          pv->mbm_local_delta);
-       rdt_submit_derive(g_rdt->cgroups[i].desc, "memory_bandwidth", "remote",
+       rdt_submit_derive(cgroup->desc, "memory_bandwidth", "remote",
                          pv->mbm_remote_delta);
      }
    }
@@@ -665,10 -427,10 +426,10 @@@ static int rdt_init(void) 
  
    /* Start monitoring */
    for (int i = 0; i < g_rdt->num_groups; i++) {
-     rdt_core_group_t *cg = &g_rdt->cgroups[i];
+     core_group_t *cg = g_rdt->cores.cgroups + i;
  
-     ret = pqos_mon_start(cg->num_cores, cg->cores, cg->events, (void *)cg->desc,
-                          g_rdt->pgroups[i]);
+     ret = pqos_mon_start(cg->num_cores, cg->cores, g_rdt->events[i],
+                          (void *)cg->desc, g_rdt->pgroups[i]);
  
      if (ret != PQOS_RETVAL_OK)
        ERROR(RDT_PLUGIN ": Error starting monitoring group %s (pqos status=%d)",