@cryptotaxi247 / netdata-1 / commits / 8bf966d6e

(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

Vilkov Adel committed Aug 12, 2019 at 11:01 UTC 8bf966d6e06281e03333742c5d650bfe60b51ded
7 files changed +322
CMakeLists.txt
+1
@@ -423,6 +423,7 @@ set(PROC_PLUGIN_FILES
423 collectors/proc.plugin/proc_vmstat.c
424 collectors/proc.plugin/proc_uptime.c
425 collectors/proc.plugin/sys_kernel_mm_ksm.c
426 + collectors/proc.plugin/sys_block_zram.c
427 collectors/proc.plugin/sys_devices_system_edac_mc.c
428 collectors/proc.plugin/sys_devices_system_node.c
429 collectors/proc.plugin/sys_fs_btrfs.c
Makefile.am
+1
@@ -280,6 +280,7 @@ PROC_PLUGIN_FILES = \
280 collectors/proc.plugin/proc_vmstat.c \
281 collectors/proc.plugin/proc_uptime.c \
282 collectors/proc.plugin/sys_kernel_mm_ksm.c \
283 + collectors/proc.plugin/sys_block_zram.c \
284 collectors/proc.plugin/sys_devices_system_edac_mc.c \
285 collectors/proc.plugin/sys_devices_system_node.c \
286 collectors/proc.plugin/sys_fs_btrfs.c \
collectors/all.h
+4
@@ -93,6 +93,10 @@
93 #define NETDATA_CHART_PRIO_MEM_HW 1500
94 #define NETDATA_CHART_PRIO_MEM_HW_ECC_CE 1550
95 #define NETDATA_CHART_PRIO_MEM_HW_ECC_UE 1560
96 +#define NETDATA_CHART_PRIO_MEM_ZRAM 1600
97 +#define NETDATA_CHART_PRIO_MEM_ZRAM_SAVINGS 1601
98 +#define NETDATA_CHART_PRIO_MEM_ZRAM_RATIO 1602
99 +#define NETDATA_CHART_PRIO_MEM_ZRAM_EFFICIENCY 1603
100
101 // Disks
102
collectors/proc.plugin/plugin_proc.c
+1
@@ -29,6 +29,7 @@ static struct proc_module {
29 { .name = "/proc/vmstat", .dim = "vmstat", .func = do_proc_vmstat },
30 { .name = "/proc/meminfo", .dim = "meminfo", .func = do_proc_meminfo },
31 { .name = "/sys/kernel/mm/ksm", .dim = "ksm", .func = do_sys_kernel_mm_ksm },
32 + { .name = "/sys/block/zram", .dim = "zram", .func = do_sys_block_zram },
33 { .name = "/sys/devices/system/edac/mc", .dim = "ecc", .func = do_proc_sys_devices_system_edac_mc },
34 { .name = "/sys/devices/system/node", .dim = "numa", .func = do_proc_sys_devices_system_node },
35
collectors/proc.plugin/plugin_proc.h
+1
@@ -41,6 +41,7 @@ extern int do_proc_sys_kernel_random_entropy_avail(int update_every, usec_t dt);
41 extern int do_proc_interrupts(int update_every, usec_t dt);
42 extern int do_proc_softirqs(int update_every, usec_t dt);
43 extern int do_sys_kernel_mm_ksm(int update_every, usec_t dt);
44 +extern int do_sys_block_zram(int update_every, usec_t dt);
45 extern int do_proc_loadavg(int update_every, usec_t dt);
46 extern int do_proc_net_stat_synproxy(int update_every, usec_t dt);
47 extern int do_proc_net_softnet_stat(int update_every, usec_t dt);
collectors/proc.plugin/sys_block_zram.c new
+288
@@ -0,0 +1,288 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "plugin_proc.h"
4 +
5 +#define PLUGIN_PROC_MODULE_ZRAM_NAME "/sys/block/zram"
6 +#define rrdset_obsolete_and_pointer_null(st) do { if(st) { rrdset_is_obsolete(st); (st) = NULL; } } while(st)
7 +
8 +typedef struct mm_stat {
9 + unsigned long long orig_data_size;
10 + unsigned long long compr_data_size;
11 + unsigned long long mem_used_total;
12 + unsigned long long mem_limit;
13 + unsigned long long mem_used_max;
14 + unsigned long long same_pages;
15 + unsigned long long pages_compacted;
16 +} MM_STAT;
17 +
18 +typedef struct zram_device {
19 + procfile *file;
20 +
21 + RRDSET *st_usage;
22 + RRDDIM *rd_compr_data_size;
23 + RRDDIM *rd_metadata_size;
24 +
25 + RRDSET *st_savings;
26 + RRDDIM *rd_original_size;
27 + RRDDIM *rd_savings_size;
28 +
29 + RRDSET *st_comp_ratio;
30 + RRDDIM *rd_comp_ratio;
31 +
32 + RRDSET *st_alloc_efficiency;
33 + RRDDIM *rd_alloc_efficiency;
34 +} ZRAM_DEVICE;
35 +
36 + // --------------------------------------------------------------------
37 +
38 +static int try_get_zram_major_number(procfile *file) {
39 + size_t i;
40 + unsigned int lines = procfile_lines(file);
41 + int id = -1;
42 + char *name = NULL;
43 + for (i = 0; i < lines; i++)
44 + {
45 + if (procfile_linewords(file, i) < 2)
46 + continue;
47 + name = procfile_lineword(file, i, 1);
48 + if (strcmp(name, "zram") == 0)
49 + {
50 + id = str2i(procfile_lineword(file, i, 0));
51 + if (id == 0)
52 + return -1;
53 + return id;
54 + }
55 + }
56 + return -1;
57 +}
58 +
59 +static inline void init_rrd(const char *name, ZRAM_DEVICE *d, int update_every) {
60 + char chart_name[RRD_ID_LENGTH_MAX + 1];
61 +
62 + snprintfz(chart_name, RRD_ID_LENGTH_MAX, "zram_usage.%s", name);
63 + d->st_usage = rrdset_create_localhost(
64 + "mem"
65 + , chart_name
66 + , chart_name
67 + , name
68 + , "mem.zram_usage"
69 + , "ZRAM Memory Usage"
70 + , "MiB"
71 + , PLUGIN_PROC_NAME
72 + , PLUGIN_PROC_MODULE_ZRAM_NAME
73 + , NETDATA_CHART_PRIO_MEM_ZRAM
74 + , update_every
75 + , RRDSET_TYPE_AREA);
76 + d->rd_compr_data_size = rrddim_add(d->st_usage, "compressed", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
77 + d->rd_metadata_size = rrddim_add(d->st_usage, "metadata", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
78 +
79 + snprintfz(chart_name, RRD_ID_LENGTH_MAX, "zram_savings.%s", name);
80 + d->st_savings = rrdset_create_localhost(
81 + "mem"
82 + , chart_name
83 + , chart_name
84 + , name
85 + , "mem.zram_savings"
86 + , "ZRAM Memory Savings"
87 + , "MiB"
88 + , PLUGIN_PROC_NAME
89 + , PLUGIN_PROC_MODULE_ZRAM_NAME
90 + , NETDATA_CHART_PRIO_MEM_ZRAM_SAVINGS
91 + , update_every
92 + , RRDSET_TYPE_AREA);
93 + d->rd_savings_size = rrddim_add(d->st_savings, "savings", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
94 + d->rd_original_size = rrddim_add(d->st_savings, "original", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
95 +
96 + snprintfz(chart_name, RRD_ID_LENGTH_MAX, "zram_ratio.%s", name);
97 + d->st_comp_ratio = rrdset_create_localhost(
98 + "mem"
99 + , chart_name
100 + , chart_name
101 + , name
102 + , "mem.zram_ratio"
103 + , "ZRAM Compression Ratio (original to compressed)"
104 + , "ratio"
105 + , PLUGIN_PROC_NAME
106 + , PLUGIN_PROC_MODULE_ZRAM_NAME
107 + , NETDATA_CHART_PRIO_MEM_ZRAM_RATIO
108 + , update_every
109 + , RRDSET_TYPE_LINE);
110 + d->rd_comp_ratio = rrddim_add(d->st_comp_ratio, "ratio", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
111 +
112 + snprintfz(chart_name, RRD_ID_LENGTH_MAX, "zram_efficiency.%s", name);
113 + d->st_alloc_efficiency = rrdset_create_localhost(
114 + "mem"
115 + , chart_name
116 + , chart_name
117 + , name
118 + , "mem.zram_efficiency"
119 + , "ZRAM Efficiency"
120 + , "percentage"
121 + , PLUGIN_PROC_NAME
122 + , PLUGIN_PROC_MODULE_ZRAM_NAME
123 + , NETDATA_CHART_PRIO_MEM_ZRAM_EFFICIENCY
124 + , update_every
125 + , RRDSET_TYPE_LINE);
126 + d->rd_alloc_efficiency = rrddim_add(d->st_alloc_efficiency, "percent", NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE);
127 +}
128 +
129 +static int init_devices(DICTIONARY *devices, unsigned int zram_id, int update_every) {
130 + int count = 0;
131 + DIR *dir = opendir("/dev");
132 + struct dirent *de;
133 + struct stat st;
134 + char filename[FILENAME_MAX + 1];
135 + procfile *ff = NULL;
136 + ZRAM_DEVICE device;
137 +
138 + if (unlikely(!dir))
139 + return 0;
140 + while ((de = readdir(dir)))
141 + {
142 + snprintfz(filename, FILENAME_MAX, "/dev/%s", de->d_name);
143 + if (unlikely(stat(filename, &st) != 0))
144 + {
145 + error("ZRAM : Unable to stat %s: %s", filename, strerror(errno));
146 + continue;
147 + }
148 + if (major(st.st_rdev) == zram_id)
149 + {
150 + info("ZRAM : Found device %s", filename);
151 + snprintfz(filename, FILENAME_MAX, "/sys/block/%s/mm_stat", de->d_name);
152 + ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
153 + if (ff == NULL)
154 + {
155 + error("ZRAM : Failed to open %s: %s", filename, strerror(errno));
156 + continue;
157 + }
158 + device.file = ff;
159 + init_rrd(de->d_name, &device, update_every);
160 + dictionary_set(devices, de->d_name, &device, sizeof(ZRAM_DEVICE));
161 + count++;
162 + }
163 + }
164 + closedir(dir);
165 + return count;
166 +}
167 +
168 +static void free_device(DICTIONARY *dict, char *name)
169 +{
170 + ZRAM_DEVICE *d = (ZRAM_DEVICE*)dictionary_get(dict, name);
171 + info("ZRAM : Disabling monitoring of device %s", name);
172 + rrdset_obsolete_and_pointer_null(d->st_usage);
173 + rrdset_obsolete_and_pointer_null(d->st_savings);
174 + rrdset_obsolete_and_pointer_null(d->st_alloc_efficiency);
175 + rrdset_obsolete_and_pointer_null(d->st_comp_ratio);
176 + dictionary_del(dict, name);
177 +}
178 + // --------------------------------------------------------------------
179 +
180 +static inline int read_mm_stat(procfile *ff, MM_STAT *stats) {
181 + ff = procfile_readall(ff);
182 + if (!ff)
183 + return -1;
184 + if (procfile_lines(ff) < 1)
185 + return -1;
186 + if (procfile_linewords(ff, 0) < 7)
187 + return -1;
188 +
189 + stats->orig_data_size = str2ull(procfile_word(ff, 0));
190 + stats->compr_data_size = str2ull(procfile_word(ff, 1));
191 + stats->mem_used_total = str2ull(procfile_word(ff, 2));
192 + stats->mem_limit = str2ull(procfile_word(ff, 3));
193 + stats->mem_used_max = str2ull(procfile_word(ff, 4));
194 + stats->same_pages = str2ull(procfile_word(ff, 5));
195 + stats->pages_compacted = str2ull(procfile_word(ff, 6));
196 + return 0;
197 +}
198 +
199 +static inline int _collect_zram_metrics(char* name, ZRAM_DEVICE *d, int advance, DICTIONARY* dict) {
200 + MM_STAT mm;
201 + int value;
202 + if (unlikely(read_mm_stat(d->file, &mm) < 0))
203 + {
204 + free_device(dict, name);
205 + return -1;
206 + }
207 +
208 + if (likely(advance))
209 + {
210 + rrdset_next(d->st_usage);
211 + rrdset_next(d->st_savings);
212 + rrdset_next(d->st_comp_ratio);
213 + rrdset_next(d->st_alloc_efficiency);
214 + }
215 + // zram_usage
216 + rrddim_set_by_pointer(d->st_usage, d->rd_compr_data_size, mm.compr_data_size);
217 + rrddim_set_by_pointer(d->st_usage, d->rd_metadata_size, mm.mem_used_total - mm.compr_data_size);
218 + rrdset_done(d->st_usage);
219 + // zram_savings
220 + rrddim_set_by_pointer(d->st_savings, d->rd_savings_size, mm.compr_data_size - mm.orig_data_size);
221 + rrddim_set_by_pointer(d->st_savings, d->rd_original_size, mm.orig_data_size);
222 + rrdset_done(d->st_savings);
223 + // zram_ratio
224 + value = mm.compr_data_size == 0 ? 1 : mm.orig_data_size * 100 / mm.compr_data_size;
225 + rrddim_set_by_pointer(d->st_comp_ratio, d->rd_comp_ratio, value);
226 + rrdset_done(d->st_comp_ratio);
227 + // zram_efficiency
228 + value = mm.mem_used_total == 0 ? 100 : (mm.compr_data_size * 1000000 / mm.mem_used_total);
229 + rrddim_set_by_pointer(d->st_alloc_efficiency, d->rd_alloc_efficiency, value);
230 + rrdset_done(d->st_alloc_efficiency);
231 + return 0;
232 +}
233 +
234 +static int collect_first_zram_metrics(char *name, void *entry, void *data) {
235 + // collect without calling rrdset_next (init only)
236 + return _collect_zram_metrics(name, (ZRAM_DEVICE *)entry, 0, (DICTIONARY *)data);
237 +}
238 +
239 +static int collect_zram_metrics(char *name, void *entry, void *data) {
240 + (void)name;
241 + // collect with calling rrdset_next
242 + return _collect_zram_metrics(name, (ZRAM_DEVICE *)entry, 1, (DICTIONARY *)data);
243 +}
244 +
245 + // --------------------------------------------------------------------
246 +
247 +int do_sys_block_zram(int update_every, usec_t dt) {
248 + (void)dt;
249 + static procfile *ff = NULL;
250 + static DICTIONARY *devices = NULL;
251 + static int initialized = 0;
252 + static int device_count = 0;
253 + int zram_id = -1;
254 + if (unlikely(!initialized))
255 + {
256 + initialized = 1;
257 + ff = procfile_open("/proc/devices", " \t:", PROCFILE_FLAG_DEFAULT);
258 + if (ff == NULL)
259 + {
260 + error("Cannot read /proc/devices");
261 + return 1;
262 + }
263 + ff = procfile_readall(ff);
264 + if (!ff)
265 + return 1;
266 + zram_id = try_get_zram_major_number(ff);
267 + if (zram_id == -1)
268 + {
269 + if (ff != NULL)
270 + procfile_close(ff);
271 + return 1;
272 + }
273 + procfile_close(ff);
274 +
275 + devices = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
276 + device_count = init_devices(devices, (unsigned int)zram_id, update_every);
277 + if (device_count < 1)
278 + return 1;
279 + dictionary_get_all_name_value(devices, collect_first_zram_metrics, devices);
280 + }
281 + else
282 + {
283 + if (unlikely(device_count < 1))
284 + return 1;
285 + dictionary_get_all_name_value(devices, collect_zram_metrics, devices);
286 + }
287 + return 0;
288 +}
\ No newline at end of file
web/gui/dashboard_info.js
+26
@@ -832,6 +832,32 @@ netdataDashboard.context = {
832 ]
833 },
834
835 + 'mem.zram_usage': {
836 + 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.'
837 + },
838 +
839 + 'mem.zram_savings': {
840 + info: 'Displays original and compressed memory data sizes.'
841 + },
842 +
843 + 'mem.zram_ratio': {
844 + heads: [
845 + netdataDashboard.gaugeChart('Compression Ratio', '12%', 'ratio', '#0099CC')
846 + ],
847 + info: 'Compression ratio, calculated as <code>100 * original_size / compressed_size</code>. More means better compression and more RAM savings.'
848 + },
849 +
850 + 'mem.zram_efficiency': {
851 + heads: [
852 + netdataDashboard.gaugeChart('Efficiency', '12%', 'percent', NETDATA.colors[0])
853 + ],
854 + commonMin: true,
855 + commonMax: true,
856 + valueRange: "[0, 100]",
857 + info: 'Memory usage efficiency, calculated as <code>100 * compressed_size / total_mem_used</code>.'
858 + },
859 +
860 +
861 'mem.pgfaults': {
862 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.'
863 },