summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVilkov Adel <vilkov.adel@gmail.com>2019-08-12 11:01:09 +0300
committerVladimir Kobal <vlad@prokk.net>2019-08-12 11:01:09 +0300
commit8bf966d6e06281e03333742c5d650bfe60b51ded (patch)
tree75aaa5d42818858eb7f42ffced9f2028847081bc
parent4e4f95411be93a83b18fc10b768263a3cd749508 (diff)
(re-open) ZRAM info collector module (proc.plugin) (#6424)
* ZRAM collector module ZRAM: Implemented zram device id detection ZRAM: Implemented zram device enumeration WIP ZRAM: Memory usage graph (needs other graphs) ZRAM: Added ratio and efficiency graph ZRAM: Added chart description and context names, code formatting * ZRAM: Proper handling of zram device removal * ZRAM: Added additional checks, removed redundant logging
-rw-r--r--CMakeLists.txt1
-rw-r--r--Makefile.am1
-rw-r--r--collectors/all.h4
-rw-r--r--collectors/proc.plugin/plugin_proc.c1
-rw-r--r--collectors/proc.plugin/plugin_proc.h1
-rw-r--r--collectors/proc.plugin/sys_block_zram.c288
-rw-r--r--web/gui/dashboard_info.js26
7 files changed, 322 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4d631f2fe7..13a9f472c9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -423,6 +423,7 @@ set(PROC_PLUGIN_FILES
collectors/proc.plugin/proc_vmstat.c
collectors/proc.plugin/proc_uptime.c
collectors/proc.plugin/sys_kernel_mm_ksm.c
+ collectors/proc.plugin/sys_block_zram.c
collectors/proc.plugin/sys_devices_system_edac_mc.c
collectors/proc.plugin/sys_devices_system_node.c
collectors/proc.plugin/sys_fs_btrfs.c
diff --git a/Makefile.am b/Makefile.am
index 0e88962c7e..7cff5ff559 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -280,6 +280,7 @@ PROC_PLUGIN_FILES = \
collectors/proc.plugin/proc_vmstat.c \
collectors/proc.plugin/proc_uptime.c \
collectors/proc.plugin/sys_kernel_mm_ksm.c \
+ collectors/proc.plugin/sys_block_zram.c \
collectors/proc.plugin/sys_devices_system_edac_mc.c \
collectors/proc.plugin/sys_devices_system_node.c \
collectors/proc.plugin/sys_fs_btrfs.c \
diff --git a/collectors/all.h b/collectors/all.h
index a18c43a2db..5fe5e9be1b 100644
--- a/collectors/all.h
+++ b/collectors/all.h
@@ -93,6 +93,10 @@
#define NETDATA_CHART_PRIO_MEM_HW 1500
#define NETDATA_CHART_PRIO_MEM_HW_ECC_CE 1550
#define NETDATA_CHART_PRIO_MEM_HW_ECC_UE 1560
+#define NETDATA_CHART_PRIO_MEM_ZRAM 1600
+#define NETDATA_CHART_PRIO_MEM_ZRAM_SAVINGS 1601
+#define NETDATA_CHART_PRIO_MEM_ZRAM_RATIO 1602
+#define NETDATA_CHART_PRIO_MEM_ZRAM_EFFICIENCY 1603
// Disks
diff --git a/collectors/proc.plugin/plugin_proc.c b/collectors/proc.plugin/plugin_proc.c
index 343acfa37b..62e974cfd0 100644
--- a/collectors/proc.plugin/plugin_proc.c
+++ b/collectors/proc.plugin/plugin_proc.c
@@ -29,6 +29,7 @@ static struct proc_module {
{ .name = "/proc/vmstat", .dim = "vmstat", .func = do_proc_vmstat },
{ .name = "/proc/meminfo", .dim = "meminfo", .func = do_proc_meminfo },
{ .name = "/sys/kernel/mm/ksm", .dim = "ksm", .func = do_sys_kernel_mm_ksm },
+ { .name = "/sys/block/zram", .dim = "zram", .func = do_sys_block_zram },
{ .name = "/sys/devices/system/edac/mc", .dim = "ecc", .func = do_proc_sys_devices_system_edac_mc },
{ .name = "/sys/devices/system/node", .dim = "numa", .func = do_proc_sys_devices_system_node },
diff --git a/collectors/proc.plugin/plugin_proc.h b/collectors/proc.plugin/plugin_proc.h
index 0c2afe7793..40a0e82d39 100644
--- a/collectors/proc.plugin/plugin_proc.h
+++ b/collectors/proc.plugin/plugin_proc.h
@@ -41,6 +41,7 @@ extern int do_proc_sys_kernel_random_entropy_avail(int update_every, usec_t dt);
extern int do_proc_interrupts(int update_every, usec_t dt);
extern int do_proc_softirqs(int update_every, usec_t dt);
extern int do_sys_kernel_mm_ksm(int update_every, usec_t dt);
+extern int do_sys_block_zram(int update_every, usec_t dt);
extern int do_proc_loadavg(int update_every, usec_t dt);
extern int do_proc_net_stat_synproxy(int update_every, usec_t dt);
extern int do_proc_net_softnet_stat(int update_every, usec_t dt);
diff --git a/collectors/proc.plugin/sys_block_zram.c b/collectors/proc.plugin/sys_block_zram.c
new file mode 100644
index 0000000000..a68a405def
--- /dev/null
+++ b/collectors/proc.plugin/sys_block_zram.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "plugin_proc.h"
+
+#define PLUGIN_PROC_MODULE_ZRAM_NAME "/sys/block/zram"
+#define rrdset_obsolete_and_pointer_null(st) do { if(st) { rrdset_is_obsolete(st); (st) = NULL; } } while(st)
+
+typedef struct mm_stat {
+ unsigned long long orig_data_size;
+ unsigned long long compr_data_size;
+ unsigned long long mem_used_total;
+ unsigned long long mem_limit;
+ unsigned long long mem_used_max;
+ unsigned long long same_pages;
+ unsigned long long pages_compacted;
+} MM_STAT;
+
+typedef struct zram_device {
+ procfile *file;
+
+ RRDSET *st_usage;
+ RRDDIM *rd_compr_data_size;
+ RRDDIM *rd_metadata_size;
+
+ RRDSET *st_savings;
+ RRDDIM *rd_original_size;
+ RRDDIM *rd_savings_size;
+
+ RRDSET *st_comp_ratio;
+ RRDDIM *rd_comp_ratio;
+
+ RRDSET *st_alloc_efficiency;
+ RRDDIM *rd_alloc_efficiency;
+} ZRAM_DEVICE;
+
+ // --------------------------------------------------------------------
+
+static int try_get_zram_major_number(procfile *file) {
+ size_t i;
+ unsigned int lines = procfile_lines(file);
+ int id = -1;
+ char *name = NULL;
+ for (i = 0; i < lines; i++)
+ {
+ if (procfile_linewords(file, i) < 2)
+ continue;
+ name = procfile_lineword(file, i, 1);
+ if (strcmp(name, "zram") == 0)
+ {
+ id = str2i(procfile_lineword(file, i, 0));
+ if (id == 0)
+ return -1;
+ return id;
+ }
+ }
+ return -1;
+}
+
+static inline void init_rrd(const char *name, ZRAM_DEVICE *d, int update_every) {
+ char chart_name[RRD_ID_LENGTH_MAX + 1];
+
+ snprintfz(chart_name, RRD_ID_LENGTH_MAX, "zram_usage.%s", name);
+ d->st_usage = rrdset_create_localhost(
+ "mem"
+ , chart_name
+ , chart_name
+ , name
+ , "mem.zram_usage"
+ , "ZRAM Memory Usage"
+ , "MiB"
+ , PLUGIN_PROC_NAME
+ , PLUGIN_PROC_MODULE_ZRAM_NAME
+ , NETDATA_CHART_PRIO_MEM_ZRAM
+ , update_every
+ , RRDSET_TYPE_AREA);
+ d->rd_compr_data_size = rrddim_add(d->st_usage, "compressed", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+ d->rd_metadata_size = rrddim_add(d->st_usage, "metadata", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+
+ snprintfz(chart_name, RRD_ID_LENGTH_MAX, "zram_savings.%s", name);
+ d->st_savings = rrdset_create_localhost(
+ "mem"
+ , chart_name
+ , chart_name
+ , name
+ , "mem.zram_savings"
+ , "ZRAM Memory Savings"
+ , "MiB"
+ , PLUGIN_PROC_NAME
+ , PLUGIN_PROC_MODULE_ZRAM_NAME
+ , NETDATA_CHART_PRIO_MEM_ZRAM_SAVINGS
+ , update_every
+ , RRDSET_TYPE_AREA);
+ d->rd_savings_size = rrddim_add(d->st_savings, "savings", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+ d->rd_original_size = rrddim_add(d->st_savings, "original", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+
+ snprintfz(chart_name, RRD_ID_LENGTH_MAX, "zram_ratio.%s", name);
+ d->st_comp_ratio = rrdset_create_localhost(
+ "mem"
+ , chart_name
+ , chart_name
+ , name
+ , "mem.zram_ratio"
+ , "ZRAM Compression Ratio (original to compressed)"
+ , "ratio"
+ , PLUGIN_PROC_NAME
+ , PLUGIN_PROC_MODULE_ZRAM_NAME
+ , NETDATA_CHART_PRIO_MEM_ZRAM_RATIO
+ , update_every
+ , RRDSET_TYPE_LINE);
+ d->rd_comp_ratio = rrddim_add(d->st_comp_ratio, "ratio", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
+
+ snprintfz(chart_name, RRD_ID_LENGTH_MAX, "zram_efficiency.%s", name);
+ d->st_alloc_efficiency = rrdset_create_localhost(
+ "mem"
+ , chart_name
+ , chart_name
+ , name
+ , "mem.zram_efficiency"
+ , "ZRAM Efficiency"
+ , "percentage"
+ , PLUGIN_PROC_NAME
+ , PLUGIN_PROC_MODULE_ZRAM_NAME
+ , NETDATA_CHART_PRIO_MEM_ZRAM_EFFICIENCY
+ , update_every
+ , RRDSET_TYPE_LINE);
+ d->rd_alloc_efficiency = rrddim_add(d->st_alloc_efficiency, "percent", NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE);
+}
+
+static int init_devices(DICTIONARY *devices, unsigned int zram_id, int update_every) {
+ int count = 0;
+ DIR *dir = opendir("/dev");
+ struct dirent *de;
+ struct stat st;
+ char filename[FILENAME_MAX + 1];
+ procfile *ff = NULL;
+ ZRAM_DEVICE device;
+
+ if (unlikely(!dir))
+ return 0;
+ while ((de = readdir(dir)))
+ {
+ snprintfz(filename, FILENAME_MAX, "/dev/%s", de->d_name);
+ if (unlikely(stat(filename, &st) != 0))
+ {
+ error("ZRAM : Unable to stat %s: %s", filename, strerror(errno));
+ continue;
+ }
+ if (major(st.st_rdev) == zram_id)
+ {
+ info("ZRAM : Found device %s", filename);
+ snprintfz(filename, FILENAME_MAX, "/sys/block/%s/mm_stat", de->d_name);
+ ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
+ if (ff == NULL)
+ {
+ error("ZRAM : Failed to open %s: %s", filename, strerror(errno));
+ continue;
+ }
+ device.file = ff;
+ init_rrd(de->d_name, &device, update_every);
+ dictionary_set(devices, de->d_name, &device, sizeof(ZRAM_DEVICE));
+ count++;
+ }
+ }
+ closedir(dir);
+ return count;
+}
+
+static void free_device(DICTIONARY *dict, char *name)
+{
+ ZRAM_DEVICE *d = (ZRAM_DEVICE*)dictionary_get(dict, name);
+ info("ZRAM : Disabling monitoring of device %s", name);
+ rrdset_obsolete_and_pointer_null(d->st_usage);
+ rrdset_obsolete_and_pointer_null(d->st_savings);
+ rrdset_obsolete_and_pointer_null(d->st_alloc_efficiency);
+ rrdset_obsolete_and_pointer_null(d->st_comp_ratio);
+ dictionary_del(dict, name);
+}
+ // --------------------------------------------------------------------
+
+static inline int read_mm_stat(procfile *ff, MM_STAT *stats) {
+ ff = procfile_readall(ff);
+ if (!ff)
+ return -1;
+ if (procfile_lines(ff) < 1)
+ return -1;
+ if (procfile_linewords(ff, 0) < 7)
+ return -1;
+
+ stats->orig_data_size = str2ull(procfile_word(ff, 0));
+ stats->compr_data_size = str2ull(procfile_word(ff, 1));
+ stats->mem_used_total = str2ull(procfile_word(ff, 2));
+ stats->mem_limit = str2ull(procfile_word(ff, 3));
+ stats->mem_used_max = str2ull(procfile_word(ff, 4));
+ stats->same_pages = str2ull(procfile_word(ff, 5));
+ stats->pages_compacted = str2ull(procfile_word(ff, 6));
+ return 0;
+}
+
+static inline int _collect_zram_metrics(char* name, ZRAM_DEVICE *d, int advance, DICTIONARY* dict) {
+ MM_STAT mm;
+ int value;
+ if (unlikely(read_mm_stat(d->file, &mm) < 0))
+ {
+ free_device(dict, name);
+ return -1;
+ }
+
+ if (likely(advance))
+ {
+ rrdset_next(d->st_usage);
+ rrdset_next(d->st_savings);
+ rrdset_next(d->st_comp_ratio);
+ rrdset_next(d->st_alloc_efficiency);
+ }
+ // zram_usage
+ rrddim_set_by_pointer(d->st_usage, d->rd_compr_data_size, mm.compr_data_size);
+ rrddim_set_by_pointer(d->st_usage, d->rd_metadata_size, mm.mem_used_total - mm.compr_data_size);
+ rrdset_done(d->st_usage);
+ // zram_savings
+ rrddim_set_by_pointer(d->st_savings, d->rd_savings_size, mm.compr_data_size - mm.orig_data_size);
+ rrddim_set_by_pointer(d->st_savings, d->rd_original_size, mm.orig_data_size);
+ rrdset_done(d->st_savings);
+ // zram_ratio
+ value = mm.compr_data_size == 0 ? 1 : mm.orig_data_size * 100 / mm.compr_data_size;
+ rrddim_set_by_pointer(d->st_comp_ratio, d->rd_comp_ratio, value);
+ rrdset_done(d->st_comp_ratio);
+ // zram_efficiency
+ value = mm.mem_used_total == 0 ? 100 : (mm.compr_data_size * 1000000 / mm.mem_used_total);
+ rrddim_set_by_pointer(d->st_alloc_efficiency, d->rd_alloc_efficiency, value);
+ rrdset_done(d->st_alloc_efficiency);
+ return 0;
+}
+
+static int collect_first_zram_metrics(char *name, void *entry, void *data) {
+ // collect without calling rrdset_next (init only)
+ return _collect_zram_metrics(name, (ZRAM_DEVICE *)entry, 0, (DICTIONARY *)data);
+}
+
+static int collect_zram_metrics(char *name, void *entry, void *data) {
+ (void)name;
+ // collect with calling rrdset_next
+ return _collect_zram_metrics(name, (ZRAM_DEVICE *)entry, 1, (DICTIONARY *)data);
+}
+
+ // --------------------------------------------------------------------
+
+int do_sys_block_zram(int update_every, usec_t dt) {
+ (void)dt;
+ static procfile *ff = NULL;
+ static DICTIONARY *devices = NULL;
+ static int initialized = 0;
+ static int device_count = 0;
+ int zram_id = -1;
+ if (unlikely(!initialized))
+ {
+ initialized = 1;
+ ff = procfile_open("/proc/devices", " \t:", PROCFILE_FLAG_DEFAULT);
+ if (ff == NULL)
+ {
+ error("Cannot read /proc/devices");
+ return 1;
+ }
+ ff = procfile_readall(ff);
+ if (!ff)
+ return 1;
+ zram_id = try_get_zram_major_number(ff);
+ if (zram_id == -1)
+ {
+ if (ff != NULL)
+ procfile_close(ff);
+ return 1;
+ }
+ procfile_close(ff);
+
+ devices = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
+ device_count = init_devices(devices, (unsigned int)zram_id, update_every);
+ if (device_count < 1)
+ return 1;
+ dictionary_get_all_name_value(devices, collect_first_zram_metrics, devices);
+ }
+ else
+ {
+ if (unlikely(device_count < 1))
+ return 1;
+ dictionary_get_all_name_value(devices, collect_zram_metrics, devices);
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/web/gui/dashboard_info.js b/web/gui/dashboard_info.js
index 6d8abba544..f05f2e0d11 100644
--- a/web/gui/dashboard_info.js
+++ b/web/gui/dashboard_info.js
@@ -832,6 +832,32 @@ netdataDashboard.context = {
]
},
+ 'mem.zram_usage': {
+ info: 'ZRAM total RAM usage metrics. ZRAM uses some memory to store metadata about stored memory pages, thus introducing an overhead which is proportional to disk size. It excludes same-element-filled-pages since no memory is allocated for them.'
+ },
+
+ 'mem.zram_savings': {
+ info: 'Displays original and compressed memory data sizes.'
+ },
+
+ 'mem.zram_ratio': {
+ heads: [
+ netdataDashboard.gaugeChart('Compression Ratio', '12%', 'ratio', '#0099CC')
+ ],
+ info: 'Compression ratio, calculated as <code>100 * original_size / compressed_size</code>. More means better compression and more RAM savings.'
+ },
+
+ 'mem.zram_efficiency': {
+ heads: [
+ netdataDashboard.gaugeChart('Efficiency', '12%', 'percent', NETDATA.colors[0])
+ ],
+ commonMin: true,
+ commonMax: true,
+ valueRange: "[0, 100]",
+ info: 'Memory usage efficiency, calculated as <code>100 * compressed_size / total_mem_used</code>.'
+ },
+
+
'mem.pgfaults': {
info: 'A <a href="https://en.wikipedia.org/wiki/Page_fault" target="_blank">page fault</a> is a type of interrupt, called trap, raised by computer hardware when a running program accesses a memory page that is mapped into the virtual address space, but not actually loaded into main memory. If the page is loaded in memory at the time the fault is generated, but is not marked in the memory management unit as being loaded in memory, then it is called a <b>minor</b> or soft page fault. A <b>major</b> page fault is generated when the system needs to load the memory page from disk or swap memory.'
},