1 #define __STDC_FORMAT_MACROS
13 #include <sys/resource.h>
25 #include <arpa/inet.h>
27 #define MAX_COMPARE_NAME 15
30 unsigned long long Hertz = 1;
37 unsigned long long file_counter = 0;
39 #define PROC_BUFFER 4096
42 // ----------------------------------------------------------------------------
45 unsigned long long usecdiff(struct timeval *now, struct timeval *last) {
46 return ((((now->tv_sec * 1000000ULL) + now->tv_usec) - ((last->tv_sec * 1000000ULL) + last->tv_usec)));
51 // skip leading spaces
52 while(*s && isspace(*s)) s++;
55 // skip tailing spaces
56 int c = strlen(s) - 1;
57 while(c >= 0 && isspace(s[c])) {
61 if(c < 0) return NULL;
66 unsigned long simple_hash(const char *name)
68 int i, len = strlen(name);
69 unsigned long hash = 0;
71 for(i = 0; i < len ;i++) hash += (i * name[i]) + i + name[i];
76 long get_processors(void)
78 char buffer[1025], *s;
81 FILE *fp = fopen("/proc/stat", "r");
84 while((s = fgets(buffer, 1024, fp))) {
85 if(strncmp(buffer, "cpu", 3) == 0) processors++;
89 if(processors < 1) processors = 1;
93 long get_pid_max(void)
95 char buffer[1025], *s;
98 FILE *fp = fopen("/proc/sys/kernel/pid_max", "r");
101 s = fgets(buffer, 1024, fp);
102 if(s) mpid = atol(buffer);
104 if(mpid < 32768) mpid = 32768;
108 unsigned long long get_hertz(void)
110 unsigned long long hz = 1;
113 if((hz = sysconf(_SC_CLK_TCK)) > 0) {
119 hz = (unsigned long long)HZ; /* <asm/param.h> */
121 /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
122 hz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
125 fprintf(stderr, "apps.plugin: ERROR: unknown HZ value. Assuming %llu.\n", hz);
130 // ----------------------------------------------------------------------------
132 // target is the point to aggregate a process tree values
135 char compare[MAX_COMPARE_NAME + 1];
136 char id[MAX_NAME + 1];
137 char name[MAX_NAME + 1];
139 unsigned long long minflt;
140 unsigned long long cminflt;
141 unsigned long long majflt;
142 unsigned long long cmajflt;
143 unsigned long long utime;
144 unsigned long long stime;
145 unsigned long long cutime;
146 unsigned long long cstime;
147 unsigned long long num_threads;
148 unsigned long long rss;
150 unsigned long long fix_minflt;
151 unsigned long long fix_cminflt;
152 unsigned long long fix_majflt;
153 unsigned long long fix_cmajflt;
154 unsigned long long fix_utime;
155 unsigned long long fix_stime;
156 unsigned long long fix_cutime;
157 unsigned long long fix_cstime;
159 unsigned long long statm_size;
160 unsigned long long statm_resident;
161 unsigned long long statm_share;
162 unsigned long long statm_text;
163 unsigned long long statm_lib;
164 unsigned long long statm_data;
165 unsigned long long statm_dirty;
167 unsigned long long io_logical_bytes_read;
168 unsigned long long io_logical_bytes_written;
169 unsigned long long io_read_calls;
170 unsigned long long io_write_calls;
171 unsigned long long io_storage_bytes_read;
172 unsigned long long io_storage_bytes_written;
173 unsigned long long io_cancelled_write_bytes;
175 unsigned long long fix_io_logical_bytes_read;
176 unsigned long long fix_io_logical_bytes_written;
177 unsigned long long fix_io_read_calls;
178 unsigned long long fix_io_write_calls;
179 unsigned long long fix_io_storage_bytes_read;
180 unsigned long long fix_io_storage_bytes_written;
181 unsigned long long fix_io_cancelled_write_bytes;
184 unsigned long long openfiles;
185 unsigned long long openpipes;
186 unsigned long long opensockets;
187 unsigned long long openinotifies;
188 unsigned long long openeventfds;
189 unsigned long long opentimerfds;
190 unsigned long long opensignalfds;
191 unsigned long long openeventpolls;
192 unsigned long long openother;
194 unsigned long processes; // how many processes have been merged to this
195 int exposed; // if set, we have sent this to netdata
196 int hidden; // if set, we set the hidden flag on the dimension
199 struct target *target; // the one that will be reported to netdata
201 } *target_root = NULL, *default_target = NULL;
205 // find or create a target
206 // there are targets that are just agregated to other target (the second argument)
207 struct target *get_target(const char *id, struct target *target)
209 const char *nid = id;
210 if(nid[0] == '-') nid++;
213 for(w = target_root ; w ; w = w->next)
214 if(strncmp(nid, w->id, MAX_NAME) == 0) return w;
216 w = calloc(sizeof(struct target), 1);
218 fprintf(stderr, "apps.plugin: cannot allocate %lu bytes of memory\n", (unsigned long)sizeof(struct target));
222 strncpy(w->id, nid, MAX_NAME);
223 strncpy(w->name, nid, MAX_NAME);
224 strncpy(w->compare, nid, MAX_COMPARE_NAME);
225 if(id[0] == '-') w->hidden = 1;
229 w->next = target_root;
232 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:"");
237 // read the process groups file
238 int read_process_groups(const char *name)
241 char filename[FILENAME_MAX + 1];
243 snprintf(filename, FILENAME_MAX, "%s/apps_%s.conf", CONFIG_DIR, name);
245 if(debug) fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename);
246 FILE *fp = fopen(filename, "r");
248 fprintf(stderr, "apps.plugin: ERROR: cannot open file '%s' (%s)\n", filename, strerror(errno));
253 while(fgets(buffer, 4096, fp) != NULL) {
254 int whidden = 0, wdebug = 0;
257 // if(debug) fprintf(stderr, "apps.plugin: \tread %s\n", buffer);
259 char *s = buffer, *t, *p;
261 if(!s || !*s || *s == '#') continue;
263 if(debug) fprintf(stderr, "apps.plugin: \tread %s\n", s);
268 if(!t || !*t) continue;
290 if(debug) fprintf(stderr, "apps.plugin: \t\ttarget %s\n", t);
292 struct target *w = NULL;
296 while((p = strsep(&s, " "))) {
298 if(!p || !*p) continue;
300 struct target *n = get_target(p, w);
308 if(w) strncpy(w->name, t, MAX_NAME);
309 if(!count) fprintf(stderr, "apps.plugin: ERROR: the line %ld on file '%s', for group '%s' does not state any process names.\n", line, filename, t);
313 default_target = get_target("+p!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing
314 strncpy(default_target->name, "other", MAX_NAME);
320 // ----------------------------------------------------------------------------
321 // data to store for each pid
326 char comm[MAX_COMPARE_NAME + 1];
334 unsigned long long minflt;
335 unsigned long long cminflt;
336 unsigned long long majflt;
337 unsigned long long cmajflt;
338 unsigned long long utime;
339 unsigned long long stime;
340 unsigned long long cutime;
341 unsigned long long cstime;
346 unsigned long long starttime;
347 unsigned long long vsize;
348 unsigned long long rss;
349 unsigned long long rsslim;
350 unsigned long long starcode;
351 unsigned long long endcode;
352 unsigned long long startstack;
353 unsigned long long kstkesp;
354 unsigned long long kstkeip;
364 uint32_t rt_priority;
366 unsigned long long delayacct_blkio_ticks;
370 unsigned long long statm_size;
371 unsigned long long statm_resident;
372 unsigned long long statm_share;
373 unsigned long long statm_text;
374 unsigned long long statm_lib;
375 unsigned long long statm_data;
376 unsigned long long statm_dirty;
378 unsigned long long io_logical_bytes_read;
379 unsigned long long io_logical_bytes_written;
380 unsigned long long io_read_calls;
381 unsigned long long io_write_calls;
382 unsigned long long io_storage_bytes_read;
383 unsigned long long io_storage_bytes_written;
384 unsigned long long io_cancelled_write_bytes;
386 #ifdef INCLUDE_CHILDS
387 unsigned long long old_utime;
388 unsigned long long old_stime;
389 unsigned long long old_minflt;
390 unsigned long long old_majflt;
392 unsigned long long old_cutime;
393 unsigned long long old_cstime;
394 unsigned long long old_cminflt;
395 unsigned long long old_cmajflt;
397 unsigned long long fix_cutime;
398 unsigned long long fix_cstime;
399 unsigned long long fix_cminflt;
400 unsigned long long fix_cmajflt;
402 unsigned long long diff_cutime;
403 unsigned long long diff_cstime;
404 unsigned long long diff_cminflt;
405 unsigned long long diff_cmajflt;
408 int *fds; // array of fds it uses
409 int fds_size; // the size of the fds array
411 int childs; // number of processes directly referencing this
412 int updated; // 1 when update
413 int merged; // 1 when it has been merged to its parent
415 struct target *target;
416 struct pid_stat *parent;
417 struct pid_stat *prev;
418 struct pid_stat *next;
419 } *root = NULL, **all_pids;
423 struct pid_stat *get_entry(pid_t pid)
426 all_pids[pid]->new_entry = 0;
427 return all_pids[pid];
430 all_pids[pid] = calloc(sizeof(struct pid_stat), 1);
432 fprintf(stderr, "apps.plugin: ERROR: Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct pid_stat));
436 all_pids[pid]->fds = calloc(sizeof(int), 100);
437 if(!all_pids[pid]->fds)
438 fprintf(stderr, "apps.plugin: ERROR: Cannot allocate %ld bytes of memory\n", (unsigned long)(sizeof(int) * 100));
439 else all_pids[pid]->fds_size = 100;
441 if(root) root->prev = all_pids[pid];
442 all_pids[pid]->next = root;
443 root = all_pids[pid];
445 all_pids[pid]->new_entry = 1;
447 return all_pids[pid];
450 void del_entry(pid_t pid)
452 if(!all_pids[pid]) return;
454 if(debug) fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm);
456 if(root == all_pids[pid]) root = all_pids[pid]->next;
457 if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
458 if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
460 if(all_pids[pid]->fds) free(all_pids[pid]->fds);
462 all_pids[pid] = NULL;
465 #ifdef INCLUDE_CHILDS
466 // print a tree view of all processes
467 int walk_down(pid_t pid, int level) {
468 struct pid_stat *p = NULL;
472 for(i = 0; i < level; i++) b[i] = '\t';
477 for(p = root; p ; p = p->next) {
479 ret += walk_down(p->pid, level+1);
485 if(!p->updated) ret += 1;
486 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"
487 , b, p->comm, p->pid, p->updated?"OK":"KILLED", p->target->name, p->childs
488 , p->utime, p->utime - p->old_utime
489 , p->stime, p->stime - p->old_stime
490 , p->cutime, p->cutime - p->old_cutime
491 , p->cstime, p->cstime - p->old_cstime
492 , p->minflt, p->minflt - p->old_minflt
493 , p->majflt, p->majflt - p->old_majflt
494 , p->cminflt, p->cminflt - p->old_cminflt
495 , p->cmajflt, p->cmajflt - p->old_cmajflt
504 // ----------------------------------------------------------------------------
506 // this is used to keep a global list of all open files of the system
507 // it is needed in order to figure out the unique files a process tree has open
509 struct file_descriptor {
516 int all_files_len = 0;
517 int all_files_size = 0;
519 #define FILETYPE_OTHER 0
520 #define FILETYPE_FILE 1
521 #define FILETYPE_PIPE 2
522 #define FILETYPE_SOCKET 3
523 #define FILETYPE_INOTIFY 4
524 #define FILETYPE_EVENTFD 5
525 #define FILETYPE_EVENTPOLL 6
526 #define FILETYPE_TIMERFD 7
527 #define FILETYPE_SIGNALFD 8
529 void file_descriptor_not_used(int id)
531 if(id > 0 && id < all_files_len) {
532 if(all_files[id].count > 0)
533 all_files[id].count--;
535 fprintf(stderr, "apps.plugin: ERROR: request to decrease counter of fd %d (%s), while the use counter is 0\n", id, all_files[id].name);
537 else fprintf(stderr, "apps.plugin: ERROR: request to decrease counter of fd %d, which is outside the array size (1 to %d)\n", id, all_files_len);
540 unsigned long file_descriptor_find_or_add(const char *name)
542 int type = FILETYPE_OTHER;
544 if(name[0] == '/') type = FILETYPE_FILE;
545 else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE;
546 else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET;
547 else if(strcmp(name, "anon_inode:inotify") == 0 || strcmp(name, "inotify") == 0) type = FILETYPE_INOTIFY;
548 else if(strcmp(name, "anon_inode:[eventfd]") == 0) type = FILETYPE_EVENTFD;
549 else if(strcmp(name, "anon_inode:[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
550 else if(strcmp(name, "anon_inode:[timerfd]") == 0) type = FILETYPE_TIMERFD;
551 else if(strcmp(name, "anon_inode:[signalfd]") == 0) type = FILETYPE_SIGNALFD;
552 else if(strncmp(name, "anon_inode:", 11) == 0) {
553 if(debug) fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
554 type = FILETYPE_OTHER;
557 if(debug) fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name);
558 type = FILETYPE_OTHER;
563 all_files = malloc(1024 * sizeof(struct file_descriptor));
564 if(!all_files) return 0;
566 all_files_size = 1024;
567 all_files_len = 1; // start from 1, skip 0
568 if(debug) fprintf(stderr, "apps.plugin: initialized fd array to %d entries\n", all_files_size);
572 unsigned long hash = simple_hash(name);
574 for( c = 0 ; c < all_files_len ; c++) {
575 if(all_files[c].hash == hash && strcmp(all_files[c].name, name) == 0) break;
579 if(c < all_files_len) {
580 all_files[c].count++;
584 // not found, search for an empty slot
585 for(c = 0 ; c < all_files_len ; c++) {
586 if(!all_files[c].count) {
587 if(debug) fprintf(stderr, "apps.plugin: re-using fd position %d (last name: %s)\n", c, all_files[c].name);
588 if(all_files[c].name) free(all_files[c].name);
593 if(c == all_files_len) {
594 // not found any emtpty slot
597 if(c >= all_files_size) {
598 // not enough memory - extend it
599 if(debug) fprintf(stderr, "apps.plugin: extending fd array to %d entries\n", all_files_size + 1024);
600 all_files = realloc(all_files, (all_files_size + 1024) * sizeof(struct file_descriptor));
601 all_files_size += 1024;
604 // else we have the slot in 'c'
606 all_files[c].name = strdup(name);
607 all_files[c].hash = hash;
608 all_files[c].type = type;
609 all_files[c].count++;
611 if(debug) fprintf(stderr, "apps.plugin: using fd position %d (name: %s)\n", c, all_files[c].name);
617 // ----------------------------------------------------------------------------
618 // update pids from proc
620 // 1. read all files in /proc
621 // 2. for each numeric directory:
622 // i. read /proc/pid/stat
623 // ii. read /proc/pid/statm
624 // iii. read /proc/pid/io (requires root access)
625 // iii. read the entries in directory /proc/pid/fd (requires root access)
627 // a. find or create a struct file_descriptor
628 // b. cleanup any old/unused file_descriptors
630 // after all these, some pids may be linked to targets, while others may not
632 // in case of errors, only 1 every 1000 errors is printed
633 // to avoid filling up all disk space
634 // if debug is enabled, all errors are printed
636 int update_from_proc(void)
638 static long count_errors = 0;
640 char buffer[PROC_BUFFER + 1];
641 char name[PROC_BUFFER + 1];
642 char filename[FILENAME_MAX+1];
643 DIR *dir = opendir("/proc");
646 struct dirent *file = NULL;
647 struct pid_stat *p = NULL;
649 // mark them all as un-updated
651 for(p = root; p ; p = p->next) {
660 while((file = readdir(dir))) {
661 char *endptr = file->d_name;
662 pid_t pid = strtoul(file->d_name, &endptr, 10);
663 if(pid <= 0 || pid > pid_max || endptr == file->d_name || *endptr != '\0') continue;
666 // --------------------------------------------------------------------
669 snprintf(filename, FILENAME_MAX, "/proc/%s/stat", file->d_name);
670 int fd = open(filename, O_RDONLY);
672 if(errno != ENOENT && errno != ESRCH) {
673 if(!count_errors++ || debug)
674 fprintf(stderr, "apps.plugin: ERROR: cannot open file '%s' for reading (%d, %s).\n", filename, errno, strerror(errno));
680 int bytes = read(fd, buffer, PROC_BUFFER);
684 if(!count_errors++ || debug)
685 fprintf(stderr, "apps.plugin: ERROR: cannot read from file '%s' (%s).\n", filename, strerror(errno));
689 if(bytes < 10) continue;
690 buffer[bytes] = '\0';
691 if(debug) fprintf(stderr, "apps.plugin: READ stat: %s", buffer);
696 int parsed = sscanf(buffer,
697 "%d (%[^)]) %c" // pid, comm, state
698 " %d %d %d %d %d" // ppid, pgrp, session, tty_nr, tpgid
699 " %" PRIu64 " %llu %llu %llu %llu" // flags, minflt, cminflt, majflt, cmajflt
700 " %llu %llu %llu %llu" // utime, stime, cutime, cstime
701 " %" PRId64 " %" PRId64 // priority, nice
703 " %" PRId64 // itrealvalue
707 " %llu %llu %llu %llu %llu %llu" // rsslim, starcode, endcode, startstack, kstkesp, kstkeip
708 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 // signal, blocked, sigignore, sigcatch
709 " %" PRIu64 " %" PRIu64 " %" PRIu64 // wchan, nswap, cnswap
710 " %d %d" // exit_signal, processor
711 " %u %u" // rt_priority, policy
712 " %llu %" PRIu64 " %" PRId64 // delayacct_blkio_ticks, guest_time, cguest_time
713 , &p->pid, name, &p->state
714 , &p->ppid, &p->pgrp, &p->session, &p->tty_nr, &p->tpgid
715 , &p->flags, &p->minflt, &p->cminflt, &p->majflt, &p->cmajflt
716 , &p->utime, &p->stime, &p->cutime, &p->cstime
717 , &p->priority, &p->nice
723 , &p->rsslim, &p->starcode, &p->endcode, &p->startstack, &p->kstkesp, &p->kstkeip
724 , &p->signal, &p->blocked, &p->sigignore, &p->sigcatch
725 , &p->wchan, &p->nswap, &p->cnswap
726 , &p->exit_signal, &p->processor
727 , &p->rt_priority, &p->policy
728 , &p->delayacct_blkio_ticks, &p->guest_time, &p->cguest_time
730 strncpy(p->comm, name, MAX_COMPARE_NAME);
731 p->comm[MAX_COMPARE_NAME] = '\0';
733 if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: VALUES: %s utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
736 if(!count_errors++ || debug || (p->target && p->target->debug))
737 fprintf(stderr, "apps.plugin: ERROR: file %s gave %d results (expected 44)\n", filename, parsed);
740 // check if it is target
741 // we do this only once, the first time this pid is loaded
743 if(debug) fprintf(stderr, "apps.plugin: \tJust added %s\n", p->comm);
746 for(w = target_root; w ; w = w->next) {
747 // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
749 if(strcmp(w->compare, p->comm) == 0) {
750 if(w->target) p->target = w->target;
753 if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
759 if(p->ppid < 0 || p->ppid > pid_max) p->ppid = 0;
762 // --------------------------------------------------------------------
765 snprintf(filename, FILENAME_MAX, "/proc/%s/statm", file->d_name);
766 fd = open(filename, O_RDONLY);
768 if(errno != ENOENT && errno != ESRCH) {
769 if(!count_errors++ || debug || (p->target && p->target->debug))
770 fprintf(stderr, "apps.plugin: ERROR: cannot open file '%s' for reading (%d, %s).\n", filename, errno, strerror(errno));
775 bytes = read(fd, buffer, PROC_BUFFER);
779 if(!count_errors++ || debug || (p->target && p->target->debug))
780 fprintf(stderr, "apps.plugin: ERROR: cannot read from file '%s' (%s).\n", filename, strerror(errno));
782 else if(bytes > 10) {
783 buffer[bytes] = '\0';
784 if(debug || (p->target && p->target->debug))
785 fprintf(stderr, "apps.plugin: READ statm: %s", buffer);
787 parsed = sscanf(buffer,
788 "%llu %llu %llu %llu %llu %llu %llu"
799 if(!count_errors++ || debug || (p->target && p->target->debug))
800 fprintf(stderr, "apps.plugin: ERROR: file %s gave %d results (expected 7)\n", filename, parsed);
805 // --------------------------------------------------------------------
808 snprintf(filename, FILENAME_MAX, "/proc/%s/io", file->d_name);
809 fd = open(filename, O_RDONLY);
811 if(errno != ENOENT && errno != ESRCH) {
812 if(!count_errors++ || debug || (p->target && p->target->debug))
813 fprintf(stderr, "apps.plugin: ERROR: cannot open file '%s' for reading (%d, %s).\n", filename, errno, strerror(errno));
818 bytes = read(fd, buffer, PROC_BUFFER);
822 if(!count_errors++ || debug || (p->target && p->target->debug))
823 fprintf(stderr, "apps.plugin: ERROR: cannot read from file '%s' (%s).\n", filename, strerror(errno));
825 else if(bytes > 10) {
826 buffer[bytes] = '\0';
827 if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: READ io: %s", buffer);
829 parsed = sscanf(buffer,
830 "rchar: %llu\nwchar: %llu\nsyscr: %llu\nsyscw: %llu\nread_bytes: %llu\nwrite_bytes: %llu\ncancelled_write_bytes: %llu"
831 , &p->io_logical_bytes_read
832 , &p->io_logical_bytes_written
835 , &p->io_storage_bytes_read
836 , &p->io_storage_bytes_written
837 , &p->io_cancelled_write_bytes
841 if(!count_errors++ || debug || (p->target && p->target->debug))
842 fprintf(stderr, "apps.plugin: ERROR: file %s gave %d results (expected 7)\n", filename, parsed);
847 // --------------------------------------------------------------------
850 snprintf(filename, FILENAME_MAX, "/proc/%s/fd", file->d_name);
851 DIR *fds = opendir(filename);
855 char fdname[FILENAME_MAX + 1];
856 char linkname[FILENAME_MAX + 1];
858 // make the array negative
859 for(c = 0 ; c < p->fds_size ; c++) p->fds[c] = -p->fds[c];
861 while((de = readdir(fds))) {
862 if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue;
864 // check if the fds array is small
865 int fdid = atol(de->d_name);
866 if(fdid < 0) continue;
867 if(fdid >= p->fds_size) {
868 // it is small, extend it
869 if(debug) fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100);
870 p->fds = realloc(p->fds, (fdid + 100) * sizeof(int));
872 fprintf(stderr, "apps.plugin: ERROR: cannot re-allocate fds for %s\n", p->comm);
877 for(c = p->fds_size ; c < (fdid + 100) ; c++) p->fds[c] = 0;
878 p->fds_size = fdid + 100;
881 if(p->fds[fdid] == 0) {
882 // we don't know this fd, get it
884 sprintf(fdname, "/proc/%s/fd/%s", file->d_name, de->d_name);
885 int l = readlink(fdname, linkname, FILENAME_MAX);
887 if(debug || (p->target && p->target->debug)) {
888 if(!count_errors++ || debug || (p->target && p->target->debug))
889 fprintf(stderr, "apps.plugin: ERROR: cannot read link %s\n", fdname);
896 // if another process already has this, we will get
898 p->fds[fdid] = file_descriptor_find_or_add(linkname);
901 // else make it positive again, we need it
902 // of course, the actual file may have changed, but we don't care so much
903 // FIXME: we could compare the inode as returned by readdir direct structure
904 else p->fds[fdid] = -p->fds[fdid];
908 // remove all the negative file descriptors
909 for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] < 0) {
910 file_descriptor_not_used(-p->fds[c]);
915 // --------------------------------------------------------------------
918 // mark it as updated
921 if(count_errors > 1000) {
922 fprintf(stderr, "apps.plugin: ERROR: %ld more errors encountered\n", count_errors - 1);
932 // ----------------------------------------------------------------------------
933 // update statistics on the targets
935 // 1. link all childs to their parents
936 // 2. go from bottom to top, marking as merged all childs to their parents
937 // this step links all parents without a target to the child target, if any
938 // 3. link all top level processes (the ones not merged) to the default target
939 // 4. go from top to bottom, linking all childs without a target, to their parent target
940 // after this step, all processes have a target
941 // [5. for each killed pid (updated = 0), remove its usage from its target]
942 // 6. zero all targets
943 // 7. concentrate all values on the targets
944 // 8. remove all killed processes
945 // 9. find the unique file count for each target
947 void update_statistics(void)
950 struct pid_stat *p = NULL;
952 // link all parents and update childs count
953 for(p = root; p ; p = p->next) {
954 if(p->ppid > 0 && p->ppid <= pid_max && all_pids[p->ppid]) {
955 if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \tparent of %d %s is %d %s\n", p->pid, p->comm, p->ppid, all_pids[p->ppid]->comm);
957 p->parent = all_pids[p->ppid];
960 else if(p->ppid != 0) fprintf(stderr, "apps.plugin: \t\tWRONG! pid %d %s states parent %d, but the later does not exist.\n", p->pid, p->comm, p->ppid);
963 // find all the procs with 0 childs and merge them to their parents
964 // repeat, until nothing more can be done.
968 for(p = root; p ; p = p->next) {
969 // if this process does not have any childs, and
970 // is not already merged, and
971 // its parent has childs waiting to be merged, and
972 // the target of this process and its parent is the same, or the parent does not have a target, or this process does not have a parent
973 // and its parent is not init
974 // then... merge them!
975 if(!p->childs && !p->merged && p->parent && p->parent->childs && (p->target == p->parent->target || !p->parent->target || !p->target) && p->ppid != 1) {
979 // the parent inherits the child's target, if it does not have a target itself
980 if(p->target && !p->parent->target) {
981 p->parent->target = p->target;
982 if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\ttarget %s is inherited by %d %s from its child %d %s.\n", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);
988 if(debug) fprintf(stderr, "apps.plugin: merged %d processes\n", found);
991 // give a default target on all top level processes
992 // init goes always to default target
993 if(all_pids[1]) all_pids[1]->target = default_target;
995 for(p = root; p ; p = p->next) {
996 // if the process is not merged itself
997 // then is is a top level process
998 if(!p->merged && !p->target) p->target = default_target;
1000 #ifdef INCLUDE_CHILDS
1001 // by the way, update the diffs
1002 // will be used later for substracting killed process times
1003 p->diff_cutime = p->utime - p->cutime;
1004 p->diff_cstime = p->stime - p->cstime;
1005 p->diff_cminflt = p->minflt - p->cminflt;
1006 p->diff_cmajflt = p->majflt - p->cmajflt;
1010 // give a target to all merged child processes
1014 for(p = root; p ; p = p->next) {
1015 if(!p->target && p->merged && p->parent && p->parent->target) {
1016 p->target = p->parent->target;
1022 #ifdef INCLUDE_CHILDS
1023 // for each killed process, remove its values from the parents
1024 // sums (we had already added them in a previous loop)
1025 for(p = root; p ; p = p->next) {
1026 if(p->updated) continue;
1028 fprintf(stderr, "apps.plugin: UNMERGING %d %s\n", p->pid, p->comm);
1030 unsigned long long diff_utime = p->utime + p->cutime + p->fix_cutime;
1031 unsigned long long diff_stime = p->stime + p->cstime + p->fix_cstime;
1032 unsigned long long diff_minflt = p->minflt + p->cminflt + p->fix_cminflt;
1033 unsigned long long diff_majflt = p->majflt + p->cmajflt + p->fix_cmajflt;
1035 struct pid_stat *t = p;
1036 while((t = t->parent)) {
1037 if(!t->updated) continue;
1039 unsigned long long x;
1040 if(diff_utime && t->diff_cutime) {
1041 x = (t->diff_cutime < diff_utime)?t->diff_cutime:diff_utime;
1043 t->diff_cutime -= x;
1045 fprintf(stderr, "apps.plugin: \t cutime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1047 if(diff_stime && t->diff_cstime) {
1048 x = (t->diff_cstime < diff_stime)?t->diff_cstime:diff_stime;
1050 t->diff_cstime -= x;
1052 fprintf(stderr, "apps.plugin: \t cstime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1054 if(diff_minflt && t->diff_cminflt) {
1055 x = (t->diff_cminflt < diff_minflt)?t->diff_cminflt:diff_minflt;
1057 t->diff_cminflt -= x;
1058 t->fix_cminflt += x;
1059 fprintf(stderr, "apps.plugin: \t cminflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1061 if(diff_majflt && t->diff_cmajflt) {
1062 x = (t->diff_cmajflt < diff_majflt)?t->diff_cmajflt:diff_majflt;
1064 t->diff_cmajflt -= x;
1065 t->fix_cmajflt += x;
1066 fprintf(stderr, "apps.plugin: \t cmajflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1070 if(diff_utime) fprintf(stderr, "apps.plugin: \t cannot fix up utime %llu\n", diff_utime);
1071 if(diff_stime) fprintf(stderr, "apps.plugin: \t cannot fix up stime %llu\n", diff_stime);
1072 if(diff_minflt) fprintf(stderr, "apps.plugin: \t cannot fix up minflt %llu\n", diff_minflt);
1073 if(diff_majflt) fprintf(stderr, "apps.plugin: \t cannot fix up majflt %llu\n", diff_majflt);
1077 // zero all the targets
1080 for (w = target_root; w ; w = w->next) {
1083 w->fds = calloc(sizeof(int), all_files_len);
1085 fprintf(stderr, "apps.plugin: ERROR: cannot allocate memory for fds in %s\n", w->name);
1100 w->statm_resident = 0;
1107 w->io_logical_bytes_read = 0;
1108 w->io_logical_bytes_written = 0;
1109 w->io_read_calls = 0;
1110 w->io_write_calls = 0;
1111 w->io_storage_bytes_read = 0;
1112 w->io_storage_bytes_written = 0;
1113 w->io_cancelled_write_bytes = 0;
1116 #ifdef INCLUDE_CHILDS
1117 if(debug) walk_down(0, 1);
1120 // concentrate everything on the targets
1121 for(p = root; p ; p = p->next) {
1123 fprintf(stderr, "apps.plugin: ERROR: pid %d %s was left without a target!\n", p->pid, p->comm);
1128 p->target->cutime += p->cutime; // - p->fix_cutime;
1129 p->target->cstime += p->cstime; // - p->fix_cstime;
1130 p->target->cminflt += p->cminflt; // - p->fix_cminflt;
1131 p->target->cmajflt += p->cmajflt; // - p->fix_cmajflt;
1133 p->target->utime += p->utime; //+ (p->pid != 1)?(p->cutime - p->fix_cutime):0;
1134 p->target->stime += p->stime; //+ (p->pid != 1)?(p->cstime - p->fix_cstime):0;
1135 p->target->minflt += p->minflt; //+ (p->pid != 1)?(p->cminflt - p->fix_cminflt):0;
1136 p->target->majflt += p->majflt; //+ (p->pid != 1)?(p->cmajflt - p->fix_cmajflt):0;
1138 p->target->num_threads += p->num_threads;
1139 p->target->rss += p->rss;
1141 p->target->statm_size += p->statm_size;
1142 p->target->statm_resident += p->statm_resident;
1143 p->target->statm_share += p->statm_share;
1144 p->target->statm_text += p->statm_text;
1145 p->target->statm_lib += p->statm_lib;
1146 p->target->statm_data += p->statm_data;
1147 p->target->statm_dirty += p->statm_dirty;
1149 p->target->io_logical_bytes_read += p->io_logical_bytes_read;
1150 p->target->io_logical_bytes_written += p->io_logical_bytes_written;
1151 p->target->io_read_calls += p->io_read_calls;
1152 p->target->io_write_calls += p->io_write_calls;
1153 p->target->io_storage_bytes_read += p->io_storage_bytes_read;
1154 p->target->io_storage_bytes_written += p->io_storage_bytes_written;
1155 p->target->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
1157 p->target->processes++;
1159 for(c = 0; c < p->fds_size ;c++) {
1160 if(p->fds[c] == 0) continue;
1161 if(p->fds[c] > 0 && p->fds[c] < all_files_len) {
1162 if(p->target->fds) p->target->fds[p->fds[c]]++;
1165 fprintf(stderr, "apps.plugin: ERROR: invalid fd number %d\n", p->fds[c]);
1168 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);
1170 /* 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);
1171 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);
1172 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);
1173 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);
1174 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);
1175 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);
1176 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);
1177 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);
1179 #ifdef INCLUDE_CHILDS
1180 p->old_utime = p->utime;
1181 p->old_cutime = p->cutime;
1182 p->old_stime = p->stime;
1183 p->old_cstime = p->cstime;
1184 p->old_minflt = p->minflt;
1185 p->old_majflt = p->majflt;
1186 p->old_cminflt = p->cminflt;
1187 p->old_cmajflt = p->cmajflt;
1191 // since the process has exited, the user
1192 // will see a drop in our charts, because the incremental
1193 // values of this process will not be there
1195 // add them to the fix_* values and they will be added to
1196 // the reported values, so that the report goes steady
1197 p->target->fix_minflt += p->minflt;
1198 p->target->fix_majflt += p->majflt;
1199 p->target->fix_utime += p->utime;
1200 p->target->fix_stime += p->stime;
1201 p->target->fix_cminflt += p->cminflt;
1202 p->target->fix_cmajflt += p->cmajflt;
1203 p->target->fix_cutime += p->cutime;
1204 p->target->fix_cstime += p->cstime;
1206 p->target->fix_io_logical_bytes_read += p->io_logical_bytes_read;
1207 p->target->fix_io_logical_bytes_written += p->io_logical_bytes_written;
1208 p->target->fix_io_read_calls += p->io_read_calls;
1209 p->target->fix_io_write_calls += p->io_write_calls;
1210 p->target->fix_io_storage_bytes_read += p->io_storage_bytes_read;
1211 p->target->fix_io_storage_bytes_written += p->io_storage_bytes_written;
1212 p->target->fix_io_cancelled_write_bytes += p->io_cancelled_write_bytes;
1216 // fprintf(stderr, "\n");
1217 // cleanup all un-updated processed (exited, killed, etc)
1218 for(p = root; p ;) {
1220 // 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);
1222 for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
1223 file_descriptor_not_used(p->fds[c]);
1234 for (w = target_root; w ; w = w->next) {
1238 w->openinotifies = 0;
1239 w->openeventfds = 0;
1240 w->opentimerfds = 0;
1241 w->opensignalfds = 0;
1242 w->openeventpolls = 0;
1245 for(c = 1; c < all_files_len ;c++) {
1246 if(w->fds && w->fds[c] > 0) switch(all_files[c].type) {
1255 case FILETYPE_SOCKET:
1259 case FILETYPE_INOTIFY:
1263 case FILETYPE_EVENTFD:
1267 case FILETYPE_TIMERFD:
1271 case FILETYPE_SIGNALFD:
1275 case FILETYPE_EVENTPOLL:
1276 w->openeventpolls++;
1289 // ----------------------------------------------------------------------------
1290 // update chart dimensions
1292 void show_dimensions(void)
1294 static struct timeval last = { 0, 0 };
1295 static struct rusage me_last;
1301 unsigned long long usec;
1302 unsigned long long cpuuser;
1303 unsigned long long cpusyst;
1306 gettimeofday(&last, NULL);
1307 getrusage(RUSAGE_SELF, &me_last);
1309 // the first time, give a zero to allow
1310 // netdata calibrate to the current time
1311 // usec = update_every * 1000000ULL;
1317 gettimeofday(&now, NULL);
1318 getrusage(RUSAGE_SELF, &me);
1320 usec = usecdiff(&now, &last);
1321 cpuuser = me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec;
1322 cpusyst = me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec;
1324 bcopy(&now, &last, sizeof(struct timeval));
1325 bcopy(&me, &me_last, sizeof(struct rusage));
1328 fprintf(stdout, "BEGIN apps.cpu %llu\n", usec);
1329 for (w = target_root; w ; w = w->next) {
1330 if(w->target || (!w->processes && !w->exposed)) continue;
1332 fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->stime + w->fix_utime + w->fix_stime);
1334 fprintf(stdout, "END\n");
1336 fprintf(stdout, "BEGIN apps.cpu_user %llu\n", usec);
1337 for (w = target_root; w ; w = w->next) {
1338 if(w->target || (!w->processes && !w->exposed)) continue;
1340 fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->fix_utime);
1342 fprintf(stdout, "END\n");
1344 fprintf(stdout, "BEGIN apps.cpu_system %llu\n", usec);
1345 for (w = target_root; w ; w = w->next) {
1346 if(w->target || (!w->processes && !w->exposed)) continue;
1348 fprintf(stdout, "SET %s = %llu\n", w->name, w->stime + w->fix_stime);
1350 fprintf(stdout, "END\n");
1352 fprintf(stdout, "BEGIN apps.threads %llu\n", usec);
1353 for (w = target_root; w ; w = w->next) {
1354 if(w->target || (!w->processes && !w->exposed)) continue;
1356 fprintf(stdout, "SET %s = %llu\n", w->name, w->num_threads);
1358 fprintf(stdout, "END\n");
1360 fprintf(stdout, "BEGIN apps.processes %llu\n", usec);
1361 for (w = target_root; w ; w = w->next) {
1362 if(w->target || (!w->processes && !w->exposed)) continue;
1364 fprintf(stdout, "SET %s = %lu\n", w->name, w->processes);
1366 fprintf(stdout, "END\n");
1368 fprintf(stdout, "BEGIN apps.mem %llu\n", usec);
1369 for (w = target_root; w ; w = w->next) {
1370 if(w->target || (!w->processes && !w->exposed)) continue;
1372 fprintf(stdout, "SET %s = %lld\n", w->name, (long long)w->statm_resident - (long long)w->statm_share);
1374 fprintf(stdout, "END\n");
1376 fprintf(stdout, "BEGIN apps.minor_faults %llu\n", usec);
1377 for (w = target_root; w ; w = w->next) {
1378 if(w->target || (!w->processes && !w->exposed)) continue;
1380 fprintf(stdout, "SET %s = %llu\n", w->name, w->minflt + w->fix_minflt);
1382 fprintf(stdout, "END\n");
1384 fprintf(stdout, "BEGIN apps.major_faults %llu\n", usec);
1385 for (w = target_root; w ; w = w->next) {
1386 if(w->target || (!w->processes && !w->exposed)) continue;
1388 fprintf(stdout, "SET %s = %llu\n", w->name, w->majflt + w->fix_majflt);
1390 fprintf(stdout, "END\n");
1392 fprintf(stdout, "BEGIN apps.lreads %llu\n", usec);
1393 for (w = target_root; w ; w = w->next) {
1394 if(w->target || (!w->processes && !w->exposed)) continue;
1396 fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_read);
1398 fprintf(stdout, "END\n");
1400 fprintf(stdout, "BEGIN apps.lwrites %llu\n", usec);
1401 for (w = target_root; w ; w = w->next) {
1402 if(w->target || (!w->processes && !w->exposed)) continue;
1404 fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_written);
1406 fprintf(stdout, "END\n");
1408 fprintf(stdout, "BEGIN apps.preads %llu\n", usec);
1409 for (w = target_root; w ; w = w->next) {
1410 if(w->target || (!w->processes && !w->exposed)) continue;
1412 fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_read);
1414 fprintf(stdout, "END\n");
1416 fprintf(stdout, "BEGIN apps.pwrites %llu\n", usec);
1417 for (w = target_root; w ; w = w->next) {
1418 if(w->target || (!w->processes && !w->exposed)) continue;
1420 fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_written);
1422 fprintf(stdout, "END\n");
1424 fprintf(stdout, "BEGIN apps.files %llu\n", usec);
1425 for (w = target_root; w ; w = w->next) {
1426 if(w->target || (!w->processes && !w->exposed)) continue;
1428 fprintf(stdout, "SET %s = %llu\n", w->name, w->openfiles);
1430 fprintf(stdout, "END\n");
1432 fprintf(stdout, "BEGIN apps.sockets %llu\n", usec);
1433 for (w = target_root; w ; w = w->next) {
1434 if(w->target || (!w->processes && !w->exposed)) continue;
1436 fprintf(stdout, "SET %s = %llu\n", w->name, w->opensockets);
1438 fprintf(stdout, "END\n");
1440 fprintf(stdout, "BEGIN apps.pipes %llu\n", usec);
1441 for (w = target_root; w ; w = w->next) {
1442 if(w->target || (!w->processes && !w->exposed)) continue;
1444 fprintf(stdout, "SET %s = %llu\n", w->name, w->openpipes);
1446 fprintf(stdout, "END\n");
1448 fprintf(stdout, "BEGIN netdata.apps_cpu %llu\n", usec);
1449 fprintf(stdout, "SET user = %llu\n", cpuuser);
1450 fprintf(stdout, "SET system = %llu\n", cpusyst);
1451 fprintf(stdout, "END\n");
1453 fprintf(stdout, "BEGIN netdata.apps_files %llu\n", usec);
1454 fprintf(stdout, "SET files = %llu\n", file_counter);
1455 fprintf(stdout, "SET pids = %ld\n", pids);
1456 fprintf(stdout, "SET fds = %d\n", all_files_len);
1457 fprintf(stdout, "SET targets = %ld\n", targets);
1458 fprintf(stdout, "END\n");
1464 // ----------------------------------------------------------------------------
1465 // generate the charts
1467 void show_charts(void)
1470 int newly_added = 0;
1472 for(w = target_root ; w ; w = w->next)
1473 if(!w->exposed && w->processes) {
1476 if(debug || w->debug) fprintf(stderr, "apps.plugin: %s just added - regenerating charts.\n", w->name);
1479 // nothing more to show
1480 if(!newly_added) return;
1482 // we have something new to show
1483 // update the charts
1484 fprintf(stdout, "CHART apps.cpu '' 'Apps CPU Time (%ld%% = %ld core%s)' 'cpu time %%' apps apps stacked 20001 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
1485 for (w = target_root; w ; w = w->next) {
1486 if(w->target || (!w->processes && !w->exposed)) continue;
1488 fprintf(stdout, "DIMENSION %s '' incremental 100 %llu %s\n", w->name, (unsigned long long)(Hertz * update_every), w->hidden?"hidden":"");
1491 fprintf(stdout, "CHART apps.mem '' 'Apps Dedicated Memory (w/o shared)' 'MB' apps apps stacked 20003 %d\n", update_every);
1492 for (w = target_root; w ; w = w->next) {
1493 if(w->target || (!w->processes && !w->exposed)) continue;
1495 fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
1498 fprintf(stdout, "CHART apps.threads '' 'Apps Threads' 'threads' apps apps stacked 20005 %d\n", update_every);
1499 for (w = target_root; w ; w = w->next) {
1500 if(w->target || (!w->processes && !w->exposed)) continue;
1502 fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
1505 fprintf(stdout, "CHART apps.processes '' 'Apps Processes' 'processes' apps apps stacked 20004 %d\n", update_every);
1506 for (w = target_root; w ; w = w->next) {
1507 if(w->target || (!w->processes && !w->exposed)) continue;
1509 fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
1512 fprintf(stdout, "CHART apps.cpu_user '' 'Apps CPU User Time (%ld%% = %ld core%s)' 'cpu time %%' apps none stacked 20020 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
1513 for (w = target_root; w ; w = w->next) {
1514 if(w->target || (!w->processes && !w->exposed)) continue;
1516 fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz * processors * update_every);
1519 fprintf(stdout, "CHART apps.cpu_system '' 'Apps CPU System Time (%ld%% = %ld core%s)' 'cpu time %%' apps none stacked 20021 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
1520 for (w = target_root; w ; w = w->next) {
1521 if(w->target || (!w->processes && !w->exposed)) continue;
1523 fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz * processors * update_every);
1526 fprintf(stdout, "CHART apps.major_faults '' 'Apps Major Page Faults (swaps in)' 'page faults/s' apps apps stacked 20010 %d\n", update_every);
1527 for (w = target_root; w ; w = w->next) {
1528 if(w->target || (!w->processes && !w->exposed)) continue;
1530 fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, update_every);
1533 fprintf(stdout, "CHART apps.minor_faults '' 'Apps Minor Page Faults' 'page faults/s' apps none stacked 20011 %d\n", update_every);
1534 for (w = target_root; w ; w = w->next) {
1535 if(w->target || (!w->processes && !w->exposed)) continue;
1537 fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, update_every);
1540 fprintf(stdout, "CHART apps.lreads '' 'Apps Disk Logical Reads' 'kilobytes/s' apps none stacked 20042 %d\n", update_every);
1541 for (w = target_root; w ; w = w->next) {
1542 if(w->target || (!w->processes && !w->exposed)) continue;
1544 fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
1547 fprintf(stdout, "CHART apps.lwrites '' 'Apps I/O Logical Writes' 'kilobytes/s' apps none stacked 20042 %d\n", update_every);
1548 for (w = target_root; w ; w = w->next) {
1549 if(w->target || (!w->processes && !w->exposed)) continue;
1551 fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
1554 fprintf(stdout, "CHART apps.preads '' 'Apps Disk Reads' 'kilobytes/s' apps apps stacked 20002 %d\n", update_every);
1555 for (w = target_root; w ; w = w->next) {
1556 if(w->target || (!w->processes && !w->exposed)) continue;
1558 fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
1561 fprintf(stdout, "CHART apps.pwrites '' 'Apps Disk Writes' 'kilobytes/s' apps apps stacked 20002 %d\n", update_every);
1562 for (w = target_root; w ; w = w->next) {
1563 if(w->target || (!w->processes && !w->exposed)) continue;
1565 fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
1568 fprintf(stdout, "CHART apps.files '' 'Apps Open Files' 'open files' apps apps stacked 20050 %d\n", update_every);
1569 for (w = target_root; w ; w = w->next) {
1570 if(w->target || (!w->processes && !w->exposed)) continue;
1572 fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
1575 fprintf(stdout, "CHART apps.sockets '' 'Apps Open Sockets' 'open sockets' apps apps stacked 20051 %d\n", update_every);
1576 for (w = target_root; w ; w = w->next) {
1577 if(w->target || (!w->processes && !w->exposed)) continue;
1579 fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
1582 fprintf(stdout, "CHART apps.pipes '' 'Apps Pipes' 'open pipes' apps none stacked 20053 %d\n", update_every);
1583 for (w = target_root; w ; w = w->next) {
1584 if(w->target || (!w->processes && !w->exposed)) continue;
1586 fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
1589 fprintf(stdout, "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' netdata netdata stacked 10000 %d\n", update_every);
1590 fprintf(stdout, "DIMENSION user '' incremental 1 %d\n", 1000 * update_every);
1591 fprintf(stdout, "DIMENSION system '' incremental 1 %d\n", 1000 * update_every);
1593 fprintf(stdout, "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' netdata netdata line 10001 %d\n", update_every);
1594 fprintf(stdout, "DIMENSION files '' incremental 1 %d\n", update_every);
1595 fprintf(stdout, "DIMENSION pids '' absolute 1 1\n");
1596 fprintf(stdout, "DIMENSION fds '' absolute 1 1\n");
1597 fprintf(stdout, "DIMENSION targets '' absolute 1 1\n");
1603 // ----------------------------------------------------------------------------
1604 // parse command line arguments
1606 void parse_args(int argc, char **argv)
1611 for(i = 1; i < argc; i++) {
1613 int n = atoi(argv[i]);
1620 if(strcmp("debug", argv[i]) == 0) {
1630 fprintf(stderr, "apps.plugin: ERROR: cannot understand option %s\n", argv[i]);
1634 if(freq > 0) update_every = freq;
1635 if(!name) name = "groups";
1637 if(read_process_groups(name)) {
1638 fprintf(stderr, "apps.plugin: ERROR: cannot read process groups %s\n", name);
1643 int main(int argc, char **argv)
1645 unsigned long started_t = time(NULL), current_t;
1646 Hertz = get_hertz();
1647 pid_max = get_pid_max();
1648 processors = get_processors();
1650 parse_args(argc, argv);
1652 all_pids = calloc(sizeof(struct pid_stat *), pid_max);
1654 fprintf(stderr, "apps.plugin: ERROR: cannot allocate %lu bytes of memory.\n", sizeof(struct pid_stat *) * pid_max);
1655 printf("DISABLE\n");
1659 unsigned long long counter = 1;
1660 unsigned long long usec = 0, susec = 0;
1661 struct timeval last, now;
1662 gettimeofday(&last, NULL);
1664 for(;1; counter++) {
1665 if(!update_from_proc()) {
1666 fprintf(stderr, "apps.plugin: ERROR: cannot allocate %lu bytes of memory.\n", sizeof(struct pid_stat *) * pid_max);
1667 printf("DISABLE\n");
1671 update_statistics();
1672 show_charts(); // this is smart enough to show only newly added apps, when needed
1675 if(debug) fprintf(stderr, "apps.plugin: done Loop No %llu\n", counter);
1678 gettimeofday(&now, NULL);
1679 usec = usecdiff(&now, &last) - susec;
1680 if(debug) fprintf(stderr, "apps.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).\n", usec + susec, usec, susec);
1682 // if the last loop took less than half the time
1683 // wait the rest of the time
1684 if(usec < (update_every * 1000000ULL / 2)) susec = (update_every * 1000000ULL) - usec;
1685 else susec = update_every * 1000000ULL / 2;
1688 bcopy(&now, &last, sizeof(struct timeval));
1690 current_t = time(NULL);
1691 if(current_t - started_t > 3600) exit(0);