]> arthur.barton.de Git - netdata.git/blobdiff - src/sys_devices_system_node.c
add support for tracking NUMA locality metrics
[netdata.git] / src / sys_devices_system_node.c
diff --git a/src/sys_devices_system_node.c b/src/sys_devices_system_node.c
new file mode 100644 (file)
index 0000000..18c3fcd
--- /dev/null
@@ -0,0 +1,127 @@
+#include "common.h"
+
+struct node {
+    char *name;
+    char *numastat_filename;
+    procfile *numastat_ff;
+    RRDSET *numastat_st;
+    struct node *next;
+};
+static struct node *numa_root = NULL;
+
+static int find_all_nodes() {
+    int numa_node_count = 0;
+    char name[FILENAME_MAX + 1];
+    snprintfz(name, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/devices/system/node");
+    char *dirname = config_get("plugin:proc:/sys/devices/system/node", "directory to monitor", name);
+
+    DIR *dir = opendir(dirname);
+    if(!dir) {
+        error("Cannot read NUMA node directory '%s'", dirname);
+        return 0;
+    }
+
+    struct dirent *de = NULL;
+    while((de = readdir(dir))) {
+        if(de->d_type != DT_DIR)
+            continue;
+
+        if(strncmp(de->d_name, "node", 4) != 0)
+            continue;
+
+        if(!isdigit(de->d_name[4]))
+            continue;
+
+        numa_node_count++;
+
+        struct node *m = callocz(1, sizeof(struct node));
+        m->name = strdupz(de->d_name);
+
+        struct stat st;
+
+        snprintfz(name, FILENAME_MAX, "%s/%s/numastat", dirname, de->d_name);
+        if(stat(name, &st) == -1) {
+            freez(m->name);
+            freez(m);
+            continue;
+        }
+
+        m->numastat_filename = strdupz(name);
+
+        m->next = numa_root;
+        numa_root = m;
+    }
+
+    closedir(dir);
+
+    return numa_node_count;
+}
+
+int do_proc_sys_devices_system_node(int update_every, usec_t dt) {
+    (void)dt;
+
+    static int numa_node_count = 0;
+
+    if(unlikely(numa_root == NULL)) {
+        numa_node_count = find_all_nodes(update_every);
+        if(unlikely(numa_root == NULL))
+            return 1;
+    }
+
+    static int do_numastat = -1;
+    struct node *m;
+
+    if(unlikely(do_numastat == -1)) {
+        do_numastat = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/node", "enable per-node numa metrics", CONFIG_ONDEMAND_ONDEMAND);
+    }
+
+    if(do_numastat == CONFIG_ONDEMAND_YES || (do_numastat == CONFIG_ONDEMAND_ONDEMAND && numa_node_count >= 2)) {
+        for(m = numa_root; m; m = m->next) {
+            if(m->numastat_filename) {
+                if(unlikely(!m->numastat_ff)) {
+                    m->numastat_ff = procfile_open(m->numastat_filename, " ", PROCFILE_FLAG_DEFAULT);
+                    if(unlikely(!m->numastat_ff))
+                        continue;
+                }
+
+                m->numastat_ff = procfile_readall(m->numastat_ff);
+                if(unlikely(!m->numastat_ff || procfile_lines(m->numastat_ff) < 1 || procfile_linewords(m->numastat_ff, 0) < 1))
+                    continue;
+
+                procfile *ff = m->numastat_ff;
+
+                RRDSET *st = m->numastat_st;
+                if(unlikely(!st)) {
+                    st = rrdset_create("mem", m->name, NULL, "numa", NULL, "NUMA events", "events/s", 1000, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "local_node", "local", 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "numa_foreign", "foreign", 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "interleave_hit", "interleave", 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "other_node", "other", 1, 1, RRDDIM_INCREMENTAL);
+
+                    m->numastat_st = st;
+                }
+                else rrdset_next(st);
+
+                uint32_t lines = procfile_lines(ff), l;
+                for(l = 0; l < lines; l++) {
+                    uint32_t words = procfile_linewords(ff, l);
+                    if(unlikely(words < 2)) {
+                        if(unlikely(words)) error("Cannot read %s numastat line %u. Expected 2 params, read %u.", m->name, l, words);
+                        continue;
+                    }
+
+                    char *name = procfile_lineword(ff, l, 0);
+                    char *value = procfile_lineword(ff, l, 1);
+                    if (unlikely(!name || !*name || !value || !*value)) continue;
+
+                    rrddim_set(st, name, strtoull(value, NULL, 10));
+                }
+                rrdset_done(st);
+            }
+        }
+    }
+
+    return 0;
+}