X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fintel_pmu.c;h=fc7d6804d7a2cf2c0f961a476487e082747bb1ea;hb=da11ce02eb202b3e01d3e2d1b40f248a84430973;hp=87a713be6ab8676912e6438f2735f0ff1fcedd8c;hpb=7853bfa986aee41b596174e7ecdc8fdeefc7d25a;p=collectd.git diff --git a/src/intel_pmu.c b/src/intel_pmu.c index 87a713be..fc7d6804 100644 --- a/src/intel_pmu.c +++ b/src/intel_pmu.c @@ -1,7 +1,7 @@ /** * collectd - src/intel_pmu.c * - * Copyright(c) 2017 Intel Corporation. All rights reserved. + * Copyright(c) 2017-2018 Intel Corporation. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,11 +23,14 @@ * * Authors: * Serhiy Pshyk + * Kamil Wiatrowski **/ #include "collectd.h" #include "common.h" +#include "utils_config_cores.h" + #include #include @@ -67,9 +70,10 @@ struct intel_pmu_ctx_s { _Bool hw_cache_events; _Bool kernel_pmu_events; _Bool sw_events; - char event_list_fn[PATH_MAX]; + char event_list_fn[PATH_MAX]; char **hw_events; size_t hw_events_count; + core_groups_list_t cores; struct eventlist *event_list; }; typedef struct intel_pmu_ctx_s intel_pmu_ctx_t; @@ -182,8 +186,6 @@ static void pmu_dump_events() { DEBUG(PMU_PLUGIN ": config : %#x", (unsigned)e->attr.config); DEBUG(PMU_PLUGIN ": size : %d", e->attr.size); } - - return; } static void pmu_dump_config(void) { @@ -196,18 +198,74 @@ static void pmu_dump_config(void) { for (size_t i = 0; i < g_ctx.hw_events_count; i++) { DEBUG(PMU_PLUGIN ": hardware_events[%zu]: %s", i, g_ctx.hw_events[i]); } +} + +static void pmu_dump_cgroups(void) { + + DEBUG(PMU_PLUGIN ": Core groups:"); - return; + for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) { + core_group_t *cgroup = g_ctx.cores.cgroups + i; + const size_t cores_size = cgroup->num_cores * 4 + 1; + char *cores = calloc(cores_size, sizeof(*cores)); + if (cores == NULL) { + DEBUG(PMU_PLUGIN ": Failed to allocate string to list cores."); + return; + } + for (size_t j = 0; j < cgroup->num_cores; j++) + if (snprintf(cores + strlen(cores), cores_size - strlen(cores), " %d", + cgroup->cores[j]) < 0) { + DEBUG(PMU_PLUGIN ": Failed to write list of cores to string."); + sfree(cores); + return; + } + + DEBUG(PMU_PLUGIN ": group[%" PRIsz "]", i); + DEBUG(PMU_PLUGIN ": description: %s", cgroup->desc); + DEBUG(PMU_PLUGIN ": cores count: %" PRIsz, cgroup->num_cores); + DEBUG(PMU_PLUGIN ": cores :%s", cores); + sfree(cores); + } } #endif /* COLLECT_DEBUG */ +static int pmu_validate_cgroups(core_group_t *cgroups, size_t len, + int max_cores) { + /* i - group index, j - core index */ + for (size_t i = 0; i < len; i++) { + for (size_t j = 0; j < cgroups[i].num_cores; j++) { + int core = (int)cgroups[i].cores[j]; + + /* Core index cannot exceed number of cores in system, + note that max_cores include both online and offline CPUs. */ + if (core >= max_cores) { + ERROR(PMU_PLUGIN ": Core %d is not valid, max core index: %d.", core, + max_cores - 1); + return -1; + } + } + /* Check if cores are set in remaining groups */ + for (size_t k = i + 1; k < len; k++) + if (config_cores_cmp_cgroups(&cgroups[i], &cgroups[k]) != 0) { + ERROR(PMU_PLUGIN ": Same cores cannot be set in different groups."); + return -1; + } + } + return 0; +} + 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."); @@ -233,11 +291,11 @@ static int pmu_config_hw_events(oconfig_item_t *ci) { } 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("ReportHardwareCacheEvents", child->key) == 0) { @@ -251,9 +309,11 @@ static int pmu_config(oconfig_item_t *ci) { ret = pmu_config_hw_events(child); } else if (strcasecmp("ReportSoftwareEvents", child->key) == 0) { ret = cf_util_get_boolean(child, &g_ctx.sw_events); + } else if (strcasecmp("Cores", child->key) == 0) { + ret = config_cores_parse(child, &g_ctx.cores); } else { ERROR(PMU_PLUGIN ": Unknown configuration parameter \"%s\".", child->key); - ret = (-1); + ret = -1; } if (ret != 0) { @@ -269,79 +329,119 @@ static int pmu_config(oconfig_item_t *ci) { return 0; } -static void pmu_submit_counter(int cpu, char *event, counter_t value) { +static void pmu_submit_counter(const char *cgroup, const char *event, + counter_t value, meta_data_t *meta) { value_list_t vl = VALUE_LIST_INIT; vl.values = &(value_t){.counter = value}; vl.values_len = 1; sstrncpy(vl.plugin, PMU_PLUGIN, sizeof(vl.plugin)); - if (cpu == -1) { - ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "all"); - } else { - ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%d", cpu); - } + sstrncpy(vl.plugin_instance, cgroup, sizeof(vl.plugin_instance)); + if (meta) + vl.meta = meta; sstrncpy(vl.type, "counter", sizeof(vl.type)); sstrncpy(vl.type_instance, event, sizeof(vl.type_instance)); 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; - struct event *e; + /* create meta data only if value was scaled */ + if (efd->val[1] == efd->val[2] || !efd->val[2]) { + return NULL; + } - for (e = g_ctx.event_list->eventlist; e; e = e->next) { - uint64_t all_value = 0; - int event_enabled = 0; - for (int i = 0; i < g_ctx.event_list->num_cpus; i++) { + meta = meta_data_create(); + if (meta == NULL) { + ERROR(PMU_PLUGIN ": meta_data_create failed."); + return NULL; + } - if (e->efd[i].fd < 0) - continue; + 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]); - event_enabled++; + return meta; +} - uint64_t value = event_scaled_value(e, i); - all_value += value; +static void pmu_dispatch_data(void) { - /* dispatch per CPU value */ - pmu_submit_counter(i, e->event, value); - } + struct event *e; + + for (e = g_ctx.event_list->eventlist; e; e = e->next) { + for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) { + core_group_t *cgroup = g_ctx.cores.cgroups + i; + uint64_t cgroup_value = 0; + int event_enabled_cgroup = 0; + meta_data_t *meta = NULL; + + for (size_t j = 0; j < cgroup->num_cores; j++) { + int core = (int)cgroup->cores[j]; + if (e->efd[core].fd < 0) + continue; + + event_enabled_cgroup++; + + /* 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, core); + cgroup_value += value; + + /* get meta data with information about scaling */ + if (cgroup->num_cores == 1) + meta = pmu_meta_data_create(&e->efd[core]); + } - 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); + if (event_enabled_cgroup > 0) { + DEBUG(PMU_PLUGIN ": %s/%s = %lu", e->event, cgroup->desc, cgroup_value); + /* dispatch per core group value */ + pmu_submit_counter(cgroup->desc, e->event, cgroup_value, meta); + meta_data_destroy(meta); + } } } - - return 0; } static int pmu_read(__attribute__((unused)) user_data_t *ud) { int ret; + struct event *e; DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__); - ret = read_all_events(g_ctx.event_list); - if (ret != 0) { - ERROR(PMU_PLUGIN ": Failed to read values of all events."); - return 0; + /* read all events only for configured cores */ + for (e = g_ctx.event_list->eventlist; e; e = e->next) { + for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) { + core_group_t *cgroup = g_ctx.cores.cgroups + i; + for (size_t j = 0; j < cgroup->num_cores; j++) { + int core = (int)cgroup->cores[j]; + if (e->efd[core].fd < 0) + continue; + + ret = read_event(e, core); + if (ret != 0) { + ERROR(PMU_PLUGIN ": Failed to read value of %s/%d event.", e->event, + core); + return ret; + } + } + } } - ret = pmu_dispatch_data(); - if (ret != 0) { - ERROR(PMU_PLUGIN ": Failed to dispatch event values."); - return 0; - } + pmu_dispatch_data(); 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++) { + 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 = @@ -354,7 +454,6 @@ static int pmu_add_events(struct eventlist *el, uint32_t type, e->attr.type = type; e->attr.config = events[i].config; e->attr.size = PERF_ATTR_SIZE_VER0; - e->next = NULL; if (!el->eventlist) el->eventlist = e; if (el->eventlist_last) @@ -376,15 +475,9 @@ static int pmu_add_hw_events(struct eventlist *el, char **e, size_t count) { if (!events) return -1; - char *s, *tmp; + char *s, *tmp = NULL; for (s = strtok_r(events, ",", &tmp); s; s = strtok_r(NULL, ",", &tmp)) { - /* Multiple events parsed in one entry */ - if (group_events_count == 1) { - /* Mark previously added event as group leader */ - el->eventlist_last->group_leader = 1; - } - /* Allocate memory for event struct that contains array of efd structs for all cores */ struct event *e = @@ -394,19 +487,26 @@ static int pmu_add_hw_events(struct eventlist *el, char **e, size_t count) { return -ENOMEM; } - if (resolve_event(s, &e->attr) == 0) { - e->next = NULL; - if (!el->eventlist) - el->eventlist = e; - if (el->eventlist_last) - el->eventlist_last->next = e; - el->eventlist_last = e; - e->event = strdup(s); - } else { - DEBUG(PMU_PLUGIN ": Cannot resolve %s", s); + 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; + } + + e->next = NULL; + if (!el->eventlist) + el->eventlist = e; + if (el->eventlist_last) + el->eventlist_last->next = e; + el->eventlist_last = e; + e->event = strdup(s); + group_events_count++; } @@ -431,6 +531,7 @@ static void pmu_free_events(struct eventlist *el) { while (e) { struct event *next = e->next; + sfree(e->event); sfree(e); e = next; } @@ -445,13 +546,18 @@ static int pmu_setup_events(struct eventlist *el, bool measure_all, for (e = el->eventlist; e; e = e->next) { - for (int i = 0; i < el->num_cpus; i++) { - if (setup_event(e, i, leader, measure_all, measure_pid) < 0) { - WARNING(PMU_PLUGIN ": perf event '%s' is not available (cpu=%d).", - e->event, i); - } else { - /* success if at least one event was set */ - ret = 0; + for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) { + core_group_t *cgroup = g_ctx.cores.cgroups + i; + for (size_t j = 0; j < cgroup->num_cores; j++) { + int core = (int)cgroup->cores[j]; + + if (setup_event(e, core, leader, measure_all, measure_pid) < 0) { + WARNING(PMU_PLUGIN ": perf event '%s' is not available (cpu=%d).", + e->event, core); + } else { + /* success if at least one event was set */ + ret = 0; + } } } @@ -475,6 +581,24 @@ static int pmu_init(void) { return -ENOMEM; } + if (g_ctx.cores.num_cgroups == 0) { + ret = config_cores_default(g_ctx.event_list->num_cpus, &g_ctx.cores); + if (ret != 0) { + ERROR(PMU_PLUGIN ": Failed to set default core groups."); + goto init_error; + } + } else { + ret = pmu_validate_cgroups(g_ctx.cores.cgroups, g_ctx.cores.num_cgroups, + g_ctx.event_list->num_cpus); + if (ret != 0) { + ERROR(PMU_PLUGIN ": Invalid core groups configuration."); + goto init_error; + } + } +#if COLLECT_DEBUG + pmu_dump_cgroups(); +#endif + if (g_ctx.hw_cache_events) { ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_HW_CACHE, g_hw_cache_events, @@ -550,6 +674,7 @@ init_error: sfree(g_ctx.hw_events); g_ctx.hw_events_count = 0; + config_cores_cleanup(&g_ctx.cores); return ret; } @@ -566,6 +691,8 @@ static int pmu_shutdown(void) { sfree(g_ctx.hw_events); g_ctx.hw_events_count = 0; + config_cores_cleanup(&g_ctx.cores); + return 0; }