X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fintel_pmu.c;h=d868c892059426d0e201f8ed0fa8c3653da2c890;hb=849f5394cce97a76da080f6cd9e5194b7f4ee0f0;hp=5174f59c243d5598e374bc59c208394d0491408b;hpb=08e419e462e8373538a7aef768bae0b2478e0e89;p=collectd.git diff --git a/src/intel_pmu.c b/src/intel_pmu.c index 5174f59c..d868c892 100644 --- a/src/intel_pmu.c +++ b/src/intel_pmu.c @@ -28,32 +28,38 @@ #include "collectd.h" #include "common.h" -#include "jevents.h" -#include "jsession.h" +#include +#include #define PMU_PLUGIN "intel_pmu" -#define HW_CACHE_READ_ACCESS (((PERF_COUNT_HW_CACHE_OP_READ) << 8) | \ - ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16)) +#define HW_CACHE_READ_ACCESS \ + (((PERF_COUNT_HW_CACHE_OP_READ) << 8) | \ + ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16)) -#define HW_CACHE_WRITE_ACCESS (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) | \ - ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16)) +#define HW_CACHE_WRITE_ACCESS \ + (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) | \ + ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16)) -#define HW_CACHE_PREFETCH_ACCESS (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) | \ - ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16)) +#define HW_CACHE_PREFETCH_ACCESS \ + (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) | \ + ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16)) -#define HW_CACHE_READ_MISS (((PERF_COUNT_HW_CACHE_OP_READ) << 8) | \ - ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) +#define HW_CACHE_READ_MISS \ + (((PERF_COUNT_HW_CACHE_OP_READ) << 8) | \ + ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) -#define HW_CACHE_WRITE_MISS (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) | \ - ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) +#define HW_CACHE_WRITE_MISS \ + (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) | \ + ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) -#define HW_CACHE_PREFETCH_MISS (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) | \ - ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) +#define HW_CACHE_PREFETCH_MISS \ + (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) | \ + ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) struct event_info { - char *name; - uint64_t config; + char *name; + uint64_t config; }; typedef struct event_info event_info_t; @@ -61,116 +67,102 @@ struct intel_pmu_ctx_s { _Bool hw_cache_events; _Bool kernel_pmu_events; _Bool sw_events; - char* hw_specific_events; + char event_list_fn[PATH_MAX]; + char **hw_events; + size_t hw_events_count; struct eventlist *event_list; }; typedef struct intel_pmu_ctx_s intel_pmu_ctx_t; event_info_t g_kernel_pmu_events[] = { - { .name = "cpu-cycles", - .config = PERF_COUNT_HW_CPU_CYCLES }, - { .name = "instructions", - .config = PERF_COUNT_HW_INSTRUCTIONS }, - { .name = "cache-references", - .config = PERF_COUNT_HW_CACHE_REFERENCES }, - { .name = "cache-misses", - .config = PERF_COUNT_HW_CACHE_MISSES }, - { .name = "branches", - .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, - { .name = "branch-misses", - .config = PERF_COUNT_HW_BRANCH_MISSES }, - { .name = "bus-cycles", - .config = PERF_COUNT_HW_BUS_CYCLES }, + {.name = "cpu-cycles", .config = PERF_COUNT_HW_CPU_CYCLES}, + {.name = "instructions", .config = PERF_COUNT_HW_INSTRUCTIONS}, + {.name = "cache-references", .config = PERF_COUNT_HW_CACHE_REFERENCES}, + {.name = "cache-misses", .config = PERF_COUNT_HW_CACHE_MISSES}, + {.name = "branches", .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS}, + {.name = "branch-misses", .config = PERF_COUNT_HW_BRANCH_MISSES}, + {.name = "bus-cycles", .config = PERF_COUNT_HW_BUS_CYCLES}, }; event_info_t g_hw_cache_events[] = { - { .name = "L1-dcache-loads", - .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_ACCESS) }, - { .name = "L1-dcache-load-misses", - .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_MISS) }, - { .name = "L1-dcache-stores", - .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_ACCESS) }, - { .name = "L1-dcache-store-misses", - .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_MISS) }, - { .name = "L1-dcache-prefetches", - .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_ACCESS) }, - { .name = "L1-dcache-prefetch-misses", - .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_MISS) }, - - { .name = "L1-icache-loads", - .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_ACCESS) }, - { .name = "L1-icache-load-misses", - .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_MISS) }, - { .name = "L1-icache-prefetches", - .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_ACCESS) }, - { .name = "L1-icache-prefetch-misses", - .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_MISS) }, - - { .name = "LLC-loads", - .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_ACCESS) }, - { .name = "LLC-load-misses", - .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_MISS) }, - { .name = "LLC-stores", - .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_ACCESS) }, - { .name = "LLC-store-misses", - .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_MISS) }, - { .name = "LLC-prefetches", - .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_ACCESS) }, - { .name = "LLC-prefetch-misses", - .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_MISS) }, - - { .name = "dTLB-loads", - .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_ACCESS) }, - { .name = "dTLB-load-misses", - .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_MISS) }, - { .name = "dTLB-stores", - .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_ACCESS) }, - { .name = "dTLB-store-misses", - .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_MISS) }, - { .name = "dTLB-prefetches", - .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_ACCESS) }, - { .name = "dTLB-prefetch-misses", - .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_MISS) }, - - { .name = "iTLB-loads", - .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_ACCESS) }, - { .name = "iTLB-load-misses", - .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_MISS) }, - - { .name = "branch-loads", - .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_ACCESS) }, - { .name = "branch-load-misses", - .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_MISS) }, + {.name = "L1-dcache-loads", + .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_ACCESS)}, + {.name = "L1-dcache-load-misses", + .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_MISS)}, + {.name = "L1-dcache-stores", + .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_ACCESS)}, + {.name = "L1-dcache-store-misses", + .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_MISS)}, + {.name = "L1-dcache-prefetches", + .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_ACCESS)}, + {.name = "L1-dcache-prefetch-misses", + .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_MISS)}, + + {.name = "L1-icache-loads", + .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_ACCESS)}, + {.name = "L1-icache-load-misses", + .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_MISS)}, + {.name = "L1-icache-prefetches", + .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_ACCESS)}, + {.name = "L1-icache-prefetch-misses", + .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_MISS)}, + + {.name = "LLC-loads", + .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_ACCESS)}, + {.name = "LLC-load-misses", + .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_MISS)}, + {.name = "LLC-stores", + .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_ACCESS)}, + {.name = "LLC-store-misses", + .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_MISS)}, + {.name = "LLC-prefetches", + .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_ACCESS)}, + {.name = "LLC-prefetch-misses", + .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_MISS)}, + + {.name = "dTLB-loads", + .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_ACCESS)}, + {.name = "dTLB-load-misses", + .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_MISS)}, + {.name = "dTLB-stores", + .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_ACCESS)}, + {.name = "dTLB-store-misses", + .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_MISS)}, + {.name = "dTLB-prefetches", + .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_ACCESS)}, + {.name = "dTLB-prefetch-misses", + .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_MISS)}, + + {.name = "iTLB-loads", + .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_ACCESS)}, + {.name = "iTLB-load-misses", + .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_MISS)}, + + {.name = "branch-loads", + .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_ACCESS)}, + {.name = "branch-load-misses", + .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_MISS)}, }; event_info_t g_sw_events[] = { - { .name = "cpu-clock", - .config = PERF_COUNT_SW_CPU_CLOCK }, + {.name = "cpu-clock", .config = PERF_COUNT_SW_CPU_CLOCK}, - { .name = "task-clock", - .config = PERF_COUNT_SW_TASK_CLOCK }, + {.name = "task-clock", .config = PERF_COUNT_SW_TASK_CLOCK}, - { .name = "context-switches", - .config = PERF_COUNT_SW_CONTEXT_SWITCHES }, + {.name = "context-switches", .config = PERF_COUNT_SW_CONTEXT_SWITCHES}, - { .name = "cpu-migrations", - .config = PERF_COUNT_SW_CPU_MIGRATIONS }, + {.name = "cpu-migrations", .config = PERF_COUNT_SW_CPU_MIGRATIONS}, - { .name = "page-faults", - .config = PERF_COUNT_SW_PAGE_FAULTS }, + {.name = "page-faults", .config = PERF_COUNT_SW_PAGE_FAULTS}, - { .name = "minor-faults", - .config = PERF_COUNT_SW_PAGE_FAULTS_MIN }, + {.name = "minor-faults", .config = PERF_COUNT_SW_PAGE_FAULTS_MIN}, - { .name = "major-faults", - .config = PERF_COUNT_SW_PAGE_FAULTS_MAJ }, + {.name = "major-faults", .config = PERF_COUNT_SW_PAGE_FAULTS_MAJ}, - { .name = "alignment-faults", - .config = PERF_COUNT_SW_ALIGNMENT_FAULTS }, + {.name = "alignment-faults", .config = PERF_COUNT_SW_ALIGNMENT_FAULTS}, - { .name = "emulation-faults", - .config = PERF_COUNT_SW_EMULATION_FAULTS }, + {.name = "emulation-faults", .config = PERF_COUNT_SW_EMULATION_FAULTS}, }; static intel_pmu_ctx_t g_ctx; @@ -186,12 +178,10 @@ static void pmu_dump_events() { DEBUG(PMU_PLUGIN ": event : %s", e->event); DEBUG(PMU_PLUGIN ": group_lead: %d", e->group_leader); DEBUG(PMU_PLUGIN ": end_group : %d", e->end_group); - DEBUG(PMU_PLUGIN ": type : 0x%X", e->attr.type); - DEBUG(PMU_PLUGIN ": config : 0x%X", (int)e->attr.config); + DEBUG(PMU_PLUGIN ": type : %#x", e->attr.type); + DEBUG(PMU_PLUGIN ": config : %#x", (unsigned)e->attr.config); DEBUG(PMU_PLUGIN ": size : %d", e->attr.size); } - - return; } static void pmu_dump_config(void) { @@ -199,33 +189,73 @@ static void pmu_dump_config(void) { DEBUG(PMU_PLUGIN ": Config:"); DEBUG(PMU_PLUGIN ": hw_cache_events : %d", g_ctx.hw_cache_events); DEBUG(PMU_PLUGIN ": kernel_pmu_events : %d", g_ctx.kernel_pmu_events); - DEBUG(PMU_PLUGIN ": sw_events : %d", g_ctx.sw_events); - DEBUG(PMU_PLUGIN ": hw_specific_events: %s", g_ctx.hw_specific_events); + DEBUG(PMU_PLUGIN ": software_events : %d", g_ctx.sw_events); - return; + for (size_t i = 0; i < g_ctx.hw_events_count; i++) { + DEBUG(PMU_PLUGIN ": hardware_events[%" PRIsz "]: %s", i, + g_ctx.hw_events[i]); + } } #endif /* COLLECT_DEBUG */ +static int pmu_config_hw_events(oconfig_item_t *ci) { + + if (strcasecmp("HardwareEvents", ci->key) != 0) { + return -EINVAL; + } + + if (g_ctx.hw_events) { + ERROR(PMU_PLUGIN ": Duplicate config for HardwareEvents."); + return -EINVAL; + } + + g_ctx.hw_events = calloc(ci->values_num, sizeof(char *)); + if (g_ctx.hw_events == NULL) { + ERROR(PMU_PLUGIN ": Failed to allocate hw events."); + return -ENOMEM; + } + + for (int i = 0; i < ci->values_num; i++) { + if (ci->values[i].type != OCONFIG_TYPE_STRING) { + WARNING(PMU_PLUGIN ": The %s option requires string arguments.", ci->key); + continue; + } + + g_ctx.hw_events[g_ctx.hw_events_count] = strdup(ci->values[i].value.string); + if (g_ctx.hw_events[g_ctx.hw_events_count] == NULL) { + ERROR(PMU_PLUGIN ": Failed to allocate hw events entry."); + return -ENOMEM; + } + + g_ctx.hw_events_count++; + } + + return 0; +} + static int pmu_config(oconfig_item_t *ci) { - int ret = 0; DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__); for (int i = 0; i < ci->children_num; i++) { + int ret = 0; oconfig_item_t *child = ci->children + i; - if (strcasecmp("HWCacheEvents", child->key) == 0) { + if (strcasecmp("ReportHardwareCacheEvents", child->key) == 0) { ret = cf_util_get_boolean(child, &g_ctx.hw_cache_events); - } else if (strcasecmp("KernelPMUEvents", child->key) == 0) { + } else if (strcasecmp("ReportKernelPMUEvents", child->key) == 0) { ret = cf_util_get_boolean(child, &g_ctx.kernel_pmu_events); - } else if (strcasecmp("HWSpecificEvents", child->key) == 0) { - ret = cf_util_get_string(child, &g_ctx.hw_specific_events); - } else if (strcasecmp("SWEvents", child->key) == 0) { + } else if (strcasecmp("EventList", child->key) == 0) { + ret = cf_util_get_string_buffer(child, g_ctx.event_list_fn, + sizeof(g_ctx.event_list_fn)); + } else if (strcasecmp("HardwareEvents", child->key) == 0) { + ret = pmu_config_hw_events(child); + } else if (strcasecmp("ReportSoftwareEvents", child->key) == 0) { ret = cf_util_get_boolean(child, &g_ctx.sw_events); - }else { + } else { ERROR(PMU_PLUGIN ": Unknown configuration parameter \"%s\".", child->key); - ret = (-1); + ret = -1; } if (ret != 0) { @@ -238,10 +268,11 @@ static int pmu_config(oconfig_item_t *ci) { pmu_dump_config(); #endif - return (0); + return 0; } -static void pmu_submit_counter(int cpu, char *event, counter_t value) { +static void pmu_submit_counter(int cpu, char *event, counter_t value, + meta_data_t *meta) { value_list_t vl = VALUE_LIST_INIT; vl.values = &(value_t){.counter = value}; @@ -251,6 +282,7 @@ static void pmu_submit_counter(int cpu, char *event, counter_t value) { if (cpu == -1) { snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "all"); } else { + vl.meta = meta; snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%d", cpu); } sstrncpy(vl.type, "counter", sizeof(vl.type)); @@ -259,7 +291,28 @@ static void pmu_submit_counter(int cpu, char *event, counter_t value) { plugin_dispatch_values(&vl); } -static int pmu_dispatch_data(void) { +meta_data_t *pmu_meta_data_create(const struct efd *efd) { + meta_data_t *meta = NULL; + + /* create meta data only if value was scaled */ + if (efd->val[1] == efd->val[2] || !efd->val[2]) { + return NULL; + } + + meta = meta_data_create(); + if (meta == NULL) { + ERROR(PMU_PLUGIN ": meta_data_create failed."); + return NULL; + } + + meta_data_add_unsigned_int(meta, "intel_pmu:raw_count", efd->val[0]); + meta_data_add_unsigned_int(meta, "intel_pmu:time_enabled", efd->val[1]); + meta_data_add_unsigned_int(meta, "intel_pmu:time_running", efd->val[2]); + + return meta; +} + +static void pmu_dispatch_data(void) { struct event *e; @@ -273,21 +326,29 @@ static int pmu_dispatch_data(void) { event_enabled++; + /* If there are more events than counters, the kernel uses time + * multiplexing. With multiplexing, at the end of the run, + * the counter is scaled basing on total time enabled vs time running. + * final_count = raw_count * time_enabled/time_running + */ uint64_t value = event_scaled_value(e, i); all_value += value; + /* get meta data with information about scaling */ + meta_data_t *meta = pmu_meta_data_create(&e->efd[i]); + /* dispatch per CPU value */ - pmu_submit_counter(i, e->event, value); + pmu_submit_counter(i, e->event, value, meta); + + meta_data_destroy(meta); } if (event_enabled > 0) { DEBUG(PMU_PLUGIN ": %-20s %'10lu", e->event, all_value); /* dispatch all CPU value */ - pmu_submit_counter(-1, e->event, all_value); + pmu_submit_counter(-1, e->event, all_value, NULL); } } - - return (0); } static int pmu_read(__attribute__((unused)) user_data_t *ud) { @@ -297,36 +358,31 @@ static int pmu_read(__attribute__((unused)) user_data_t *ud) { ret = read_all_events(g_ctx.event_list); if (ret != 0) { - DEBUG(PMU_PLUGIN ": Failed to read values of all events."); - return (0); + ERROR(PMU_PLUGIN ": Failed to read values of all events."); + return ret; } - ret = pmu_dispatch_data(); - if (ret != 0) { - DEBUG(PMU_PLUGIN ": Failed to dispatch event values."); - return (0); - } + pmu_dispatch_data(); - return (0); + return 0; } static int pmu_add_events(struct eventlist *el, uint32_t type, - event_info_t *events, int count) { + event_info_t *events, size_t count) { - for (int i = 0; i < count; i++) { - struct event *e = calloc(sizeof(struct event) + - sizeof(struct efd) * el->num_cpus, 1); + for (size_t i = 0; i < count; i++) { + /* Allocate memory for event struct that contains array of efd structs + for all cores */ + struct event *e = + calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1); if (e == NULL) { ERROR(PMU_PLUGIN ": Failed to allocate event structure"); - return (-ENOMEM); + return -ENOMEM; } e->attr.type = type; e->attr.config = events[i].config; e->attr.size = PERF_ATTR_SIZE_VER0; - e->group_leader = false; - e->end_group = false; - e->next = NULL; if (!el->eventlist) el->eventlist = e; if (el->eventlist_last) @@ -335,40 +391,43 @@ static int pmu_add_events(struct eventlist *el, uint32_t type, e->event = strdup(events[i].name); } - return (0); + return 0; } -static int pmu_parse_events(struct eventlist *el, char *events) { - char *s, *tmp; - - events = strdup(events); - if (!events) - return -1; - - for (s = strtok_r(events, ",", &tmp); - s; - s = strtok_r(NULL, ",", &tmp)) { - bool group_leader = false, end_group = false; - int len; - - if (s[0] == '{') { - s++; - group_leader = true; - } else if (len = strlen(s), len > 0 && s[len - 1] == '}') { - s[len - 1] = 0; - end_group = true; - } +static int pmu_add_hw_events(struct eventlist *el, char **e, size_t count) { - struct event *e = calloc(sizeof(struct event) + - sizeof(struct efd) * el->num_cpus, 1); - if (e == NULL) { - free(events); - return (-ENOMEM); - } + for (size_t i = 0; i < count; i++) { + + size_t group_events_count = 0; + + char *events = strdup(e[i]); + if (!events) + return -1; + + char *s, *tmp; + for (s = strtok_r(events, ",", &tmp); s; s = strtok_r(NULL, ",", &tmp)) { + + /* Allocate memory for event struct that contains array of efd structs + for all cores */ + struct event *e = + calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1); + if (e == NULL) { + free(events); + return -ENOMEM; + } + + if (resolve_event(s, &e->attr) != 0) { + WARNING(PMU_PLUGIN ": Cannot resolve %s", s); + sfree(e); + continue; + } + + /* Multiple events parsed in one entry */ + if (group_events_count == 1) { + /* Mark previously added event as group leader */ + el->eventlist_last->group_leader = 1; + } - if (resolve_event(s, &e->attr) == 0) { - e->group_leader = group_leader; - e->end_group = end_group; e->next = NULL; if (!el->eventlist) el->eventlist = e; @@ -376,15 +435,20 @@ static int pmu_parse_events(struct eventlist *el, char *events) { el->eventlist_last->next = e; el->eventlist_last = e; e->event = strdup(s); - } else { - DEBUG(PMU_PLUGIN ": Cannot resolve %s", s); - sfree(e); + + group_events_count++; } - } - free(events); + /* Multiple events parsed in one entry */ + if (group_events_count > 1) { + /* Mark last added event as group end */ + el->eventlist_last->end_group = 1; + } + + free(events); + } - return (0); + return 0; } static void pmu_free_events(struct eventlist *el) { @@ -437,12 +501,13 @@ static int pmu_init(void) { g_ctx.event_list = alloc_eventlist(); if (g_ctx.event_list == NULL) { ERROR(PMU_PLUGIN ": Failed to allocate event list."); - return (-ENOMEM); + return -ENOMEM; } if (g_ctx.hw_cache_events) { - ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_HW_CACHE, - g_hw_cache_events, STATIC_ARRAY_SIZE(g_hw_cache_events)); + ret = + pmu_add_events(g_ctx.event_list, PERF_TYPE_HW_CACHE, g_hw_cache_events, + STATIC_ARRAY_SIZE(g_hw_cache_events)); if (ret != 0) { ERROR(PMU_PLUGIN ": Failed to add hw cache events."); goto init_error; @@ -451,25 +516,35 @@ static int pmu_init(void) { if (g_ctx.kernel_pmu_events) { ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_HARDWARE, - g_kernel_pmu_events, STATIC_ARRAY_SIZE(g_kernel_pmu_events)); + g_kernel_pmu_events, + STATIC_ARRAY_SIZE(g_kernel_pmu_events)); if (ret != 0) { - ERROR(PMU_PLUGIN ": Failed to parse kernel PMU events."); + ERROR(PMU_PLUGIN ": Failed to add kernel PMU events."); goto init_error; } } /* parse events names if config option is present and is not empty */ - if (g_ctx.hw_specific_events && (strlen(g_ctx.hw_specific_events) != 0)) { - ret = pmu_parse_events(g_ctx.event_list, g_ctx.hw_specific_events); + if (g_ctx.hw_events_count) { + + ret = read_events(g_ctx.event_list_fn); if (ret != 0) { - ERROR(PMU_PLUGIN ": Failed to parse hw specific events."); + ERROR(PMU_PLUGIN ": Failed to read event list file '%s'.", + g_ctx.event_list_fn); + return ret; + } + + ret = pmu_add_hw_events(g_ctx.event_list, g_ctx.hw_events, + g_ctx.hw_events_count); + if (ret != 0) { + ERROR(PMU_PLUGIN ": Failed to add hardware events."); goto init_error; } } if (g_ctx.sw_events) { - ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_SOFTWARE, - g_sw_events, STATIC_ARRAY_SIZE(g_sw_events)); + ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_SOFTWARE, g_sw_events, + STATIC_ARRAY_SIZE(g_sw_events)); if (ret != 0) { ERROR(PMU_PLUGIN ": Failed to add software events."); goto init_error; @@ -492,13 +567,17 @@ static int pmu_init(void) { ": Events list is empty. No events were setup for monitoring."); } - return (0); + return 0; init_error: pmu_free_events(g_ctx.event_list); sfree(g_ctx.event_list); - sfree(g_ctx.hw_specific_events); + for (size_t i = 0; i < g_ctx.hw_events_count; i++) { + sfree(g_ctx.hw_events[i]); + } + sfree(g_ctx.hw_events); + g_ctx.hw_events_count = 0; return ret; } @@ -509,9 +588,13 @@ static int pmu_shutdown(void) { pmu_free_events(g_ctx.event_list); sfree(g_ctx.event_list); - sfree(g_ctx.hw_specific_events); + for (size_t i = 0; i < g_ctx.hw_events_count; i++) { + sfree(g_ctx.hw_events[i]); + } + sfree(g_ctx.hw_events); + g_ctx.hw_events_count = 0; - return (0); + return 0; } void module_register(void) {