/**
* collectd - src/intel_rdt.c
*
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ * Copyright(c) 2016-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
*
* Authors:
* Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ * Starzyk, Mateusz <mateuszx.starzyk@intel.com>
+ * Wojciech Andralojc <wojciechx.andralojc@intel.com>
**/
-#include "common.h"
#include "collectd.h"
-
+#include "utils/common/common.h"
+#include "utils/config_cores/config_cores.h"
#include <pqos.h>
#define RDT_PLUGIN "intel_rdt"
+/* PQOS API STUB
+ * In future: Start monitoring for PID group. For perf grouping will be added.
+ * Currently: Start monitoring only for the first PID.
+ */
+__attribute__((unused)) static int
+pqos_mon_start_pids(const unsigned num_pids, const pid_t *pids,
+ const enum pqos_mon_event event, void *context,
+ struct pqos_mon_data *group) {
+
+ assert(num_pids > 0);
+ assert(pids);
+ return pqos_mon_start_pid(pids[0], event, context, group);
+}
+
+/* PQOS API STUB
+ * In future: Add PIDs to the monitoring group. Supported for resctrl monitoring
+ * only.
+ * Currently: Does nothing.
+ */
+__attribute__((unused)) static int
+pqos_mon_add_pids(const unsigned num_pids, const pid_t *pids, void *context,
+ struct pqos_mon_data *group) {
+ return PQOS_RETVAL_OK;
+}
+
+/* PQOS API STUB
+ * In future: Remove PIDs from the monitoring group. Supported for resctrl
+ * monitoring only.
+ * Currently: Does nothing.
+ */
+__attribute__((unused)) static int
+pqos_mon_remove_pids(const unsigned num_pids, const pid_t *pids, void *context,
+ struct pqos_mon_data *group) {
+ return PQOS_RETVAL_OK;
+}
+
+#define RDT_PLUGIN "intel_rdt"
+
#define RDT_MAX_SOCKETS 8
#define RDT_MAX_SOCKET_CORES 64
#define RDT_MAX_CORES (RDT_MAX_SOCKET_CORES * RDT_MAX_SOCKETS)
+/*
+ * Process name inside comm file is limited to 16 chars.
+ * More info here: http://man7.org/linux/man-pages/man5/proc.5.html
+ */
+#define RDT_MAX_NAME_LEN 16
+#define RDT_MAX_NAMES_GROUPS 64
+
typedef enum {
UNKNOWN = 0,
CONFIGURATION_ERROR,
} rdt_config_status;
-struct rdt_core_group_s {
+struct rdt_name_group_s {
char *desc;
- size_t num_cores;
- unsigned *cores;
+ size_t num_names;
+ char **names;
enum pqos_mon_event events;
};
-typedef struct rdt_core_group_s rdt_core_group_t;
+typedef struct rdt_name_group_s rdt_name_group_t;
struct rdt_ctx_s {
- rdt_core_group_t cgroups[RDT_MAX_CORES];
- struct pqos_mon_data *pgroups[RDT_MAX_CORES];
- size_t num_groups;
+ core_groups_list_t cores;
+ enum pqos_mon_event events[RDT_MAX_CORES];
+ struct pqos_mon_data *pcgroups[RDT_MAX_CORES];
+ rdt_name_group_t ngroups[RDT_MAX_NAMES_GROUPS];
+ struct pqos_mon_data *pngroups[RDT_MAX_NAMES_GROUPS];
+ size_t num_ngroups;
const struct pqos_cpuinfo *pqos_cpu;
const struct pqos_cap *pqos_cap;
const struct pqos_capability *cap_mon;
};
typedef struct rdt_ctx_s rdt_ctx_t;
-static rdt_ctx_t *g_rdt = NULL;
+static rdt_ctx_t *g_rdt;
static rdt_config_status g_state = UNKNOWN;
-static int isdup(const uint64_t *nums, size_t size, uint64_t val) {
+static int isdupstr(const char *names[], const size_t size, const char *name) {
for (size_t i = 0; i < size; i++)
- if (nums[i] == val)
+ if (strncmp(names[i], name, (size_t)RDT_MAX_NAME_LEN) == 0)
return 1;
- return 0;
-}
-
-static int strtouint64(const char *s, uint64_t *n) {
- char *endptr = NULL;
-
- assert(s != NULL);
- assert(n != NULL);
-
- *n = strtoull(s, &endptr, 0);
-
- if (!(*s != '\0' && *endptr == '\0')) {
- DEBUG(RDT_PLUGIN ": Error converting '%s' to unsigned number.", s);
- return -EINVAL;
- }
return 0;
}
/*
* NAME
- * strlisttonums
+ * strlisttoarray
*
* DESCRIPTION
- * Converts string of characters representing list of numbers into array of
- * numbers. Allowed formats are:
- * 0,1,2,3
- * 0-10,20-18
- * 1,3,5-8,10,0x10-12
- *
- * Numbers can be in decimal or hexadecimal format.
+ * Converts string representing list of strings into array of strings.
+ * Allowed format is:
+ * name,name1,name2,name3
*
* PARAMETERS
- * `s' String representing list of unsigned numbers.
- * `nums' Array to put converted numeric values into.
- * `max' Maximum number of elements that nums can accommodate.
+ * `str_list' String representing list of strings.
+ * `names' Array to put extracted strings into.
+ * `names_num' Variable to put number of extracted strings.
*
* RETURN VALUE
- * Number of elements placed into nums.
+ * Number of elements placed into names.
*/
-static size_t strlisttonums(char *s, uint64_t *nums, size_t max) {
- int ret;
- size_t index = 0;
+static int strlisttoarray(char *str_list, char ***names, size_t *names_num) {
char *saveptr = NULL;
- if (s == NULL || nums == NULL || max == 0)
- return index;
+ if (str_list == NULL || names == NULL)
+ return -EINVAL;
for (;;) {
- char *p = NULL;
- char *token = NULL;
-
- token = strtok_r(s, ",", &saveptr);
+ char *token = strtok_r(str_list, ",", &saveptr);
if (token == NULL)
break;
- s = NULL;
+ str_list = NULL;
while (isspace(*token))
token++;
+
if (*token == '\0')
continue;
- p = strchr(token, '-');
- if (p != NULL) {
- uint64_t n, start, end;
- *p = '\0';
- ret = strtouint64(token, &start);
- if (ret < 0)
- return 0;
- ret = strtouint64(p + 1, &end);
- if (ret < 0)
- return 0;
- if (start > end) {
- return 0;
+ if (!(isdupstr((const char **)*names, *names_num, token)))
+ if (0 != strarray_add(names, names_num, token)) {
+ ERROR(RDT_PLUGIN ": Error allocating process name string");
+ return -ENOMEM;
}
- for (n = start; n <= end; n++) {
- if (!(isdup(nums, index, n))) {
- nums[index] = n;
- index++;
- }
- if (index >= max)
- return index;
- }
- } else {
- uint64_t val;
-
- ret = strtouint64(token, &val);
- if (ret < 0)
- return 0;
-
- if (!(isdup(nums, index, val))) {
- nums[index] = val;
- index++;
- }
- if (index >= max)
- return index;
- }
}
- return index;
+ return 0;
}
/*
* NAME
- * cgroup_cmp
+ * ngroup_cmp
*
* DESCRIPTION
- * Function to compare cores in 2 core groups.
+ * Function to compare names in two name groups.
*
* PARAMETERS
- * `cg_a' Pointer to core group a.
- * `cg_b' Pointer to core group b.
+ * `ng_a' Pointer to name group a.
+ * `ng_b' Pointer to name group b.
*
* RETURN VALUE
- * 1 if both groups contain the same cores
- * 0 if none of their cores match
- * -1 if some but not all cores match
+ * 1 if both groups contain the same names
+ * 0 if none of their names match
+ * -1 if some but not all names match
*/
-static int cgroup_cmp(const rdt_core_group_t *cg_a,
- const rdt_core_group_t *cg_b) {
- int found = 0;
+static int ngroup_cmp(const rdt_name_group_t *ng_a,
+ const rdt_name_group_t *ng_b) {
+ unsigned found = 0;
- assert(cg_a != NULL);
- assert(cg_b != NULL);
+ assert(ng_a != NULL);
+ assert(ng_b != NULL);
- const int sz_a = cg_a->num_cores;
- const int sz_b = cg_b->num_cores;
- const unsigned *tab_a = cg_a->cores;
- const unsigned *tab_b = cg_b->cores;
+ const size_t sz_a = (unsigned)ng_a->num_names;
+ const size_t sz_b = (unsigned)ng_b->num_names;
+ const char **tab_a = (const char **)ng_a->names;
+ const char **tab_b = (const char **)ng_b->names;
- for (int i = 0; i < sz_a; i++) {
- for (int j = 0; j < sz_b; j++)
- if (tab_a[i] == tab_b[j])
+ for (size_t i = 0; i < sz_a; i++) {
+ for (size_t j = 0; j < sz_b; j++)
+ if (strncmp(tab_a[i], tab_b[j], (size_t)RDT_MAX_NAME_LEN) == 0)
found++;
}
- /* if no cores are the same */
+ /* if no names are the same */
if (!found)
return 0;
- /* if group contains same cores */
- if (sz_a == sz_b && sz_b == found)
+ /* if group contains same names */
+ if (sz_a == sz_b && sz_b == (size_t)found)
return 1;
- /* if not all cores are the same */
+ /* if not all names are the same */
return -1;
}
-static int cgroup_set(rdt_core_group_t *cg, char *desc, uint64_t *cores,
- size_t num_cores) {
- assert(cg != NULL);
- assert(desc != NULL);
- assert(cores != NULL);
- assert(num_cores > 0);
-
- cg->cores = calloc(num_cores, sizeof(unsigned));
- if (cg->cores == NULL) {
- ERROR(RDT_PLUGIN ": Error allocating core group table");
- return -ENOMEM;
- }
- cg->num_cores = num_cores;
- cg->desc = strdup(desc);
- if (cg->desc == NULL) {
- ERROR(RDT_PLUGIN ": Error allocating core group description");
- sfree(cg->cores);
- return -ENOMEM;
- }
-
- for (size_t i = 0; i < num_cores; i++)
- cg->cores[i] = (unsigned)cores[i];
-
- return 0;
-}
-
/*
* NAME
- * oconfig_to_cgroups
+ * oconfig_to_ngroups
*
* DESCRIPTION
- * Function to set the descriptions and cores for each core group.
+ * Function to set the descriptions and names for each process names group.
* Takes a config option containing list of strings that are used to set
- * core group values.
+ * process group values.
*
* PARAMETERS
- * `item' Config option containing core groups.
- * `groups' Table of core groups to set values in.
- * `max_groups' Maximum number of core groups allowed.
+ * `item' Config option containing process names groups.
+ * `groups' Table of process name groups to set values in.
+ * `max_groups' Maximum number of process name groups allowed.
*
* RETURN VALUE
- * On success, the number of core groups set up. On error, appropriate
+ * On success, the number of name groups set up. On error, appropriate
* negative error value.
*/
-static int oconfig_to_cgroups(oconfig_item_t *item, rdt_core_group_t *groups,
- size_t max_groups) {
+static int oconfig_to_ngroups(const oconfig_item_t *item,
+ rdt_name_group_t *groups,
+ const size_t max_groups) {
int index = 0;
assert(groups != NULL);
for (int j = 0; j < item->values_num; j++) {
int ret;
- size_t n;
- uint64_t cores[RDT_MAX_CORES] = {0};
char value[DATA_MAX_NAME_LEN];
if ((item->values[j].value.string == NULL) ||
sstrncpy(value, item->values[j].value.string, sizeof(value));
- n = strlisttonums(value, cores, STATIC_ARRAY_SIZE(cores));
- if (n == 0) {
- ERROR(RDT_PLUGIN ": Error parsing core group (%s)",
+ ret = strlisttoarray(value, &groups[index].names, &groups[index].num_names);
+ if (ret != 0 || groups[index].num_names == 0) {
+ ERROR(RDT_PLUGIN ": Error parsing process names group (%s)",
item->values[j].value.string);
return -EINVAL;
}
- /* set core group info */
- ret = cgroup_set(&groups[index], item->values[j].value.string, cores, n);
- if (ret < 0)
- return ret;
+ /* set group description info */
+ groups[index].desc = sstrdup(item->values[j].value.string);
+ if (groups[index].desc == NULL) {
+ ERROR(RDT_PLUGIN ": Error allocating name group description");
+ return -ENOMEM;
+ }
index++;
- if (index >= max_groups) {
- WARNING(RDT_PLUGIN ": Too many core groups configured");
+ if (index >= (const int)max_groups) {
+ WARNING(RDT_PLUGIN ": Too many process names groups configured");
return index;
}
}
return;
DEBUG(RDT_PLUGIN ": Core Groups Dump");
- DEBUG(RDT_PLUGIN ": groups count: %zu", g_rdt->num_groups);
+ DEBUG(RDT_PLUGIN ": groups count: %" PRIsz, g_rdt->cores.num_cgroups);
- for (int i = 0; i < g_rdt->num_groups; i++) {
+ for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) {
+ core_group_t *cgroup = g_rdt->cores.cgroups + i;
memset(cores, 0, sizeof(cores));
- for (int j = 0; j < g_rdt->cgroups[i].num_cores; j++) {
+ for (size_t j = 0; j < cgroup->num_cores; j++) {
snprintf(cores + strlen(cores), sizeof(cores) - strlen(cores) - 1, " %d",
- g_rdt->cgroups[i].cores[j]);
+ cgroup->cores[j]);
}
- DEBUG(RDT_PLUGIN ": group[%d]:", i);
- DEBUG(RDT_PLUGIN ": description: %s", g_rdt->cgroups[i].desc);
+ DEBUG(RDT_PLUGIN ": group[%zu]:", i);
+ DEBUG(RDT_PLUGIN ": description: %s", cgroup->desc);
DEBUG(RDT_PLUGIN ": cores: %s", cores);
- DEBUG(RDT_PLUGIN ": events: 0x%X", g_rdt->cgroups[i].events);
+ DEBUG(RDT_PLUGIN ": events: 0x%X", g_rdt->events[i]);
+ }
+
+ return;
+}
+
+static void rdt_dump_ngroups(void) {
+
+ char names[DATA_MAX_NAME_LEN];
+
+ if (g_rdt == NULL)
+ return;
+
+ DEBUG(RDT_PLUGIN ": Process Names Groups Dump");
+ DEBUG(RDT_PLUGIN ": groups count: %" PRIsz, g_rdt->num_ngroups);
+
+ for (size_t i = 0; i < g_rdt->num_ngroups; i++) {
+ memset(names, 0, sizeof(names));
+ for (size_t j = 0; j < g_rdt->ngroups[i].num_names; j++)
+ snprintf(names + strlen(names), sizeof(names) - strlen(names) - 1, " %s",
+ g_rdt->ngroups[i].names[j]);
+
+ DEBUG(RDT_PLUGIN ": group[%d]:", (int)i);
+ DEBUG(RDT_PLUGIN ": description: %s", g_rdt->ngroups[i].desc);
+ DEBUG(RDT_PLUGIN ": process names:%s", names);
+ DEBUG(RDT_PLUGIN ": events: 0x%X", g_rdt->ngroups[i].events);
}
return;
static void rdt_dump_data(void) {
/*
* CORE - monitored group of cores
+ * NAME - monitored group of processes
* RMID - Resource Monitoring ID associated with the monitored group
* LLC - last level cache occupancy
* MBL - local memory bandwidth
* MBR - remote memory bandwidth
*/
DEBUG(" CORE RMID LLC[KB] MBL[MB] MBR[MB]");
- for (int i = 0; i < g_rdt->num_groups; i++) {
+ for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) {
+
+ const struct pqos_event_values *pv = &g_rdt->pcgroups[i]->values;
- const struct pqos_event_values *pv = &g_rdt->pgroups[i]->values;
+ double llc = bytes_to_kb(pv->llc);
+ double mbr = bytes_to_mb(pv->mbm_remote_delta);
+ double mbl = bytes_to_mb(pv->mbm_local_delta);
+
+ DEBUG(" [%s] %8u %10.1f %10.1f %10.1f", g_rdt->cores.cgroups[i].desc,
+ g_rdt->pcgroups[i]->poll_ctx[0].rmid, llc, mbl, mbr);
+ }
+
+ DEBUG(" NAME RMID LLC[KB] MBL[MB] MBR[MB]");
+ for (size_t i = 0; i < g_rdt->num_ngroups; i++) {
+
+ if (g_rdt->pngroups[i]->poll_ctx == NULL)
+ continue;
+
+ const struct pqos_event_values *pv = &g_rdt->pngroups[i]->values;
double llc = bytes_to_kb(pv->llc);
double mbr = bytes_to_mb(pv->mbm_remote_delta);
double mbl = bytes_to_mb(pv->mbm_local_delta);
- DEBUG(" [%s] %8u %10.1f %10.1f %10.1f", g_rdt->cgroups[i].desc,
- g_rdt->pgroups[i]->poll_ctx[0].rmid, llc, mbl, mbr);
+ DEBUG(" [%s] %8u %10.1f %10.1f %10.1f", g_rdt->ngroups[i].desc,
+ g_rdt->pngroups[i]->poll_ctx[0].rmid, llc, mbl, mbr);
}
}
#endif /* COLLECT_DEBUG */
static void rdt_free_cgroups(void) {
+ config_cores_cleanup(&g_rdt->cores);
for (int i = 0; i < RDT_MAX_CORES; i++) {
- sfree(g_rdt->cgroups[i].desc);
-
- sfree(g_rdt->cgroups[i].cores);
- g_rdt->cgroups[i].num_cores = 0;
+ sfree(g_rdt->pcgroups[i]);
+ }
+}
- sfree(g_rdt->pgroups[i]);
+static void rdt_free_ngroups(void) {
+ for (int i = 0; i < RDT_MAX_NAMES_GROUPS; i++) {
+ sfree(g_rdt->ngroups[i].desc);
+ strarray_free(g_rdt->ngroups[i].names, g_rdt->ngroups[i].num_names);
+ g_rdt->ngroups[i].num_names = 0;
+ sfree(g_rdt->pngroups[i]);
}
}
static int rdt_default_cgroups(void) {
- int ret;
+ unsigned num_cores = g_rdt->pqos_cpu->num_cores;
+
+ g_rdt->cores.cgroups = calloc(num_cores, sizeof(*g_rdt->cores.cgroups));
+ if (g_rdt->cores.cgroups == NULL) {
+ ERROR(RDT_PLUGIN ": Error allocating core groups array");
+ return -ENOMEM;
+ }
+ g_rdt->cores.num_cgroups = num_cores;
/* configure each core in separate group */
- for (unsigned i = 0; i < g_rdt->pqos_cpu->num_cores; i++) {
+ for (unsigned i = 0; i < num_cores; i++) {
+ core_group_t *cgroup = g_rdt->cores.cgroups + i;
char desc[DATA_MAX_NAME_LEN];
- uint64_t core = i;
-
- snprintf(desc, sizeof(desc), "%d", g_rdt->pqos_cpu->cores[i].lcore);
/* set core group info */
- ret = cgroup_set(&g_rdt->cgroups[i], desc, &core, 1);
- if (ret < 0)
- return ret;
+ cgroup->cores = calloc(1, sizeof(*cgroup->cores));
+ if (cgroup->cores == NULL) {
+ ERROR(RDT_PLUGIN ": Error allocating cores array");
+ rdt_free_cgroups();
+ return -ENOMEM;
+ }
+ cgroup->num_cores = 1;
+ cgroup->cores[0] = i;
+
+ snprintf(desc, sizeof(desc), "%d", g_rdt->pqos_cpu->cores[i].lcore);
+ cgroup->desc = strdup(desc);
+ if (cgroup->desc == NULL) {
+ ERROR(RDT_PLUGIN ": Error allocating core group description");
+ rdt_free_cgroups();
+ return -ENOMEM;
+ }
}
- return g_rdt->pqos_cpu->num_cores;
+ return num_cores;
}
-static int rdt_is_core_id_valid(int core_id) {
+static int rdt_is_core_id_valid(unsigned int core_id) {
- for (int i = 0; i < g_rdt->pqos_cpu->num_cores; i++)
+ for (unsigned int i = 0; i < g_rdt->pqos_cpu->num_cores; i++)
if (core_id == g_rdt->pqos_cpu->cores[i].lcore)
return 1;
return 0;
}
-static int rdt_config_cgroups(oconfig_item_t *item) {
- int n = 0;
- enum pqos_mon_event events = 0;
+static int rdt_is_proc_name_valid(const char *name) {
- if (item == NULL) {
- DEBUG(RDT_PLUGIN ": cgroups_config: Invalid argument.");
- return -EINVAL;
- }
-
- DEBUG(RDT_PLUGIN ": Core groups [%d]:", item->values_num);
- for (int j = 0; j < item->values_num; j++) {
- if (item->values[j].type != OCONFIG_TYPE_STRING) {
- ERROR(RDT_PLUGIN ": given core group value is not a string [idx=%d]", j);
- return -EINVAL;
+ if (name != NULL) {
+ unsigned len = strlen(name);
+ if (len > 0 && len <= RDT_MAX_NAME_LEN)
+ return 1;
+ else {
+ DEBUG(RDT_PLUGIN
+ ": Process name \'%s\' is too long. Max supported len is %d chars.",
+ name, RDT_MAX_NAME_LEN);
}
- DEBUG(RDT_PLUGIN ": [%d]: %s", j, item->values[j].value.string);
}
- n = oconfig_to_cgroups(item, g_rdt->cgroups, g_rdt->pqos_cpu->num_cores);
- if (n < 0) {
+ return 0;
+}
+
+static int rdt_config_cgroups(oconfig_item_t *item) {
+ size_t n = 0;
+ enum pqos_mon_event events = 0;
+
+ if (config_cores_parse(item, &g_rdt->cores) < 0) {
rdt_free_cgroups();
ERROR(RDT_PLUGIN ": Error parsing core groups configuration.");
return -EINVAL;
}
+ n = g_rdt->cores.num_cgroups;
/* validate configured core id values */
- for (int group_idx = 0; group_idx < n; group_idx++) {
- for (int core_idx = 0; core_idx < g_rdt->cgroups[group_idx].num_cores;
- core_idx++) {
- if (!rdt_is_core_id_valid(g_rdt->cgroups[group_idx].cores[core_idx])) {
- ERROR(RDT_PLUGIN ": Core group '%s' contains invalid core id '%d'",
- g_rdt->cgroups[group_idx].desc,
- (int)g_rdt->cgroups[group_idx].cores[core_idx]);
+ for (size_t group_idx = 0; group_idx < n; group_idx++) {
+ core_group_t *cgroup = g_rdt->cores.cgroups + group_idx;
+ for (size_t core_idx = 0; core_idx < cgroup->num_cores; core_idx++) {
+ if (!rdt_is_core_id_valid(cgroup->cores[core_idx])) {
+ ERROR(RDT_PLUGIN ": Core group '%s' contains invalid core id '%u'",
+ cgroup->desc, cgroup->cores[core_idx]);
rdt_free_cgroups();
return -EINVAL;
}
if (n == 0) {
/* create default core groups if "Cores" config option is empty */
- n = rdt_default_cgroups();
- if (n < 0) {
+ int ret = rdt_default_cgroups();
+ if (ret < 0) {
rdt_free_cgroups();
ERROR(RDT_PLUGIN ": Error creating default core groups configuration.");
- return n;
+ return ret;
}
+ n = (size_t)ret;
INFO(RDT_PLUGIN
": No core groups configured. Default core groups created.");
}
/* Get all available events on this platform */
- for (int i = 0; i < g_rdt->cap_mon->u.mon->num_events; i++)
+ for (unsigned int i = 0; i < g_rdt->cap_mon->u.mon->num_events; i++)
events |= g_rdt->cap_mon->u.mon->events[i].type;
events &= ~(PQOS_PERF_EVENT_LLC_MISS);
g_rdt->pqos_cpu->num_cores);
DEBUG(RDT_PLUGIN ": Available events to monitor: %#x", events);
- g_rdt->num_groups = n;
+ g_rdt->cores.num_cgroups = n;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
int found = 0;
- found = cgroup_cmp(&g_rdt->cgroups[j], &g_rdt->cgroups[i]);
+ found = config_cores_cmp_cgroups(&g_rdt->cores.cgroups[j],
+ &g_rdt->cores.cgroups[i]);
if (found != 0) {
rdt_free_cgroups();
ERROR(RDT_PLUGIN ": Cannot monitor same cores in different groups.");
}
}
- g_rdt->cgroups[i].events = events;
- g_rdt->pgroups[i] = calloc(1, sizeof(*g_rdt->pgroups[i]));
- if (g_rdt->pgroups[i] == NULL) {
+ g_rdt->events[i] = events;
+ g_rdt->pcgroups[i] = calloc(1, sizeof(*g_rdt->pcgroups[i]));
+ if (g_rdt->pcgroups[i] == NULL) {
rdt_free_cgroups();
ERROR(RDT_PLUGIN ": Failed to allocate memory for monitoring data.");
return -ENOMEM;
return 0;
}
+static int rdt_config_ngroups(const oconfig_item_t *item) {
+ int n = 0;
+ enum pqos_mon_event events = 0;
+
+ if (item == NULL) {
+ DEBUG(RDT_PLUGIN ": ngroups_config: Invalid argument.");
+ return -EINVAL;
+ }
+
+ DEBUG(RDT_PLUGIN ": Process names groups [%d]:", item->values_num);
+ for (int j = 0; j < item->values_num; j++) {
+ if (item->values[j].type != OCONFIG_TYPE_STRING) {
+ ERROR(RDT_PLUGIN
+ ": given process names group value is not a string [idx=%d]",
+ j);
+ return -EINVAL;
+ }
+ DEBUG(RDT_PLUGIN ": [%d]: %s", j, item->values[j].value.string);
+ }
+
+ n = oconfig_to_ngroups(item, g_rdt->ngroups, RDT_MAX_NAMES_GROUPS);
+ if (n < 0) {
+ rdt_free_ngroups();
+ ERROR(RDT_PLUGIN ": Error parsing process name groups configuration.");
+ return -EINVAL;
+ }
+
+ /* validate configured process name values */
+ for (int group_idx = 0; group_idx < n; group_idx++) {
+ for (size_t name_idx = 0; name_idx < g_rdt->ngroups[group_idx].num_names;
+ name_idx++) {
+ if (!rdt_is_proc_name_valid(g_rdt->ngroups[group_idx].names[name_idx])) {
+ ERROR(RDT_PLUGIN ": Process name group '%s' contains invalid name '%s'",
+ g_rdt->ngroups[group_idx].desc,
+ g_rdt->ngroups[group_idx].names[name_idx]);
+ rdt_free_ngroups();
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (n == 0) {
+ ERROR(RDT_PLUGIN ": Empty process name groups configured.");
+ return -EINVAL;
+ }
+
+ /* Get all available events on this platform */
+ for (unsigned i = 0; i < g_rdt->cap_mon->u.mon->num_events; i++)
+ events |= g_rdt->cap_mon->u.mon->events[i].type;
+
+ events &= ~(PQOS_PERF_EVENT_LLC_MISS);
+
+ DEBUG(RDT_PLUGIN ": Available events to monitor: %#x", events);
+
+ g_rdt->num_ngroups = n;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < i; j++) {
+ int found = ngroup_cmp(&g_rdt->ngroups[j], &g_rdt->ngroups[i]);
+ if (found != 0) {
+ rdt_free_ngroups();
+ ERROR(RDT_PLUGIN
+ ": Cannot monitor same process name in different groups.");
+ return -EINVAL;
+ }
+ }
+
+ g_rdt->ngroups[i].events = events;
+ g_rdt->pngroups[i] = calloc(1, sizeof(*g_rdt->pngroups[i]));
+ if (g_rdt->pngroups[i] == NULL) {
+ rdt_free_ngroups();
+ ERROR(RDT_PLUGIN
+ ": Failed to allocate memory for process name monitoring data.");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+/* Helper typedef for process name array
+ * Extra 1 char is added for string null termination.
+ */
+typedef char proc_comm_t[RDT_MAX_NAME_LEN + 1];
+
+/* Linked one-way list of pids. */
+typedef struct pids_list_s {
+ pid_t pid;
+ struct pids_list_s *next;
+} pids_list_t;
+
+/* Holds process name and list of pids assigned to that name */
+typedef struct proc_pids_s {
+ proc_comm_t proccess_name;
+ pids_list_t *pids;
+} proc_pids_t;
+
+/*
+ * NAME
+ * pids_list_add_pid
+ *
+ * DESCRIPTION
+ * Adds pid at the end of the pids list.
+ * Allocates memory for new pid element, it is up to user to free it.
+ *
+ * PARAMETERS
+ * `list' Head of target pids_list.
+ * `pid' Pid to be added.
+ *
+ * RETURN VALUE
+ * On success, returns 0.
+ * -1 on memory allocation error.
+ */
+static int pids_list_add_pid(pids_list_t **list, const pid_t pid) {
+ pids_list_t *new_element = calloc(1, sizeof(*new_element));
+
+ if (new_element == NULL) {
+ ERROR(RDT_PLUGIN ": Alloc error\n");
+ return -1;
+ }
+ new_element->pid = pid;
+ new_element->next = NULL;
+
+ pids_list_t **current = list;
+ while (*current != NULL) {
+ current = &((*current)->next);
+ }
+ *current = new_element;
+ return 0;
+}
+
+/*
+ * NAME
+ * read_proc_name
+ *
+ * DESCRIPTION
+ * Reads process name from given pid directory.
+ * Strips new-line character (\n).
+ *
+ * PARAMETERS
+ * `procfs_path` Path to systems proc directory (e.g. /proc)
+ * `pid_entry' Dirent for PID directory
+ * `name' Output buffer for process name, recommended proc_comm.
+ * `out_size' Output buffer size, recommended sizeof(proc_comm)
+ *
+ * RETURN VALUE
+ * On success, the number of read bytes (includes stripped \n).
+ * -1 on file open error
+ */
+static int read_proc_name(const char *procfs_path,
+ const struct dirent *pid_entry, char *name,
+ const size_t out_size) {
+ assert(procfs_path);
+ assert(pid_entry);
+ assert(name);
+ assert(out_size);
+ memset(name, 0, out_size);
+
+ const char *comm_file_name = "comm";
+
+ char *path = ssnprintf_alloc("%s/%s/%s", procfs_path, pid_entry->d_name,
+ comm_file_name);
+
+ FILE *f = fopen(path, "r");
+ if (f == NULL) {
+ ERROR(RDT_PLUGIN ": Failed to open comm file, error: %d\n", errno);
+ sfree(path);
+ return -1;
+ }
+ size_t read_length = fread(name, sizeof(char), out_size, f);
+ fclose(f);
+ sfree(path);
+ /* strip new line ending */
+ char *newline = strchr(name, '\n');
+ if (newline) {
+ *newline = '\0';
+ }
+
+ return read_length;
+}
+
+/*
+ * NAME
+ * get_pid_number
+ *
+ * DESCRIPTION
+ * Gets pid number for given /proc/pid directory entry or
+ * returns error if input directory does not hold PID information.
+ *
+ * PARAMETERS
+ * `entry' Dirent for PID directory
+ * `pid' PID number to be filled
+ *
+ * RETURN VALUE
+ * 0 on success. Negative number on error:
+ * -1: given entry is not a directory
+ * -2: PID conversion error
+ */
+static int get_pid_number(struct dirent *entry, pid_t *pid) {
+ char *tmp_end; /* used for strtoul error check*/
+
+ if (pid == NULL || entry == NULL)
+ return -1;
+
+ if (entry->d_type != DT_DIR)
+ return -1;
+
+ /* trying to get pid number from directory name*/
+ *pid = strtoul(entry->d_name, &tmp_end, 10);
+ if (*tmp_end != '\0') {
+ return -2; /* conversion failed, not proc-pid */
+ }
+ /* all checks passed, marking as success */
+ return 0;
+}
+
+/*
+ * NAME
+ * fetch_pids_for_procs
+ *
+ * DESCRIPTION
+ * Finds PIDs matching given process's names.
+ * Searches all PID directories in /proc fs and
+ * allocates memory for proc_pids structs, it is up to user to free it.
+ * Output array will have same element count as input array.
+ *
+ * PARAMETERS
+ * `procfs_path' Path to systems proc directory (e.g. /proc)
+ * `procs' Array of null-terminated strings with
+ * process' names to search for
+ * `procs_size' procs array element count
+ * `proc_pids_array' Address of pointer, under which new
+ * array of proc_pids will be allocated. Must be NULL.
+ *
+ * RETURN VALUE
+ * 0 on success. Negative number on error:
+ * -1: could not open /proc dir
+ */
+__attribute__((unused)) /* TODO: remove this attribute when PID monitoring is
+ implemented */
+static int
+fetch_pids_for_procs(const char *procfs_path, const char **procs_names_array,
+ const size_t procs_names_array_size,
+ proc_pids_t **proc_pids_array) {
+ assert(procfs_path);
+ assert(procs_names_array);
+ assert(procs_names_array_size);
+ assert(proc_pids_array);
+ assert(NULL == *proc_pids_array);
+
+ DIR *proc_dir = opendir(procfs_path);
+ if (proc_dir == NULL) {
+ ERROR(RDT_PLUGIN ": Could not open %s directory, error: %d", procfs_path,
+ errno);
+ return -1;
+ }
+
+ /* Copy procs names to output array. Initialize pids list with NULL value. */
+ (*proc_pids_array) =
+ calloc(procs_names_array_size, sizeof(**proc_pids_array));
+ for (size_t i = 0; i < procs_names_array_size; ++i) {
+ sstrncpy((*proc_pids_array)[i].proccess_name, procs_names_array[i],
+ STATIC_ARRAY_SIZE((*proc_pids_array)[i].proccess_name));
+ (*proc_pids_array)[i].pids = NULL;
+ }
+
+ /* Go through procfs and find PIDS and their comms */
+ struct dirent *entry;
+ while ((entry = readdir(proc_dir)) != NULL) {
+
+ pid_t pid;
+ int pid_conversion = get_pid_number(entry, &pid);
+ if (pid_conversion < 0)
+ continue;
+
+ proc_comm_t comm;
+ int read_result =
+ read_proc_name(procfs_path, entry, comm, sizeof(proc_comm_t));
+ if (read_result <= 0) {
+ ERROR(RDT_PLUGIN ": Comm file skipped. Read result: %d", read_result);
+ continue;
+ }
+
+ /* Try to find comm in input procs array (proc_pids_array has same names) */
+ for (size_t i = 0; i < procs_names_array_size; ++i) {
+ if (0 == strncmp(comm, (*proc_pids_array)[i].proccess_name,
+ STATIC_ARRAY_SIZE(comm)))
+ pids_list_add_pid(&((*proc_pids_array)[i].pids), pid);
+ }
+ }
+
+ int close_result = closedir(proc_dir);
+ if (0 != close_result) {
+ ERROR(RDT_PLUGIN ": failed to close %s directory, error: %d", procfs_path,
+ errno);
+ return -1;
+ }
+ return 0;
+}
+
static void rdt_pqos_log(void *context, const size_t size, const char *msg) {
DEBUG(RDT_PLUGIN ": %s", msg);
}
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *child = ci->children + i;
- if (strcasecmp("Cores", child->key) == 0) {
+ if (strncasecmp("Cores", child->key, (size_t)strlen("Cores")) == 0) {
if (rdt_config_cgroups(child) != 0) {
g_state = CONFIGURATION_ERROR;
/* if we return -1 at this point collectd
#if COLLECT_DEBUG
rdt_dump_cgroups();
#endif /* COLLECT_DEBUG */
+ } else if (strncasecmp("Processes", child->key,
+ (size_t)strlen("Processes")) == 0) {
+ if (rdt_config_ngroups(child) != 0) {
+ g_state = CONFIGURATION_ERROR;
+ /* if we return -1 at this point collectd
+ reports a failure in configuration and
+ aborts
+ */
+ return (0);
+ }
+
+#if COLLECT_DEBUG
+ rdt_dump_ngroups();
+#endif /* COLLECT_DEBUG */
} else {
ERROR(RDT_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
}
return 0;
}
-static void rdt_submit_derive(char *cgroup, char *type, char *type_instance,
- derive_t value) {
+static void rdt_submit_derive(const char *cgroup, const char *type,
+ const char *type_instance, derive_t value) {
value_list_t vl = VALUE_LIST_INIT;
vl.values = &(value_t){.derive = value};
plugin_dispatch_values(&vl);
}
-static void rdt_submit_gauge(char *cgroup, char *type, char *type_instance,
- gauge_t value) {
+static void rdt_submit_gauge(const char *cgroup, const char *type,
+ const char *type_instance, gauge_t value) {
value_list_t vl = VALUE_LIST_INIT;
vl.values = &(value_t){.gauge = value};
return -EINVAL;
}
- ret = pqos_mon_poll(&g_rdt->pgroups[0], (unsigned)g_rdt->num_groups);
+ ret = pqos_mon_poll(&g_rdt->pcgroups[0], (unsigned)g_rdt->cores.num_cgroups);
if (ret != PQOS_RETVAL_OK) {
ERROR(RDT_PLUGIN ": Failed to poll monitoring data.");
return -1;
rdt_dump_data();
#endif /* COLLECT_DEBUG */
- for (int i = 0; i < g_rdt->num_groups; i++) {
+ for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) {
+ core_group_t *cgroup = g_rdt->cores.cgroups + i;
enum pqos_mon_event mbm_events =
(PQOS_MON_EVENT_LMEM_BW | PQOS_MON_EVENT_TMEM_BW |
PQOS_MON_EVENT_RMEM_BW);
- const struct pqos_event_values *pv = &g_rdt->pgroups[i]->values;
+ const struct pqos_event_values *pv = &g_rdt->pcgroups[i]->values;
/* Submit only monitored events data */
- if (g_rdt->cgroups[i].events & PQOS_MON_EVENT_L3_OCCUP)
- rdt_submit_gauge(g_rdt->cgroups[i].desc, "bytes", "llc", pv->llc);
+ if (g_rdt->events[i] & PQOS_MON_EVENT_L3_OCCUP)
+ rdt_submit_gauge(cgroup->desc, "bytes", "llc", pv->llc);
- if (g_rdt->cgroups[i].events & PQOS_PERF_EVENT_IPC)
- rdt_submit_gauge(g_rdt->cgroups[i].desc, "ipc", NULL, pv->ipc);
+ if (g_rdt->events[i] & PQOS_PERF_EVENT_IPC)
+ rdt_submit_gauge(cgroup->desc, "ipc", NULL, pv->ipc);
- if (g_rdt->cgroups[i].events & mbm_events) {
- rdt_submit_derive(g_rdt->cgroups[i].desc, "memory_bandwidth", "local",
+ if (g_rdt->events[i] & mbm_events) {
+ rdt_submit_derive(cgroup->desc, "memory_bandwidth", "local",
pv->mbm_local_delta);
- rdt_submit_derive(g_rdt->cgroups[i].desc, "memory_bandwidth", "remote",
+ rdt_submit_derive(cgroup->desc, "memory_bandwidth", "remote",
pv->mbm_remote_delta);
}
}
return ret;
/* Start monitoring */
- for (int i = 0; i < g_rdt->num_groups; i++) {
- rdt_core_group_t *cg = &g_rdt->cgroups[i];
+ for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) {
+ core_group_t *cg = g_rdt->cores.cgroups + i;
- ret = pqos_mon_start(cg->num_cores, cg->cores, cg->events, (void *)cg->desc,
- g_rdt->pgroups[i]);
+ ret = pqos_mon_start(cg->num_cores, cg->cores, g_rdt->events[i],
+ (void *)cg->desc, g_rdt->pcgroups[i]);
if (ret != PQOS_RETVAL_OK)
ERROR(RDT_PLUGIN ": Error starting monitoring group %s (pqos status=%d)",
return 0;
/* Stop monitoring */
- for (int i = 0; i < g_rdt->num_groups; i++) {
- pqos_mon_stop(g_rdt->pgroups[i]);
+ for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) {
+ pqos_mon_stop(g_rdt->pcgroups[i]);
}
ret = pqos_fini();