@cryptotaxi247 / netdata-1 / commits / da9e1df4c

apps.plugin: major new features and optimizations: now it reports system usage (all apps charts, cpu, memory, disk, etc) per username and per user group; apps.plugin is now almost a total re-write, 10% faster than the previous after the new features added; fixed a bug at the dashboard where some info was not presented; #146, #106, #85

Costa Tsaousis (ktsaou) committed Apr 8, 2016 at 05:49 UTC da9e1df4cbde9208d30b99937046b667e82e88a1
6 files changed +889 -449
.gitignore
+5
@@ -64,3 +64,8 @@ system/netdata-init-d
64
65 netdata-installer.log
66 netdata-uninstaller.sh
67 +
68 +gmon.out
69 +gmon.txt
70 +
71 +apps.plugin-profiler.sh
src/apps_plugin.c
+820 -444
@@ -13,6 +13,7 @@
13 #include <sys/types.h>
14 #include <sys/time.h>
15 #include <sys/wait.h>
16 +#include <sys/stat.h>
17
18 #include <sys/resource.h>
19 #include <sys/stat.h>
@@ -27,9 +28,14 @@
28 #include <dirent.h>
29 #include <arpa/inet.h>
30
31 +#include <sys/types.h>
32 +#include <pwd.h>
33 +#include <grp.h>
34 +
35 +#include "avl.h"
36 +
37 #include "common.h"
38 #include "log.h"
32 -#include "avl.h"
39 #include "procfile.h"
40
41 #define MAX_COMPARE_NAME 15
@@ -50,6 +56,7 @@ char *config_dir = CONFIG_DIR;
56 #ifdef NETDATA_INTERNAL_CHECKS
57 // ----------------------------------------------------------------------------
58 // memory debugger
59 +// do not use in production systems - it mis-aligns allocated memory
60
61 struct allocations {
62 size_t allocations;
@@ -203,11 +210,12 @@ char *strdup_debug(const char *file, int line, const char *function, const char
210
211
212 // ----------------------------------------------------------------------------
206 -// helper functions
213 +// system functions
214 +// to retrieve settings of the system
215
216 procfile *ff = NULL;
217
210 -long get_processors(void) {
218 +long get_system_cpus(void) {
219 int processors = 0;
220
221 char filename[FILENAME_MAX + 1];
@@ -235,7 +243,7 @@ long get_processors(void) {
243 return processors;
244 }
245
238 -long get_pid_max(void) {
246 +long get_system_pid_max(void) {
247 long mpid = 32768;
248
249 char filename[FILENAME_MAX + 1];
@@ -256,7 +264,7 @@ long get_pid_max(void) {
264 return mpid;
265 }
266
259 -unsigned long long get_hertz(void)
267 +unsigned long long get_system_hertz(void)
268 {
269 unsigned long long myhz = 1;
270
@@ -280,13 +288,18 @@ unsigned long long get_hertz(void)
288
289 // ----------------------------------------------------------------------------
290 // target
283 -// target is the point to aggregate a process tree values
291 +// target is the structure that process data are aggregated
292
293 struct target {
294 char compare[MAX_COMPARE_NAME + 1];
295 + uint32_t hash;
296 +
297 char id[MAX_NAME + 1];
298 char name[MAX_NAME + 1];
299
300 + uid_t uid;
301 + gid_t gid;
302 +
303 unsigned long long minflt;
304 unsigned long long cminflt;
305 unsigned long long majflt;
@@ -347,25 +360,103 @@ struct target {
360 int hidden; // if set, we set the hidden flag on the dimension
361 int debug;
362
350 - struct target *target; // the one that will be reported to netdata
363 + struct target *target; // the one that will be reported to netdata
364 struct target *next;
352 -} *target_root = NULL, *default_target = NULL;
365 +};
366
354 -long targets = 0;
367
356 -// find or create a target
357 -// there are targets that are just agregated to other target (the second argument)
358 -struct target *get_target(const char *id, struct target *target)
368 +// ----------------------------------------------------------------------------
369 +// apps_groups.conf
370 +// aggregate all processes in groups, to have a limited number of dimensions
371 +
372 +struct target *apps_groups_root_target = NULL;
373 +struct target *apps_groups_default_target = NULL;
374 +long apps_groups_targets = 0;
375 +
376 +struct target *users_root_target = NULL;
377 +struct target *groups_root_target = NULL;
378 +
379 +struct target *get_users_target(uid_t uid)
380 +{
381 + struct target *w;
382 + for(w = users_root_target ; w ; w = w->next)
383 + if(w->uid == uid) return w;
384 +
385 + w = calloc(sizeof(struct target), 1);
386 + if(unlikely(!w)) {
387 + error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
388 + return NULL;
389 + }
390 +
391 + snprintf(w->compare, MAX_COMPARE_NAME, "%d", uid);
392 + w->hash = simple_hash(w->compare);
393 +
394 + snprintf(w->id, MAX_NAME, "%d", uid);
395 +
396 + struct passwd *pw = getpwuid(uid);
397 + if(!pw)
398 + snprintf(w->name, MAX_NAME, "%d", uid);
399 + else
400 + snprintf(w->name, MAX_NAME, "%s", pw->pw_name);
401 +
402 + w->uid = uid;
403 +
404 + w->next = users_root_target;
405 + users_root_target = w;
406 +
407 + if(unlikely(debug))
408 + fprintf(stderr, "apps.plugin: added uid %d ('%s') target\n", w->uid, w->name);
409 +
410 + return w;
411 +}
412 +
413 +struct target *get_groups_target(gid_t gid)
414 +{
415 + struct target *w;
416 + for(w = groups_root_target ; w ; w = w->next)
417 + if(w->gid == gid) return w;
418 +
419 + w = calloc(sizeof(struct target), 1);
420 + if(unlikely(!w)) {
421 + error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
422 + return NULL;
423 + }
424 +
425 + snprintf(w->compare, MAX_COMPARE_NAME, "%d", gid);
426 + w->hash = simple_hash(w->compare);
427 +
428 + snprintf(w->id, MAX_NAME, "%d", gid);
429 +
430 + struct group *gr = getgrgid(gid);
431 + if(!gr)
432 + snprintf(w->name, MAX_NAME, "%d", gid);
433 + else
434 + snprintf(w->name, MAX_NAME, "%s", gr->gr_name);
435 +
436 + w->gid = gid;
437 +
438 + w->next = groups_root_target;
439 + groups_root_target = w;
440 +
441 + if(unlikely(debug))
442 + fprintf(stderr, "apps.plugin: added gid %d ('%s') target\n", w->gid, w->name);
443 +
444 + return w;
445 +}
446 +
447 +// find or create a new target
448 +// there are targets that are just aggregated to other target (the second argument)
449 +struct target *get_apps_groups_target(const char *id, struct target *target)
450 {
451 const char *nid = id;
452 if(nid[0] == '-') nid++;
453
454 struct target *w;
364 - for(w = target_root ; w ; w = w->next)
455 + for(w = apps_groups_root_target ; w ; w = w->next)
456 if(strncmp(nid, w->id, MAX_NAME) == 0) return w;
457
458 w = calloc(sizeof(struct target), 1);
368 - if(!w) {
459 + if(unlikely(!w)) {
460 error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
461 return NULL;
462 }
@@ -373,29 +464,34 @@ struct target *get_target(const char *id, struct target *target)
464 strncpy(w->id, nid, MAX_NAME);
465 strncpy(w->name, nid, MAX_NAME);
466 strncpy(w->compare, nid, MAX_COMPARE_NAME);
467 + w->hash = simple_hash(w->compare);
468 +
469 if(id[0] == '-') w->hidden = 1;
470
471 w->target = target;
472
380 - w->next = target_root;
381 - target_root = w;
473 + w->next = apps_groups_root_target;
474 + apps_groups_root_target = w;
475
383 - if(debug) fprintf(stderr, "apps.plugin: adding hook for process '%s', compare '%s' on target '%s'\n", w->id, w->compare, w->target?w->target->id:"");
476 + if(unlikely(debug))
477 + fprintf(stderr, "apps.plugin: adding hook for process '%s', compare '%s' on target '%s'\n", w->id, w->compare, w->target?w->target->id:"");
478
479 return w;
480 }
481
388 -// read the process groups file
389 -int read_process_groups(const char *name)
482 +// read the apps_groups.conf file
483 +int read_apps_groups_conf(const char *name)
484 {
485 char buffer[4096+1];
486 char filename[FILENAME_MAX + 1];
487
488 snprintf(filename, FILENAME_MAX, "%s/apps_%s.conf", config_dir, name);
489
396 - if(debug) fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename);
490 + if(unlikely(debug))
491 + fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename);
492 +
493 FILE *fp = fopen(filename, "r");
398 - if(!fp) {
494 + if(unlikely(!fp)) {
495 error("Cannot open file '%s'", filename);
496 return 1;
497 }
@@ -465,7 +561,7 @@ int read_process_groups(const char *name)
561 blen = strlen(buffer);
562 }
563
468 - struct target *n = get_target(buffer, w);
564 + struct target *n = get_apps_groups_target(buffer, w);
565 n->hidden = whidden;
566 n->debug = wdebug;
567 if(!w) w = n;
@@ -481,8 +577,8 @@ int read_process_groups(const char *name)
577 }
578 fclose(fp);
579
484 - default_target = get_target("+p!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing
485 - strncpy(default_target->name, "other", MAX_NAME);
580 + apps_groups_default_target = get_apps_groups_target("+p!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing
581 + strncpy(apps_groups_default_target->name, "other", MAX_NAME);
582
583 return 0;
584 }
@@ -538,6 +634,9 @@ struct pid_stat {
634 // uint64_t guest_time;
635 // int64_t cguest_time;
636
637 + uid_t uid;
638 + gid_t gid;
639 +
640 unsigned long long statm_size;
641 unsigned long long statm_resident;
642 unsigned long long statm_share;
@@ -554,7 +653,29 @@ struct pid_stat {
653 unsigned long long io_storage_bytes_written;
654 unsigned long long io_cancelled_write_bytes;
655
557 -#ifdef INCLUDE_CHILDS
656 + // we need the last values
657 + // for all incremental counters
658 + // so that when a process switches users/groups
659 + // we will subtract these values from the old
660 + // target
661 + unsigned long long last_minflt;
662 + unsigned long long last_cminflt;
663 + unsigned long long last_majflt;
664 + unsigned long long last_cmajflt;
665 + unsigned long long last_utime;
666 + unsigned long long last_stime;
667 + unsigned long long last_cutime;
668 + unsigned long long last_cstime;
669 +
670 + unsigned long long last_io_logical_bytes_read;
671 + unsigned long long last_io_logical_bytes_written;
672 + unsigned long long last_io_read_calls;
673 + unsigned long long last_io_write_calls;
674 + unsigned long long last_io_storage_bytes_read;
675 + unsigned long long last_io_storage_bytes_written;
676 + unsigned long long last_io_cancelled_write_bytes;
677 +
678 +#ifdef AGGREGATE_CHILDREN_TO_PARENTS
679 unsigned long long old_utime;
680 unsigned long long old_stime;
681 unsigned long long old_minflt;
@@ -574,19 +695,24 @@ struct pid_stat {
695 unsigned long long diff_cstime;
696 unsigned long long diff_cminflt;
697 unsigned long long diff_cmajflt;
577 -#endif /* INCLUDE_CHILDS */
698 +#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
699
579 - int *fds; // array of fds it uses
580 - int fds_size; // the size of the fds array
700 + int *fds; // array of fds it uses
701 + int fds_size; // the size of the fds array
702
582 - int children_count; // number of processes directly referencing this
583 - int updated; // 1 when update
584 - int merged; // 1 when it has been merged to its parent
703 + int children_count; // number of processes directly referencing this
704 + int updated; // 1 when update
705 + int merged; // 1 when it has been merged to its parent
706 int new_entry;
586 - struct target *target;
707 +
708 + struct target *target; // app_groups.conf targets
709 + struct target *user_target; // uid based targets
710 + struct target *group_target; // gid based targets
711 +
712 struct pid_stat *parent;
713 struct pid_stat *prev;
714 struct pid_stat *next;
715 +
716 } *root_of_pids = NULL, **all_pids;
717
718 long all_pids_count = 0;
@@ -638,11 +764,31 @@ void del_pid_entry(pid_t pid)
764 // ----------------------------------------------------------------------------
765 // update pids from proc
766
767 +int read_proc_pid_ownership(struct pid_stat *p) {
768 + char filename[FILENAME_MAX + 1];
769 +
770 + snprintf(filename, FILENAME_MAX, "%s/proc/%d", host_prefix, p->pid);
771 +
772 + // ----------------------------------------
773 + // read uid and gid
774 +
775 + struct stat st;
776 + if(stat(filename, &st) != 0)
777 + return 1;
778 +
779 + p->uid = st.st_uid;
780 + p->gid = st.st_gid;
781 +
782 + return 0;
783 +}
784 +
785 int read_proc_pid_stat(struct pid_stat *p) {
786 char filename[FILENAME_MAX + 1];
787
788 snprintf(filename, FILENAME_MAX, "%s/proc/%d/stat", host_prefix, p->pid);
789
790 + // ----------------------------------------
791 +
792 ff = procfile_reopen(ff, filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
793 if(!ff) return 1;
794
@@ -784,51 +930,10 @@ int read_proc_pid_io(struct pid_stat *p) {
930 }
931
932
787 -// ----------------------------------------------------------------------------
788 -
789 -#ifdef INCLUDE_CHILDS
790 -// print a tree view of all processes
791 -int walk_down(pid_t pid, int level) {
792 - struct pid_stat *p = NULL;
793 - char b[level+3];
794 - int i, ret = 0;
795 -
796 - for(i = 0; i < level; i++) b[i] = '\t';
797 - b[level] = '|';
798 - b[level+1] = '-';
799 - b[level+2] = '\0';
800 -
801 - for(p = root_of_pids; p ; p = p->next) {
802 - if(p->ppid == pid) {
803 - ret += walk_down(p->pid, level+1);
804 - }
805 - }
806 -
807 - p = all_pids[pid];
808 - if(p) {
809 - if(!p->updated) ret += 1;
810 - if(ret) fprintf(stderr, "%s %s %d [%s, %s] c=%d u=%llu+%llu, s=%llu+%llu, cu=%llu+%llu, cs=%llu+%llu, n=%llu+%llu, j=%llu+%llu, cn=%llu+%llu, cj=%llu+%llu\n"
811 - , b, p->comm, p->pid, p->updated?"OK":"KILLED", p->target->name, p->children_count
812 - , p->utime, p->utime - p->old_utime
813 - , p->stime, p->stime - p->old_stime
814 - , p->cutime, p->cutime - p->old_cutime
815 - , p->cstime, p->cstime - p->old_cstime
816 - , p->minflt, p->minflt - p->old_minflt
817 - , p->majflt, p->majflt - p->old_majflt
818 - , p->cminflt, p->cminflt - p->old_cminflt
819 - , p->cmajflt, p->cmajflt - p->old_cmajflt
820 - );
821 - }
822 -
823 - return ret;
824 -}
825 -#endif /* INCLUDE_CHILDS */
826 -
827 -
933 // ----------------------------------------------------------------------------
934 // file descriptor
935 // this is used to keep a global list of all open files of the system
831 -// it is needed in order to figure out the unique files a process tree has open
936 +// it is needed in order to calculate the unique files processes have open
937
938 #define FILE_DESCRIPTORS_INCREASE_STEP 100
939
@@ -868,11 +973,13 @@ int file_descriptor_iterator(avl *a) { if(a) {}; return 0; }
973 avl_tree all_files_index = {
974 NULL,
975 file_descriptor_compare,
976 +#ifndef AVL_WITHOUT_PTHREADS
977 #ifdef AVL_LOCK_WITH_MUTEX
978 PTHREAD_MUTEX_INITIALIZER
979 #else
980 PTHREAD_RWLOCK_INITIALIZER
981 #endif
982 +#endif /* AVL_WITHOUT_PTHREADS */
983 };
984
985 static struct file_descriptor *file_descriptor_find(const char *name, uint32_t hash) {
@@ -1049,6 +1156,81 @@ int file_descriptor_find_or_add(const char *name)
1156 return c;
1157 }
1158
1159 +int read_pid_file_descriptors(struct pid_stat *p) {
1160 + char dirname[FILENAME_MAX+1];
1161 +
1162 + snprintf(dirname, FILENAME_MAX, "%s/proc/%d/fd", host_prefix, p->pid);
1163 + DIR *fds = opendir(dirname);
1164 + if(fds) {
1165 + int c;
1166 + struct dirent *de;
1167 + char fdname[FILENAME_MAX + 1];
1168 + char linkname[FILENAME_MAX + 1];
1169 +
1170 + // make the array negative
1171 + for(c = 0 ; c < p->fds_size ; c++)
1172 + p->fds[c] = -p->fds[c];
1173 +
1174 + while((de = readdir(fds))) {
1175 + if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
1176 + continue;
1177 +
1178 + // check if the fds array is small
1179 + int fdid = atoi(de->d_name);
1180 + if(fdid < 0) continue;
1181 + if(fdid >= p->fds_size) {
1182 + // it is small, extend it
1183 + if(debug) fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100);
1184 + p->fds = realloc(p->fds, (fdid + 100) * sizeof(int));
1185 + if(!p->fds) {
1186 + error("Cannot re-allocate fds for %s", p->comm);
1187 + break;
1188 + }
1189 +
1190 + // and initialize it
1191 + for(c = p->fds_size ; c < (fdid + 100) ; c++) p->fds[c] = 0;
1192 + p->fds_size = fdid + 100;
1193 + }
1194 +
1195 + if(p->fds[fdid] == 0) {
1196 + // we don't know this fd, get it
1197 +
1198 + sprintf(fdname, "%s/proc/%d/fd/%s", host_prefix, p->pid, de->d_name);
1199 + ssize_t l = readlink(fdname, linkname, FILENAME_MAX);
1200 + if(l == -1) {
1201 + if(debug || (p->target && p->target->debug)) {
1202 + if(debug || (p->target && p->target->debug))
1203 + error("Cannot read link %s", fdname);
1204 + }
1205 + continue;
1206 + }
1207 + linkname[l] = '\0';
1208 + file_counter++;
1209 +
1210 + // if another process already has this, we will get
1211 + // the same id
1212 + p->fds[fdid] = file_descriptor_find_or_add(linkname);
1213 + }
1214 +
1215 + // else make it positive again, we need it
1216 + // of course, the actual file may have changed, but we don't care so much
1217 + // FIXME: we could compare the inode as returned by readdir direct structure
1218 + else p->fds[fdid] = -p->fds[fdid];
1219 + }
1220 + closedir(fds);
1221 +
1222 + // remove all the negative file descriptors
1223 + for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] < 0) {
1224 + file_descriptor_not_used(-p->fds[c]);
1225 + p->fds[c] = 0;
1226 + }
1227 + }
1228 + else return 1;
1229 +
1230 + return 0;
1231 +}
1232 +
1233 +// ----------------------------------------------------------------------------
1234
1235 // 1. read all files in /proc
1236 // 2. for each numeric directory:
@@ -1066,11 +1248,10 @@ int file_descriptor_find_or_add(const char *name)
1248 // to avoid filling up all disk space
1249 // if debug is enabled, all errors are printed
1250
1069 -int update_from_proc(void)
1251 +int collect_data_for_all_processes_from_proc(void)
1252 {
1253 static long count_errors = 0;
1254
1073 - char filename[FILENAME_MAX+1];
1255 char dirname[FILENAME_MAX + 1];
1256
1257 snprintf(dirname, FILENAME_MAX, "%s/proc", host_prefix);
@@ -1089,35 +1270,65 @@ int update_from_proc(void)
1270 p->children_count = 0;
1271 p->merged = 0;
1272 p->new_entry = 0;
1273 +
1274 + p->last_minflt = p->minflt;
1275 + p->last_cminflt = p->cminflt;
1276 + p->last_majflt = p->majflt;
1277 + p->last_cmajflt = p->cmajflt;
1278 + p->last_utime = p->utime;
1279 + p->last_stime = p->stime;
1280 + p->last_cutime = p->cutime;
1281 + p->last_cstime = p->cstime;
1282 +
1283 + p->last_io_logical_bytes_read = p->io_logical_bytes_read;
1284 + p->last_io_logical_bytes_written = p->io_logical_bytes_written;
1285 + p->last_io_read_calls = p->io_read_calls;
1286 + p->last_io_write_calls = p->io_write_calls;
1287 + p->last_io_storage_bytes_read = p->io_storage_bytes_read;
1288 + p->last_io_storage_bytes_written = p->io_storage_bytes_written;
1289 + p->last_io_cancelled_write_bytes = p->io_cancelled_write_bytes;
1290 }
1291
1292 while((file = readdir(dir))) {
1293 char *endptr = file->d_name;
1294 pid_t pid = (pid_t) strtoul(file->d_name, &endptr, 10);
1097 - if(pid <= 0 || pid > pid_max || endptr == file->d_name || *endptr != '\0') continue;
1295 +
1296 + // make sure we read a valid number
1297 + if(unlikely(pid <= 0 || pid > pid_max || endptr == file->d_name || *endptr != '\0'))
1298 + continue;
1299
1300 p = get_pid_entry(pid);
1100 - if(!p) continue;
1301 + if(unlikely(!p)) continue;
1302 +
1303
1304 // --------------------------------------------------------------------
1305 // /proc/<pid>/stat
1306
1105 - if(read_proc_pid_stat(p)) {
1307 + if(unlikely(read_proc_pid_stat(p))) {
1308 if(!count_errors++ || debug || (p->target && p->target->debug))
1309 error("Cannot process %s/proc/%d/stat", host_prefix, pid);
1310
1311 + // there is no reason to proceed if we cannot get its status
1312 continue;
1313 }
1111 - if(p->ppid < 0 || p->ppid > pid_max) p->ppid = 0;
1314 +
1315 + // check its parent pid
1316 + if(unlikely(p->ppid < 0 || p->ppid > pid_max)) {
1317 + if(unlikely(!count_errors++ || debug || (p->target && p->target->debug)))
1318 + error("Pid %d states invalid parent pid %d. Using 0.", pid, p->ppid);
1319 +
1320 + p->ppid = 0;
1321 + }
1322
1323
1324 // --------------------------------------------------------------------
1325 // /proc/<pid>/statm
1326
1117 - if(read_proc_pid_statm(p)) {
1118 - if(!count_errors++ || debug || (p->target && p->target->debug))
1327 + if(unlikely(read_proc_pid_statm(p))) {
1328 + if(unlikely(!count_errors++ || debug || (p->target && p->target->debug)))
1329 error("Cannot process %s/proc/%d/statm", host_prefix, pid);
1330
1331 + // there is no reason to proceed if we cannot get its memory status
1332 continue;
1333 }
1334
@@ -1125,8 +1336,8 @@ int update_from_proc(void)
1336 // --------------------------------------------------------------------
1337 // /proc/<pid>/io
1338
1128 - if(read_proc_pid_io(p)) {
1129 - if(!count_errors++ || debug || (p->target && p->target->debug))
1339 + if(unlikely(read_proc_pid_io(p))) {
1340 + if(unlikely(!count_errors++ || debug || (p->target && p->target->debug)))
1341 error("Cannot process %s/proc/%d/io", host_prefix, pid);
1342
1343 // on systems without /proc/X/io
@@ -1134,23 +1345,33 @@ int update_from_proc(void)
1345 // continue;
1346 }
1347
1348 + // --------------------------------------------------------------------
1349 + // <pid> ownership
1350 +
1351 + if(unlikely(read_proc_pid_ownership(p))) {
1352 + if(unlikely(!count_errors++ || debug || (p->target && p->target->debug)))
1353 + error("Cannot stat %s/proc/%d", host_prefix, pid);
1354 + }
1355 +
1356 // --------------------------------------------------------------------
1357 // link it
1358
1359 // check if it is target
1360 // we do this only once, the first time this pid is loaded
1142 - if(p->new_entry) {
1361 + if(unlikely(p->new_entry)) {
1362 if(debug) fprintf(stderr, "apps.plugin: \tJust added %s\n", p->comm);
1363 + uint32_t hash = simple_hash(p->comm);
1364
1365 struct target *w;
1146 - for(w = target_root; w ; w = w->next) {
1366 + for(w = apps_groups_root_target; w ; w = w->next) {
1367 // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
1368
1149 - if(strcmp(w->compare, p->comm) == 0) {
1369 + if(w->hash == hash && strcmp(w->compare, p->comm) == 0) {
1370 if(w->target) p->target = w->target;
1371 else p->target = w;
1372
1153 - if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
1373 + if(debug || (p->target && p->target->debug))
1374 + fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
1375 }
1376 }
1377 }
@@ -1158,69 +1379,9 @@ int update_from_proc(void)
1379 // --------------------------------------------------------------------
1380 // /proc/<pid>/fd
1381
1161 - snprintf(filename, FILENAME_MAX, "%s/proc/%s/fd", host_prefix, file->d_name);
1162 - DIR *fds = opendir(filename);
1163 - if(fds) {
1164 - int c;
1165 - struct dirent *de;
1166 - char fdname[FILENAME_MAX + 1];
1167 - char linkname[FILENAME_MAX + 1];
1168 -
1169 - // make the array negative
1170 - for(c = 0 ; c < p->fds_size ; c++) p->fds[c] = -p->fds[c];
1171 -
1172 - while((de = readdir(fds))) {
1173 - if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue;
1174 -
1175 - // check if the fds array is small
1176 - int fdid = atoi(de->d_name);
1177 - if(fdid < 0) continue;
1178 - if(fdid >= p->fds_size) {
1179 - // it is small, extend it
1180 - if(debug) fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100);
1181 - p->fds = realloc(p->fds, (fdid + 100) * sizeof(int));
1182 - if(!p->fds) {
1183 - error("Cannot re-allocate fds for %s", p->comm);
1184 - break;
1185 - }
1186 -
1187 - // and initialize it
1188 - for(c = p->fds_size ; c < (fdid + 100) ; c++) p->fds[c] = 0;
1189 - p->fds_size = fdid + 100;
1190 - }
1191 -
1192 - if(p->fds[fdid] == 0) {
1193 - // we don't know this fd, get it
1194 -
1195 - sprintf(fdname, "%s/proc/%s/fd/%s", host_prefix, file->d_name, de->d_name);
1196 - ssize_t l = readlink(fdname, linkname, FILENAME_MAX);
1197 - if(l == -1) {
1198 - if(debug || (p->target && p->target->debug)) {
1199 - if(!count_errors++ || debug || (p->target && p->target->debug))
1200 - error("Cannot read link %s", fdname);
1201 - }
1202 - continue;
1203 - }
1204 - linkname[l] = '\0';
1205 - file_counter++;
1206 -
1207 - // if another process already has this, we will get
1208 - // the same id
1209 - p->fds[fdid] = file_descriptor_find_or_add(linkname);
1210 - }
1211 -
1212 - // else make it positive again, we need it
1213 - // of course, the actual file may have changed, but we don't care so much
1214 - // FIXME: we could compare the inode as returned by readdir direct structure
1215 - else p->fds[fdid] = -p->fds[fdid];
1216 - }
1217 - closedir(fds);
1218 -
1219 - // remove all the negative file descriptors
1220 - for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] < 0) {
1221 - file_descriptor_not_used(-p->fds[c]);
1222 - p->fds[c] = 0;
1223 - }
1382 + if(unlikely(read_pid_file_descriptors(p))) {
1383 + if(unlikely(!count_errors++ || debug || (p->target && p->target->debug)))
1384 + error("Cannot process entries in %s/proc/%d/fd", host_prefix, pid);
1385 }
1386
1387 // --------------------------------------------------------------------
@@ -1229,7 +1390,8 @@ int update_from_proc(void)
1390 // mark it as updated
1391 p->updated = 1;
1392 }
1232 - if(count_errors > 1000) {
1393 +
1394 + if(unlikely(count_errors > 1000)) {
1395 error("%ld more errors encountered\n", count_errors - 1);
1396 count_errors = 0;
1397 }
@@ -1240,6 +1402,48 @@ int update_from_proc(void)
1402 }
1403
1404
1405 +// ----------------------------------------------------------------------------
1406 +
1407 +#ifdef AGGREGATE_CHILDREN_TO_PARENTS
1408 +// print a tree view of all processes
1409 +int debug_childrens_aggregations(pid_t pid, int level) {
1410 + struct pid_stat *p = NULL;
1411 + char b[level+3];
1412 + int i, ret = 0;
1413 +
1414 + for(i = 0; i < level; i++) b[i] = '\t';
1415 + b[level] = '|';
1416 + b[level+1] = '-';
1417 + b[level+2] = '\0';
1418 +
1419 + for(p = root_of_pids; p ; p = p->next) {
1420 + if(p->ppid == pid) {
1421 + ret += debug_childrens_aggregations(p->pid, level+1);
1422 + }
1423 + }
1424 +
1425 + p = all_pids[pid];
1426 + if(p) {
1427 + if(!p->updated) ret += 1;
1428 + if(ret) fprintf(stderr, "%s %s %d [%s, %s] c=%d u=%llu+%llu, s=%llu+%llu, cu=%llu+%llu, cs=%llu+%llu, n=%llu+%llu, j=%llu+%llu, cn=%llu+%llu, cj=%llu+%llu\n"
1429 + , b, p->comm, p->pid, p->updated?"OK":"KILLED", p->target->name, p->children_count
1430 + , p->utime, p->utime - p->old_utime
1431 + , p->stime, p->stime - p->old_stime
1432 + , p->cutime, p->cutime - p->old_cutime
1433 + , p->cstime, p->cstime - p->old_cstime
1434 + , p->minflt, p->minflt - p->old_minflt
1435 + , p->majflt, p->majflt - p->old_majflt
1436 + , p->cminflt, p->cminflt - p->old_cminflt
1437 + , p->cmajflt, p->cmajflt - p->old_cmajflt
1438 + );
1439 + }
1440 +
1441 + return ret;
1442 +}
1443 +#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
1444 +
1445 +
1446 +
1447 // ----------------------------------------------------------------------------
1448 // update statistics on the targets
1449
@@ -1250,17 +1454,15 @@ int update_from_proc(void)
1454 // 4. go from top to bottom, linking all childs without a target, to their parent target
1455 // after this step, all processes have a target
1456 // [5. for each killed pid (updated = 0), remove its usage from its target]
1253 -// 6. zero all targets
1254 -// 7. concentrate all values on the targets
1457 +// 6. zero all apps_groups_targets
1458 +// 7. concentrate all values on the apps_groups_targets
1459 // 8. remove all killed processes
1460 // 9. find the unique file count for each target
1461 +// check: update_apps_groups_statistics()
1462
1258 -void update_statistics(void)
1259 -{
1260 - int c;
1463 +void link_all_processes_to_their_parents(void) {
1464 struct pid_stat *p = NULL;
1465
1263 -
1466 // link all children to their parents
1467 // and update children count on parents
1468 for(p = root_of_pids; p ; p = p->next) {
@@ -1281,7 +1483,90 @@ void update_statistics(void)
1483 else if(p->ppid != 0)
1484 error("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
1485 }
1486 +}
1487 +
1488 +#ifdef AGGREGATE_CHILDREN_TO_PARENTS
1489 +void aggregate_children_to_parents(void) {
1490 + struct pid_stat *p = NULL;
1491 +
1492 + // for each killed process, remove its values from the parents
1493 + // sums (we had already added them in a previous loop)
1494 + for(p = root_of_pids; p ; p = p->next) {
1495 + if(p->updated) continue;
1496 +
1497 + if(debug) fprintf(stderr, "apps.plugin: UNMERGING %d %s\n", p->pid, p->comm);
1498 +
1499 + unsigned long long diff_utime = p->utime + p->cutime + p->fix_cutime;
1500 + unsigned long long diff_stime = p->stime + p->cstime + p->fix_cstime;
1501 + unsigned long long diff_minflt = p->minflt + p->cminflt + p->fix_cminflt;
1502 + unsigned long long diff_majflt = p->majflt + p->cmajflt + p->fix_cmajflt;
1503 +
1504 + struct pid_stat *t = p;
1505 + while((t = t->parent)) {
1506 + if(!t->updated) continue;
1507 +
1508 + unsigned long long x;
1509 + if(diff_utime && t->diff_cutime) {
1510 + x = (t->diff_cutime < diff_utime)?t->diff_cutime:diff_utime;
1511 + diff_utime -= x;
1512 + t->diff_cutime -= x;
1513 + t->fix_cutime += x;
1514 + if(debug) fprintf(stderr, "apps.plugin: \t cutime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1515 + }
1516 + if(diff_stime && t->diff_cstime) {
1517 + x = (t->diff_cstime < diff_stime)?t->diff_cstime:diff_stime;
1518 + diff_stime -= x;
1519 + t->diff_cstime -= x;
1520 + t->fix_cstime += x;
1521 + if(debug) fprintf(stderr, "apps.plugin: \t cstime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1522 + }
1523 + if(diff_minflt && t->diff_cminflt) {
1524 + x = (t->diff_cminflt < diff_minflt)?t->diff_cminflt:diff_minflt;
1525 + diff_minflt -= x;
1526 + t->diff_cminflt -= x;
1527 + t->fix_cminflt += x;
1528 + if(debug) fprintf(stderr, "apps.plugin: \t cminflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1529 + }
1530 + if(diff_majflt && t->diff_cmajflt) {
1531 + x = (t->diff_cmajflt < diff_majflt)?t->diff_cmajflt:diff_majflt;
1532 + diff_majflt -= x;
1533 + t->diff_cmajflt -= x;
1534 + t->fix_cmajflt += x;
1535 + if(debug) fprintf(stderr, "apps.plugin: \t cmajflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1536 + }
1537 + }
1538 +
1539 + if(diff_utime) error("Cannot fix up utime %llu", diff_utime);
1540 + if(diff_stime) error("Cannot fix up stime %llu", diff_stime);
1541 + if(diff_minflt) error("Cannot fix up minflt %llu", diff_minflt);
1542 + if(diff_majflt) error("Cannot fix up majflt %llu", diff_majflt);
1543 + }
1544 +}
1545 +#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
1546
1547 +void cleanup_non_existing_pids(void) {
1548 + int c;
1549 + struct pid_stat *p = NULL;
1550 +
1551 + for(p = root_of_pids; p ;) {
1552 + if(!p->updated) {
1553 +// fprintf(stderr, "\tEXITED %d %s [parent %d %s, target %s] utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->parent->pid, p->parent->comm, p->target->name, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
1554 +
1555 + for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
1556 + file_descriptor_not_used(p->fds[c]);
1557 + p->fds[c] = 0;
1558 + }
1559 +
1560 + pid_t r = p->pid;
1561 + p = p->next;
1562 + del_pid_entry(r);
1563 + }
1564 + else p = p->next;
1565 + }
1566 +}
1567 +
1568 +void apply_apps_groups_targets_inheritance(void) {
1569 + struct pid_stat *p = NULL;
1570
1571 // children that do not have a target
1572 // inherit their target from their parent
@@ -1346,23 +1631,23 @@ void update_statistics(void)
1631
1632 // init goes always to default target
1633 if(all_pids[1])
1349 - all_pids[1]->target = default_target;
1634 + all_pids[1]->target = apps_groups_default_target;
1635
1636 // give a default target on all top level processes
1637 for(p = root_of_pids; p ; p = p->next) {
1638 // if the process is not merged itself
1639 // then is is a top level process
1640 if(!p->merged && !p->target)
1356 - p->target = default_target;
1641 + p->target = apps_groups_default_target;
1642
1358 -#ifdef INCLUDE_CHILDS
1643 +#ifdef AGGREGATE_CHILDREN_TO_PARENTS
1644 // by the way, update the diffs
1645 // will be used later for subtracting killed process times
1646 p->diff_cutime = p->utime - p->cutime;
1647 p->diff_cstime = p->stime - p->cstime;
1648 p->diff_cminflt = p->minflt - p->cminflt;
1649 p->diff_cmajflt = p->majflt - p->cmajflt;
1365 -#endif /* INCLUDE_CHILDS */
1650 +#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
1651 }
1652
1653 // give a target to all merged child processes
@@ -1379,71 +1664,17 @@ void update_statistics(void)
1664 }
1665 }
1666 }
1667 +}
1668
1383 -#ifdef INCLUDE_CHILDS
1384 - // for each killed process, remove its values from the parents
1385 - // sums (we had already added them in a previous loop)
1386 - for(p = root_of_pids; p ; p = p->next) {
1387 - if(p->updated) continue;
1388 -
1389 - if(debug) fprintf(stderr, "apps.plugin: UNMERGING %d %s\n", p->pid, p->comm);
1390 -
1391 - unsigned long long diff_utime = p->utime + p->cutime + p->fix_cutime;
1392 - unsigned long long diff_stime = p->stime + p->cstime + p->fix_cstime;
1393 - unsigned long long diff_minflt = p->minflt + p->cminflt + p->fix_cminflt;
1394 - unsigned long long diff_majflt = p->majflt + p->cmajflt + p->fix_cmajflt;
1395 -
1396 - struct pid_stat *t = p;
1397 - while((t = t->parent)) {
1398 - if(!t->updated) continue;
1399 -
1400 - unsigned long long x;
1401 - if(diff_utime && t->diff_cutime) {
1402 - x = (t->diff_cutime < diff_utime)?t->diff_cutime:diff_utime;
1403 - diff_utime -= x;
1404 - t->diff_cutime -= x;
1405 - t->fix_cutime += x;
1406 - if(debug) fprintf(stderr, "apps.plugin: \t cutime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1407 - }
1408 - if(diff_stime && t->diff_cstime) {
1409 - x = (t->diff_cstime < diff_stime)?t->diff_cstime:diff_stime;
1410 - diff_stime -= x;
1411 - t->diff_cstime -= x;
1412 - t->fix_cstime += x;
1413 - if(debug) fprintf(stderr, "apps.plugin: \t cstime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1414 - }
1415 - if(diff_minflt && t->diff_cminflt) {
1416 - x = (t->diff_cminflt < diff_minflt)?t->diff_cminflt:diff_minflt;
1417 - diff_minflt -= x;
1418 - t->diff_cminflt -= x;
1419 - t->fix_cminflt += x;
1420 - if(debug) fprintf(stderr, "apps.plugin: \t cminflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1421 - }
1422 - if(diff_majflt && t->diff_cmajflt) {
1423 - x = (t->diff_cmajflt < diff_majflt)?t->diff_cmajflt:diff_majflt;
1424 - diff_majflt -= x;
1425 - t->diff_cmajflt -= x;
1426 - t->fix_cmajflt += x;
1427 - if(debug) fprintf(stderr, "apps.plugin: \t cmajflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1428 - }
1429 - }
1430 -
1431 - if(diff_utime) error("Cannot fix up utime %llu", diff_utime);
1432 - if(diff_stime) error("Cannot fix up stime %llu", diff_stime);
1433 - if(diff_minflt) error("Cannot fix up minflt %llu", diff_minflt);
1434 - if(diff_majflt) error("Cannot fix up majflt %llu", diff_majflt);
1435 - }
1436 -#endif /* INCLUDE_CHILDS */
1437 -
1438 - // zero all the targets
1439 - targets = 0;
1669 +long zero_all_targets(struct target *root) {
1670 struct target *w;
1441 - for (w = target_root; w ; w = w->next) {
1442 - targets++;
1671 + long count = 0;
1672
1444 - w->fds = calloc(sizeof(int), (size_t) all_files_size);
1445 - if(!w->fds)
1446 - error("Cannot allocate memory for fds in %s", w->name);
1673 + for (w = root; w ; w = w->next) {
1674 + count++;
1675 +
1676 + if(w->fds) free(w->fds);
1677 + w->fds = NULL;
1678
1679 w->minflt = 0;
1680 w->majflt = 0;
@@ -1474,131 +1705,175 @@ void update_statistics(void)
1705 w->io_cancelled_write_bytes = 0;
1706 }
1707
1477 -#ifdef INCLUDE_CHILDS
1478 - if(debug) walk_down(0, 1);
1479 -#endif /* INCLUDE_CHILDS */
1708 + return count;
1709 +}
1710
1481 - // concentrate everything on the targets
1482 - for(p = root_of_pids; p ; p = p->next) {
1483 - if(!p->target) {
1484 - error("pid %d %s was left without a target!", p->pid, p->comm);
1485 - continue;
1486 - }
1711 +void aggregate_pid_on_target(struct target *w, struct pid_stat *p, struct target *o) {
1712 + if(unlikely(!w->fds)) {
1713 + w->fds = calloc(sizeof(int), (size_t) all_files_size);
1714 + if(unlikely(!w->fds))
1715 + error("Cannot allocate memory for fds in %s", w->name);
1716 + }
1717
1488 - if(p->updated) {
1489 - p->target->cutime += p->cutime; // - p->fix_cutime;
1490 - p->target->cstime += p->cstime; // - p->fix_cstime;
1491 - p->target->cminflt += p->cminflt; // - p->fix_cminflt;
1492 - p->target->cmajflt += p->cmajflt; // - p->fix_cmajflt;
1718 + if(likely(p->updated)) {
1719 + w->cutime += p->cutime; // - p->fix_cutime;
1720 + w->cstime += p->cstime; // - p->fix_cstime;
1721 + w->cminflt += p->cminflt; // - p->fix_cminflt;
1722 + w->cmajflt += p->cmajflt; // - p->fix_cmajflt;
1723
1494 - p->target->utime += p->utime; //+ (p->pid != 1)?(p->cutime - p->fix_cutime):0;
1495 - p->target->stime += p->stime; //+ (p->pid != 1)?(p->cstime - p->fix_cstime):0;
1496 - p->target->minflt += p->minflt; //+ (p->pid != 1)?(p->cminflt - p->fix_cminflt):0;
1497 - p->target->majflt += p->majflt; //+ (p->pid != 1)?(p->cmajflt - p->fix_cmajflt):0;
1724 + w->utime += p->utime; //+ (p->pid != 1)?(p->cutime - p->fix_cutime):0;
1725 + w->stime += p->stime; //+ (p->pid != 1)?(p->cstime - p->fix_cstime):0;
1726 + w->minflt += p->minflt; //+ (p->pid != 1)?(p->cminflt - p->fix_cminflt):0;
1727 + w->majflt += p->majflt; //+ (p->pid != 1)?(p->cmajflt - p->fix_cmajflt):0;
1728
1499 - //if(p->num_threads < 0)
1500 - // error("Negative threads number for pid '%s' (%d): %d", p->comm, p->pid, p->num_threads);
1729 + //if(p->num_threads < 0)
1730 + // error("Negative threads number for pid '%s' (%d): %d", p->comm, p->pid, p->num_threads);
1731
1502 - //if(p->num_threads > 10000)
1503 - // error("Excessive threads number for pid '%s' (%d): %d", p->comm, p->pid, p->num_threads);
1732 + //if(p->num_threads > 10000)
1733 + // error("Excessive threads number for pid '%s' (%d): %d", p->comm, p->pid, p->num_threads);
1734
1505 - p->target->num_threads += p->num_threads;
1506 - p->target->rss += p->rss;
1735 + w->num_threads += p->num_threads;
1736 + w->rss += p->rss;
1737
1508 - p->target->statm_size += p->statm_size;
1509 - p->target->statm_resident += p->statm_resident;
1510 - p->target->statm_share += p->statm_share;
1511 - p->target->statm_text += p->statm_text;
1512 - p->target->statm_lib += p->statm_lib;
1513 - p->target->statm_data += p->statm_data;
1514 - p->target->statm_dirty += p->statm_dirty;
1738 + w->statm_size += p->statm_size;
1739 + w->statm_resident += p->statm_resident;
1740 + w->statm_share += p->statm_share;
1741 + w->statm_text += p->statm_text;
1742 + w->statm_lib += p->statm_lib;
1743 + w->statm_data += p->statm_data;
1744 + w->statm_dirty += p->statm_dirty;
1745
1516 - p->target->io_logical_bytes_read += p->io_logical_bytes_read;
1517 - p->target->io_logical_bytes_written += p->io_logical_bytes_written;
1518 - p->target->io_read_calls += p->io_read_calls;
1519 - p->target->io_write_calls += p->io_write_calls;
1520 - p->target->io_storage_bytes_read += p->io_storage_bytes_read;
1521 - p->target->io_storage_bytes_written += p->io_storage_bytes_written;
1522 - p->target->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
1746 + w->io_logical_bytes_read += p->io_logical_bytes_read;
1747 + w->io_logical_bytes_written += p->io_logical_bytes_written;
1748 + w->io_read_calls += p->io_read_calls;
1749 + w->io_write_calls += p->io_write_calls;
1750 + w->io_storage_bytes_read += p->io_storage_bytes_read;
1751 + w->io_storage_bytes_written += p->io_storage_bytes_written;
1752 + w->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
1753
1524 - p->target->processes++;
1754 + w->processes++;
1755
1756 + if(likely(w->fds)) {
1757 + int c;
1758 for(c = 0; c < p->fds_size ;c++) {
1759 if(p->fds[c] == 0) continue;
1528 - if(p->fds[c] < all_files_size) {
1529 - if(p->target->fds) p->target->fds[p->fds[c]]++;
1760 +
1761 + if(likely(p->fds[c] < all_files_size)) {
1762 + if(w->fds) w->fds[p->fds[c]]++;
1763 }
1764 else
1765 error("Invalid fd number %d", p->fds[c]);
1766 }
1767 + }
1768
1535 - if(debug || p->target->debug) fprintf(stderr, "apps.plugin: \tAgregating %s pid %d on %s utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->pid, p->target->name, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
1536 -
1537 -/* if(p->utime - p->old_utime > 100) fprintf(stderr, "BIG CHANGE: %d %s utime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->utime - p->old_utime, p->old_utime, p->utime);
1538 - if(p->cutime - p->old_cutime > 100) fprintf(stderr, "BIG CHANGE: %d %s cutime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cutime - p->old_cutime, p->old_cutime, p->cutime);
1539 - if(p->stime - p->old_stime > 100) fprintf(stderr, "BIG CHANGE: %d %s stime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->stime - p->old_stime, p->old_stime, p->stime);
1540 - if(p->cstime - p->old_cstime > 100) fprintf(stderr, "BIG CHANGE: %d %s cstime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cstime - p->old_cstime, p->old_cstime, p->cstime);
1541 - if(p->minflt - p->old_minflt > 5000) fprintf(stderr, "BIG CHANGE: %d %s minflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->minflt - p->old_minflt, p->old_minflt, p->minflt);
1542 - if(p->majflt - p->old_majflt > 5000) fprintf(stderr, "BIG CHANGE: %d %s majflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->majflt - p->old_majflt, p->old_majflt, p->majflt);
1543 - if(p->cminflt - p->old_cminflt > 15000) fprintf(stderr, "BIG CHANGE: %d %s cminflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cminflt - p->old_cminflt, p->old_cminflt, p->cminflt);
1544 - if(p->cmajflt - p->old_cmajflt > 15000) fprintf(stderr, "BIG CHANGE: %d %s cmajflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cmajflt - p->old_cmajflt, p->old_cmajflt, p->cmajflt);
1769 + if(unlikely(debug || w->debug))
1770 + fprintf(stderr, "apps.plugin: \tAgregating %s pid %d on %s utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->pid, w->name, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
1771 +
1772 +/* if(p->utime - p->old_utime > 100) fprintf(stderr, "BIG CHANGE: %d %s utime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->utime - p->old_utime, p->old_utime, p->utime);
1773 + if(p->cutime - p->old_cutime > 100) fprintf(stderr, "BIG CHANGE: %d %s cutime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cutime - p->old_cutime, p->old_cutime, p->cutime);
1774 + if(p->stime - p->old_stime > 100) fprintf(stderr, "BIG CHANGE: %d %s stime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->stime - p->old_stime, p->old_stime, p->stime);
1775 + if(p->cstime - p->old_cstime > 100) fprintf(stderr, "BIG CHANGE: %d %s cstime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cstime - p->old_cstime, p->old_cstime, p->cstime);
1776 + if(p->minflt - p->old_minflt > 5000) fprintf(stderr, "BIG CHANGE: %d %s minflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->minflt - p->old_minflt, p->old_minflt, p->minflt);
1777 + if(p->majflt - p->old_majflt > 5000) fprintf(stderr, "BIG CHANGE: %d %s majflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->majflt - p->old_majflt, p->old_majflt, p->majflt);
1778 + if(p->cminflt - p->old_cminflt > 15000) fprintf(stderr, "BIG CHANGE: %d %s cminflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cminflt - p->old_cminflt, p->old_cminflt, p->cminflt);
1779 + if(p->cmajflt - p->old_cmajflt > 15000) fprintf(stderr, "BIG CHANGE: %d %s cmajflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cmajflt - p->old_cmajflt, p->old_cmajflt, p->cmajflt);
1780 */
1546 -#ifdef INCLUDE_CHILDS
1547 - p->old_utime = p->utime;
1548 - p->old_cutime = p->cutime;
1549 - p->old_stime = p->stime;
1550 - p->old_cstime = p->cstime;
1551 - p->old_minflt = p->minflt;
1552 - p->old_majflt = p->majflt;
1553 - p->old_cminflt = p->cminflt;
1554 - p->old_cmajflt = p->cmajflt;
1555 -#endif /* INCLUDE_CHILDS */
1556 - }
1557 - else {
1558 - // since the process has exited, the user
1559 - // will see a drop in our charts, because the incremental
1560 - // values of this process will not be there
1561 -
1562 - // add them to the fix_* values and they will be added to
1563 - // the reported values, so that the report goes steady
1564 - p->target->fix_minflt += p->minflt;
1565 - p->target->fix_majflt += p->majflt;
1566 - p->target->fix_utime += p->utime;
1567 - p->target->fix_stime += p->stime;
1568 - p->target->fix_cminflt += p->cminflt;
1569 - p->target->fix_cmajflt += p->cmajflt;
1570 - p->target->fix_cutime += p->cutime;
1571 - p->target->fix_cstime += p->cstime;
1572 -
1573 - p->target->fix_io_logical_bytes_read += p->io_logical_bytes_read;
1574 - p->target->fix_io_logical_bytes_written += p->io_logical_bytes_written;
1575 - p->target->fix_io_read_calls += p->io_read_calls;
1576 - p->target->fix_io_write_calls += p->io_write_calls;
1577 - p->target->fix_io_storage_bytes_read += p->io_storage_bytes_read;
1578 - p->target->fix_io_storage_bytes_written += p->io_storage_bytes_written;
1579 - p->target->fix_io_cancelled_write_bytes += p->io_cancelled_write_bytes;
1781 +#ifdef AGGREGATE_CHILDREN_TO_PARENTS
1782 + p->old_utime = p->utime;
1783 + p->old_cutime = p->cutime;
1784 + p->old_stime = p->stime;
1785 + p->old_cstime = p->cstime;
1786 + p->old_minflt = p->minflt;
1787 + p->old_majflt = p->majflt;
1788 + p->old_cminflt = p->cminflt;
1789 + p->old_cmajflt = p->cmajflt;
1790 +#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
1791 +
1792 + if(o) {
1793 + // since the process switched target
1794 + // for all incremental values
1795 + // we have to subtract its OLD values from the new target
1796 + // and add its OLD values to the old target
1797 +
1798 + // IMPORTANT
1799 + // We add/subtract the last/OLD values we added to the target
1800 +
1801 + w->fix_cutime -= p->last_cutime;
1802 + w->fix_cstime -= p->last_cstime;
1803 + w->fix_cminflt -= p->last_cminflt;
1804 + w->fix_cmajflt -= p->last_cmajflt;
1805 +
1806 + w->fix_utime -= p->last_utime;
1807 + w->fix_stime -= p->last_stime;
1808 + w->fix_minflt -= p->last_minflt;
1809 + w->fix_majflt -= p->last_majflt;
1810 +
1811 +
1812 + w->fix_io_logical_bytes_read -= p->last_io_logical_bytes_read;
1813 + w->fix_io_logical_bytes_written -= p->last_io_logical_bytes_written;
1814 + w->fix_io_read_calls -= p->last_io_read_calls;
1815 + w->fix_io_write_calls -= p->last_io_write_calls;
1816 + w->fix_io_storage_bytes_read -= p->last_io_storage_bytes_read;
1817 + w->fix_io_storage_bytes_written -= p->last_io_storage_bytes_written;
1818 + w->fix_io_cancelled_write_bytes -= p->last_io_cancelled_write_bytes;
1819 +
1820 + // ---
1821 +
1822 + o->fix_cutime += p->last_cutime;
1823 + o->fix_cstime += p->last_cstime;
1824 + o->fix_cminflt += p->last_cminflt;
1825 + o->fix_cmajflt += p->last_cmajflt;
1826 +
1827 + o->fix_utime += p->last_utime;
1828 + o->fix_stime += p->last_stime;
1829 + o->fix_minflt += p->last_minflt;
1830 + o->fix_majflt += p->last_majflt;
1831 +
1832 + o->fix_io_logical_bytes_read += p->last_io_logical_bytes_read;
1833 + o->fix_io_logical_bytes_written += p->last_io_logical_bytes_written;
1834 + o->fix_io_read_calls += p->last_io_read_calls;
1835 + o->fix_io_write_calls += p->last_io_write_calls;
1836 + o->fix_io_storage_bytes_read += p->last_io_storage_bytes_read;
1837 + o->fix_io_storage_bytes_written += p->last_io_storage_bytes_written;
1838 + o->fix_io_cancelled_write_bytes += p->last_io_cancelled_write_bytes;
1839 }
1840 }
1841 + else {
1842 + // if(o) fprintf(stderr, "apps.plugin: \t\tpid %d (%s) is not updated by OLD target %s (%s) is present.\n", p->pid, p->comm, o->id, o->name);
1843 +
1844 + // since the process has exited, the user
1845 + // will see a drop in our charts, because the incremental
1846 + // values of this process will not be there
1847 +
1848 + // add them to the fix_* values and they will be added to
1849 + // the reported values, so that the report goes steady
1850 + w->fix_minflt += p->minflt;
1851 + w->fix_majflt += p->majflt;
1852 + w->fix_utime += p->utime;
1853 + w->fix_stime += p->stime;
1854 + w->fix_cminflt += p->cminflt;
1855 + w->fix_cmajflt += p->cmajflt;
1856 + w->fix_cutime += p->cutime;
1857 + w->fix_cstime += p->cstime;
1858 +
1859 + w->fix_io_logical_bytes_read += p->io_logical_bytes_read;
1860 + w->fix_io_logical_bytes_written += p->io_logical_bytes_written;
1861 + w->fix_io_read_calls += p->io_read_calls;
1862 + w->fix_io_write_calls += p->io_write_calls;
1863 + w->fix_io_storage_bytes_read += p->io_storage_bytes_read;
1864 + w->fix_io_storage_bytes_written += p->io_storage_bytes_written;
1865 + w->fix_io_cancelled_write_bytes += p->io_cancelled_write_bytes;
1866 + }
1867
1583 -// fprintf(stderr, "\n");
1584 - // cleanup all un-updated processed (exited, killed, etc)
1585 - for(p = root_of_pids; p ;) {
1586 - if(!p->updated) {
1587 -// fprintf(stderr, "\tEXITED %d %s [parent %d %s, target %s] utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->parent->pid, p->parent->comm, p->target->name, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
1868 +}
1869
1589 - for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
1590 - file_descriptor_not_used(p->fds[c]);
1591 - p->fds[c] = 0;
1592 - }
1870 +void count_targets_fds(struct target *root) {
1871 + int c;
1872 + struct target *w;
1873
1594 - pid_t r = p->pid;
1595 - p = p->next;
1596 - del_pid_entry(r);
1597 - }
1598 - else p = p->next;
1599 - }
1874 + for (w = root; w ; w = w->next) {
1875 + if(!w->fds) continue;
1876
1601 - for (w = target_root; w ; w = w->next) {
1877 w->openfiles = 0;
1878 w->openpipes = 0;
1879 w->opensockets = 0;
@@ -1610,7 +1885,8 @@ void update_statistics(void)
1885 w->openother = 0;
1886
1887 for(c = 1; c < all_files_size ;c++) {
1613 - if(w->fds && w->fds[c] > 0) switch(all_files[c].type) {
1888 + if(w->fds[c] > 0)
1889 + switch(all_files[c].type) {
1890 case FILETYPE_FILE:
1891 w->openfiles++;
1892 break;
@@ -1653,15 +1929,90 @@ void update_statistics(void)
1929 }
1930 }
1931
1932 +void calculate_netdata_statistics(void)
1933 +{
1934 + link_all_processes_to_their_parents();
1935 + apply_apps_groups_targets_inheritance();
1936 +
1937 +#ifdef AGGREGATE_CHILDREN_TO_PARENTS
1938 + aggregate_children_to_parents();
1939 +#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
1940 +
1941 + zero_all_targets(users_root_target);
1942 + zero_all_targets(groups_root_target);
1943 + apps_groups_targets = zero_all_targets(apps_groups_root_target);
1944 +
1945 +#ifdef AGGREGATE_CHILDREN_TO_PARENTS
1946 + if(debug)
1947 + debug_childrens_aggregations(0, 1);
1948 +#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
1949 +
1950 + // this has to be done, before the cleanup
1951 + struct pid_stat *p = NULL;
1952 + struct target *w = NULL, *o = NULL;
1953 +
1954 + // concentrate everything on the apps_groups_targets
1955 + for(p = root_of_pids; p ; p = p->next) {
1956 +
1957 + // --------------------------------------------------------------------
1958 + // apps_groups targets
1959 + if(likely(p->target))
1960 + aggregate_pid_on_target(p->target, p, NULL);
1961 + else
1962 + error("pid %d %s was left without a target!", p->pid, p->comm);
1963 +
1964 +
1965 + // --------------------------------------------------------------------
1966 + // user targets
1967 + o = p->user_target;
1968 + if(likely(p->user_target && p->user_target->uid == p->uid))
1969 + w = p->user_target;
1970 + else {
1971 + if(unlikely(debug && p->user_target))
1972 + fprintf(stderr, "apps.plugin: \t\tpid %d (%s) switched user from %d (%s) to %d.\n", p->pid, p->comm, p->user_target->uid, p->user_target->name, p->uid);
1973 +
1974 + w = p->user_target = get_users_target(p->uid);
1975 + }
1976 +
1977 + if(likely(w))
1978 + aggregate_pid_on_target(w, p, o);
1979 + else
1980 + error("pid %d %s was left without a user target!", p->pid, p->comm);
1981 +
1982 +
1983 + // --------------------------------------------------------------------
1984 + // group targets
1985 + o = p->group_target;
1986 + if(likely(p->group_target && p->group_target->gid == p->gid))
1987 + w = p->group_target;
1988 + else {
1989 + if(unlikely(debug && p->group_target))
1990 + fprintf(stderr, "apps.plugin: \t\tpid %d (%s) switched group from %d (%s) to %d.\n", p->pid, p->comm, p->group_target->gid, p->group_target->name, p->gid);
1991 +
1992 + w = p->group_target = get_groups_target(p->gid);
1993 + }
1994 +
1995 + if(likely(w))
1996 + aggregate_pid_on_target(w, p, o);
1997 + else
1998 + error("pid %d %s was left without a group target!", p->pid, p->comm);
1999 +
2000 + }
2001 +
2002 + count_targets_fds(apps_groups_root_target);
2003 + count_targets_fds(users_root_target);
2004 + count_targets_fds(groups_root_target);
2005 +
2006 + cleanup_non_existing_pids();
2007 +}
2008 +
2009 // ----------------------------------------------------------------------------
2010 // update chart dimensions
2011
1659 -void show_dimensions(void)
1660 -{
2012 +unsigned long long send_resource_usage_to_netdata() {
2013 static struct timeval last = { 0, 0 };
2014 static struct rusage me_last;
2015
1664 - struct target *w;
2016 struct timeval now;
2017 struct rusage me;
2018
@@ -1692,138 +2043,145 @@ void show_dimensions(void)
2043 bcopy(&me, &me_last, sizeof(struct rusage));
2044 }
2045
1695 - fprintf(stdout, "BEGIN apps.cpu %llu\n", usec);
1696 - for (w = target_root; w ; w = w->next) {
2046 + fprintf(stdout, "BEGIN netdata.apps_cpu %llu\n", usec);
2047 + fprintf(stdout, "SET user = %llu\n", cpuuser);
2048 + fprintf(stdout, "SET system = %llu\n", cpusyst);
2049 + fprintf(stdout, "END\n");
2050 +
2051 + fprintf(stdout, "BEGIN netdata.apps_files %llu\n", usec);
2052 + fprintf(stdout, "SET files = %llu\n", file_counter);
2053 + fprintf(stdout, "SET pids = %ld\n", all_pids_count);
2054 + fprintf(stdout, "SET fds = %d\n", all_files_len);
2055 + fprintf(stdout, "SET targets = %ld\n", apps_groups_targets);
2056 + fprintf(stdout, "END\n");
2057 +
2058 + return usec;
2059 +}
2060 +
2061 +void send_collected_data_to_netdata(struct target *root, const char *type, unsigned long long usec)
2062 +{
2063 + struct target *w;
2064 +
2065 + fprintf(stdout, "BEGIN %s.cpu %llu\n", type, usec);
2066 + for (w = root; w ; w = w->next) {
2067 if(w->target || (!w->processes && !w->exposed)) continue;
2068
2069 fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->stime + w->fix_utime + w->fix_stime);
2070 }
2071 fprintf(stdout, "END\n");
2072
1703 - fprintf(stdout, "BEGIN apps.cpu_user %llu\n", usec);
1704 - for (w = target_root; w ; w = w->next) {
2073 + fprintf(stdout, "BEGIN %s.cpu_user %llu\n", type, usec);
2074 + for (w = root; w ; w = w->next) {
2075 if(w->target || (!w->processes && !w->exposed)) continue;
2076
2077 fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->fix_utime);
2078 }
2079 fprintf(stdout, "END\n");
2080
1711 - fprintf(stdout, "BEGIN apps.cpu_system %llu\n", usec);
1712 - for (w = target_root; w ; w = w->next) {
2081 + fprintf(stdout, "BEGIN %s.cpu_system %llu\n", type, usec);
2082 + for (w = root; w ; w = w->next) {
2083 if(w->target || (!w->processes && !w->exposed)) continue;
2084
2085 fprintf(stdout, "SET %s = %llu\n", w->name, w->stime + w->fix_stime);
2086 }
2087 fprintf(stdout, "END\n");
2088
1719 - fprintf(stdout, "BEGIN apps.threads %llu\n", usec);
1720 - for (w = target_root; w ; w = w->next) {
2089 + fprintf(stdout, "BEGIN %s.threads %llu\n", type, usec);
2090 + for (w = root; w ; w = w->next) {
2091 if(w->target || (!w->processes && !w->exposed)) continue;
2092
2093 fprintf(stdout, "SET %s = %llu\n", w->name, w->num_threads);
2094 }
2095 fprintf(stdout, "END\n");
2096
1727 - fprintf(stdout, "BEGIN apps.processes %llu\n", usec);
1728 - for (w = target_root; w ; w = w->next) {
2097 + fprintf(stdout, "BEGIN %s.processes %llu\n", type, usec);
2098 + for (w = root; w ; w = w->next) {
2099 if(w->target || (!w->processes && !w->exposed)) continue;
2100
2101 fprintf(stdout, "SET %s = %lu\n", w->name, w->processes);
2102 }
2103 fprintf(stdout, "END\n");
2104
1735 - fprintf(stdout, "BEGIN apps.mem %llu\n", usec);
1736 - for (w = target_root; w ; w = w->next) {
2105 + fprintf(stdout, "BEGIN %s.mem %llu\n", type, usec);
2106 + for (w = root; w ; w = w->next) {
2107 if(w->target || (!w->processes && !w->exposed)) continue;
2108
2109 fprintf(stdout, "SET %s = %lld\n", w->name, (long long)w->statm_resident - (long long)w->statm_share);
2110 }
2111 fprintf(stdout, "END\n");
2112
1743 - fprintf(stdout, "BEGIN apps.minor_faults %llu\n", usec);
1744 - for (w = target_root; w ; w = w->next) {
2113 + fprintf(stdout, "BEGIN %s.minor_faults %llu\n", type, usec);
2114 + for (w = root; w ; w = w->next) {
2115 if(w->target || (!w->processes && !w->exposed)) continue;
2116
2117 fprintf(stdout, "SET %s = %llu\n", w->name, w->minflt + w->fix_minflt);
2118 }
2119 fprintf(stdout, "END\n");
2120
1751 - fprintf(stdout, "BEGIN apps.major_faults %llu\n", usec);
1752 - for (w = target_root; w ; w = w->next) {
2121 + fprintf(stdout, "BEGIN %s.major_faults %llu\n", type, usec);
2122 + for (w = root; w ; w = w->next) {
2123 if(w->target || (!w->processes && !w->exposed)) continue;
2124
2125 fprintf(stdout, "SET %s = %llu\n", w->name, w->majflt + w->fix_majflt);
2126 }
2127 fprintf(stdout, "END\n");
2128
1759 - fprintf(stdout, "BEGIN apps.lreads %llu\n", usec);
1760 - for (w = target_root; w ; w = w->next) {
2129 + fprintf(stdout, "BEGIN %s.lreads %llu\n", type, usec);
2130 + for (w = root; w ; w = w->next) {
2131 if(w->target || (!w->processes && !w->exposed)) continue;
2132
1763 - fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_read);
2133 + fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_read + w->fix_io_logical_bytes_read);
2134 }
2135 fprintf(stdout, "END\n");
2136
1767 - fprintf(stdout, "BEGIN apps.lwrites %llu\n", usec);
1768 - for (w = target_root; w ; w = w->next) {
2137 + fprintf(stdout, "BEGIN %s.lwrites %llu\n", type, usec);
2138 + for (w = root; w ; w = w->next) {
2139 if(w->target || (!w->processes && !w->exposed)) continue;
2140
1771 - fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_written);
2141 + fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_written + w->fix_io_logical_bytes_written);
2142 }
2143 fprintf(stdout, "END\n");
2144
1775 - fprintf(stdout, "BEGIN apps.preads %llu\n", usec);
1776 - for (w = target_root; w ; w = w->next) {
2145 + fprintf(stdout, "BEGIN %s.preads %llu\n", type, usec);
2146 + for (w = root; w ; w = w->next) {
2147 if(w->target || (!w->processes && !w->exposed)) continue;
2148
1779 - fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_read);
2149 + fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_read + w->fix_io_storage_bytes_read);
2150 }
2151 fprintf(stdout, "END\n");
2152
1783 - fprintf(stdout, "BEGIN apps.pwrites %llu\n", usec);
1784 - for (w = target_root; w ; w = w->next) {
2153 + fprintf(stdout, "BEGIN %s.pwrites %llu\n", type, usec);
2154 + for (w = root; w ; w = w->next) {
2155 if(w->target || (!w->processes && !w->exposed)) continue;
2156
1787 - fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_written);
2157 + fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_written + w->fix_io_storage_bytes_written);
2158 }
2159 fprintf(stdout, "END\n");
2160
1791 - fprintf(stdout, "BEGIN apps.files %llu\n", usec);
1792 - for (w = target_root; w ; w = w->next) {
2161 + fprintf(stdout, "BEGIN %s.files %llu\n", type, usec);
2162 + for (w = root; w ; w = w->next) {
2163 if(w->target || (!w->processes && !w->exposed)) continue;
2164
2165 fprintf(stdout, "SET %s = %llu\n", w->name, w->openfiles);
2166 }
2167 fprintf(stdout, "END\n");
2168
1799 - fprintf(stdout, "BEGIN apps.sockets %llu\n", usec);
1800 - for (w = target_root; w ; w = w->next) {
2169 + fprintf(stdout, "BEGIN %s.sockets %llu\n", type, usec);
2170 + for (w = root; w ; w = w->next) {
2171 if(w->target || (!w->processes && !w->exposed)) continue;
2172
2173 fprintf(stdout, "SET %s = %llu\n", w->name, w->opensockets);
2174 }
2175 fprintf(stdout, "END\n");
2176
1807 - fprintf(stdout, "BEGIN apps.pipes %llu\n", usec);
1808 - for (w = target_root; w ; w = w->next) {
2177 + fprintf(stdout, "BEGIN %s.pipes %llu\n", type, usec);
2178 + for (w = root; w ; w = w->next) {
2179 if(w->target || (!w->processes && !w->exposed)) continue;
2180
2181 fprintf(stdout, "SET %s = %llu\n", w->name, w->openpipes);
2182 }
2183 fprintf(stdout, "END\n");
2184
1815 - fprintf(stdout, "BEGIN netdata.apps_cpu %llu\n", usec);
1816 - fprintf(stdout, "SET user = %llu\n", cpuuser);
1817 - fprintf(stdout, "SET system = %llu\n", cpusyst);
1818 - fprintf(stdout, "END\n");
1819 -
1820 - fprintf(stdout, "BEGIN netdata.apps_files %llu\n", usec);
1821 - fprintf(stdout, "SET files = %llu\n", file_counter);
1822 - fprintf(stdout, "SET pids = %ld\n", all_pids_count);
1823 - fprintf(stdout, "SET fds = %d\n", all_files_len);
1824 - fprintf(stdout, "SET targets = %ld\n", targets);
1825 - fprintf(stdout, "END\n");
1826 -
2185 fflush(stdout);
2186 }
2187
@@ -1831,12 +2189,12 @@ void show_dimensions(void)
2189 // ----------------------------------------------------------------------------
2190 // generate the charts
2191
1834 -void show_charts(void)
2192 +void send_charts_updates_to_netdata(struct target *root, const char *type, const char *title)
2193 {
2194 struct target *w;
2195 int newly_added = 0;
2196
1839 - for(w = target_root ; w ; w = w->next)
2197 + for(w = root ; w ; w = w->next)
2198 if(!w->exposed && w->processes) {
2199 newly_added++;
2200 w->exposed = 1;
@@ -1848,122 +2206,110 @@ void show_charts(void)
2206
2207 // we have something new to show
2208 // update the charts
1851 - fprintf(stdout, "CHART apps.cpu '' 'Apps CPU Time (%ld%% = %ld core%s)' 'cpu time %%' cpu apps.cpu stacked 20001 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
1852 - for (w = target_root; w ; w = w->next) {
2209 + fprintf(stdout, "CHART %s.cpu '' '%s CPU Time (%ld%% = %ld core%s)' 'cpu time %%' cpu %s.cpu stacked 20001 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
2210 + for (w = root; w ; w = w->next) {
2211 if(w->target || (!w->processes && !w->exposed)) continue;
2212
2213 fprintf(stdout, "DIMENSION %s '' incremental 100 %llu %s\n", w->name, Hertz, w->hidden ? "hidden,noreset" : "noreset");
2214 }
2215
1858 - fprintf(stdout, "CHART apps.mem '' 'Apps Dedicated Memory (w/o shared)' 'MB' mem apps.mem stacked 20003 %d\n", update_every);
1859 - for (w = target_root; w ; w = w->next) {
2216 + fprintf(stdout, "CHART %s.mem '' '%s Dedicated Memory (w/o shared)' 'MB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every);
2217 + for (w = root; w ; w = w->next) {
2218 if(w->target || (!w->processes && !w->exposed)) continue;
2219
2220 fprintf(stdout, "DIMENSION %s '' absolute %ld %ld noreset\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
2221 }
2222
1865 - fprintf(stdout, "CHART apps.threads '' 'Apps Threads' 'threads' processes apps.threads stacked 20005 %d\n", update_every);
1866 - for (w = target_root; w ; w = w->next) {
2223 + fprintf(stdout, "CHART %s.threads '' '%s Threads' 'threads' processes %s.threads stacked 20005 %d\n", type, title, type, update_every);
2224 + for (w = root; w ; w = w->next) {
2225 if(w->target || (!w->processes && !w->exposed)) continue;
2226
2227 fprintf(stdout, "DIMENSION %s '' absolute 1 1 noreset\n", w->name);
2228 }
2229
1872 - fprintf(stdout, "CHART apps.processes '' 'Apps Processes' 'processes' processes apps.processes stacked 20004 %d\n", update_every);
1873 - for (w = target_root; w ; w = w->next) {
2230 + fprintf(stdout, "CHART %s.processes '' '%s Processes' 'processes' processes %s.processes stacked 20004 %d\n", type, title, type, update_every);
2231 + for (w = root; w ; w = w->next) {
2232 if(w->target || (!w->processes && !w->exposed)) continue;
2233
2234 fprintf(stdout, "DIMENSION %s '' absolute 1 1 noreset\n", w->name);
2235 }
2236
1879 - fprintf(stdout, "CHART apps.cpu_user '' 'Apps CPU User Time (%ld%% = %ld core%s)' 'cpu time %%' cpu apps.cpu_user stacked 20020 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
1880 - for (w = target_root; w ; w = w->next) {
2237 + fprintf(stdout, "CHART %s.cpu_user '' '%s CPU User Time (%ld%% = %ld core%s)' 'cpu time %%' cpu %s.cpu_user stacked 20020 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
2238 + for (w = root; w ; w = w->next) {
2239 if(w->target || (!w->processes && !w->exposed)) continue;
2240
2241 fprintf(stdout, "DIMENSION %s '' incremental 100 %llu noreset\n", w->name, Hertz * processors);
2242 }
2243
1886 - fprintf(stdout, "CHART apps.cpu_system '' 'Apps CPU System Time (%ld%% = %ld core%s)' 'cpu time %%' cpu apps.cpu_system stacked 20021 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
1887 - for (w = target_root; w ; w = w->next) {
2244 + fprintf(stdout, "CHART %s.cpu_system '' '%s CPU System Time (%ld%% = %ld core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20021 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
2245 + for (w = root; w ; w = w->next) {
2246 if(w->target || (!w->processes && !w->exposed)) continue;
2247
2248 fprintf(stdout, "DIMENSION %s '' incremental 100 %llu noreset\n", w->name, Hertz * processors);
2249 }
2250
1893 - fprintf(stdout, "CHART apps.major_faults '' 'Apps Major Page Faults (swap read)' 'page faults/s' swap apps.major_faults stacked 20010 %d\n", update_every);
1894 - for (w = target_root; w ; w = w->next) {
2251 + fprintf(stdout, "CHART %s.major_faults '' '%s Major Page Faults (swap read)' 'page faults/s' swap %s.major_faults stacked 20010 %d\n", type, title, type, update_every);
2252 + for (w = root; w ; w = w->next) {
2253 if(w->target || (!w->processes && !w->exposed)) continue;
2254
2255 fprintf(stdout, "DIMENSION %s '' incremental 1 1 noreset\n", w->name);
2256 }
2257
1900 - fprintf(stdout, "CHART apps.minor_faults '' 'Apps Minor Page Faults' 'page faults/s' mem apps.minor_faults stacked 20011 %d\n", update_every);
1901 - for (w = target_root; w ; w = w->next) {
2258 + fprintf(stdout, "CHART %s.minor_faults '' '%s Minor Page Faults' 'page faults/s' mem %s.minor_faults stacked 20011 %d\n", type, title, type, update_every);
2259 + for (w = root; w ; w = w->next) {
2260 if(w->target || (!w->processes && !w->exposed)) continue;
2261
2262 fprintf(stdout, "DIMENSION %s '' incremental 1 1 noreset\n", w->name);
2263 }
2264
1907 - fprintf(stdout, "CHART apps.lreads '' 'Apps Disk Logical Reads' 'kilobytes/s' disk apps.lreads stacked 20042 %d\n", update_every);
1908 - for (w = target_root; w ; w = w->next) {
2265 + fprintf(stdout, "CHART %s.lreads '' '%s Disk Logical Reads' 'kilobytes/s' disk %s.lreads stacked 20042 %d\n", type, title, type, update_every);
2266 + for (w = root; w ; w = w->next) {
2267 if(w->target || (!w->processes && !w->exposed)) continue;
2268
2269 fprintf(stdout, "DIMENSION %s '' incremental 1 %d noreset\n", w->name, 1024);
2270 }
2271
1914 - fprintf(stdout, "CHART apps.lwrites '' 'Apps I/O Logical Writes' 'kilobytes/s' disk apps.lwrites stacked 20042 %d\n", update_every);
1915 - for (w = target_root; w ; w = w->next) {
2272 + fprintf(stdout, "CHART %s.lwrites '' '%s I/O Logical Writes' 'kilobytes/s' disk %s.lwrites stacked 20042 %d\n", type, title, type, update_every);
2273 + for (w = root; w ; w = w->next) {
2274 if(w->target || (!w->processes && !w->exposed)) continue;
2275
2276 fprintf(stdout, "DIMENSION %s '' incremental 1 %d noreset\n", w->name, 1024);
2277 }
2278
1921 - fprintf(stdout, "CHART apps.preads '' 'Apps Disk Reads' 'kilobytes/s' disk apps.preads stacked 20002 %d\n", update_every);
1922 - for (w = target_root; w ; w = w->next) {
2279 + fprintf(stdout, "CHART %s.preads '' '%s Disk Reads' 'kilobytes/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every);
2280 + for (w = root; w ; w = w->next) {
2281 if(w->target || (!w->processes && !w->exposed)) continue;
2282
2283 fprintf(stdout, "DIMENSION %s '' incremental 1 %d noreset\n", w->name, 1024);
2284 }
2285
1928 - fprintf(stdout, "CHART apps.pwrites '' 'Apps Disk Writes' 'kilobytes/s' disk apps.pwrites stacked 20002 %d\n", update_every);
1929 - for (w = target_root; w ; w = w->next) {
2286 + fprintf(stdout, "CHART %s.pwrites '' '%s Disk Writes' 'kilobytes/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every);
2287 + for (w = root; w ; w = w->next) {
2288 if(w->target || (!w->processes && !w->exposed)) continue;
2289
2290 fprintf(stdout, "DIMENSION %s '' incremental 1 %d noreset\n", w->name, 1024);
2291 }
2292
1935 - fprintf(stdout, "CHART apps.files '' 'Apps Open Files' 'open files' disk apps.files stacked 20050 %d\n", update_every);
1936 - for (w = target_root; w ; w = w->next) {
2293 + fprintf(stdout, "CHART %s.files '' '%s Open Files' 'open files' disk %s.files stacked 20050 %d\n", type, title, type, update_every);
2294 + for (w = root; w ; w = w->next) {
2295 if(w->target || (!w->processes && !w->exposed)) continue;
2296
2297 fprintf(stdout, "DIMENSION %s '' absolute 1 1 noreset\n", w->name);
2298 }
2299
1942 - fprintf(stdout, "CHART apps.sockets '' 'Apps Open Sockets' 'open sockets' net apps.sockets stacked 20051 %d\n", update_every);
1943 - for (w = target_root; w ; w = w->next) {
2300 + fprintf(stdout, "CHART %s.sockets '' '%s Open Sockets' 'open sockets' net %s.sockets stacked 20051 %d\n", type, title, type, update_every);
2301 + for (w = root; w ; w = w->next) {
2302 if(w->target || (!w->processes && !w->exposed)) continue;
2303
2304 fprintf(stdout, "DIMENSION %s '' absolute 1 1 noreset\n", w->name);
2305 }
2306
1949 - fprintf(stdout, "CHART apps.pipes '' 'Apps Pipes' 'open pipes' processes apps.pipes stacked 20053 %d\n", update_every);
1950 - for (w = target_root; w ; w = w->next) {
2307 + fprintf(stdout, "CHART %s.pipes '' '%s Pipes' 'open pipes' processes %s.pipes stacked 20053 %d\n", type, title, type, update_every);
2308 + for (w = root; w ; w = w->next) {
2309 if(w->target || (!w->processes && !w->exposed)) continue;
2310
2311 fprintf(stdout, "DIMENSION %s '' absolute 1 1 noreset\n", w->name);
2312 }
1955 -
1956 - fprintf(stdout, "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %d\n", update_every);
1957 - fprintf(stdout, "DIMENSION user '' incremental 1 %d\n", 1000);
1958 - fprintf(stdout, "DIMENSION system '' incremental 1 %d\n", 1000);
1959 -
1960 - fprintf(stdout, "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_files line 140001 %d\n", update_every);
1961 - fprintf(stdout, "DIMENSION files '' incremental 1 1\n");
1962 - fprintf(stdout, "DIMENSION pids '' absolute 1 1\n");
1963 - fprintf(stdout, "DIMENSION fds '' absolute 1 1\n");
1964 - fprintf(stdout, "DIMENSION targets '' absolute 1 1\n");
1965 -
1966 - fflush(stdout);
2313 }
2314
2315
@@ -2002,12 +2348,18 @@ void parse_args(int argc, char **argv)
2348 if(freq > 0) update_every = freq;
2349 if(!name) name = "groups";
2350
2005 - if(read_process_groups(name)) {
2351 + if(read_apps_groups_conf(name)) {
2352 error("Cannot read process groups %s", name);
2353 exit(1);
2354 }
2355 }
2356
2357 +unsigned long long sutime() {
2358 + struct timeval now;
2359 + gettimeofday(&now, NULL);
2360 + return now.tv_sec * 1000000ULL + now.tv_usec;
2361 +}
2362 +
2363 int main(int argc, char **argv)
2364 {
2365 // debug_flags = D_PROCFILE;
@@ -2035,9 +2387,9 @@ int main(int argc, char **argv)
2387
2388 time_t started_t = time(NULL);
2389 time_t current_t;
2038 - Hertz = get_hertz();
2039 - pid_max = get_pid_max();
2040 - processors = get_processors();
2390 + Hertz = get_system_hertz();
2391 + pid_max = get_system_pid_max();
2392 + processors = get_system_cpus();
2393
2394 parse_args(argc, argv);
2395
@@ -2048,39 +2400,63 @@ int main(int argc, char **argv)
2400 exit(1);
2401 }
2402
2051 - unsigned long long counter = 1;
2052 - unsigned long long usec = 0, susec = 0;
2053 - struct timeval last, now;
2054 - gettimeofday(&last, NULL);
2403 + fprintf(stdout, "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %d\n", update_every);
2404 + fprintf(stdout, "DIMENSION user '' incremental 1 %d\n", 1000);
2405 + fprintf(stdout, "DIMENSION system '' incremental 1 %d\n", 1000);
2406
2407 + fprintf(stdout, "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_files line 140001 %d\n", update_every);
2408 + fprintf(stdout, "DIMENSION files '' incremental 1 1\n");
2409 + fprintf(stdout, "DIMENSION pids '' absolute 1 1\n");
2410 + fprintf(stdout, "DIMENSION fds '' absolute 1 1\n");
2411 + fprintf(stdout, "DIMENSION targets '' absolute 1 1\n");
2412 +
2413 +
2414 +#ifndef PROFILING_MODE
2415 + unsigned long long sunext = (time(NULL) - (time(NULL) % update_every) + update_every) * 1000000ULL;
2416 + unsigned long long sunow;
2417 +#endif /* PROFILING_MODE */
2418 +
2419 + unsigned long long counter = 1;
2420 for(;1; counter++) {
2057 - if(!update_from_proc()) {
2058 - error("Cannot allocate %lu bytes of memory.", sizeof(struct pid_stat *) * pid_max);
2421 +#ifndef PROFILING_MODE
2422 + // delay until it is our time to run
2423 + while((sunow = sutime()) < sunext)
2424 + usleep((useconds_t)(sunext - sunow));
2425 +
2426 + // find the next time we need to run
2427 + while(sutime() > sunext)
2428 + sunext += update_every * 1000000ULL;
2429 +#endif /* PROFILING_MODE */
2430 +
2431 + if(!collect_data_for_all_processes_from_proc()) {
2432 + error("Cannot collect /proc data for running processes. Disabling apps.plugin...");
2433 printf("DISABLE\n");
2434 exit(1);
2435 }
2436
2063 - update_statistics();
2064 - show_charts(); // this is smart enough to show only newly added apps, when needed
2065 - show_dimensions();
2437 + calculate_netdata_statistics();
2438
2067 - if(debug) fprintf(stderr, "apps.plugin: done Loop No %llu\n", counter);
2068 - fflush(NULL);
2439 + unsigned long long dt = send_resource_usage_to_netdata();
2440
2070 - gettimeofday(&now, NULL);
2071 - usec = usecdiff(&now, &last) - susec;
2072 - if(debug) fprintf(stderr, "apps.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).\n", usec + susec, usec, susec);
2441 + // this is smart enough to show only newly added apps, when needed
2442 + send_charts_updates_to_netdata(apps_groups_root_target, "apps", "Apps");
2443 + send_charts_updates_to_netdata(users_root_target, "users", "Users");
2444 + send_charts_updates_to_netdata(groups_root_target, "groups", "User Groups");
2445
2074 - // if the last loop took less than half the time
2075 - // wait the rest of the time
2076 - if(usec < (update_every * 1000000ULL / 2)) susec = (update_every * 1000000ULL) - usec;
2077 - else susec = update_every * 1000000ULL / 2;
2446 + send_collected_data_to_netdata(apps_groups_root_target, "apps", dt);
2447 + send_collected_data_to_netdata(users_root_target, "users", dt);
2448 + send_collected_data_to_netdata(groups_root_target, "groups", dt);
2449
2079 - usleep((useconds_t) susec);
2080 - bcopy(&now, &last, sizeof(struct timeval));
2450 + if(debug) fprintf(stderr, "apps.plugin: done Loop No %llu\n", counter);
2451 + fflush(NULL);
2452
2082 - // restart once per day (14400 seconds)
2453 current_t = time(NULL);
2454 +
2455 +#ifndef PROFILING_MODE
2456 + // restart check (14400 seconds)
2457 if(current_t - started_t > 14400) exit(0);
2458 +#else
2459 + if(current_t - started_t > 10) exit(0);
2460 +#endif /* PROFILING_MODE */
2461 }
2462 }
src/avl.c
+21
@@ -145,19 +145,23 @@ int _avl_insert(avl_tree* t, avl* a) {
145 }
146 }
147 int avl_insert(avl_tree* t, avl* a) {
148 +#ifndef AVL_WITHOUT_PTHREADS
149 #ifdef AVL_LOCK_WITH_MUTEX
150 pthread_mutex_lock(&t->mutex);
151 #else
152 pthread_rwlock_wrlock(&t->rwlock);
153 #endif
154 +#endif /* AVL_WITHOUT_PTHREADS */
155
156 int ret = _avl_insert(t, a);
157
158 +#ifndef AVL_WITHOUT_PTHREADS
159 #ifdef AVL_LOCK_WITH_MUTEX
160 pthread_mutex_unlock(&t->mutex);
161 #else
162 pthread_rwlock_unlock(&t->rwlock);
163 #endif
164 +#endif /* AVL_WITHOUT_PTHREADS */
165 return ret;
166 }
167
@@ -243,19 +247,23 @@ int _avl_remove(avl_tree* t, avl* a) {
247 }
248
249 int avl_remove(avl_tree* t, avl* a) {
250 +#ifndef AVL_WITHOUT_PTHREADS
251 #ifdef AVL_LOCK_WITH_MUTEX
252 pthread_mutex_lock(&t->mutex);
253 #else
254 pthread_rwlock_wrlock(&t->rwlock);
255 #endif
256 +#endif /* AVL_WITHOUT_PTHREADS */
257
258 int ret = _avl_remove(t, a);
259
260 +#ifndef AVL_WITHOUT_PTHREADS
261 #ifdef AVL_LOCK_WITH_MUTEX
262 pthread_mutex_unlock(&t->mutex);
263 #else
264 pthread_rwlock_unlock(&t->rwlock);
265 #endif
266 +#endif /* AVL_WITHOUT_PTHREADS */
267 return ret;
268 }
269
@@ -299,19 +307,23 @@ int _avl_removeroot(avl_tree* t) {
307 }
308
309 int avl_removeroot(avl_tree* t) {
310 +#ifndef AVL_WITHOUT_PTHREADS
311 #ifdef AVL_LOCK_WITH_MUTEX
312 pthread_mutex_lock(&t->mutex);
313 #else
314 pthread_rwlock_wrlock(&t->rwlock);
315 #endif
316 +#endif /* AVL_WITHOUT_PTHREADS */
317
318 int ret = _avl_removeroot(t);
319
320 +#ifndef AVL_WITHOUT_PTHREADS
321 #ifdef AVL_LOCK_WITH_MUTEX
322 pthread_mutex_unlock(&t->mutex);
323 #else
324 pthread_rwlock_unlock(&t->rwlock);
325 #endif
326 +#endif /* AVL_WITHOUT_PTHREADS */
327 return ret;
328 }
329
@@ -363,19 +375,23 @@ int _avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl*), avl** ret) {
375 }
376
377 int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl*), avl** ret) {
378 +#ifndef AVL_WITHOUT_PTHREADS
379 #ifdef AVL_LOCK_WITH_MUTEX
380 pthread_mutex_lock(&t->mutex);
381 #else
382 pthread_rwlock_wrlock(&t->rwlock);
383 #endif
384 +#endif /* AVL_WITHOUT_PTHREADS */
385
386 int ret2 = _avl_range(t, a, b, iter, ret);
387
388 +#ifndef AVL_WITHOUT_PTHREADS
389 #ifdef AVL_LOCK_WITH_MUTEX
390 pthread_mutex_unlock(&t->mutex);
391 #else
392 pthread_rwlock_unlock(&t->rwlock);
393 #endif
394 +#endif /* AVL_WITHOUT_PTHREADS */
395
396 return ret2;
397 }
@@ -392,7 +408,9 @@ void avl_init(avl_tree* t, int (*compar)(void* a, void* b)) {
408 t->root = NULL;
409 t->compar = compar;
410
411 +#ifndef AVL_WITHOUT_PTHREADS
412 int lock;
413 +
414 #ifdef AVL_LOCK_WITH_MUTEX
415 lock = pthread_mutex_init(&t->mutex, NULL);
416 #else
@@ -401,4 +419,7 @@ void avl_init(avl_tree* t, int (*compar)(void* a, void* b)) {
419
420 if(lock != 0)
421 fatal("Failed to initialize AVL mutex/rwlock, error: %d", lock);
422 +
423 +#endif /* AVL_WITHOUT_PTHREADS */
424 +
425 }
src/avl.h
+6 -2
@@ -15,7 +15,9 @@
15 #ifndef _AVL_H
16 #define _AVL_H 1
17
18 +#ifndef AVL_WITHOUT_PTHREADS
19 #include <pthread.h>
20 +#endif /* AVL_WITHOUT_PTHREADS */
21
22 // #define AVL_LOCK_WITH_MUTEX 1
23
@@ -33,11 +35,13 @@ typedef struct avl_tree {
35 avl* root;
36 int (*compar)(void* a, void* b);
37
38 +#ifndef AVL_WITHOUT_PTHREADS
39 #ifdef AVL_LOCK_WITH_MUTEX
40 pthread_mutex_t mutex;
38 -#else
41 +#else /* AVL_LOCK_WITH_MUTEX */
42 pthread_rwlock_t rwlock;
40 -#endif
43 +#endif /* AVL_LOCK_WITH_MUTEX */
44 +#endif /* AVL_WITHOUT_PTHREADS */
45 } avl_tree;
46
47 /* Public methods */
src/log.c
-2
@@ -143,9 +143,7 @@ void log_access( const char *fmt, ... )
143 vfprintf( stdaccess, fmt, args );
144 va_end( args );
145 fprintf( stdaccess, "\n");
146 -#ifdef NETDATA_INTERNAL_CHECKS
146 fflush( stdaccess );
148 -#endif
147 }
148
149 if(access_log_syslog) {
web/index.html
+37 -1
@@ -939,6 +939,18 @@ var menuData = {
939 height: 1.5
940 },
941
942 + 'users': {
943 + title: 'Users',
944 + info: 'Per user statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics per user.<br/><b>IMPORTANT</b>: The values shown here are not 100% accurate. They only include values for the processes running. If an application is spawning childs continiously, which are terminated in just a few milliseconds (like shell scripts do), the values reported will be inaccurate. Linux does report the values for the exited childs of a process. However, these values are reported to the parent process <b>only when the child exits</b>. If these values, of the exited child processes, were also aggregated in the charts below, the charts would have been full of spikes, presenting unrealisting utilization for each process group. So, we decided to ignore these values and present only the utilization of <b>the currently running processes</b>.',
945 + height: 1.5
946 + },
947 +
948 + 'groups': {
949 + title: 'User Groups',
950 + info: 'Per user group statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics per user group.<br/><b>IMPORTANT</b>: The values shown here are not 100% accurate. They only include values for the processes running. If an application is spawning childs continiously, which are terminated in just a few milliseconds (like shell scripts do), the values reported will be inaccurate. Linux does report the values for the exited childs of a process. However, these values are reported to the parent process <b>only when the child exits</b>. If these values, of the exited child processes, were also aggregated in the charts below, the charts would have been full of spikes, presenting unrealisting utilization for each process group. So, we decided to ignore these values and present only the utilization of <b>the currently running processes</b>.',
951 + height: 1.5
952 + },
953 +
954 'netdata': {
955 title: 'Netdata Monitoring',
956 info: undefined
@@ -1075,6 +1087,30 @@ var chartData = {
1087 height: 2.0
1088 },
1089
1090 + 'users.cpu': {
1091 + height: 2.0
1092 + },
1093 +
1094 + 'users.preads': {
1095 + height: 2.0
1096 + },
1097 +
1098 + 'users.pwrites': {
1099 + height: 2.0
1100 + },
1101 +
1102 + 'groups.cpu': {
1103 + height: 2.0
1104 + },
1105 +
1106 + 'groups.preads': {
1107 + height: 2.0
1108 + },
1109 +
1110 + 'groups.pwrites': {
1111 + height: 2.0
1112 + },
1113 +
1114 'tc.qos': {
1115 heads: [
1116 function(id) {
@@ -1224,7 +1260,7 @@ function menuTitle(menu) {
1260 }
1261
1262 function menuInfo(menu) {
1227 - return anyAttribute(menuData, 'title', menu, null);
1263 + return anyAttribute(menuData, 'info', menu, null);
1264 }
1265
1266 function menuHeight(menu, relative) {