Merge pull request #3329 from efuss/fix-3311
[collectd.git] / src / intel_pmu.c
1 /**
2  * collectd - src/intel_pmu.c
3  *
4  * Copyright(c) 2017-2018 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 "utils/common/common.h"
31
32 #include "utils/config_cores/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[%" PRIsz "]: %s", i,
200           g_ctx.hw_events[i]);
201   }
202 }
203
204 static void pmu_dump_cgroups(void) {
205
206   DEBUG(PMU_PLUGIN ": Core groups:");
207
208   for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
209     core_group_t *cgroup = g_ctx.cores.cgroups + i;
210     const size_t cores_size = cgroup->num_cores * 4 + 1;
211     char *cores = calloc(cores_size, sizeof(*cores));
212     if (cores == NULL) {
213       DEBUG(PMU_PLUGIN ": Failed to allocate string to list cores.");
214       return;
215     }
216     for (size_t j = 0; j < cgroup->num_cores; j++)
217       if (snprintf(cores + strlen(cores), cores_size - strlen(cores), " %d",
218                    cgroup->cores[j]) < 0) {
219         DEBUG(PMU_PLUGIN ": Failed to write list of cores to string.");
220         sfree(cores);
221         return;
222       }
223
224     DEBUG(PMU_PLUGIN ":   group[%" PRIsz "]", i);
225     DEBUG(PMU_PLUGIN ":     description: %s", cgroup->desc);
226     DEBUG(PMU_PLUGIN ":     cores count: %" PRIsz, cgroup->num_cores);
227     DEBUG(PMU_PLUGIN ":     cores      :%s", cores);
228     sfree(cores);
229   }
230 }
231
232 #endif /* COLLECT_DEBUG */
233
234 static int pmu_validate_cgroups(core_group_t *cgroups, size_t len,
235                                 int max_cores) {
236   /* i - group index, j - core index */
237   for (size_t i = 0; i < len; i++) {
238     for (size_t j = 0; j < cgroups[i].num_cores; j++) {
239       int core = (int)cgroups[i].cores[j];
240
241       /* Core index cannot exceed number of cores in system,
242          note that max_cores include both online and offline CPUs. */
243       if (core >= max_cores) {
244         ERROR(PMU_PLUGIN ": Core %d is not valid, max core index: %d.", core,
245               max_cores - 1);
246         return -1;
247       }
248     }
249     /* Check if cores are set in remaining groups */
250     for (size_t k = i + 1; k < len; k++)
251       if (config_cores_cmp_cgroups(&cgroups[i], &cgroups[k]) != 0) {
252         ERROR(PMU_PLUGIN ": Same cores cannot be set in different groups.");
253         return -1;
254       }
255   }
256   return 0;
257 }
258
259 static int pmu_config_hw_events(oconfig_item_t *ci) {
260
261   if (strcasecmp("HardwareEvents", ci->key) != 0) {
262     return -EINVAL;
263   }
264
265   if (g_ctx.hw_events) {
266     ERROR(PMU_PLUGIN ": Duplicate config for HardwareEvents.");
267     return -EINVAL;
268   }
269
270   g_ctx.hw_events = calloc(ci->values_num, sizeof(*g_ctx.hw_events));
271   if (g_ctx.hw_events == NULL) {
272     ERROR(PMU_PLUGIN ": Failed to allocate hw events.");
273     return -ENOMEM;
274   }
275
276   for (int i = 0; i < ci->values_num; i++) {
277     if (ci->values[i].type != OCONFIG_TYPE_STRING) {
278       WARNING(PMU_PLUGIN ": The %s option requires string arguments.", ci->key);
279       continue;
280     }
281
282     g_ctx.hw_events[g_ctx.hw_events_count] = strdup(ci->values[i].value.string);
283     if (g_ctx.hw_events[g_ctx.hw_events_count] == NULL) {
284       ERROR(PMU_PLUGIN ": Failed to allocate hw events entry.");
285       return -ENOMEM;
286     }
287
288     g_ctx.hw_events_count++;
289   }
290
291   return 0;
292 }
293
294 static int pmu_config(oconfig_item_t *ci) {
295
296   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
297
298   for (int i = 0; i < ci->children_num; i++) {
299     int ret = 0;
300     oconfig_item_t *child = ci->children + i;
301
302     if (strcasecmp("ReportHardwareCacheEvents", child->key) == 0) {
303       ret = cf_util_get_boolean(child, &g_ctx.hw_cache_events);
304     } else if (strcasecmp("ReportKernelPMUEvents", child->key) == 0) {
305       ret = cf_util_get_boolean(child, &g_ctx.kernel_pmu_events);
306     } else if (strcasecmp("EventList", child->key) == 0) {
307       ret = cf_util_get_string_buffer(child, g_ctx.event_list_fn,
308                                       sizeof(g_ctx.event_list_fn));
309     } else if (strcasecmp("HardwareEvents", child->key) == 0) {
310       ret = pmu_config_hw_events(child);
311     } else if (strcasecmp("ReportSoftwareEvents", child->key) == 0) {
312       ret = cf_util_get_boolean(child, &g_ctx.sw_events);
313     } else if (strcasecmp("Cores", child->key) == 0) {
314       ret = config_cores_parse(child, &g_ctx.cores);
315     } else {
316       ERROR(PMU_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
317       ret = -1;
318     }
319
320     if (ret != 0) {
321       DEBUG(PMU_PLUGIN ": %s:%d ret=%d", __FUNCTION__, __LINE__, ret);
322       return ret;
323     }
324   }
325
326 #if COLLECT_DEBUG
327   pmu_dump_config();
328 #endif
329
330   return 0;
331 }
332
333 static void pmu_submit_counter(const char *cgroup, const char *event,
334                                counter_t value, meta_data_t *meta) {
335   value_list_t vl = VALUE_LIST_INIT;
336
337   vl.values = &(value_t){.counter = value};
338   vl.values_len = 1;
339
340   sstrncpy(vl.plugin, PMU_PLUGIN, sizeof(vl.plugin));
341   sstrncpy(vl.plugin_instance, cgroup, sizeof(vl.plugin_instance));
342   if (meta)
343     vl.meta = meta;
344   sstrncpy(vl.type, "counter", sizeof(vl.type));
345   sstrncpy(vl.type_instance, event, sizeof(vl.type_instance));
346
347   plugin_dispatch_values(&vl);
348 }
349
350 meta_data_t *pmu_meta_data_create(const struct efd *efd) {
351   meta_data_t *meta = NULL;
352
353   /* create meta data only if value was scaled */
354   if (efd->val[1] == efd->val[2] || !efd->val[2]) {
355     return NULL;
356   }
357
358   meta = meta_data_create();
359   if (meta == NULL) {
360     ERROR(PMU_PLUGIN ": meta_data_create failed.");
361     return NULL;
362   }
363
364   meta_data_add_unsigned_int(meta, "intel_pmu:raw_count", efd->val[0]);
365   meta_data_add_unsigned_int(meta, "intel_pmu:time_enabled", efd->val[1]);
366   meta_data_add_unsigned_int(meta, "intel_pmu:time_running", efd->val[2]);
367
368   return meta;
369 }
370
371 static void pmu_dispatch_data(void) {
372
373   struct event *e;
374
375   for (e = g_ctx.event_list->eventlist; e; e = e->next) {
376     for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
377       core_group_t *cgroup = g_ctx.cores.cgroups + i;
378       uint64_t cgroup_value = 0;
379       int event_enabled_cgroup = 0;
380       meta_data_t *meta = NULL;
381
382       for (size_t j = 0; j < cgroup->num_cores; j++) {
383         int core = (int)cgroup->cores[j];
384         if (e->efd[core].fd < 0)
385           continue;
386
387         event_enabled_cgroup++;
388
389         /* If there are more events than counters, the kernel uses time
390          * multiplexing. With multiplexing, at the end of the run,
391          * the counter is scaled basing on total time enabled vs time running.
392          * final_count = raw_count * time_enabled/time_running
393          */
394         uint64_t value = event_scaled_value(e, core);
395         cgroup_value += value;
396
397         /* get meta data with information about scaling */
398         if (cgroup->num_cores == 1)
399           meta = pmu_meta_data_create(&e->efd[core]);
400       }
401
402       if (event_enabled_cgroup > 0) {
403         DEBUG(PMU_PLUGIN ": %s/%s = %lu", e->event, cgroup->desc, cgroup_value);
404         /* dispatch per core group value */
405         pmu_submit_counter(cgroup->desc, e->event, cgroup_value, meta);
406         meta_data_destroy(meta);
407       }
408     }
409   }
410 }
411
412 static int pmu_read(__attribute__((unused)) user_data_t *ud) {
413   int ret;
414   struct event *e;
415
416   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
417
418   /* read all events only for configured cores */
419   for (e = g_ctx.event_list->eventlist; e; e = e->next) {
420     for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
421       core_group_t *cgroup = g_ctx.cores.cgroups + i;
422       for (size_t j = 0; j < cgroup->num_cores; j++) {
423         int core = (int)cgroup->cores[j];
424         if (e->efd[core].fd < 0)
425           continue;
426
427         ret = read_event(e, core);
428         if (ret != 0) {
429           ERROR(PMU_PLUGIN ": Failed to read value of %s/%d event.", e->event,
430                 core);
431           return ret;
432         }
433       }
434     }
435   }
436
437   pmu_dispatch_data();
438
439   return 0;
440 }
441
442 static int pmu_add_events(struct eventlist *el, uint32_t type,
443                           event_info_t *events, size_t count) {
444
445   for (size_t i = 0; i < count; i++) {
446     /* Allocate memory for event struct that contains array of efd structs
447        for all cores */
448     struct event *e =
449         calloc(1, sizeof(struct event) + sizeof(struct efd) * el->num_cpus);
450     if (e == NULL) {
451       ERROR(PMU_PLUGIN ": Failed to allocate event structure");
452       return -ENOMEM;
453     }
454
455     e->attr.type = type;
456     e->attr.config = events[i].config;
457     e->attr.size = PERF_ATTR_SIZE_VER0;
458     if (!el->eventlist)
459       el->eventlist = e;
460     if (el->eventlist_last)
461       el->eventlist_last->next = e;
462     el->eventlist_last = e;
463     e->event = strdup(events[i].name);
464   }
465
466   return 0;
467 }
468
469 static int pmu_add_hw_events(struct eventlist *el, char **e, size_t count) {
470
471   for (size_t i = 0; i < count; i++) {
472
473     size_t group_events_count = 0;
474
475     char *events = strdup(e[i]);
476     if (!events)
477       return -1;
478
479     char *s, *tmp = NULL;
480     for (s = strtok_r(events, ",", &tmp); s; s = strtok_r(NULL, ",", &tmp)) {
481
482       /* Allocate memory for event struct that contains array of efd structs
483          for all cores */
484       struct event *e =
485           calloc(1, sizeof(struct event) + sizeof(struct efd) * el->num_cpus);
486       if (e == NULL) {
487         free(events);
488         return -ENOMEM;
489       }
490
491       if (resolve_event(s, &e->attr) != 0) {
492         WARNING(PMU_PLUGIN ": Cannot resolve %s", s);
493         sfree(e);
494         continue;
495       }
496
497       /* Multiple events parsed in one entry */
498       if (group_events_count == 1) {
499         /* Mark previously added event as group leader */
500         el->eventlist_last->group_leader = 1;
501       }
502
503       e->next = NULL;
504       if (!el->eventlist)
505         el->eventlist = e;
506       if (el->eventlist_last)
507         el->eventlist_last->next = e;
508       el->eventlist_last = e;
509       e->event = strdup(s);
510
511       group_events_count++;
512     }
513
514     /* Multiple events parsed in one entry */
515     if (group_events_count > 1) {
516       /* Mark last added event as group end */
517       el->eventlist_last->end_group = 1;
518     }
519
520     free(events);
521   }
522
523   return 0;
524 }
525
526 static void pmu_free_events(struct eventlist *el) {
527
528   if (el == NULL)
529     return;
530
531   struct event *e = el->eventlist;
532
533   while (e) {
534     struct event *next = e->next;
535     sfree(e->event);
536     sfree(e);
537     e = next;
538   }
539
540   el->eventlist = NULL;
541 }
542
543 static int pmu_setup_events(struct eventlist *el, bool measure_all,
544                             int measure_pid) {
545   struct event *e, *leader = NULL;
546   int ret = -1;
547
548   for (e = el->eventlist; e; e = e->next) {
549
550     for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
551       core_group_t *cgroup = g_ctx.cores.cgroups + i;
552       for (size_t j = 0; j < cgroup->num_cores; j++) {
553         int core = (int)cgroup->cores[j];
554
555         if (setup_event(e, core, leader, measure_all, measure_pid) < 0) {
556           WARNING(PMU_PLUGIN ": perf event '%s' is not available (cpu=%d).",
557                   e->event, core);
558         } else {
559           /* success if at least one event was set */
560           ret = 0;
561         }
562       }
563     }
564
565     if (e->group_leader)
566       leader = e;
567     if (e->end_group)
568       leader = NULL;
569   }
570
571   return ret;
572 }
573
574 static int pmu_init(void) {
575   int ret;
576
577   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
578
579   g_ctx.event_list = alloc_eventlist();
580   if (g_ctx.event_list == NULL) {
581     ERROR(PMU_PLUGIN ": Failed to allocate event list.");
582     return -ENOMEM;
583   }
584
585   if (g_ctx.cores.num_cgroups == 0) {
586     ret = config_cores_default(g_ctx.event_list->num_cpus, &g_ctx.cores);
587     if (ret != 0) {
588       ERROR(PMU_PLUGIN ": Failed to set default core groups.");
589       goto init_error;
590     }
591   } else {
592     ret = pmu_validate_cgroups(g_ctx.cores.cgroups, g_ctx.cores.num_cgroups,
593                                g_ctx.event_list->num_cpus);
594     if (ret != 0) {
595       ERROR(PMU_PLUGIN ": Invalid core groups configuration.");
596       goto init_error;
597     }
598   }
599 #if COLLECT_DEBUG
600   pmu_dump_cgroups();
601 #endif
602
603   if (g_ctx.hw_cache_events) {
604     ret =
605         pmu_add_events(g_ctx.event_list, PERF_TYPE_HW_CACHE, g_hw_cache_events,
606                        STATIC_ARRAY_SIZE(g_hw_cache_events));
607     if (ret != 0) {
608       ERROR(PMU_PLUGIN ": Failed to add hw cache events.");
609       goto init_error;
610     }
611   }
612
613   if (g_ctx.kernel_pmu_events) {
614     ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_HARDWARE,
615                          g_kernel_pmu_events,
616                          STATIC_ARRAY_SIZE(g_kernel_pmu_events));
617     if (ret != 0) {
618       ERROR(PMU_PLUGIN ": Failed to add kernel PMU events.");
619       goto init_error;
620     }
621   }
622
623   /* parse events names if config option is present and is not empty */
624   if (g_ctx.hw_events_count) {
625
626     ret = read_events(g_ctx.event_list_fn);
627     if (ret != 0) {
628       ERROR(PMU_PLUGIN ": Failed to read event list file '%s'.",
629             g_ctx.event_list_fn);
630       return ret;
631     }
632
633     ret = pmu_add_hw_events(g_ctx.event_list, g_ctx.hw_events,
634                             g_ctx.hw_events_count);
635     if (ret != 0) {
636       ERROR(PMU_PLUGIN ": Failed to add hardware events.");
637       goto init_error;
638     }
639   }
640
641   if (g_ctx.sw_events) {
642     ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_SOFTWARE, g_sw_events,
643                          STATIC_ARRAY_SIZE(g_sw_events));
644     if (ret != 0) {
645       ERROR(PMU_PLUGIN ": Failed to add software events.");
646       goto init_error;
647     }
648   }
649
650 #if COLLECT_DEBUG
651   pmu_dump_events();
652 #endif
653
654   if (g_ctx.event_list->eventlist != NULL) {
655     /* measure all processes */
656     ret = pmu_setup_events(g_ctx.event_list, true, -1);
657     if (ret != 0) {
658       ERROR(PMU_PLUGIN ": Failed to setup perf events for the event list.");
659       goto init_error;
660     }
661   } else {
662     WARNING(PMU_PLUGIN
663             ": Events list is empty. No events were setup for monitoring.");
664   }
665
666   return 0;
667
668 init_error:
669
670   pmu_free_events(g_ctx.event_list);
671   sfree(g_ctx.event_list);
672   for (size_t i = 0; i < g_ctx.hw_events_count; i++) {
673     sfree(g_ctx.hw_events[i]);
674   }
675   sfree(g_ctx.hw_events);
676   g_ctx.hw_events_count = 0;
677
678   config_cores_cleanup(&g_ctx.cores);
679
680   return ret;
681 }
682
683 static int pmu_shutdown(void) {
684
685   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
686
687   pmu_free_events(g_ctx.event_list);
688   sfree(g_ctx.event_list);
689   for (size_t i = 0; i < g_ctx.hw_events_count; i++) {
690     sfree(g_ctx.hw_events[i]);
691   }
692   sfree(g_ctx.hw_events);
693   g_ctx.hw_events_count = 0;
694
695   config_cores_cleanup(&g_ctx.cores);
696
697   return 0;
698 }
699
700 void module_register(void) {
701   plugin_register_init(PMU_PLUGIN, pmu_init);
702   plugin_register_complex_config(PMU_PLUGIN, pmu_config);
703   plugin_register_complex_read(NULL, PMU_PLUGIN, pmu_read, 0, NULL);
704   plugin_register_shutdown(PMU_PLUGIN, pmu_shutdown);
705 }