master
c 298 lines 10.8 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "plugin_proc.h"
4
5 struct node {
6 char *name;
7
8 struct {
9 char *filename;
10 procfile *ff;
11 RRDSET *st;
12 } numastat;
13
14 struct {
15 char *filename;
16 procfile *ff;
17 RRDSET *st_mem_usage;
18 RRDSET *st_mem_activity;
19 } meminfo;
20
21 struct node *next;
22 };
23 static struct node *numa_root = NULL;
24 static int numa_node_count = 0;
25
26 static int find_all_nodes() {
27 int numa_node_count = 0;
28 char name[FILENAME_MAX + 1];
29 snprintfz(name, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/system/node");
30 const char *dirname = inicfg_get(&netdata_config, "plugin:proc:/sys/devices/system/node", "directory to monitor", name);
31
32 DIR *dir = opendir(dirname);
33 if(!dir) {
34 nd_log(
35 NDLS_COLLECTORS, errno == ENOENT ? NDLP_INFO : NDLP_ERR, "Cannot read NUMA node directory '%s'", dirname);
36 return 0;
37 }
38
39 struct dirent *de = NULL;
40 while((de = readdir(dir))) {
41 if(de->d_type != DT_DIR)
42 continue;
43
44 if(strncmp(de->d_name, "node", 4) != 0)
45 continue;
46
47 if(!isdigit(de->d_name[4]))
48 continue;
49
50 numa_node_count++;
51
52 struct node *m = callocz(1, sizeof(struct node));
53 m->name = strdupz(de->d_name);
54
55 struct stat st;
56
57 snprintfz(name, FILENAME_MAX, "%s/%s/numastat", dirname, de->d_name);
58 if(stat(name, &st) == -1) {
59 freez(m->name);
60 freez(m);
61 continue;
62 }
63 m->numastat.filename = strdupz(name);
64
65 snprintfz(name, FILENAME_MAX, "%s/%s/meminfo", dirname, de->d_name);
66 if(stat(name, &st) == -1) {
67 freez(m->numastat.filename);
68 freez(m->name);
69 freez(m);
70 continue;
71 }
72 m->meminfo.filename = strdupz(name);
73
74 m->next = numa_root;
75 numa_root = m;
76 }
77
78 closedir(dir);
79
80 return numa_node_count;
81 }
82
83 static void do_muma_numastat(struct node *m, int update_every) {
84 static uint32_t hash_local_node = 0, hash_numa_foreign = 0, hash_interleave_hit = 0, hash_other_node = 0, hash_numa_hit = 0, hash_numa_miss = 0;
85 static bool initialized = false;
86
87 if(unlikely(!initialized)) {
88 initialized = true;
89 hash_local_node = simple_hash("local_node");
90 hash_numa_foreign = simple_hash("numa_foreign");
91 hash_interleave_hit = simple_hash("interleave_hit");
92 hash_other_node = simple_hash("other_node");
93 hash_numa_hit = simple_hash("numa_hit");
94 hash_numa_miss = simple_hash("numa_miss");
95 }
96
97 if (m->numastat.filename) {
98 if(unlikely(!m->numastat.ff)) {
99 m->numastat.ff = procfile_open(m->numastat.filename, " ", PROCFILE_FLAG_DEFAULT);
100
101 if(unlikely(!m->numastat.ff))
102 return;
103 }
104
105 m->numastat.ff = procfile_readall(m->numastat.ff);
106 if(unlikely(!m->numastat.ff || procfile_lines(m->numastat.ff) < 1 || procfile_linewords(m->numastat.ff, 0) < 1))
107 return;
108
109 if(unlikely(!m->numastat.st)) {
110 m->numastat.st = rrdset_create_localhost(
111 "numa_node_stat"
112 , m->name
113 , NULL
114 , "numa"
115 , "mem.numa_node_stat"
116 , "NUMA Node Memory Allocation Events"
117 , "events/s"
118 , PLUGIN_PROC_NAME
119 , "/sys/devices/system/node"
120 , NETDATA_CHART_PRIO_MEM_NUMA_NODES_NUMASTAT
121 , update_every
122 , RRDSET_TYPE_LINE
123 );
124
125 rrdlabels_add(m->numastat.st->rrdlabels, "numa_node", m->name, RRDLABEL_SRC_AUTO);
126
127 rrddim_add(m->numastat.st, "numa_hit", "hit", 1, 1, RRD_ALGORITHM_INCREMENTAL);
128 rrddim_add(m->numastat.st, "numa_miss", "miss", 1, 1, RRD_ALGORITHM_INCREMENTAL);
129 rrddim_add(m->numastat.st, "local_node", "local", 1, 1, RRD_ALGORITHM_INCREMENTAL);
130 rrddim_add(m->numastat.st, "numa_foreign", "foreign", 1, 1, RRD_ALGORITHM_INCREMENTAL);
131 rrddim_add(m->numastat.st, "interleave_hit", "interleave", 1, 1, RRD_ALGORITHM_INCREMENTAL);
132 rrddim_add(m->numastat.st, "other_node", "other", 1, 1, RRD_ALGORITHM_INCREMENTAL);
133
134 }
135
136 size_t lines = procfile_lines(m->numastat.ff), l;
137 for(l = 0; l < lines; l++) {
138 size_t words = procfile_linewords(m->numastat.ff, l);
139
140 if(unlikely(words < 2)) {
141 if(unlikely(words))
142 collector_error("Cannot read %s line %zu. Expected 2 params, read %zu.", m->numastat.filename, l, words);
143 continue;
144 }
145
146 char *name = procfile_lineword(m->numastat.ff, l, 0);
147 char *value = procfile_lineword(m->numastat.ff, l, 1);
148
149 if (unlikely(!name || !*name || !value || !*value))
150 continue;
151
152 uint32_t hash = simple_hash(name);
153
154 if ((hash == hash_numa_hit && !strcmp(name, "numa_hit")) ||
155 (hash == hash_numa_miss && !strcmp(name, "numa_miss")) ||
156 (hash == hash_local_node && !strcmp(name, "local_node")) ||
157 (hash == hash_numa_foreign && !strcmp(name, "numa_foreign")) ||
158 (hash == hash_interleave_hit && !strcmp(name, "interleave_hit")) ||
159 (hash == hash_other_node && !strcmp(name, "other_node"))) {
160 rrddim_set(m->numastat.st, name, (collected_number)str2kernel_uint_t(value));
161 }
162 }
163
164 rrdset_done(m->numastat.st);
165 }
166 }
167
168 static void do_numa_meminfo(struct node *m, int update_every) {
169 static uint32_t hash_MemFree = 0, hash_MemUsed = 0, hash_ActiveAnon = 0, hash_InactiveAnon = 0, hash_ActiveFile = 0,
170 hash_InactiveFile = 0;
171 static bool initialized = false;
172
173 if (unlikely(!initialized)) {
174 initialized = true;
175 hash_MemFree = simple_hash("MemFree");
176 hash_MemUsed = simple_hash("MemUsed");
177 hash_ActiveAnon = simple_hash("Active(anon)");
178 hash_InactiveAnon = simple_hash("Inactive(anon)");
179 hash_ActiveFile = simple_hash("Active(file)");
180 hash_InactiveFile = simple_hash("Inactive(file)");
181 }
182
183 if (m->meminfo.filename) {
184 if (unlikely(!m->meminfo.ff)) {
185 m->meminfo.ff = procfile_open(m->meminfo.filename, " :", PROCFILE_FLAG_DEFAULT);
186 if (unlikely(!m->meminfo.ff))
187 return;
188 }
189
190 m->meminfo.ff = procfile_readall(m->meminfo.ff);
191 if (unlikely(!m->meminfo.ff || procfile_lines(m->meminfo.ff) < 1 || procfile_linewords(m->meminfo.ff, 0) < 1))
192 return;
193
194 if (unlikely(!m->meminfo.st_mem_usage)) {
195 m->meminfo.st_mem_usage = rrdset_create_localhost(
196 "numa_node_mem_usage",
197 m->name,
198 NULL,
199 "numa",
200 "mem.numa_node_mem_usage",
201 "NUMA Node Memory Usage",
202 "bytes",
203 PLUGIN_PROC_NAME,
204 "/sys/devices/system/node",
205 NETDATA_CHART_PRIO_MEM_NUMA_NODES_MEMINFO,
206 update_every,
207 RRDSET_TYPE_STACKED);
208
209 rrdlabels_add(m->meminfo.st_mem_usage->rrdlabels, "numa_node", m->name, RRDLABEL_SRC_AUTO);
210
211 rrddim_add(m->meminfo.st_mem_usage, "MemFree", "free", 1, 1, RRD_ALGORITHM_ABSOLUTE);
212 rrddim_add(m->meminfo.st_mem_usage, "MemUsed", "used", 1, 1, RRD_ALGORITHM_ABSOLUTE);
213 }
214 if (unlikely(!m->meminfo.st_mem_activity)) {
215 m->meminfo.st_mem_activity = rrdset_create_localhost(
216 "numa_node_mem_activity",
217 m->name,
218 NULL,
219 "numa",
220 "mem.numa_node_mem_activity",
221 "NUMA Node Memory Activity",
222 "bytes",
223 PLUGIN_PROC_NAME,
224 "/sys/devices/system/node",
225 NETDATA_CHART_PRIO_MEM_NUMA_NODES_ACTIVITY,
226 update_every,
227 RRDSET_TYPE_STACKED);
228
229 rrdlabels_add(m->meminfo.st_mem_activity->rrdlabels, "numa_node", m->name, RRDLABEL_SRC_AUTO);
230
231 rrddim_add(m->meminfo.st_mem_activity, "Active(anon)", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
232 rrddim_add(m->meminfo.st_mem_activity, "Inactive(anon)", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
233 rrddim_add(m->meminfo.st_mem_activity, "Active(file)", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
234 rrddim_add(m->meminfo.st_mem_activity, "Inactive(file)", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
235 }
236
237 size_t lines = procfile_lines(m->meminfo.ff), l;
238 for (l = 0; l < lines; l++) {
239 size_t words = procfile_linewords(m->meminfo.ff, l);
240
241 if (unlikely(words < 4)) {
242 if (words)
243 collector_error(
244 "Cannot read %s line %zu. Expected 4 params, read %zu.", m->meminfo.filename, l, words);
245 continue;
246 }
247
248 char *name = procfile_lineword(m->meminfo.ff, l, 2);
249 char *value = procfile_lineword(m->meminfo.ff, l, 3);
250
251 if (unlikely(!name || !*name || !value || !*value))
252 continue;
253
254 uint32_t hash = simple_hash(name);
255
256 if ((hash == hash_MemFree && !strcmp(name, "MemFree")) ||
257 (hash == hash_MemUsed && !strcmp(name, "MemUsed"))) {
258 rrddim_set(m->meminfo.st_mem_usage, name, (collected_number)str2kernel_uint_t(value) * 1024);
259 } else if (
260 (hash == hash_ActiveAnon && !strcmp(name, "Active(anon)")) ||
261 (hash == hash_InactiveAnon && !strcmp(name, "Inactive(anon)")) ||
262 (hash == hash_ActiveFile && !strcmp(name, "Active(file)")) ||
263 (hash == hash_InactiveFile && !strcmp(name, "Inactive(file)"))) {
264 rrddim_set(m->meminfo.st_mem_activity, name, (collected_number)str2kernel_uint_t(value) * 1024);
265 }
266 }
267 rrdset_done(m->meminfo.st_mem_usage);
268 rrdset_done(m->meminfo.st_mem_activity);
269 }
270 }
271
272 int do_proc_sys_devices_system_node(int update_every, usec_t dt) {
273 (void)dt;
274 struct node *m;
275
276 static int do_numastat = -1;
277
278 if(unlikely(do_numastat == -1)) {
279 do_numastat = inicfg_get_boolean_ondemand(
280 &netdata_config, "plugin:proc:/sys/devices/system/node", "enable per-node numa metrics", CONFIG_BOOLEAN_AUTO);
281 }
282
283 if(unlikely(numa_root == NULL)) {
284 numa_node_count = find_all_nodes();
285 if(unlikely(numa_root == NULL))
286 return 1;
287 }
288
289 if (do_numastat == CONFIG_BOOLEAN_YES || (do_numastat == CONFIG_BOOLEAN_AUTO && numa_node_count >= 2)) {
290 for (m = numa_root; m; m = m->next) {
291 do_muma_numastat(m, update_every);
292 do_numa_meminfo(m, update_every);
293 }
294 return 0;
295 }
296
297 return 1;
298 }