intel_pmu: formatting by clang-format
[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  **/
27
28 #include "collectd.h"
29 #include "common.h"
30
31 #include "jevents.h"
32 #include "jsession.h"
33
34 #define PMU_PLUGIN "intel_pmu"
35
36 #define HW_CACHE_READ_ACCESS                                                   \
37   (((PERF_COUNT_HW_CACHE_OP_READ) << 8) |                                      \
38    ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
39
40 #define HW_CACHE_WRITE_ACCESS                                                  \
41   (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) |                                     \
42    ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
43
44 #define HW_CACHE_PREFETCH_ACCESS                                               \
45   (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) |                                  \
46    ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
47
48 #define HW_CACHE_READ_MISS                                                     \
49   (((PERF_COUNT_HW_CACHE_OP_READ) << 8) |                                      \
50    ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
51
52 #define HW_CACHE_WRITE_MISS                                                    \
53   (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) |                                     \
54    ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
55
56 #define HW_CACHE_PREFETCH_MISS                                                 \
57   (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) |                                  \
58    ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
59
60 struct event_info {
61   char *name;
62   uint64_t config;
63 };
64 typedef struct event_info event_info_t;
65
66 struct intel_pmu_ctx_s {
67   _Bool hw_cache_events;
68   _Bool kernel_pmu_events;
69   _Bool sw_events;
70   char *hw_specific_events;
71   struct eventlist *event_list;
72 };
73 typedef struct intel_pmu_ctx_s intel_pmu_ctx_t;
74
75 event_info_t g_kernel_pmu_events[] = {
76     {.name = "cpu-cycles", .config = PERF_COUNT_HW_CPU_CYCLES},
77     {.name = "instructions", .config = PERF_COUNT_HW_INSTRUCTIONS},
78     {.name = "cache-references", .config = PERF_COUNT_HW_CACHE_REFERENCES},
79     {.name = "cache-misses", .config = PERF_COUNT_HW_CACHE_MISSES},
80     {.name = "branches", .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS},
81     {.name = "branch-misses", .config = PERF_COUNT_HW_BRANCH_MISSES},
82     {.name = "bus-cycles", .config = PERF_COUNT_HW_BUS_CYCLES},
83 };
84
85 event_info_t g_hw_cache_events[] = {
86
87     {.name = "L1-dcache-loads",
88      .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_ACCESS)},
89     {.name = "L1-dcache-load-misses",
90      .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_MISS)},
91     {.name = "L1-dcache-stores",
92      .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_ACCESS)},
93     {.name = "L1-dcache-store-misses",
94      .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_MISS)},
95     {.name = "L1-dcache-prefetches",
96      .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_ACCESS)},
97     {.name = "L1-dcache-prefetch-misses",
98      .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_MISS)},
99
100     {.name = "L1-icache-loads",
101      .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_ACCESS)},
102     {.name = "L1-icache-load-misses",
103      .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_MISS)},
104     {.name = "L1-icache-prefetches",
105      .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_ACCESS)},
106     {.name = "L1-icache-prefetch-misses",
107      .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_MISS)},
108
109     {.name = "LLC-loads",
110      .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_ACCESS)},
111     {.name = "LLC-load-misses",
112      .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_MISS)},
113     {.name = "LLC-stores",
114      .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_ACCESS)},
115     {.name = "LLC-store-misses",
116      .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_MISS)},
117     {.name = "LLC-prefetches",
118      .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_ACCESS)},
119     {.name = "LLC-prefetch-misses",
120      .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_MISS)},
121
122     {.name = "dTLB-loads",
123      .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_ACCESS)},
124     {.name = "dTLB-load-misses",
125      .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_MISS)},
126     {.name = "dTLB-stores",
127      .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_ACCESS)},
128     {.name = "dTLB-store-misses",
129      .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_MISS)},
130     {.name = "dTLB-prefetches",
131      .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_ACCESS)},
132     {.name = "dTLB-prefetch-misses",
133      .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_MISS)},
134
135     {.name = "iTLB-loads",
136      .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_ACCESS)},
137     {.name = "iTLB-load-misses",
138      .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_MISS)},
139
140     {.name = "branch-loads",
141      .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_ACCESS)},
142     {.name = "branch-load-misses",
143      .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_MISS)},
144 };
145
146 event_info_t g_sw_events[] = {
147     {.name = "cpu-clock", .config = PERF_COUNT_SW_CPU_CLOCK},
148
149     {.name = "task-clock", .config = PERF_COUNT_SW_TASK_CLOCK},
150
151     {.name = "context-switches", .config = PERF_COUNT_SW_CONTEXT_SWITCHES},
152
153     {.name = "cpu-migrations", .config = PERF_COUNT_SW_CPU_MIGRATIONS},
154
155     {.name = "page-faults", .config = PERF_COUNT_SW_PAGE_FAULTS},
156
157     {.name = "minor-faults", .config = PERF_COUNT_SW_PAGE_FAULTS_MIN},
158
159     {.name = "major-faults", .config = PERF_COUNT_SW_PAGE_FAULTS_MAJ},
160
161     {.name = "alignment-faults", .config = PERF_COUNT_SW_ALIGNMENT_FAULTS},
162
163     {.name = "emulation-faults", .config = PERF_COUNT_SW_EMULATION_FAULTS},
164 };
165
166 static intel_pmu_ctx_t g_ctx;
167
168 #if COLLECT_DEBUG
169 static void pmu_dump_events() {
170
171   DEBUG(PMU_PLUGIN ": Events:");
172
173   struct event *e;
174
175   for (e = g_ctx.event_list->eventlist; e; e = e->next) {
176     DEBUG(PMU_PLUGIN ":   event       : %s", e->event);
177     DEBUG(PMU_PLUGIN ":     group_lead: %d", e->group_leader);
178     DEBUG(PMU_PLUGIN ":     end_group : %d", e->end_group);
179     DEBUG(PMU_PLUGIN ":     type      : 0x%X", e->attr.type);
180     DEBUG(PMU_PLUGIN ":     config    : 0x%X", (int)e->attr.config);
181     DEBUG(PMU_PLUGIN ":     size      : %d", e->attr.size);
182   }
183
184   return;
185 }
186
187 static void pmu_dump_config(void) {
188
189   DEBUG(PMU_PLUGIN ": Config:");
190   DEBUG(PMU_PLUGIN ":   hw_cache_events   : %d", g_ctx.hw_cache_events);
191   DEBUG(PMU_PLUGIN ":   kernel_pmu_events : %d", g_ctx.kernel_pmu_events);
192   DEBUG(PMU_PLUGIN ":   sw_events         : %d", g_ctx.sw_events);
193   DEBUG(PMU_PLUGIN ":   hw_specific_events: %s", g_ctx.hw_specific_events);
194
195   return;
196 }
197
198 #endif /* COLLECT_DEBUG */
199
200 static int pmu_config(oconfig_item_t *ci) {
201   int ret = 0;
202
203   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
204
205   for (int i = 0; i < ci->children_num; i++) {
206     oconfig_item_t *child = ci->children + i;
207
208     if (strcasecmp("HWCacheEvents", child->key) == 0) {
209       ret = cf_util_get_boolean(child, &g_ctx.hw_cache_events);
210     } else if (strcasecmp("KernelPMUEvents", child->key) == 0) {
211       ret = cf_util_get_boolean(child, &g_ctx.kernel_pmu_events);
212     } else if (strcasecmp("HWSpecificEvents", child->key) == 0) {
213       ret = cf_util_get_string(child, &g_ctx.hw_specific_events);
214     } else if (strcasecmp("SWEvents", child->key) == 0) {
215       ret = cf_util_get_boolean(child, &g_ctx.sw_events);
216     } else {
217       ERROR(PMU_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
218       ret = (-1);
219     }
220
221     if (ret != 0) {
222       DEBUG(PMU_PLUGIN ": %s:%d ret=%d", __FUNCTION__, __LINE__, ret);
223       return ret;
224     }
225   }
226
227 #if COLLECT_DEBUG
228   pmu_dump_config();
229 #endif
230
231   return (0);
232 }
233
234 static void pmu_submit_counter(int cpu, char *event, counter_t value) {
235   value_list_t vl = VALUE_LIST_INIT;
236
237   vl.values = &(value_t){.counter = value};
238   vl.values_len = 1;
239
240   sstrncpy(vl.plugin, PMU_PLUGIN, sizeof(vl.plugin));
241   if (cpu == -1) {
242     snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "all");
243   } else {
244     snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%d", cpu);
245   }
246   sstrncpy(vl.type, "counter", sizeof(vl.type));
247   sstrncpy(vl.type_instance, event, sizeof(vl.type_instance));
248
249   plugin_dispatch_values(&vl);
250 }
251
252 static int pmu_dispatch_data(void) {
253
254   struct event *e;
255
256   for (e = g_ctx.event_list->eventlist; e; e = e->next) {
257     uint64_t all_value = 0;
258     int event_enabled = 0;
259     for (int i = 0; i < g_ctx.event_list->num_cpus; i++) {
260
261       if (e->efd[i].fd < 0)
262         continue;
263
264       event_enabled++;
265
266       uint64_t value = event_scaled_value(e, i);
267       all_value += value;
268
269       /* dispatch per CPU value */
270       pmu_submit_counter(i, e->event, value);
271     }
272
273     if (event_enabled > 0) {
274       DEBUG(PMU_PLUGIN ": %-20s %'10lu", e->event, all_value);
275       /* dispatch all CPU value */
276       pmu_submit_counter(-1, e->event, all_value);
277     }
278   }
279
280   return (0);
281 }
282
283 static int pmu_read(__attribute__((unused)) user_data_t *ud) {
284   int ret;
285
286   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
287
288   ret = read_all_events(g_ctx.event_list);
289   if (ret != 0) {
290     DEBUG(PMU_PLUGIN ": Failed to read values of all events.");
291     return (0);
292   }
293
294   ret = pmu_dispatch_data();
295   if (ret != 0) {
296     DEBUG(PMU_PLUGIN ": Failed to dispatch event values.");
297     return (0);
298   }
299
300   return (0);
301 }
302
303 static int pmu_add_events(struct eventlist *el, uint32_t type,
304                           event_info_t *events, int count) {
305
306   for (int i = 0; i < count; i++) {
307     struct event *e =
308         calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1);
309     if (e == NULL) {
310       ERROR(PMU_PLUGIN ": Failed to allocate event structure");
311       return (-ENOMEM);
312     }
313
314     e->attr.type = type;
315     e->attr.config = events[i].config;
316     e->attr.size = PERF_ATTR_SIZE_VER0;
317     e->group_leader = false;
318     e->end_group = false;
319     e->next = NULL;
320     if (!el->eventlist)
321       el->eventlist = e;
322     if (el->eventlist_last)
323       el->eventlist_last->next = e;
324     el->eventlist_last = e;
325     e->event = strdup(events[i].name);
326   }
327
328   return (0);
329 }
330
331 static int pmu_parse_events(struct eventlist *el, char *events) {
332   char *s, *tmp;
333
334   events = strdup(events);
335   if (!events)
336     return -1;
337
338   for (s = strtok_r(events, ",", &tmp); s; s = strtok_r(NULL, ",", &tmp)) {
339     bool group_leader = false, end_group = false;
340     int len;
341
342     if (s[0] == '{') {
343       s++;
344       group_leader = true;
345     } else if (len = strlen(s), len > 0 && s[len - 1] == '}') {
346       s[len - 1] = 0;
347       end_group = true;
348     }
349
350     struct event *e =
351         calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1);
352     if (e == NULL) {
353       free(events);
354       return (-ENOMEM);
355     }
356
357     if (resolve_event(s, &e->attr) == 0) {
358       e->group_leader = group_leader;
359       e->end_group = end_group;
360       e->next = NULL;
361       if (!el->eventlist)
362         el->eventlist = e;
363       if (el->eventlist_last)
364         el->eventlist_last->next = e;
365       el->eventlist_last = e;
366       e->event = strdup(s);
367     } else {
368       DEBUG(PMU_PLUGIN ": Cannot resolve %s", s);
369       sfree(e);
370     }
371   }
372
373   free(events);
374
375   return (0);
376 }
377
378 static void pmu_free_events(struct eventlist *el) {
379
380   if (el == NULL)
381     return;
382
383   struct event *e = el->eventlist;
384
385   while (e) {
386     struct event *next = e->next;
387     sfree(e);
388     e = next;
389   }
390
391   el->eventlist = NULL;
392 }
393
394 static int pmu_setup_events(struct eventlist *el, bool measure_all,
395                             int measure_pid) {
396   struct event *e, *leader = NULL;
397   int ret = -1;
398
399   for (e = el->eventlist; e; e = e->next) {
400
401     for (int i = 0; i < el->num_cpus; i++) {
402       if (setup_event(e, i, leader, measure_all, measure_pid) < 0) {
403         WARNING(PMU_PLUGIN ": perf event '%s' is not available (cpu=%d).",
404                 e->event, i);
405       } else {
406         /* success if at least one event was set */
407         ret = 0;
408       }
409     }
410
411     if (e->group_leader)
412       leader = e;
413     if (e->end_group)
414       leader = NULL;
415   }
416
417   return ret;
418 }
419
420 static int pmu_init(void) {
421   int ret;
422
423   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
424
425   g_ctx.event_list = alloc_eventlist();
426   if (g_ctx.event_list == NULL) {
427     ERROR(PMU_PLUGIN ": Failed to allocate event list.");
428     return (-ENOMEM);
429   }
430
431   if (g_ctx.hw_cache_events) {
432     ret =
433         pmu_add_events(g_ctx.event_list, PERF_TYPE_HW_CACHE, g_hw_cache_events,
434                        STATIC_ARRAY_SIZE(g_hw_cache_events));
435     if (ret != 0) {
436       ERROR(PMU_PLUGIN ": Failed to add hw cache events.");
437       goto init_error;
438     }
439   }
440
441   if (g_ctx.kernel_pmu_events) {
442     ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_HARDWARE,
443                          g_kernel_pmu_events,
444                          STATIC_ARRAY_SIZE(g_kernel_pmu_events));
445     if (ret != 0) {
446       ERROR(PMU_PLUGIN ": Failed to parse kernel PMU events.");
447       goto init_error;
448     }
449   }
450
451   /* parse events names if config option is present and is not empty */
452   if (g_ctx.hw_specific_events && (strlen(g_ctx.hw_specific_events) != 0)) {
453     ret = pmu_parse_events(g_ctx.event_list, g_ctx.hw_specific_events);
454     if (ret != 0) {
455       ERROR(PMU_PLUGIN ": Failed to parse hw specific events.");
456       goto init_error;
457     }
458   }
459
460   if (g_ctx.sw_events) {
461     ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_SOFTWARE, g_sw_events,
462                          STATIC_ARRAY_SIZE(g_sw_events));
463     if (ret != 0) {
464       ERROR(PMU_PLUGIN ": Failed to add software events.");
465       goto init_error;
466     }
467   }
468
469 #if COLLECT_DEBUG
470   pmu_dump_events();
471 #endif
472
473   if (g_ctx.event_list->eventlist != NULL) {
474     /* measure all processes */
475     ret = pmu_setup_events(g_ctx.event_list, true, -1);
476     if (ret != 0) {
477       ERROR(PMU_PLUGIN ": Failed to setup perf events for the event list.");
478       goto init_error;
479     }
480   } else {
481     WARNING(PMU_PLUGIN
482             ": Events list is empty. No events were setup for monitoring.");
483   }
484
485   return (0);
486
487 init_error:
488
489   pmu_free_events(g_ctx.event_list);
490   sfree(g_ctx.event_list);
491   sfree(g_ctx.hw_specific_events);
492
493   return ret;
494 }
495
496 static int pmu_shutdown(void) {
497
498   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
499
500   pmu_free_events(g_ctx.event_list);
501   sfree(g_ctx.event_list);
502   sfree(g_ctx.hw_specific_events);
503
504   return (0);
505 }
506
507 void module_register(void) {
508   plugin_register_init(PMU_PLUGIN, pmu_init);
509   plugin_register_complex_config(PMU_PLUGIN, pmu_config);
510   plugin_register_complex_read(NULL, PMU_PLUGIN, pmu_read, 0, NULL);
511   plugin_register_shutdown(PMU_PLUGIN, pmu_shutdown);
512 }