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 (((PERF_COUNT_HW_CACHE_OP_READ) << 8) | \
37 ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
39 #define HW_CACHE_WRITE_ACCESS (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) | \
40 ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
42 #define HW_CACHE_PREFETCH_ACCESS (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) | \
43 ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
45 #define HW_CACHE_READ_MISS (((PERF_COUNT_HW_CACHE_OP_READ) << 8) | \
46 ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
48 #define HW_CACHE_WRITE_MISS (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) | \
49 ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
51 #define HW_CACHE_PREFETCH_MISS (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) | \
52 ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
58 typedef struct event_info event_info_t;
60 struct intel_pmu_ctx_s {
61 _Bool hw_cache_events;
62 _Bool kernel_pmu_events;
64 char* hw_specific_events;
65 struct eventlist *event_list;
67 typedef struct intel_pmu_ctx_s intel_pmu_ctx_t;
69 event_info_t g_kernel_pmu_events[] = {
70 { .name = "cpu-cycles",
71 .config = PERF_COUNT_HW_CPU_CYCLES },
72 { .name = "instructions",
73 .config = PERF_COUNT_HW_INSTRUCTIONS },
74 { .name = "cache-references",
75 .config = PERF_COUNT_HW_CACHE_REFERENCES },
76 { .name = "cache-misses",
77 .config = PERF_COUNT_HW_CACHE_MISSES },
79 .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
80 { .name = "branch-misses",
81 .config = PERF_COUNT_HW_BRANCH_MISSES },
82 { .name = "bus-cycles",
83 .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",
149 .config = PERF_COUNT_SW_CPU_CLOCK },
151 { .name = "task-clock",
152 .config = PERF_COUNT_SW_TASK_CLOCK },
154 { .name = "context-switches",
155 .config = PERF_COUNT_SW_CONTEXT_SWITCHES },
157 { .name = "cpu-migrations",
158 .config = PERF_COUNT_SW_CPU_MIGRATIONS },
160 { .name = "page-faults",
161 .config = PERF_COUNT_SW_PAGE_FAULTS },
163 { .name = "minor-faults",
164 .config = PERF_COUNT_SW_PAGE_FAULTS_MIN },
166 { .name = "major-faults",
167 .config = PERF_COUNT_SW_PAGE_FAULTS_MAJ },
169 { .name = "alignment-faults",
170 .config = PERF_COUNT_SW_ALIGNMENT_FAULTS },
172 { .name = "emulation-faults",
173 .config = PERF_COUNT_SW_EMULATION_FAULTS },
176 static intel_pmu_ctx_t g_ctx;
179 static void pmu_dump_events() {
181 DEBUG(PMU_PLUGIN ": Events:");
185 for (e = g_ctx.event_list->eventlist; e; e = e->next) {
186 DEBUG(PMU_PLUGIN ": event : %s", e->event);
187 DEBUG(PMU_PLUGIN ": group_lead: %d", e->group_leader);
188 DEBUG(PMU_PLUGIN ": end_group : %d", e->end_group);
189 DEBUG(PMU_PLUGIN ": type : 0x%X", e->attr.type);
190 DEBUG(PMU_PLUGIN ": config : 0x%X", (int)e->attr.config);
191 DEBUG(PMU_PLUGIN ": size : %d", e->attr.size);
197 static void pmu_dump_config(void) {
199 DEBUG(PMU_PLUGIN ": Config:");
200 DEBUG(PMU_PLUGIN ": hw_cache_events : %d", g_ctx.hw_cache_events);
201 DEBUG(PMU_PLUGIN ": kernel_pmu_events : %d", g_ctx.kernel_pmu_events);
202 DEBUG(PMU_PLUGIN ": sw_events : %d", g_ctx.sw_events);
203 DEBUG(PMU_PLUGIN ": hw_specific_events: %s", g_ctx.hw_specific_events);
208 #endif /* COLLECT_DEBUG */
210 static int pmu_config(oconfig_item_t *ci) {
213 DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
215 for (int i = 0; i < ci->children_num; i++) {
216 oconfig_item_t *child = ci->children + i;
218 if (strcasecmp("HWCacheEvents", child->key) == 0) {
219 ret = cf_util_get_boolean(child, &g_ctx.hw_cache_events);
220 } else if (strcasecmp("KernelPMUEvents", child->key) == 0) {
221 ret = cf_util_get_boolean(child, &g_ctx.kernel_pmu_events);
222 } else if (strcasecmp("HWSpecificEvents", child->key) == 0) {
223 ret = cf_util_get_string(child, &g_ctx.hw_specific_events);
224 } else if (strcasecmp("SWEvents", child->key) == 0) {
225 ret = cf_util_get_boolean(child, &g_ctx.sw_events);
227 ERROR(PMU_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
232 DEBUG(PMU_PLUGIN ": %s:%d ret=%d", __FUNCTION__, __LINE__, ret);
244 static void pmu_submit_counter(int cpu, char *event, counter_t value) {
245 value_list_t vl = VALUE_LIST_INIT;
247 vl.values = &(value_t){.counter = value};
250 sstrncpy(vl.plugin, PMU_PLUGIN, sizeof(vl.plugin));
252 snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "all");
254 snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%d", cpu);
256 sstrncpy(vl.type, "counter", sizeof(vl.type));
257 sstrncpy(vl.type_instance, event, sizeof(vl.type_instance));
259 plugin_dispatch_values(&vl);
262 static int pmu_dispatch_data(void) {
266 for (e = g_ctx.event_list->eventlist; e; e = e->next) {
267 uint64_t all_value = 0;
268 int event_enabled = 0;
269 for (int i = 0; i < g_ctx.event_list->num_cpus; i++) {
271 if (e->efd[i].fd < 0)
276 uint64_t value = event_scaled_value(e, i);
279 /* dispatch per CPU value */
280 pmu_submit_counter(i, e->event, value);
283 if (event_enabled > 0) {
284 DEBUG(PMU_PLUGIN ": %-20s %'10lu", e->event, all_value);
285 /* dispatch all CPU value */
286 pmu_submit_counter(-1, e->event, all_value);
293 static int pmu_read(__attribute__((unused)) user_data_t *ud) {
296 DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
298 ret = read_all_events(g_ctx.event_list);
300 DEBUG(PMU_PLUGIN ": Failed to read values of all events.");
304 ret = pmu_dispatch_data();
306 DEBUG(PMU_PLUGIN ": Failed to dispatch event values.");
313 static int pmu_add_events(struct eventlist *el, uint32_t type,
314 event_info_t *events, int count) {
316 for (int i = 0; i < count; i++) {
317 struct event *e = calloc(sizeof(struct event) +
318 sizeof(struct efd) * el->num_cpus, 1);
320 ERROR(PMU_PLUGIN ": Failed to allocate event structure");
325 e->attr.config = events[i].config;
326 e->attr.size = PERF_ATTR_SIZE_VER0;
327 e->group_leader = false;
328 e->end_group = false;
332 if (el->eventlist_last)
333 el->eventlist_last->next = e;
334 el->eventlist_last = e;
335 e->event = strdup(events[i].name);
341 static int pmu_parse_events(struct eventlist *el, char *events) {
344 events = strdup(events);
348 for (s = strtok_r(events, ",", &tmp);
350 s = strtok_r(NULL, ",", &tmp)) {
351 bool group_leader = false, end_group = false;
357 } else if (len = strlen(s), len > 0 && s[len - 1] == '}') {
362 struct event *e = calloc(sizeof(struct event) +
363 sizeof(struct efd) * el->num_cpus, 1);
369 if (resolve_event(s, &e->attr) == 0) {
370 e->group_leader = group_leader;
371 e->end_group = end_group;
375 if (el->eventlist_last)
376 el->eventlist_last->next = e;
377 el->eventlist_last = e;
378 e->event = strdup(s);
380 DEBUG(PMU_PLUGIN ": Cannot resolve %s", s);
390 static void pmu_free_events(struct eventlist *el) {
395 struct event *e = el->eventlist;
398 struct event *next = e->next;
403 el->eventlist = NULL;
406 static int pmu_setup_events(struct eventlist *el, bool measure_all,
408 struct event *e, *leader = NULL;
411 for (e = el->eventlist; e; e = e->next) {
413 for (int i = 0; i < el->num_cpus; i++) {
414 if (setup_event(e, i, leader, measure_all, measure_pid) < 0) {
415 WARNING(PMU_PLUGIN ": perf event '%s' is not available (cpu=%d).",
418 /* success if at least one event was set */
432 static int pmu_init(void) {
435 DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
437 g_ctx.event_list = alloc_eventlist();
438 if (g_ctx.event_list == NULL) {
439 ERROR(PMU_PLUGIN ": Failed to allocate event list.");
443 if (g_ctx.hw_cache_events) {
444 ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_HW_CACHE,
445 g_hw_cache_events, STATIC_ARRAY_SIZE(g_hw_cache_events));
447 ERROR(PMU_PLUGIN ": Failed to add hw cache events.");
452 if (g_ctx.kernel_pmu_events) {
453 ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_HARDWARE,
454 g_kernel_pmu_events, STATIC_ARRAY_SIZE(g_kernel_pmu_events));
456 ERROR(PMU_PLUGIN ": Failed to parse kernel PMU events.");
461 /* parse events names if config option is present and is not empty */
462 if (g_ctx.hw_specific_events && (strlen(g_ctx.hw_specific_events) != 0)) {
463 ret = pmu_parse_events(g_ctx.event_list, g_ctx.hw_specific_events);
465 ERROR(PMU_PLUGIN ": Failed to parse hw specific events.");
470 if (g_ctx.sw_events) {
471 ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_SOFTWARE,
472 g_sw_events, STATIC_ARRAY_SIZE(g_sw_events));
474 ERROR(PMU_PLUGIN ": Failed to add software events.");
483 if (g_ctx.event_list->eventlist != NULL) {
484 /* measure all processes */
485 ret = pmu_setup_events(g_ctx.event_list, true, -1);
487 ERROR(PMU_PLUGIN ": Failed to setup perf events for the event list.");
492 ": Events list is empty. No events were setup for monitoring.");
499 pmu_free_events(g_ctx.event_list);
500 sfree(g_ctx.event_list);
501 sfree(g_ctx.hw_specific_events);
506 static int pmu_shutdown(void) {
508 DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
510 pmu_free_events(g_ctx.event_list);
511 sfree(g_ctx.event_list);
512 sfree(g_ctx.hw_specific_events);
517 void module_register(void) {
518 plugin_register_init(PMU_PLUGIN, pmu_init);
519 plugin_register_complex_config(PMU_PLUGIN, pmu_config);
520 plugin_register_complex_read(NULL, PMU_PLUGIN, pmu_read, 0, NULL);
521 plugin_register_shutdown(PMU_PLUGIN, pmu_shutdown);