2 * collectd - src/intel_pmu.c
4 * Copyright(c) 2017 Intel Corporation. All rights reserved.
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
25 * Serhiy Pshyk <serhiyx.pshyk@intel.com>
34 #define PMU_PLUGIN "intel_pmu"
36 #define HW_CACHE_READ_ACCESS \
37 (((PERF_COUNT_HW_CACHE_OP_READ) << 8) | \
38 ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
40 #define HW_CACHE_WRITE_ACCESS \
41 (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) | \
42 ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
44 #define HW_CACHE_PREFETCH_ACCESS \
45 (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) | \
46 ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
48 #define HW_CACHE_READ_MISS \
49 (((PERF_COUNT_HW_CACHE_OP_READ) << 8) | \
50 ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
52 #define HW_CACHE_WRITE_MISS \
53 (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) | \
54 ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
56 #define HW_CACHE_PREFETCH_MISS \
57 (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) | \
58 ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
64 typedef struct event_info event_info_t;
66 struct intel_pmu_ctx_s {
67 _Bool hw_cache_events;
68 _Bool kernel_pmu_events;
71 size_t hw_events_count;
72 struct eventlist *event_list;
74 typedef struct intel_pmu_ctx_s intel_pmu_ctx_t;
76 event_info_t g_kernel_pmu_events[] = {
77 {.name = "cpu-cycles", .config = PERF_COUNT_HW_CPU_CYCLES},
78 {.name = "instructions", .config = PERF_COUNT_HW_INSTRUCTIONS},
79 {.name = "cache-references", .config = PERF_COUNT_HW_CACHE_REFERENCES},
80 {.name = "cache-misses", .config = PERF_COUNT_HW_CACHE_MISSES},
81 {.name = "branches", .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS},
82 {.name = "branch-misses", .config = PERF_COUNT_HW_BRANCH_MISSES},
83 {.name = "bus-cycles", .config = PERF_COUNT_HW_BUS_CYCLES},
86 event_info_t g_hw_cache_events[] = {
88 {.name = "L1-dcache-loads",
89 .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_ACCESS)},
90 {.name = "L1-dcache-load-misses",
91 .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_MISS)},
92 {.name = "L1-dcache-stores",
93 .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_ACCESS)},
94 {.name = "L1-dcache-store-misses",
95 .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_MISS)},
96 {.name = "L1-dcache-prefetches",
97 .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_ACCESS)},
98 {.name = "L1-dcache-prefetch-misses",
99 .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_MISS)},
101 {.name = "L1-icache-loads",
102 .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_ACCESS)},
103 {.name = "L1-icache-load-misses",
104 .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_MISS)},
105 {.name = "L1-icache-prefetches",
106 .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_ACCESS)},
107 {.name = "L1-icache-prefetch-misses",
108 .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_MISS)},
110 {.name = "LLC-loads",
111 .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_ACCESS)},
112 {.name = "LLC-load-misses",
113 .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_MISS)},
114 {.name = "LLC-stores",
115 .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_ACCESS)},
116 {.name = "LLC-store-misses",
117 .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_MISS)},
118 {.name = "LLC-prefetches",
119 .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_ACCESS)},
120 {.name = "LLC-prefetch-misses",
121 .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_MISS)},
123 {.name = "dTLB-loads",
124 .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_ACCESS)},
125 {.name = "dTLB-load-misses",
126 .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_MISS)},
127 {.name = "dTLB-stores",
128 .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_ACCESS)},
129 {.name = "dTLB-store-misses",
130 .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_MISS)},
131 {.name = "dTLB-prefetches",
132 .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_ACCESS)},
133 {.name = "dTLB-prefetch-misses",
134 .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_MISS)},
136 {.name = "iTLB-loads",
137 .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_ACCESS)},
138 {.name = "iTLB-load-misses",
139 .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_MISS)},
141 {.name = "branch-loads",
142 .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_ACCESS)},
143 {.name = "branch-load-misses",
144 .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_MISS)},
147 event_info_t g_sw_events[] = {
148 {.name = "cpu-clock", .config = PERF_COUNT_SW_CPU_CLOCK},
150 {.name = "task-clock", .config = PERF_COUNT_SW_TASK_CLOCK},
152 {.name = "context-switches", .config = PERF_COUNT_SW_CONTEXT_SWITCHES},
154 {.name = "cpu-migrations", .config = PERF_COUNT_SW_CPU_MIGRATIONS},
156 {.name = "page-faults", .config = PERF_COUNT_SW_PAGE_FAULTS},
158 {.name = "minor-faults", .config = PERF_COUNT_SW_PAGE_FAULTS_MIN},
160 {.name = "major-faults", .config = PERF_COUNT_SW_PAGE_FAULTS_MAJ},
162 {.name = "alignment-faults", .config = PERF_COUNT_SW_ALIGNMENT_FAULTS},
164 {.name = "emulation-faults", .config = PERF_COUNT_SW_EMULATION_FAULTS},
167 static intel_pmu_ctx_t g_ctx;
170 static void pmu_dump_events() {
172 DEBUG(PMU_PLUGIN ": Events:");
176 for (e = g_ctx.event_list->eventlist; e; e = e->next) {
177 DEBUG(PMU_PLUGIN ": event : %s", e->event);
178 DEBUG(PMU_PLUGIN ": group_lead: %d", e->group_leader);
179 DEBUG(PMU_PLUGIN ": end_group : %d", e->end_group);
180 DEBUG(PMU_PLUGIN ": type : %#x", e->attr.type);
181 DEBUG(PMU_PLUGIN ": config : %#x", (unsigned)e->attr.config);
182 DEBUG(PMU_PLUGIN ": size : %d", e->attr.size);
188 static void pmu_dump_config(void) {
190 DEBUG(PMU_PLUGIN ": Config:");
191 DEBUG(PMU_PLUGIN ": hw_cache_events : %d", g_ctx.hw_cache_events);
192 DEBUG(PMU_PLUGIN ": kernel_pmu_events : %d", g_ctx.kernel_pmu_events);
193 DEBUG(PMU_PLUGIN ": software_events : %d", g_ctx.sw_events);
195 for (size_t i = 0; i < g_ctx.hw_events_count; i++) {
196 DEBUG(PMU_PLUGIN ": hardware_events[%zu]: %s", i, g_ctx.hw_events[i]);
202 #endif /* COLLECT_DEBUG */
204 static int pmu_config_hw_events(oconfig_item_t *ci) {
206 if (strcasecmp("HardwareEvents", ci->key) != 0) {
210 g_ctx.hw_events = calloc(ci->values_num, sizeof(char *));
211 if (g_ctx.hw_events == NULL) {
212 ERROR(PMU_PLUGIN ": Failed to allocate hw events.");
216 for (int i = 0; i < ci->values_num; i++) {
217 if (ci->values[i].type != OCONFIG_TYPE_STRING) {
218 WARNING(PMU_PLUGIN ": The %s option requires string arguments.", ci->key);
222 g_ctx.hw_events[g_ctx.hw_events_count] = strdup(ci->values[i].value.string);
223 if (g_ctx.hw_events[g_ctx.hw_events_count] == NULL) {
224 ERROR(PMU_PLUGIN ": Failed to allocate hw events entry.");
228 g_ctx.hw_events_count++;
234 static int pmu_config(oconfig_item_t *ci) {
237 DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
239 for (int i = 0; i < ci->children_num; i++) {
240 oconfig_item_t *child = ci->children + i;
242 if (strcasecmp("ReportHardwareCacheEvents", child->key) == 0) {
243 ret = cf_util_get_boolean(child, &g_ctx.hw_cache_events);
244 } else if (strcasecmp("ReportKernelPMUEvents", child->key) == 0) {
245 ret = cf_util_get_boolean(child, &g_ctx.kernel_pmu_events);
246 } else if (strcasecmp("HardwareEvents", child->key) == 0) {
247 ret = pmu_config_hw_events(child);
248 } else if (strcasecmp("ReportSoftwareEvents", child->key) == 0) {
249 ret = cf_util_get_boolean(child, &g_ctx.sw_events);
251 ERROR(PMU_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
256 DEBUG(PMU_PLUGIN ": %s:%d ret=%d", __FUNCTION__, __LINE__, ret);
268 static void pmu_submit_counter(int cpu, char *event, counter_t value) {
269 value_list_t vl = VALUE_LIST_INIT;
271 vl.values = &(value_t){.counter = value};
274 sstrncpy(vl.plugin, PMU_PLUGIN, sizeof(vl.plugin));
276 ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "all");
278 ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%d", cpu);
280 sstrncpy(vl.type, "counter", sizeof(vl.type));
281 sstrncpy(vl.type_instance, event, sizeof(vl.type_instance));
283 plugin_dispatch_values(&vl);
286 static int pmu_dispatch_data(void) {
290 for (e = g_ctx.event_list->eventlist; e; e = e->next) {
291 uint64_t all_value = 0;
292 int event_enabled = 0;
293 for (int i = 0; i < g_ctx.event_list->num_cpus; i++) {
295 if (e->efd[i].fd < 0)
300 uint64_t value = event_scaled_value(e, i);
303 /* dispatch per CPU value */
304 pmu_submit_counter(i, e->event, value);
307 if (event_enabled > 0) {
308 DEBUG(PMU_PLUGIN ": %-20s %'10lu", e->event, all_value);
309 /* dispatch all CPU value */
310 pmu_submit_counter(-1, e->event, all_value);
317 static int pmu_read(__attribute__((unused)) user_data_t *ud) {
320 DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
322 ret = read_all_events(g_ctx.event_list);
324 ERROR(PMU_PLUGIN ": Failed to read values of all events.");
328 ret = pmu_dispatch_data();
330 ERROR(PMU_PLUGIN ": Failed to dispatch event values.");
337 static int pmu_add_events(struct eventlist *el, uint32_t type,
338 event_info_t *events, int count) {
340 for (int i = 0; i < count; i++) {
341 /* Allocate memory for event struct that contains array of efd structs
344 calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1);
346 ERROR(PMU_PLUGIN ": Failed to allocate event structure");
351 e->attr.config = events[i].config;
352 e->attr.size = PERF_ATTR_SIZE_VER0;
356 if (el->eventlist_last)
357 el->eventlist_last->next = e;
358 el->eventlist_last = e;
359 e->event = strdup(events[i].name);
365 static int pmu_add_hw_events(struct eventlist *el, char **e, size_t count) {
367 for (size_t i = 0; i < count; i++) {
369 size_t group_events_count = 0;
371 char *events = strdup(e[i]);
376 for (s = strtok_r(events, ",", &tmp); s; s = strtok_r(NULL, ",", &tmp)) {
378 /* Multiple events parsed in one entry */
379 if (group_events_count == 1) {
380 /* Mark previously added event as group leader */
381 el->eventlist_last->group_leader = 1;
384 /* Allocate memory for event struct that contains array of efd structs
387 calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1);
393 if (resolve_event(s, &e->attr) == 0) {
397 if (el->eventlist_last)
398 el->eventlist_last->next = e;
399 el->eventlist_last = e;
400 e->event = strdup(s);
402 DEBUG(PMU_PLUGIN ": Cannot resolve %s", s);
406 group_events_count++;
409 /* Multiple events parsed in one entry */
410 if (group_events_count > 1) {
411 /* Mark last added event as group end */
412 el->eventlist_last->end_group = 1;
421 static void pmu_free_events(struct eventlist *el) {
426 struct event *e = el->eventlist;
429 struct event *next = e->next;
434 el->eventlist = NULL;
437 static int pmu_setup_events(struct eventlist *el, bool measure_all,
439 struct event *e, *leader = NULL;
442 for (e = el->eventlist; e; e = e->next) {
444 for (int i = 0; i < el->num_cpus; i++) {
445 if (setup_event(e, i, leader, measure_all, measure_pid) < 0) {
446 WARNING(PMU_PLUGIN ": perf event '%s' is not available (cpu=%d).",
449 /* success if at least one event was set */
463 static int pmu_init(void) {
466 DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
468 g_ctx.event_list = alloc_eventlist();
469 if (g_ctx.event_list == NULL) {
470 ERROR(PMU_PLUGIN ": Failed to allocate event list.");
474 if (g_ctx.hw_cache_events) {
476 pmu_add_events(g_ctx.event_list, PERF_TYPE_HW_CACHE, g_hw_cache_events,
477 STATIC_ARRAY_SIZE(g_hw_cache_events));
479 ERROR(PMU_PLUGIN ": Failed to add hw cache events.");
484 if (g_ctx.kernel_pmu_events) {
485 ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_HARDWARE,
487 STATIC_ARRAY_SIZE(g_kernel_pmu_events));
489 ERROR(PMU_PLUGIN ": Failed to add kernel PMU events.");
494 /* parse events names if config option is present and is not empty */
495 if (g_ctx.hw_events_count) {
496 ret = pmu_add_hw_events(g_ctx.event_list, g_ctx.hw_events,
497 g_ctx.hw_events_count);
499 ERROR(PMU_PLUGIN ": Failed to add hardware events.");
504 if (g_ctx.sw_events) {
505 ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_SOFTWARE, g_sw_events,
506 STATIC_ARRAY_SIZE(g_sw_events));
508 ERROR(PMU_PLUGIN ": Failed to add software events.");
517 if (g_ctx.event_list->eventlist != NULL) {
518 /* measure all processes */
519 ret = pmu_setup_events(g_ctx.event_list, true, -1);
521 ERROR(PMU_PLUGIN ": Failed to setup perf events for the event list.");
526 ": Events list is empty. No events were setup for monitoring.");
533 pmu_free_events(g_ctx.event_list);
534 sfree(g_ctx.event_list);
535 for (size_t i = 0; i < g_ctx.hw_events_count; i++) {
536 sfree(g_ctx.hw_events[i]);
538 sfree(g_ctx.hw_events);
539 g_ctx.hw_events_count = 0;
545 static int pmu_shutdown(void) {
547 DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
549 pmu_free_events(g_ctx.event_list);
550 sfree(g_ctx.event_list);
551 for (size_t i = 0; i < g_ctx.hw_events_count; i++) {
552 sfree(g_ctx.hw_events[i]);
554 sfree(g_ctx.hw_events);
555 g_ctx.hw_events_count = 0;
560 void module_register(void) {
561 plugin_register_init(PMU_PLUGIN, pmu_init);
562 plugin_register_complex_config(PMU_PLUGIN, pmu_config);
563 plugin_register_complex_read(NULL, PMU_PLUGIN, pmu_read, 0, NULL);
564 plugin_register_shutdown(PMU_PLUGIN, pmu_shutdown);