master
c 1,020 lines 42.3 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "plugin_proc.h"
4
5 #define PLUGIN_PROC_MODULE_STAT_NAME "/proc/stat"
6
7 #define _COMMON_PLUGIN_NAME PLUGIN_PROC_NAME
8 #define _COMMON_PLUGIN_MODULE_NAME PLUGIN_PROC_MODULE_STAT_NAME
9 #include "../common-contexts/common-contexts.h"
10
11 struct per_core_single_number_file {
12 unsigned char found:1;
13 const char *filename;
14 int fd;
15 collected_number value;
16 RRDDIM *rd;
17 };
18
19 struct last_ticks {
20 collected_number frequency;
21 collected_number ticks;
22 };
23
24 // This is an extension of struct per_core_single_number_file at CPU_FREQ_INDEX.
25 // Either scaling_cur_freq or time_in_state file is used at one time.
26 struct per_core_time_in_state_file {
27 const char *filename;
28 procfile *ff;
29 size_t last_ticks_len;
30 struct last_ticks *last_ticks;
31 };
32
33 #define CORE_THROTTLE_COUNT_INDEX 0
34 #define PACKAGE_THROTTLE_COUNT_INDEX 1
35 #define CPU_FREQ_INDEX 2
36 #define PER_CORE_FILES 3
37
38 struct cpu_chart {
39 const char *id;
40
41 RRDSET *st;
42 RRDDIM *rd_user;
43 RRDDIM *rd_nice;
44 RRDDIM *rd_system;
45 RRDDIM *rd_idle;
46 RRDDIM *rd_iowait;
47 RRDDIM *rd_irq;
48 RRDDIM *rd_softirq;
49 RRDDIM *rd_steal;
50 RRDDIM *rd_guest;
51 RRDDIM *rd_guest_nice;
52
53 bool per_core_files_found;
54 struct per_core_single_number_file files[PER_CORE_FILES];
55
56 struct per_core_time_in_state_file time_in_state_files;
57 };
58
59 static int keep_per_core_fds_open = CONFIG_BOOLEAN_YES;
60 static int keep_cpuidle_fds_open = CONFIG_BOOLEAN_YES;
61
62 static int read_per_core_files(struct cpu_chart *all_cpu_charts, size_t len, size_t index) {
63 char buf[50 + 1];
64 size_t x, files_read = 0, files_nonzero = 0;
65
66 for(x = 0; x < len ; x++) {
67 struct per_core_single_number_file *f = &all_cpu_charts[x].files[index];
68
69 f->found = 0;
70
71 if(unlikely(!f->filename))
72 continue;
73
74 if(unlikely(f->fd == -1)) {
75 f->fd = open(f->filename, O_RDONLY | O_CLOEXEC);
76 if (unlikely(f->fd == -1)) {
77 collector_error("Cannot open file '%s'", f->filename);
78 continue;
79 }
80 }
81
82 ssize_t ret = read(f->fd, buf, 50);
83 if(unlikely(ret < 0)) {
84 // cannot read that file
85
86 collector_error("Cannot read file '%s'", f->filename);
87 close(f->fd);
88 f->fd = -1;
89 continue;
90 }
91 else {
92 // successful read
93
94 // terminate the buffer
95 buf[ret] = '\0';
96
97 if(unlikely(keep_per_core_fds_open != CONFIG_BOOLEAN_YES)) {
98 close(f->fd);
99 f->fd = -1;
100 }
101 else if(lseek(f->fd, 0, SEEK_SET) == -1) {
102 collector_error("Cannot seek in file '%s'", f->filename);
103 close(f->fd);
104 f->fd = -1;
105 }
106 }
107
108 files_read++;
109 f->found = 1;
110
111 f->value = str2ll(buf, NULL);
112 if(likely(f->value != 0))
113 files_nonzero++;
114 }
115
116 if(files_read == 0)
117 return -1;
118
119 if(files_nonzero == 0)
120 return 0;
121
122 return (int)files_nonzero;
123 }
124
125 static int read_per_core_time_in_state_files(struct cpu_chart *all_cpu_charts, size_t len, size_t index) {
126 size_t x, files_read = 0, files_nonzero = 0;
127
128 for(x = 0; x < len ; x++) {
129 struct per_core_single_number_file *f = &all_cpu_charts[x].files[index];
130 struct per_core_time_in_state_file *tsf = &all_cpu_charts[x].time_in_state_files;
131
132 f->found = 0;
133
134 if(unlikely(!tsf->filename))
135 continue;
136
137 if(unlikely(!tsf->ff)) {
138 tsf->ff = procfile_open(tsf->filename, " \t:", PROCFILE_FLAG_DEFAULT);
139 if(unlikely(!tsf->ff))
140 {
141 collector_error("Cannot open file '%s'", tsf->filename);
142 continue;
143 }
144 }
145
146 tsf->ff = procfile_readall(tsf->ff);
147 if(unlikely(!tsf->ff)) {
148 collector_error("Cannot read file '%s'", tsf->filename);
149 procfile_close(tsf->ff);
150 tsf->ff = NULL;
151 continue;
152 }
153 else {
154 // successful read
155
156 size_t lines = procfile_lines(tsf->ff), l;
157 size_t words;
158 unsigned long long total_ticks_since_last = 0, avg_freq = 0;
159
160 // Check if there is at least one frequency in time_in_state
161 if (procfile_word(tsf->ff, 0)[0] == '\0') {
162 if(unlikely(keep_per_core_fds_open != CONFIG_BOOLEAN_YES)) {
163 procfile_close(tsf->ff);
164 tsf->ff = NULL;
165 }
166 // TODO: Is there a better way to avoid spikes than calculating the average over
167 // the whole period under schedutil governor?
168 // freez(tsf->last_ticks);
169 // tsf->last_ticks = NULL;
170 // tsf->last_ticks_len = 0;
171 continue;
172 }
173
174 if (unlikely(tsf->last_ticks_len < lines || tsf->last_ticks == NULL)) {
175 tsf->last_ticks = reallocz(tsf->last_ticks, sizeof(struct last_ticks) * lines);
176 memset(tsf->last_ticks, 0, sizeof(struct last_ticks) * lines);
177 tsf->last_ticks_len = lines;
178 }
179
180 f->value = 0;
181
182 for(l = 0; l < lines - 1 ;l++) {
183 unsigned long long frequency = 0, ticks = 0, ticks_since_last = 0;
184
185 words = procfile_linewords(tsf->ff, l);
186 if(unlikely(words < 2)) {
187 collector_error("Cannot read time_in_state line. Expected 2 params, read %zu.", words);
188 continue;
189 }
190 frequency = str2ull(procfile_lineword(tsf->ff, l, 0), NULL);
191 ticks = str2ull(procfile_lineword(tsf->ff, l, 1), NULL);
192
193 // It is assumed that frequencies are static and sorted
194 ticks_since_last = ticks - tsf->last_ticks[l].ticks;
195 tsf->last_ticks[l].frequency = frequency;
196 tsf->last_ticks[l].ticks = ticks;
197
198 total_ticks_since_last += ticks_since_last;
199 avg_freq += frequency * ticks_since_last;
200
201 }
202
203 if (likely(total_ticks_since_last)) {
204 avg_freq /= total_ticks_since_last;
205 f->value = avg_freq;
206 }
207
208 if(unlikely(keep_per_core_fds_open != CONFIG_BOOLEAN_YES)) {
209 procfile_close(tsf->ff);
210 tsf->ff = NULL;
211 }
212 }
213
214 files_read++;
215
216 f->found = 1;
217
218 if(likely(f->value != 0))
219 files_nonzero++;
220 }
221
222 if(unlikely(files_read == 0))
223 return -1;
224
225 if(unlikely(files_nonzero == 0))
226 return 0;
227
228 return (int)files_nonzero;
229 }
230
231 static void chart_per_core_files(struct cpu_chart *all_cpu_charts, size_t len, size_t index, RRDSET *st, collected_number multiplier, collected_number divisor, RRD_ALGORITHM algorithm) {
232 size_t x;
233 for(x = 0; x < len ; x++) {
234 struct per_core_single_number_file *f = &all_cpu_charts[x].files[index];
235
236 if(unlikely(!f->found))
237 continue;
238
239 if(unlikely(!f->rd))
240 f->rd = rrddim_add(st, all_cpu_charts[x].id, NULL, multiplier, divisor, algorithm);
241
242 rrddim_set_by_pointer(st, f->rd, f->value);
243 }
244 }
245
246 struct cpuidle_state {
247 char *name;
248
249 char *time_filename;
250 int time_fd;
251
252 collected_number value;
253
254 RRDDIM *rd;
255 };
256
257 struct per_core_cpuidle_chart {
258 RRDSET *st;
259
260 RRDDIM *active_time_rd;
261 collected_number active_time;
262 collected_number last_active_time;
263
264 struct cpuidle_state *cpuidle_state;
265 size_t cpuidle_state_len;
266 int rescan_cpu_states;
267 };
268
269 static void* wake_cpu_thread(void* core) {
270 pthread_t thread;
271 cpu_set_t cpu_set;
272 static size_t cpu_wakeups = 0;
273 static int errors = 0;
274
275 CPU_ZERO(&cpu_set);
276 CPU_SET(*(int*)core, &cpu_set);
277
278 thread = pthread_self();
279 if(unlikely(pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpu_set))) {
280 if(unlikely(errors < 8)) {
281 collector_error("Cannot set CPU affinity for core %d", *(int*)core);
282 errors++;
283 }
284 else if(unlikely(errors < 9)) {
285 collector_error("CPU affinity errors are disabled");
286 errors++;
287 }
288 }
289
290 // Make the CPU core do something to force it to update its idle counters
291 cpu_wakeups++;
292
293 return 0;
294 }
295
296 static int read_schedstat(const char *schedstat_filename, struct per_core_cpuidle_chart **cpuidle_charts_address, size_t *schedstat_cores_found) {
297 static size_t cpuidle_charts_len = 0;
298 static procfile *ff = NULL;
299 struct per_core_cpuidle_chart *cpuidle_charts = *cpuidle_charts_address;
300 size_t cores_found = 0;
301
302 if(unlikely(!ff)) {
303 ff = procfile_open(schedstat_filename, " \t:", PROCFILE_FLAG_DEFAULT);
304 if(unlikely(!ff)) return 1;
305 }
306
307 ff = procfile_readall(ff);
308 if(unlikely(!ff)) return 1;
309
310 size_t lines = procfile_lines(ff), l;
311 size_t words;
312
313 for(l = 0; l < lines ;l++) {
314 char *row_key = procfile_lineword(ff, l, 0);
315
316 // faster strncmp(row_key, "cpu", 3) == 0
317 if(likely(row_key[0] == 'c' && row_key[1] == 'p' && row_key[2] == 'u')) {
318 words = procfile_linewords(ff, l);
319 if(unlikely(words < 10)) {
320 collector_error("Cannot read /proc/schedstat cpu line. Expected 9 params, read %zu.", words);
321 return 1;
322 }
323 cores_found++;
324
325 size_t core = str2ul(&row_key[3]);
326 if(unlikely(core >= cores_found)) {
327 collector_error("Core %zu found but no more than %zu cores were expected.", core, cores_found);
328 return 1;
329 }
330
331 if(unlikely(cpuidle_charts_len < cores_found)) {
332 cpuidle_charts = reallocz(cpuidle_charts, sizeof(struct per_core_cpuidle_chart) * cores_found);
333 *cpuidle_charts_address = cpuidle_charts;
334 memset(cpuidle_charts + cpuidle_charts_len, 0, sizeof(struct per_core_cpuidle_chart) * (cores_found - cpuidle_charts_len));
335 cpuidle_charts_len = cores_found;
336 }
337
338 cpuidle_charts[core].active_time = str2ull(procfile_lineword(ff, l, 7), NULL) / 1000;
339 }
340 }
341
342 *schedstat_cores_found = cores_found;
343 return 0;
344 }
345
346 static int read_one_state(char *buf, const char *filename, int *fd) {
347 ssize_t ret = read(*fd, buf, 50);
348
349 if(unlikely(ret <= 0)) {
350 // cannot read that file
351 collector_error("Cannot read file '%s'", filename);
352 close(*fd);
353 *fd = -1;
354 return 0;
355 }
356 else {
357 // successful read
358
359 // terminate the buffer
360 buf[ret - 1] = '\0';
361
362 if(unlikely(keep_cpuidle_fds_open != CONFIG_BOOLEAN_YES)) {
363 close(*fd);
364 *fd = -1;
365 }
366 else if(lseek(*fd, 0, SEEK_SET) == -1) {
367 collector_error("Cannot seek in file '%s'", filename);
368 close(*fd);
369 *fd = -1;
370 }
371 }
372
373 return 1;
374 }
375
376 static int read_cpuidle_states(const char *cpuidle_name_filename, const char *cpuidle_time_filename, struct per_core_cpuidle_chart *cpuidle_charts, size_t core) {
377 char filename[FILENAME_MAX + 1];
378 static char next_state_filename[FILENAME_MAX + 1];
379 struct stat stbuf;
380 struct per_core_cpuidle_chart *cc = &cpuidle_charts[core];
381 size_t state;
382
383 if(unlikely(!cc->cpuidle_state_len || cc->rescan_cpu_states)) {
384 int state_file_found = 1; // check at least one state
385
386 if(cc->cpuidle_state_len) {
387 for(state = 0; state < cc->cpuidle_state_len; state++) {
388 freez(cc->cpuidle_state[state].name);
389
390 freez(cc->cpuidle_state[state].time_filename);
391 close(cc->cpuidle_state[state].time_fd);
392 cc->cpuidle_state[state].time_fd = -1;
393 }
394
395 freez(cc->cpuidle_state);
396 cc->cpuidle_state = NULL;
397 cc->cpuidle_state_len = 0;
398
399 cc->active_time_rd = NULL;
400 cc->st = NULL;
401 }
402
403 while(likely(state_file_found)) {
404 snprintfz(filename, FILENAME_MAX, cpuidle_name_filename, core, cc->cpuidle_state_len);
405 if (stat(filename, &stbuf) == 0)
406 cc->cpuidle_state_len++;
407 else
408 state_file_found = 0;
409 }
410 snprintfz(next_state_filename, FILENAME_MAX, cpuidle_name_filename, core, cc->cpuidle_state_len);
411
412 if(likely(cc->cpuidle_state_len))
413 cc->cpuidle_state = callocz(cc->cpuidle_state_len, sizeof(struct cpuidle_state));
414
415 for(state = 0; state < cc->cpuidle_state_len; state++) {
416 char name_buf[50 + 1];
417 snprintfz(filename, FILENAME_MAX, cpuidle_name_filename, core, state);
418
419 int fd = open(filename, O_RDONLY | O_CLOEXEC, 0666);
420 if(unlikely(fd == -1)) {
421 collector_error("Cannot open file '%s'", filename);
422 cc->rescan_cpu_states = 1;
423 return 1;
424 }
425
426 ssize_t r = read(fd, name_buf, 50);
427 if(unlikely(r < 1)) {
428 collector_error("Cannot read file '%s'", filename);
429 close(fd);
430 cc->rescan_cpu_states = 1;
431 return 1;
432 }
433
434 name_buf[r - 1] = '\0'; // erase extra character
435 cc->cpuidle_state[state].name = strdupz(trim(name_buf));
436 close(fd);
437
438 snprintfz(filename, FILENAME_MAX, cpuidle_time_filename, core, state);
439 cc->cpuidle_state[state].time_filename = strdupz(filename);
440 cc->cpuidle_state[state].time_fd = -1;
441 }
442
443 cc->rescan_cpu_states = 0;
444 }
445
446 for(state = 0; state < cc->cpuidle_state_len; state++) {
447
448 struct cpuidle_state *cs = &cc->cpuidle_state[state];
449
450 if(unlikely(cs->time_fd == -1)) {
451 cs->time_fd = open(cs->time_filename, O_RDONLY | O_CLOEXEC);
452 if (unlikely(cs->time_fd == -1)) {
453 collector_error("Cannot open file '%s'", cs->time_filename);
454 cc->rescan_cpu_states = 1;
455 return 1;
456 }
457 }
458
459 char time_buf[50 + 1];
460 if(likely(read_one_state(time_buf, cs->time_filename, &cs->time_fd))) {
461 cs->value = str2ll(time_buf, NULL);
462 }
463 else {
464 cc->rescan_cpu_states = 1;
465 return 1;
466 }
467 }
468
469 // check if the number of states was increased
470 if(unlikely(stat(next_state_filename, &stbuf) == 0)) {
471 cc->rescan_cpu_states = 1;
472 return 1;
473 }
474
475 return 0;
476 }
477
478 static const RRDVAR_ACQUIRED *cpus_var = NULL;
479
480 void proc_stat_plugin_cleanup(void) {
481 // Cleanup any acquired RRDVARs
482 if (cpus_var) {
483 rrdvar_host_variable_release(localhost, cpus_var);
484 cpus_var = NULL;
485 }
486 }
487
488 int do_proc_stat(int update_every, usec_t dt) {
489 (void)dt;
490
491 static struct cpu_chart *all_cpu_charts = NULL;
492 static size_t all_cpu_charts_size = 0;
493 static procfile *ff = NULL;
494 static int do_cpu = -1, do_cpu_cores = -1, do_interrupts = -1, do_context = -1, do_forks = -1, do_processes = -1,
495 do_core_throttle_count = -1, do_package_throttle_count = -1, do_cpu_freq = -1, do_cpuidle = -1;
496 static uint32_t hash_intr, hash_ctxt, hash_processes, hash_procs_running, hash_procs_blocked;
497 static const char *core_throttle_count_filename = NULL, *package_throttle_count_filename = NULL, *scaling_cur_freq_filename = NULL,
498 *time_in_state_filename = NULL, *schedstat_filename = NULL, *cpuidle_name_filename = NULL, *cpuidle_time_filename = NULL;
499
500 static int accurate_freq_avail = 0, accurate_freq_is_used = 0;
501 size_t cores_found = (size_t)os_get_system_cpus();
502
503 if(unlikely(do_cpu == -1)) {
504 do_cpu = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/stat", "cpu utilization", CONFIG_BOOLEAN_YES);
505 do_cpu_cores = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/stat", "per cpu core utilization", CONFIG_BOOLEAN_NO);
506 do_interrupts = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/stat", "cpu interrupts", CONFIG_BOOLEAN_YES);
507 do_context = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/stat", "context switches", CONFIG_BOOLEAN_YES);
508 do_forks = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/stat", "processes started", CONFIG_BOOLEAN_YES);
509 do_processes = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/stat", "processes running", CONFIG_BOOLEAN_YES);
510
511 // give sane defaults based on the number of processors
512 if(unlikely(os_get_system_cpus() > 128)) {
513 // the system has too many processors
514 keep_per_core_fds_open = CONFIG_BOOLEAN_NO;
515 do_core_throttle_count = CONFIG_BOOLEAN_NO;
516 do_package_throttle_count = CONFIG_BOOLEAN_NO;
517 do_cpu_freq = CONFIG_BOOLEAN_NO;
518 do_cpuidle = CONFIG_BOOLEAN_NO;
519 }
520 else {
521 // the system has a reasonable number of processors
522 keep_per_core_fds_open = CONFIG_BOOLEAN_YES;
523 do_core_throttle_count = CONFIG_BOOLEAN_AUTO;
524 do_package_throttle_count = CONFIG_BOOLEAN_NO;
525 do_cpu_freq = CONFIG_BOOLEAN_YES;
526 do_cpuidle = CONFIG_BOOLEAN_NO;
527 }
528 if(unlikely(os_get_system_cpus() > 24)) {
529 // the system has too many processors
530 keep_cpuidle_fds_open = CONFIG_BOOLEAN_NO;
531 }
532 else {
533 // the system has a reasonable number of processors
534 keep_cpuidle_fds_open = CONFIG_BOOLEAN_YES;
535 }
536
537 keep_per_core_fds_open = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/stat", "keep per core files open", keep_per_core_fds_open);
538 keep_cpuidle_fds_open = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/stat", "keep cpuidle files open", keep_cpuidle_fds_open);
539 do_core_throttle_count = inicfg_get_boolean_ondemand(&netdata_config, "plugin:proc:/proc/stat", "core_throttle_count", do_core_throttle_count);
540 do_package_throttle_count = inicfg_get_boolean_ondemand(&netdata_config, "plugin:proc:/proc/stat", "package_throttle_count", do_package_throttle_count);
541 do_cpu_freq = inicfg_get_boolean_ondemand(&netdata_config, "plugin:proc:/proc/stat", "cpu frequency", do_cpu_freq);
542 do_cpuidle = inicfg_get_boolean_ondemand(&netdata_config, "plugin:proc:/proc/stat", "cpu idle states", do_cpuidle);
543
544 hash_intr = simple_hash("intr");
545 hash_ctxt = simple_hash("ctxt");
546 hash_processes = simple_hash("processes");
547 hash_procs_running = simple_hash("procs_running");
548 hash_procs_blocked = simple_hash("procs_blocked");
549
550 char filename[FILENAME_MAX + 1];
551 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/system/cpu/%s/thermal_throttle/core_throttle_count");
552 core_throttle_count_filename = inicfg_get(&netdata_config, "plugin:proc:/proc/stat", "core_throttle_count filename to monitor", filename);
553
554 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/system/cpu/%s/thermal_throttle/package_throttle_count");
555 package_throttle_count_filename = inicfg_get(&netdata_config, "plugin:proc:/proc/stat", "package_throttle_count filename to monitor", filename);
556
557 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/system/cpu/%s/cpufreq/scaling_cur_freq");
558 scaling_cur_freq_filename = inicfg_get(&netdata_config, "plugin:proc:/proc/stat", "scaling_cur_freq filename to monitor", filename);
559
560 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/system/cpu/%s/cpufreq/stats/time_in_state");
561 time_in_state_filename = inicfg_get(&netdata_config, "plugin:proc:/proc/stat", "time_in_state filename to monitor", filename);
562
563 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/schedstat");
564 schedstat_filename = inicfg_get(&netdata_config, "plugin:proc:/proc/stat", "schedstat filename to monitor", filename);
565
566 if(do_cpuidle != CONFIG_BOOLEAN_NO) {
567 struct stat stbuf;
568
569 if (stat(schedstat_filename, &stbuf))
570 do_cpuidle = CONFIG_BOOLEAN_NO;
571 }
572
573 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/system/cpu/cpu%zu/cpuidle/state%zu/name");
574 cpuidle_name_filename = inicfg_get(&netdata_config, "plugin:proc:/proc/stat", "cpuidle name filename to monitor", filename);
575
576 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/system/cpu/cpu%zu/cpuidle/state%zu/time");
577 cpuidle_time_filename = inicfg_get(&netdata_config, "plugin:proc:/proc/stat", "cpuidle time filename to monitor", filename);
578 }
579
580 if(unlikely(!ff)) {
581 char filename[FILENAME_MAX + 1];
582 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/stat");
583 ff = procfile_open(inicfg_get(&netdata_config, "plugin:proc:/proc/stat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
584 if(unlikely(!ff)) return 1;
585 }
586
587 ff = procfile_readall(ff);
588 if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time
589
590 size_t lines = procfile_lines(ff), l;
591 size_t words;
592
593 unsigned long long processes = 0, running = 0 , blocked = 0;
594
595 for(l = 0; l < lines ;l++) {
596 char *row_key = procfile_lineword(ff, l, 0);
597 uint32_t hash = simple_hash(row_key);
598
599 // faster strncmp(row_key, "cpu", 3) == 0
600 if(likely(row_key[0] == 'c' && row_key[1] == 'p' && row_key[2] == 'u')) {
601 words = procfile_linewords(ff, l);
602 if(unlikely(words < 9)) {
603 collector_error("Cannot read /proc/stat cpu line. Expected 9 params, read %zu.", words);
604 continue;
605 }
606
607 size_t core = (row_key[3] == '\0') ? 0 : str2ul(&row_key[3]) + 1;
608 if (likely(core > 0))
609 cores_found = core;
610
611 bool do_any_core_metric = do_cpu_cores || do_core_throttle_count || do_cpu_freq || do_cpuidle;
612
613 if (likely((core == 0 && do_cpu) || (core > 0 && do_any_core_metric))) {
614 if (unlikely(core >= all_cpu_charts_size)) {
615 size_t old_cpu_charts_size = all_cpu_charts_size;
616 all_cpu_charts_size = core + 1;
617 all_cpu_charts = reallocz(all_cpu_charts, sizeof(struct cpu_chart) * all_cpu_charts_size);
618 memset(&all_cpu_charts[old_cpu_charts_size], 0, sizeof(struct cpu_chart) * (all_cpu_charts_size - old_cpu_charts_size));
619 }
620
621 struct cpu_chart *cpu_chart = &all_cpu_charts[core];
622
623 if (unlikely(!cpu_chart->id))
624 cpu_chart->id = strdupz(row_key);
625
626 if (core > 0 && !cpu_chart->per_core_files_found) {
627 cpu_chart->per_core_files_found = true;
628
629 char filename[FILENAME_MAX + 1];
630 struct stat stbuf;
631
632 if (do_core_throttle_count != CONFIG_BOOLEAN_NO) {
633 snprintfz(filename, FILENAME_MAX, core_throttle_count_filename, cpu_chart->id);
634 if (stat(filename, &stbuf) == 0) {
635 cpu_chart->files[CORE_THROTTLE_COUNT_INDEX].filename = strdupz(filename);
636 cpu_chart->files[CORE_THROTTLE_COUNT_INDEX].fd = -1;
637 do_core_throttle_count = CONFIG_BOOLEAN_YES;
638 }
639 }
640
641 if (do_package_throttle_count != CONFIG_BOOLEAN_NO) {
642 snprintfz(filename, FILENAME_MAX, package_throttle_count_filename, cpu_chart->id);
643 if (stat(filename, &stbuf) == 0) {
644 cpu_chart->files[PACKAGE_THROTTLE_COUNT_INDEX].filename = strdupz(filename);
645 cpu_chart->files[PACKAGE_THROTTLE_COUNT_INDEX].fd = -1;
646 do_package_throttle_count = CONFIG_BOOLEAN_YES;
647 }
648 }
649
650 if (do_cpu_freq != CONFIG_BOOLEAN_NO) {
651 snprintfz(filename, FILENAME_MAX, scaling_cur_freq_filename, cpu_chart->id);
652 if (stat(filename, &stbuf) == 0) {
653 cpu_chart->files[CPU_FREQ_INDEX].filename = strdupz(filename);
654 cpu_chart->files[CPU_FREQ_INDEX].fd = -1;
655 do_cpu_freq = CONFIG_BOOLEAN_YES;
656 }
657
658 snprintfz(filename, FILENAME_MAX, time_in_state_filename, cpu_chart->id);
659 if (stat(filename, &stbuf) == 0) {
660 cpu_chart->time_in_state_files.filename = strdupz(filename);
661 cpu_chart->time_in_state_files.ff = NULL;
662 do_cpu_freq = CONFIG_BOOLEAN_YES;
663 accurate_freq_avail = 1;
664 }
665 }
666 }
667 }
668
669 if(likely((core == 0 && do_cpu) || (core > 0 && do_cpu_cores))) {
670 unsigned long long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0, guest_nice = 0;
671
672 user = str2ull(procfile_lineword(ff, l, 1), NULL);
673 nice = str2ull(procfile_lineword(ff, l, 2), NULL);
674 system = str2ull(procfile_lineword(ff, l, 3), NULL);
675 idle = str2ull(procfile_lineword(ff, l, 4), NULL);
676 iowait = str2ull(procfile_lineword(ff, l, 5), NULL);
677 irq = str2ull(procfile_lineword(ff, l, 6), NULL);
678 softirq = str2ull(procfile_lineword(ff, l, 7), NULL);
679 steal = str2ull(procfile_lineword(ff, l, 8), NULL);
680
681 guest = str2ull(procfile_lineword(ff, l, 9), NULL);
682 user -= guest;
683
684 guest_nice = str2ull(procfile_lineword(ff, l, 10), NULL);
685 nice -= guest_nice;
686
687 char *title, *type, *context, *family;
688 long priority;
689
690 struct cpu_chart *cpu_chart = &all_cpu_charts[core];
691
692 char *id = row_key;
693
694 if(unlikely(!cpu_chart->st)) {
695 if(unlikely(core == 0)) {
696 title = "Total CPU utilization";
697 type = "system";
698 context = "system.cpu";
699 family = id;
700 priority = NETDATA_CHART_PRIO_SYSTEM_CPU;
701 }
702 else {
703 title = "Core utilization";
704 type = "cpu";
705 context = "cpu.cpu";
706 family = "utilization";
707 priority = NETDATA_CHART_PRIO_CPU_PER_CORE;
708 }
709
710 cpu_chart->st = rrdset_create_localhost(
711 type
712 , id
713 , NULL
714 , family
715 , context
716 , title
717 , "percentage"
718 , PLUGIN_PROC_NAME
719 , PLUGIN_PROC_MODULE_STAT_NAME
720 , priority + core
721 , update_every
722 , RRDSET_TYPE_STACKED
723 );
724
725 long multiplier = 1;
726 long divisor = 1; // sysconf(_SC_CLK_TCK);
727
728 cpu_chart->rd_guest_nice = rrddim_add(cpu_chart->st, "guest_nice", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
729 cpu_chart->rd_guest = rrddim_add(cpu_chart->st, "guest", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
730 cpu_chart->rd_steal = rrddim_add(cpu_chart->st, "steal", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
731 cpu_chart->rd_softirq = rrddim_add(cpu_chart->st, "softirq", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
732 cpu_chart->rd_irq = rrddim_add(cpu_chart->st, "irq", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
733 cpu_chart->rd_user = rrddim_add(cpu_chart->st, "user", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
734 cpu_chart->rd_system = rrddim_add(cpu_chart->st, "system", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
735 cpu_chart->rd_nice = rrddim_add(cpu_chart->st, "nice", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
736 cpu_chart->rd_iowait = rrddim_add(cpu_chart->st, "iowait", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
737 cpu_chart->rd_idle = rrddim_add(cpu_chart->st, "idle", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
738 rrddim_hide(cpu_chart->st, "idle");
739
740 if (core > 0) {
741 char cpu_core[50 + 1];
742 snprintfz(cpu_core, 50, "cpu%zu", core - 1);
743 rrdlabels_add(cpu_chart->st->rrdlabels, "cpu", cpu_core, RRDLABEL_SRC_AUTO);
744 }
745
746 if(unlikely(core == 0 && cpus_var == NULL))
747 cpus_var = rrdvar_host_variable_add_and_acquire(localhost, "active_processors");
748 }
749
750 rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_user, user);
751 rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_nice, nice);
752 rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_system, system);
753 rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_idle, idle);
754 rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_iowait, iowait);
755 rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_irq, irq);
756 rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_softirq, softirq);
757 rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_steal, steal);
758 rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_guest, guest);
759 rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_guest_nice, guest_nice);
760 rrdset_done(cpu_chart->st);
761 }
762 }
763 else if(unlikely(hash == hash_intr && strcmp(row_key, "intr") == 0)) {
764 if(likely(do_interrupts)) {
765 unsigned long long value = str2ull(procfile_lineword(ff, l, 1), NULL);
766 common_interrupts(value, update_every, NULL);
767 }
768 }
769 else if(unlikely(hash == hash_ctxt && strcmp(row_key, "ctxt") == 0)) {
770 if(likely(do_context)) {
771 unsigned long long value = str2ull(procfile_lineword(ff, l, 1), NULL);
772 common_system_context_switch(value, update_every);
773 }
774 }
775 else if(unlikely(hash == hash_processes && !processes && strcmp(row_key, "processes") == 0)) {
776 processes = str2ull(procfile_lineword(ff, l, 1), NULL);
777 }
778 else if(unlikely(hash == hash_procs_running && !running && strcmp(row_key, "procs_running") == 0)) {
779 running = str2ull(procfile_lineword(ff, l, 1), NULL);
780 }
781 else if(unlikely(hash == hash_procs_blocked && !blocked && strcmp(row_key, "procs_blocked") == 0)) {
782 blocked = str2ull(procfile_lineword(ff, l, 1), NULL);
783 }
784 }
785
786 // --------------------------------------------------------------------
787
788 if(likely(do_forks)) {
789 static RRDSET *st_forks = NULL;
790 static RRDDIM *rd_started = NULL;
791
792 if(unlikely(!st_forks)) {
793 st_forks = rrdset_create_localhost(
794 "system"
795 , "forks"
796 , NULL
797 , "processes"
798 , NULL
799 , "Started Processes"
800 , "processes/s"
801 , PLUGIN_PROC_NAME
802 , PLUGIN_PROC_MODULE_STAT_NAME
803 , NETDATA_CHART_PRIO_SYSTEM_FORKS
804 , update_every
805 , RRDSET_TYPE_LINE
806 );
807
808 rd_started = rrddim_add(st_forks, "started", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
809 }
810
811 rrddim_set_by_pointer(st_forks, rd_started, processes);
812 rrdset_done(st_forks);
813 }
814
815 // --------------------------------------------------------------------
816
817 if(likely(do_processes)) {
818 common_system_processes(running, blocked, update_every);
819 }
820
821 if(likely(all_cpu_charts_size > 1)) {
822 if(likely(do_core_throttle_count != CONFIG_BOOLEAN_NO)) {
823 int r = read_per_core_files(&all_cpu_charts[1], all_cpu_charts_size - 1, CORE_THROTTLE_COUNT_INDEX);
824 if(likely(r != -1 && (do_core_throttle_count == CONFIG_BOOLEAN_YES || r > 0))) {
825 do_core_throttle_count = CONFIG_BOOLEAN_YES;
826
827 static RRDSET *st_core_throttle_count = NULL;
828
829 if (unlikely(!st_core_throttle_count)) {
830 st_core_throttle_count = rrdset_create_localhost(
831 "cpu"
832 , "core_throttling"
833 , NULL
834 , "throttling"
835 , "cpu.core_throttling"
836 , "Core Thermal Throttling Events"
837 , "events/s"
838 , PLUGIN_PROC_NAME
839 , PLUGIN_PROC_MODULE_STAT_NAME
840 , NETDATA_CHART_PRIO_CORE_THROTTLING
841 , update_every
842 , RRDSET_TYPE_LINE
843 );
844 }
845
846 chart_per_core_files(&all_cpu_charts[1], all_cpu_charts_size - 1, CORE_THROTTLE_COUNT_INDEX, st_core_throttle_count, 1, 1, RRD_ALGORITHM_INCREMENTAL);
847 rrdset_done(st_core_throttle_count);
848 }
849 }
850
851 if(likely(do_package_throttle_count != CONFIG_BOOLEAN_NO)) {
852 int r = read_per_core_files(&all_cpu_charts[1], all_cpu_charts_size - 1, PACKAGE_THROTTLE_COUNT_INDEX);
853 if(likely(r != -1 && (do_package_throttle_count == CONFIG_BOOLEAN_YES || r > 0))) {
854 do_package_throttle_count = CONFIG_BOOLEAN_YES;
855
856 static RRDSET *st_package_throttle_count = NULL;
857
858 if(unlikely(!st_package_throttle_count)) {
859 st_package_throttle_count = rrdset_create_localhost(
860 "cpu"
861 , "package_throttling"
862 , NULL
863 , "throttling"
864 , "cpu.package_throttling"
865 , "Package Thermal Throttling Events"
866 , "events/s"
867 , PLUGIN_PROC_NAME
868 , PLUGIN_PROC_MODULE_STAT_NAME
869 , NETDATA_CHART_PRIO_PACKAGE_THROTTLING
870 , update_every
871 , RRDSET_TYPE_LINE
872 );
873 }
874
875 chart_per_core_files(&all_cpu_charts[1], all_cpu_charts_size - 1, PACKAGE_THROTTLE_COUNT_INDEX, st_package_throttle_count, 1, 1, RRD_ALGORITHM_INCREMENTAL);
876 rrdset_done(st_package_throttle_count);
877 }
878 }
879
880 if(likely(do_cpu_freq != CONFIG_BOOLEAN_NO)) {
881 char filename[FILENAME_MAX + 1];
882 int r = 0;
883
884 if (accurate_freq_avail) {
885 r = read_per_core_time_in_state_files(&all_cpu_charts[1], all_cpu_charts_size - 1, CPU_FREQ_INDEX);
886 if(r > 0 && !accurate_freq_is_used) {
887 accurate_freq_is_used = 1;
888 snprintfz(filename, FILENAME_MAX, time_in_state_filename, "cpu*");
889 collector_info("cpufreq is using %s", filename);
890 }
891 }
892 if (r < 1) {
893 r = read_per_core_files(&all_cpu_charts[1], all_cpu_charts_size - 1, CPU_FREQ_INDEX);
894 if(accurate_freq_is_used) {
895 accurate_freq_is_used = 0;
896 snprintfz(filename, FILENAME_MAX, scaling_cur_freq_filename, "cpu*");
897 collector_info("cpufreq fell back to %s", filename);
898 }
899 }
900
901 if(likely(r != -1 && (do_cpu_freq == CONFIG_BOOLEAN_YES || r > 0))) {
902 do_cpu_freq = CONFIG_BOOLEAN_YES;
903
904 static RRDSET *st_scaling_cur_freq = NULL;
905
906 if(unlikely(!st_scaling_cur_freq)) {
907 st_scaling_cur_freq = rrdset_create_localhost(
908 "cpu"
909 , "cpufreq"
910 , NULL
911 , "cpufreq"
912 , "cpufreq.cpufreq"
913 , "Current CPU Frequency"
914 , "MHz"
915 , PLUGIN_PROC_NAME
916 , PLUGIN_PROC_MODULE_STAT_NAME
917 , NETDATA_CHART_PRIO_CPUFREQ_SCALING_CUR_FREQ
918 , update_every
919 , RRDSET_TYPE_LINE
920 );
921 }
922
923 chart_per_core_files(&all_cpu_charts[1], all_cpu_charts_size - 1, CPU_FREQ_INDEX, st_scaling_cur_freq, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
924 rrdset_done(st_scaling_cur_freq);
925 }
926 }
927 }
928
929 // --------------------------------------------------------------------
930
931 static struct per_core_cpuidle_chart *cpuidle_charts = NULL;
932 size_t schedstat_cores_found = 0;
933
934 if(likely(do_cpuidle != CONFIG_BOOLEAN_NO && !read_schedstat(schedstat_filename, &cpuidle_charts, &schedstat_cores_found))) {
935 int cpu_states_updated = 0;
936 size_t core, state;
937
938
939 // proc.plugin runs on Linux systems only. Multi-platform compatibility is not needed here,
940 // so bare pthread functions are used to avoid unneeded overheads.
941 for(core = 0; core < schedstat_cores_found; core++) {
942 if(unlikely(!(cpuidle_charts[core].active_time - cpuidle_charts[core].last_active_time))) {
943 pthread_t thread;
944 cpu_set_t global_cpu_set;
945
946 if (likely(!pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &global_cpu_set))) {
947 if (unlikely(!CPU_ISSET(core, &global_cpu_set))) {
948 continue;
949 }
950 }
951 else
952 collector_error("Cannot read current process affinity");
953
954 // These threads are very ephemeral and don't need to have a specific name
955 if(unlikely(pthread_create(&thread, NULL, wake_cpu_thread, (void *)&core)))
956 collector_error("Cannot create wake_cpu_thread");
957 else if(unlikely(pthread_join(thread, NULL)))
958 collector_error("Cannot join wake_cpu_thread");
959 cpu_states_updated = 1;
960 }
961 }
962
963 if(unlikely(!cpu_states_updated || !read_schedstat(schedstat_filename, &cpuidle_charts, &schedstat_cores_found))) {
964 for(core = 0; core < schedstat_cores_found; core++) {
965 cpuidle_charts[core].last_active_time = cpuidle_charts[core].active_time;
966
967 int r = read_cpuidle_states(cpuidle_name_filename, cpuidle_time_filename, cpuidle_charts, core);
968 if(likely(r != -1 && (do_cpuidle == CONFIG_BOOLEAN_YES || r > 0))) {
969 do_cpuidle = CONFIG_BOOLEAN_YES;
970
971 char cpuidle_chart_id[RRD_ID_LENGTH_MAX + 1];
972 snprintfz(cpuidle_chart_id, RRD_ID_LENGTH_MAX, "cpu%zu_cpuidle", core);
973
974 if(unlikely(!cpuidle_charts[core].st)) {
975 cpuidle_charts[core].st = rrdset_create_localhost(
976 "cpu"
977 , cpuidle_chart_id
978 , NULL
979 , "cpuidle"
980 , "cpuidle.cpu_cstate_residency_time"
981 , "C-state residency time"
982 , "percentage"
983 , PLUGIN_PROC_NAME
984 , PLUGIN_PROC_MODULE_STAT_NAME
985 , NETDATA_CHART_PRIO_CPUIDLE + core
986 , update_every
987 , RRDSET_TYPE_STACKED
988 );
989
990 char corebuf[50+1];
991 snprintfz(corebuf, sizeof(corebuf) - 1, "cpu%zu", core);
992 rrdlabels_add(cpuidle_charts[core].st->rrdlabels, "cpu", corebuf, RRDLABEL_SRC_AUTO);
993
994 char cpuidle_dim_id[RRD_ID_LENGTH_MAX + 1];
995 cpuidle_charts[core].active_time_rd = rrddim_add(cpuidle_charts[core].st, "active", "C0 (active)", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
996 for(state = 0; state < cpuidle_charts[core].cpuidle_state_len; state++) {
997 strncpyz(cpuidle_dim_id, cpuidle_charts[core].cpuidle_state[state].name, RRD_ID_LENGTH_MAX);
998 for(int i = 0; cpuidle_dim_id[i]; i++)
999 cpuidle_dim_id[i] = tolower(cpuidle_dim_id[i]);
1000 cpuidle_charts[core].cpuidle_state[state].rd = rrddim_add(cpuidle_charts[core].st, cpuidle_dim_id,
1001 cpuidle_charts[core].cpuidle_state[state].name,
1002 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
1003 }
1004 }
1005
1006 rrddim_set_by_pointer(cpuidle_charts[core].st, cpuidle_charts[core].active_time_rd, cpuidle_charts[core].active_time);
1007 for(state = 0; state < cpuidle_charts[core].cpuidle_state_len; state++) {
1008 rrddim_set_by_pointer(cpuidle_charts[core].st, cpuidle_charts[core].cpuidle_state[state].rd, cpuidle_charts[core].cpuidle_state[state].value);
1009 }
1010 rrdset_done(cpuidle_charts[core].st);
1011 }
1012 }
1013 }
1014 }
1015
1016 if(cpus_var)
1017 rrdvar_host_variable_set(localhost, cpus_var, cores_found);
1018
1019 return 0;
1020 }