intel_pmu: fix epel6 compilation issue
[collectd.git] / src / intel_pmu.c
1 /**
2  * collectd - src/intel_pmu.c
3  *
4  * Copyright(c) 2017 Intel Corporation. All rights reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  * Authors:
25  *   Serhiy Pshyk <serhiyx.pshyk@intel.com>
26  *   Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
27  **/
28
29 #include "collectd.h"
30 #include "common.h"
31
32 #include "utils_config_cores.h"
33
34 #include <jevents.h>
35 #include <jsession.h>
36
37 #define PMU_PLUGIN "intel_pmu"
38
39 #define HW_CACHE_READ_ACCESS                                                   \
40   (((PERF_COUNT_HW_CACHE_OP_READ) << 8) |                                      \
41    ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
42
43 #define HW_CACHE_WRITE_ACCESS                                                  \
44   (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) |                                     \
45    ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
46
47 #define HW_CACHE_PREFETCH_ACCESS                                               \
48   (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) |                                  \
49    ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
50
51 #define HW_CACHE_READ_MISS                                                     \
52   (((PERF_COUNT_HW_CACHE_OP_READ) << 8) |                                      \
53    ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
54
55 #define HW_CACHE_WRITE_MISS                                                    \
56   (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) |                                     \
57    ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
58
59 #define HW_CACHE_PREFETCH_MISS                                                 \
60   (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) |                                  \
61    ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
62
63 struct event_info {
64   char *name;
65   uint64_t config;
66 };
67 typedef struct event_info event_info_t;
68
69 struct intel_pmu_ctx_s {
70   _Bool hw_cache_events;
71   _Bool kernel_pmu_events;
72   _Bool sw_events;
73   char event_list_fn[PATH_MAX];
74   char **hw_events;
75   size_t hw_events_count;
76   core_groups_list_t cores;
77   struct eventlist *event_list;
78 };
79 typedef struct intel_pmu_ctx_s intel_pmu_ctx_t;
80
81 event_info_t g_kernel_pmu_events[] = {
82     {.name = "cpu-cycles", .config = PERF_COUNT_HW_CPU_CYCLES},
83     {.name = "instructions", .config = PERF_COUNT_HW_INSTRUCTIONS},
84     {.name = "cache-references", .config = PERF_COUNT_HW_CACHE_REFERENCES},
85     {.name = "cache-misses", .config = PERF_COUNT_HW_CACHE_MISSES},
86     {.name = "branches", .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS},
87     {.name = "branch-misses", .config = PERF_COUNT_HW_BRANCH_MISSES},
88     {.name = "bus-cycles", .config = PERF_COUNT_HW_BUS_CYCLES},
89 };
90
91 event_info_t g_hw_cache_events[] = {
92
93     {.name = "L1-dcache-loads",
94      .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_ACCESS)},
95     {.name = "L1-dcache-load-misses",
96      .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_MISS)},
97     {.name = "L1-dcache-stores",
98      .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_ACCESS)},
99     {.name = "L1-dcache-store-misses",
100      .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_MISS)},
101     {.name = "L1-dcache-prefetches",
102      .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_ACCESS)},
103     {.name = "L1-dcache-prefetch-misses",
104      .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_MISS)},
105
106     {.name = "L1-icache-loads",
107      .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_ACCESS)},
108     {.name = "L1-icache-load-misses",
109      .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_MISS)},
110     {.name = "L1-icache-prefetches",
111      .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_ACCESS)},
112     {.name = "L1-icache-prefetch-misses",
113      .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_MISS)},
114
115     {.name = "LLC-loads",
116      .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_ACCESS)},
117     {.name = "LLC-load-misses",
118      .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_MISS)},
119     {.name = "LLC-stores",
120      .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_ACCESS)},
121     {.name = "LLC-store-misses",
122      .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_MISS)},
123     {.name = "LLC-prefetches",
124      .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_ACCESS)},
125     {.name = "LLC-prefetch-misses",
126      .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_MISS)},
127
128     {.name = "dTLB-loads",
129      .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_ACCESS)},
130     {.name = "dTLB-load-misses",
131      .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_MISS)},
132     {.name = "dTLB-stores",
133      .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_ACCESS)},
134     {.name = "dTLB-store-misses",
135      .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_MISS)},
136     {.name = "dTLB-prefetches",
137      .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_ACCESS)},
138     {.name = "dTLB-prefetch-misses",
139      .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_MISS)},
140
141     {.name = "iTLB-loads",
142      .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_ACCESS)},
143     {.name = "iTLB-load-misses",
144      .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_MISS)},
145
146     {.name = "branch-loads",
147      .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_ACCESS)},
148     {.name = "branch-load-misses",
149      .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_MISS)},
150 };
151
152 event_info_t g_sw_events[] = {
153     {.name = "cpu-clock", .config = PERF_COUNT_SW_CPU_CLOCK},
154
155     {.name = "task-clock", .config = PERF_COUNT_SW_TASK_CLOCK},
156
157     {.name = "context-switches", .config = PERF_COUNT_SW_CONTEXT_SWITCHES},
158
159     {.name = "cpu-migrations", .config = PERF_COUNT_SW_CPU_MIGRATIONS},
160
161     {.name = "page-faults", .config = PERF_COUNT_SW_PAGE_FAULTS},
162
163     {.name = "minor-faults", .config = PERF_COUNT_SW_PAGE_FAULTS_MIN},
164
165     {.name = "major-faults", .config = PERF_COUNT_SW_PAGE_FAULTS_MAJ},
166
167     {.name = "alignment-faults", .config = PERF_COUNT_SW_ALIGNMENT_FAULTS},
168
169     {.name = "emulation-faults", .config = PERF_COUNT_SW_EMULATION_FAULTS},
170 };
171
172 static intel_pmu_ctx_t g_ctx;
173
174 #if COLLECT_DEBUG
175 static void pmu_dump_events() {
176
177   DEBUG(PMU_PLUGIN ": Events:");
178
179   struct event *e;
180
181   for (e = g_ctx.event_list->eventlist; e; e = e->next) {
182     DEBUG(PMU_PLUGIN ":   event       : %s", e->event);
183     DEBUG(PMU_PLUGIN ":     group_lead: %d", e->group_leader);
184     DEBUG(PMU_PLUGIN ":     end_group : %d", e->end_group);
185     DEBUG(PMU_PLUGIN ":     type      : %#x", e->attr.type);
186     DEBUG(PMU_PLUGIN ":     config    : %#x", (unsigned)e->attr.config);
187     DEBUG(PMU_PLUGIN ":     size      : %d", e->attr.size);
188   }
189 }
190
191 static void pmu_dump_config(void) {
192
193   DEBUG(PMU_PLUGIN ": Config:");
194   DEBUG(PMU_PLUGIN ":   hw_cache_events   : %d", g_ctx.hw_cache_events);
195   DEBUG(PMU_PLUGIN ":   kernel_pmu_events : %d", g_ctx.kernel_pmu_events);
196   DEBUG(PMU_PLUGIN ":   software_events   : %d", g_ctx.sw_events);
197
198   for (size_t i = 0; i < g_ctx.hw_events_count; i++) {
199     DEBUG(PMU_PLUGIN ":   hardware_events[%zu]: %s", i, g_ctx.hw_events[i]);
200   }
201 }
202
203 static void pmu_dump_cgroups(void) {
204
205   DEBUG(PMU_PLUGIN ": Core groups:");
206
207   for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
208     core_group_t *cgroup = g_ctx.cores.cgroups + i;
209     const size_t cores_size = cgroup->num_cores * 4 + 1;
210     char *cores = calloc(cores_size, sizeof(*cores));
211     if (cores == NULL) {
212       DEBUG(PMU_PLUGIN ": Failed to allocate string to list cores.");
213       return;
214     }
215     for (size_t j = 0; j < cgroup->num_cores; j++)
216       snprintf(cores + strlen(cores), cores_size - strlen(cores), " %d",
217                cgroup->cores[j]);
218
219     DEBUG(PMU_PLUGIN ":   group[%" PRIsz "]", i);
220     DEBUG(PMU_PLUGIN ":     description: %s", cgroup->desc);
221     DEBUG(PMU_PLUGIN ":     cores count: %" PRIsz, cgroup->num_cores);
222     DEBUG(PMU_PLUGIN ":     cores      :%s", cores);
223     sfree(cores);
224   }
225 }
226
227 #endif /* COLLECT_DEBUG */
228
229 static int pmu_validate_cgroups(core_group_t *cgroups, size_t len,
230                                 int max_cores) {
231   /* i - group index, j - core index */
232   for (size_t i = 0; i < len; i++) {
233     for (size_t j = 0; j < cgroups[i].num_cores; j++) {
234       int core = (int)cgroups[i].cores[j];
235
236       /* Core index cannot exceed number of cores in system,
237          note that max_cores include both online and offline CPUs. */
238       if (core >= max_cores) {
239         ERROR(PMU_PLUGIN ": Core %d is not valid, max core index: %d.", core,
240               max_cores - 1);
241         return -1;
242       }
243     }
244     /* Check if cores are set in remaining groups */
245     for (size_t k = i + 1; k < len; k++)
246       if (config_cores_cmp_cgroups(&cgroups[i], &cgroups[k]) != 0) {
247         ERROR(PMU_PLUGIN ": Same cores cannot be set in different groups.");
248         return -1;
249       }
250   }
251   return 0;
252 }
253
254 static int pmu_config_hw_events(oconfig_item_t *ci) {
255
256   if (strcasecmp("HardwareEvents", ci->key) != 0) {
257     return -EINVAL;
258   }
259
260   if (g_ctx.hw_events) {
261     ERROR(PMU_PLUGIN ": Duplicate config for HardwareEvents.");
262     return -EINVAL;
263   }
264
265   g_ctx.hw_events = calloc(ci->values_num, sizeof(char *));
266   if (g_ctx.hw_events == NULL) {
267     ERROR(PMU_PLUGIN ": Failed to allocate hw events.");
268     return -ENOMEM;
269   }
270
271   for (int i = 0; i < ci->values_num; i++) {
272     if (ci->values[i].type != OCONFIG_TYPE_STRING) {
273       WARNING(PMU_PLUGIN ": The %s option requires string arguments.", ci->key);
274       continue;
275     }
276
277     g_ctx.hw_events[g_ctx.hw_events_count] = strdup(ci->values[i].value.string);
278     if (g_ctx.hw_events[g_ctx.hw_events_count] == NULL) {
279       ERROR(PMU_PLUGIN ": Failed to allocate hw events entry.");
280       return -ENOMEM;
281     }
282
283     g_ctx.hw_events_count++;
284   }
285
286   return 0;
287 }
288
289 static int pmu_config(oconfig_item_t *ci) {
290
291   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
292
293   for (int i = 0; i < ci->children_num; i++) {
294     int ret = 0;
295     oconfig_item_t *child = ci->children + i;
296
297     if (strcasecmp("ReportHardwareCacheEvents", child->key) == 0) {
298       ret = cf_util_get_boolean(child, &g_ctx.hw_cache_events);
299     } else if (strcasecmp("ReportKernelPMUEvents", child->key) == 0) {
300       ret = cf_util_get_boolean(child, &g_ctx.kernel_pmu_events);
301     } else if (strcasecmp("EventList", child->key) == 0) {
302       ret = cf_util_get_string_buffer(child, g_ctx.event_list_fn,
303                                       sizeof(g_ctx.event_list_fn));
304     } else if (strcasecmp("HardwareEvents", child->key) == 0) {
305       ret = pmu_config_hw_events(child);
306     } else if (strcasecmp("ReportSoftwareEvents", child->key) == 0) {
307       ret = cf_util_get_boolean(child, &g_ctx.sw_events);
308     } else if (strcasecmp("Cores", child->key) == 0) {
309       ret = config_cores_parse(child, &g_ctx.cores);
310     } else {
311       ERROR(PMU_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
312       ret = -1;
313     }
314
315     if (ret != 0) {
316       DEBUG(PMU_PLUGIN ": %s:%d ret=%d", __FUNCTION__, __LINE__, ret);
317       return ret;
318     }
319   }
320
321 #if COLLECT_DEBUG
322   pmu_dump_config();
323 #endif
324
325   return 0;
326 }
327
328 static void pmu_submit_counter(char *cgroup, char *event, counter_t value,
329                                meta_data_t *meta) {
330   value_list_t vl = VALUE_LIST_INIT;
331
332   vl.values = &(value_t){.counter = value};
333   vl.values_len = 1;
334
335   sstrncpy(vl.plugin, PMU_PLUGIN, sizeof(vl.plugin));
336   sstrncpy(vl.plugin_instance, cgroup, sizeof(vl.plugin_instance));
337   if (meta)
338     vl.meta = meta;
339   sstrncpy(vl.type, "counter", sizeof(vl.type));
340   sstrncpy(vl.type_instance, event, sizeof(vl.type_instance));
341
342   plugin_dispatch_values(&vl);
343 }
344
345 meta_data_t *pmu_meta_data_create(const struct efd *efd) {
346   meta_data_t *meta = NULL;
347
348   /* create meta data only if value was scaled */
349   if (efd->val[1] == efd->val[2] || !efd->val[2]) {
350     return NULL;
351   }
352
353   meta = meta_data_create();
354   if (meta == NULL) {
355     ERROR(PMU_PLUGIN ": meta_data_create failed.");
356     return NULL;
357   }
358
359   meta_data_add_unsigned_int(meta, "intel_pmu:raw_count", efd->val[0]);
360   meta_data_add_unsigned_int(meta, "intel_pmu:time_enabled", efd->val[1]);
361   meta_data_add_unsigned_int(meta, "intel_pmu:time_running", efd->val[2]);
362
363   return meta;
364 }
365
366 static void pmu_dispatch_data(void) {
367
368   struct event *e;
369
370   for (e = g_ctx.event_list->eventlist; e; e = e->next) {
371     for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
372       core_group_t *cgroup = g_ctx.cores.cgroups + i;
373       uint64_t cgroup_value = 0;
374       int event_enabled_cgroup = 0;
375       meta_data_t *meta = NULL;
376
377       for (size_t j = 0; j < cgroup->num_cores; j++) {
378         int core = (int)cgroup->cores[j];
379         if (e->efd[core].fd < 0)
380           continue;
381
382         event_enabled_cgroup++;
383
384         /* If there are more events than counters, the kernel uses time
385          * multiplexing. With multiplexing, at the end of the run,
386          * the counter is scaled basing on total time enabled vs time running.
387          * final_count = raw_count * time_enabled/time_running
388          */
389         uint64_t value = event_scaled_value(e, core);
390         cgroup_value += value;
391
392         /* get meta data with information about scaling */
393         if (cgroup->num_cores == 1)
394           meta = pmu_meta_data_create(&e->efd[core]);
395       }
396
397       if (event_enabled_cgroup > 0) {
398         DEBUG(PMU_PLUGIN ": %s/%s = %lu", e->event, cgroup->desc, cgroup_value);
399         /* dispatch per core group value */
400         pmu_submit_counter(cgroup->desc, e->event, cgroup_value, meta);
401         meta_data_destroy(meta);
402       }
403     }
404   }
405 }
406
407 static int pmu_read(__attribute__((unused)) user_data_t *ud) {
408   int ret;
409   struct event *e;
410
411   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
412
413   /* read all events only for configured cores */
414   for (e = g_ctx.event_list->eventlist; e; e = e->next) {
415     for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
416       core_group_t *cgroup = g_ctx.cores.cgroups + i;
417       for (size_t j = 0; j < cgroup->num_cores; j++) {
418         int core = (int)cgroup->cores[j];
419         if (e->efd[core].fd < 0)
420           continue;
421
422         ret = read_event(e, core);
423         if (ret != 0) {
424           ERROR(PMU_PLUGIN ": Failed to read value of %s/%d event.", e->event,
425                 core);
426           return ret;
427         }
428       }
429     }
430   }
431
432   pmu_dispatch_data();
433
434   return 0;
435 }
436
437 static int pmu_add_events(struct eventlist *el, uint32_t type,
438                           event_info_t *events, size_t count) {
439
440   for (size_t i = 0; i < count; i++) {
441     /* Allocate memory for event struct that contains array of efd structs
442        for all cores */
443     struct event *e =
444         calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1);
445     if (e == NULL) {
446       ERROR(PMU_PLUGIN ": Failed to allocate event structure");
447       return -ENOMEM;
448     }
449
450     e->attr.type = type;
451     e->attr.config = events[i].config;
452     e->attr.size = PERF_ATTR_SIZE_VER0;
453     if (!el->eventlist)
454       el->eventlist = e;
455     if (el->eventlist_last)
456       el->eventlist_last->next = e;
457     el->eventlist_last = e;
458     e->event = strdup(events[i].name);
459   }
460
461   return 0;
462 }
463
464 static int pmu_add_hw_events(struct eventlist *el, char **e, size_t count) {
465
466   for (size_t i = 0; i < count; i++) {
467
468     size_t group_events_count = 0;
469
470     char *events = strdup(e[i]);
471     if (!events)
472       return -1;
473
474     char *s, *tmp = NULL;
475     for (s = strtok_r(events, ",", &tmp); s; s = strtok_r(NULL, ",", &tmp)) {
476
477       /* Allocate memory for event struct that contains array of efd structs
478          for all cores */
479       struct event *e =
480           calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1);
481       if (e == NULL) {
482         free(events);
483         return -ENOMEM;
484       }
485
486       if (resolve_event(s, &e->attr) != 0) {
487         WARNING(PMU_PLUGIN ": Cannot resolve %s", s);
488         sfree(e);
489         continue;
490       }
491
492       /* Multiple events parsed in one entry */
493       if (group_events_count == 1) {
494         /* Mark previously added event as group leader */
495         el->eventlist_last->group_leader = 1;
496       }
497
498       e->next = NULL;
499       if (!el->eventlist)
500         el->eventlist = e;
501       if (el->eventlist_last)
502         el->eventlist_last->next = e;
503       el->eventlist_last = e;
504       e->event = strdup(s);
505
506       group_events_count++;
507     }
508
509     /* Multiple events parsed in one entry */
510     if (group_events_count > 1) {
511       /* Mark last added event as group end */
512       el->eventlist_last->end_group = 1;
513     }
514
515     free(events);
516   }
517
518   return 0;
519 }
520
521 static void pmu_free_events(struct eventlist *el) {
522
523   if (el == NULL)
524     return;
525
526   struct event *e = el->eventlist;
527
528   while (e) {
529     struct event *next = e->next;
530     sfree(e->event);
531     sfree(e);
532     e = next;
533   }
534
535   el->eventlist = NULL;
536 }
537
538 static int pmu_setup_events(struct eventlist *el, bool measure_all,
539                             int measure_pid) {
540   struct event *e, *leader = NULL;
541   int ret = -1;
542
543   for (e = el->eventlist; e; e = e->next) {
544
545     for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
546       core_group_t *cgroup = g_ctx.cores.cgroups + i;
547       for (size_t j = 0; j < cgroup->num_cores; j++) {
548         int core = (int)cgroup->cores[j];
549
550         if (setup_event(e, core, leader, measure_all, measure_pid) < 0) {
551           WARNING(PMU_PLUGIN ": perf event '%s' is not available (cpu=%d).",
552                   e->event, core);
553         } else {
554           /* success if at least one event was set */
555           ret = 0;
556         }
557       }
558     }
559
560     if (e->group_leader)
561       leader = e;
562     if (e->end_group)
563       leader = NULL;
564   }
565
566   return ret;
567 }
568
569 static int pmu_init(void) {
570   int ret;
571
572   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
573
574   g_ctx.event_list = alloc_eventlist();
575   if (g_ctx.event_list == NULL) {
576     ERROR(PMU_PLUGIN ": Failed to allocate event list.");
577     return -ENOMEM;
578   }
579
580   if (g_ctx.cores.num_cgroups == 0) {
581     ret = config_cores_default(g_ctx.event_list->num_cpus, &g_ctx.cores);
582     if (ret != 0) {
583       ERROR(PMU_PLUGIN ": Failed to set default core groups.");
584       goto init_error;
585     }
586   } else {
587     ret = pmu_validate_cgroups(g_ctx.cores.cgroups, g_ctx.cores.num_cgroups,
588                                g_ctx.event_list->num_cpus);
589     if (ret != 0) {
590       ERROR(PMU_PLUGIN ": Invalid core groups configuration.");
591       goto init_error;
592     }
593   }
594 #if COLLECT_DEBUG
595   pmu_dump_cgroups();
596 #endif
597
598   if (g_ctx.hw_cache_events) {
599     ret =
600         pmu_add_events(g_ctx.event_list, PERF_TYPE_HW_CACHE, g_hw_cache_events,
601                        STATIC_ARRAY_SIZE(g_hw_cache_events));
602     if (ret != 0) {
603       ERROR(PMU_PLUGIN ": Failed to add hw cache events.");
604       goto init_error;
605     }
606   }
607
608   if (g_ctx.kernel_pmu_events) {
609     ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_HARDWARE,
610                          g_kernel_pmu_events,
611                          STATIC_ARRAY_SIZE(g_kernel_pmu_events));
612     if (ret != 0) {
613       ERROR(PMU_PLUGIN ": Failed to add kernel PMU events.");
614       goto init_error;
615     }
616   }
617
618   /* parse events names if config option is present and is not empty */
619   if (g_ctx.hw_events_count) {
620
621     ret = read_events(g_ctx.event_list_fn);
622     if (ret != 0) {
623       ERROR(PMU_PLUGIN ": Failed to read event list file '%s'.",
624             g_ctx.event_list_fn);
625       return ret;
626     }
627
628     ret = pmu_add_hw_events(g_ctx.event_list, g_ctx.hw_events,
629                             g_ctx.hw_events_count);
630     if (ret != 0) {
631       ERROR(PMU_PLUGIN ": Failed to add hardware events.");
632       goto init_error;
633     }
634   }
635
636   if (g_ctx.sw_events) {
637     ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_SOFTWARE, g_sw_events,
638                          STATIC_ARRAY_SIZE(g_sw_events));
639     if (ret != 0) {
640       ERROR(PMU_PLUGIN ": Failed to add software events.");
641       goto init_error;
642     }
643   }
644
645 #if COLLECT_DEBUG
646   pmu_dump_events();
647 #endif
648
649   if (g_ctx.event_list->eventlist != NULL) {
650     /* measure all processes */
651     ret = pmu_setup_events(g_ctx.event_list, true, -1);
652     if (ret != 0) {
653       ERROR(PMU_PLUGIN ": Failed to setup perf events for the event list.");
654       goto init_error;
655     }
656   } else {
657     WARNING(PMU_PLUGIN
658             ": Events list is empty. No events were setup for monitoring.");
659   }
660
661   return 0;
662
663 init_error:
664
665   pmu_free_events(g_ctx.event_list);
666   sfree(g_ctx.event_list);
667   for (size_t i = 0; i < g_ctx.hw_events_count; i++) {
668     sfree(g_ctx.hw_events[i]);
669   }
670   sfree(g_ctx.hw_events);
671   g_ctx.hw_events_count = 0;
672
673   config_cores_cleanup(&g_ctx.cores);
674
675   return ret;
676 }
677
678 static int pmu_shutdown(void) {
679
680   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
681
682   pmu_free_events(g_ctx.event_list);
683   sfree(g_ctx.event_list);
684   for (size_t i = 0; i < g_ctx.hw_events_count; i++) {
685     sfree(g_ctx.hw_events[i]);
686   }
687   sfree(g_ctx.hw_events);
688   g_ctx.hw_events_count = 0;
689
690   config_cores_cleanup(&g_ctx.cores);
691
692   return 0;
693 }
694
695 void module_register(void) {
696   plugin_register_init(PMU_PLUGIN, pmu_init);
697   plugin_register_complex_config(PMU_PLUGIN, pmu_config);
698   plugin_register_complex_read(NULL, PMU_PLUGIN, pmu_read, 0, NULL);
699   plugin_register_shutdown(PMU_PLUGIN, pmu_shutdown);
700 }