]> arthur.barton.de Git - netdata.git/blob - src/apps_plugin.c
Merge pull request #1313 from rlefevre/monotonic-clock
[netdata.git] / src / apps_plugin.c
1 #include "common.h"
2
3 #define MAX_COMPARE_NAME 100
4 #define MAX_NAME 100
5 #define MAX_CMDLINE 1024
6
7 // the rates we are going to send to netdata
8 // will have this detail
9 // a value of:
10 // 1 will send just integer parts to netdata
11 // 100 will send 2 decimal points
12 // 1000 will send 3 decimal points
13 // etc.
14 #define RATES_DETAIL 10000ULL
15
16 int debug = 0;
17
18 int update_every = 1;
19 unsigned long long global_iterations_counter = 1;
20 unsigned long long file_counter = 0;
21 int proc_pid_cmdline_is_needed = 0;
22 int include_exited_childs = 1;
23 char *config_dir = CONFIG_DIR;
24
25 pid_t *all_pids_sortlist = NULL;
26
27 // will be automatically set to 1, if guest values are collected
28 int show_guest_time = 0;
29 int show_guest_time_old = 0;
30
31 int enable_guest_charts = 0;
32 int enable_file_charts = 1;
33 int enable_users_charts = 1;
34 int enable_groups_charts = 1;
35
36 // ----------------------------------------------------------------------------
37
38 void netdata_cleanup_and_exit(int ret) {
39     exit(ret);
40 }
41
42
43 // ----------------------------------------------------------------------------
44 // target
45 // target is the structure that process data are aggregated
46
47 struct target {
48     char compare[MAX_COMPARE_NAME + 1];
49     uint32_t comparehash;
50     size_t comparelen;
51
52     char id[MAX_NAME + 1];
53     uint32_t idhash;
54
55     char name[MAX_NAME + 1];
56
57     uid_t uid;
58     gid_t gid;
59
60     unsigned long long minflt;
61     unsigned long long cminflt;
62     unsigned long long majflt;
63     unsigned long long cmajflt;
64     unsigned long long utime;
65     unsigned long long stime;
66     unsigned long long gtime;
67     unsigned long long cutime;
68     unsigned long long cstime;
69     unsigned long long cgtime;
70     unsigned long long num_threads;
71     // unsigned long long rss;
72
73     unsigned long long statm_size;
74     unsigned long long statm_resident;
75     unsigned long long statm_share;
76     // unsigned long long statm_text;
77     // unsigned long long statm_lib;
78     // unsigned long long statm_data;
79     // unsigned long long statm_dirty;
80
81     unsigned long long io_logical_bytes_read;
82     unsigned long long io_logical_bytes_written;
83     // unsigned long long io_read_calls;
84     // unsigned long long io_write_calls;
85     unsigned long long io_storage_bytes_read;
86     unsigned long long io_storage_bytes_written;
87     // unsigned long long io_cancelled_write_bytes;
88
89     int *fds;
90     unsigned long long openfiles;
91     unsigned long long openpipes;
92     unsigned long long opensockets;
93     unsigned long long openinotifies;
94     unsigned long long openeventfds;
95     unsigned long long opentimerfds;
96     unsigned long long opensignalfds;
97     unsigned long long openeventpolls;
98     unsigned long long openother;
99
100     unsigned long processes;    // how many processes have been merged to this
101     int exposed;                // if set, we have sent this to netdata
102     int hidden;                 // if set, we set the hidden flag on the dimension
103     int debug;
104     int ends_with;
105     int starts_with;            // if set, the compare string matches only the
106                                 // beginning of the command
107
108     struct target *target;      // the one that will be reported to netdata
109     struct target *next;
110 };
111
112
113 // ----------------------------------------------------------------------------
114 // apps_groups.conf
115 // aggregate all processes in groups, to have a limited number of dimensions
116
117 struct target *apps_groups_root_target = NULL;
118 struct target *apps_groups_default_target = NULL;
119 long apps_groups_targets = 0;
120
121 struct target *users_root_target = NULL;
122 struct target *groups_root_target = NULL;
123
124 struct target *get_users_target(uid_t uid)
125 {
126     struct target *w;
127     for(w = users_root_target ; w ; w = w->next)
128         if(w->uid == uid) return w;
129
130     w = callocz(sizeof(struct target), 1);
131     snprintfz(w->compare, MAX_COMPARE_NAME, "%u", uid);
132     w->comparehash = simple_hash(w->compare);
133     w->comparelen = strlen(w->compare);
134
135     snprintfz(w->id, MAX_NAME, "%u", uid);
136     w->idhash = simple_hash(w->id);
137
138     struct passwd *pw = getpwuid(uid);
139     if(!pw)
140         snprintfz(w->name, MAX_NAME, "%u", uid);
141     else
142         snprintfz(w->name, MAX_NAME, "%s", pw->pw_name);
143
144     netdata_fix_chart_name(w->name);
145
146     w->uid = uid;
147
148     w->next = users_root_target;
149     users_root_target = w;
150
151     if(unlikely(debug))
152         fprintf(stderr, "apps.plugin: added uid %u ('%s') target\n", w->uid, w->name);
153
154     return w;
155 }
156
157 struct target *get_groups_target(gid_t gid)
158 {
159     struct target *w;
160     for(w = groups_root_target ; w ; w = w->next)
161         if(w->gid == gid) return w;
162
163     w = callocz(sizeof(struct target), 1);
164     snprintfz(w->compare, MAX_COMPARE_NAME, "%u", gid);
165     w->comparehash = simple_hash(w->compare);
166     w->comparelen = strlen(w->compare);
167
168     snprintfz(w->id, MAX_NAME, "%u", gid);
169     w->idhash = simple_hash(w->id);
170
171     struct group *gr = getgrgid(gid);
172     if(!gr)
173         snprintfz(w->name, MAX_NAME, "%u", gid);
174     else
175         snprintfz(w->name, MAX_NAME, "%s", gr->gr_name);
176
177     netdata_fix_chart_name(w->name);
178
179     w->gid = gid;
180
181     w->next = groups_root_target;
182     groups_root_target = w;
183
184     if(unlikely(debug))
185         fprintf(stderr, "apps.plugin: added gid %u ('%s') target\n", w->gid, w->name);
186
187     return w;
188 }
189
190 // find or create a new target
191 // there are targets that are just aggregated to other target (the second argument)
192 struct target *get_apps_groups_target(const char *id, struct target *target) {
193     int tdebug = 0, thidden = 0, ends_with = 0;
194     const char *nid = id;
195
196     while(nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
197         if(nid[0] == '-') thidden = 1;
198         if(nid[0] == '+') tdebug = 1;
199         if(nid[0] == '*') ends_with = 1;
200         nid++;
201     }
202     uint32_t hash = simple_hash(id);
203
204     struct target *w, *last = apps_groups_root_target;
205     for(w = apps_groups_root_target ; w ; w = w->next) {
206         if(w->idhash == hash && strncmp(nid, w->id, MAX_NAME) == 0)
207             return w;
208
209         last = w;
210     }
211
212     w = callocz(sizeof(struct target), 1);
213     strncpyz(w->id, nid, MAX_NAME);
214     w->idhash = simple_hash(w->id);
215
216     strncpyz(w->name, nid, MAX_NAME);
217
218     strncpyz(w->compare, nid, MAX_COMPARE_NAME);
219     size_t len = strlen(w->compare);
220     if(w->compare[len - 1] == '*') {
221         w->compare[len - 1] = '\0';
222         w->starts_with = 1;
223     }
224     w->ends_with = ends_with;
225
226     if(w->starts_with && w->ends_with)
227         proc_pid_cmdline_is_needed = 1;
228
229     w->comparehash = simple_hash(w->compare);
230     w->comparelen = strlen(w->compare);
231
232     w->hidden = thidden;
233     w->debug = tdebug;
234     w->target = target;
235
236     // append it, to maintain the order in apps_groups.conf
237     if(last) last->next = w;
238     else apps_groups_root_target = w;
239
240     if(unlikely(debug))
241         fprintf(stderr, "apps.plugin: ADDING TARGET ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n"
242                 , w->id
243                 , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
244                 , w->target?w->target->id:w->id
245                 , (w->hidden)?"hidden":"-"
246                 , (w->debug)?"debug":"-"
247         );
248
249     return w;
250 }
251
252 // read the apps_groups.conf file
253 int read_apps_groups_conf(const char *name)
254 {
255     char filename[FILENAME_MAX + 1];
256
257     snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", config_dir, name);
258
259     if(unlikely(debug))
260         fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename);
261
262     // ----------------------------------------
263
264     procfile *ff = procfile_open(filename, " :\t", PROCFILE_FLAG_DEFAULT);
265     if(!ff) return 1;
266
267     procfile_set_quotes(ff, "'\"");
268
269     ff = procfile_readall(ff);
270     if(!ff)
271         return 1;
272
273     unsigned long line, lines = procfile_lines(ff);
274
275     for(line = 0; line < lines ;line++) {
276         unsigned long word, words = procfile_linewords(ff, line);
277         struct target *w = NULL;
278
279         char *t = procfile_lineword(ff, line, 0);
280         if(!t || !*t) continue;
281
282         for(word = 0; word < words ;word++) {
283             char *s = procfile_lineword(ff, line, word);
284             if(!s || !*s) continue;
285             if(*s == '#') break;
286
287             if(t == s) continue;
288
289             struct target *n = get_apps_groups_target(s, w);
290             if(!n) {
291                 error("Cannot create target '%s' (line %lu, word %lu)", s, line, word);
292                 continue;
293             }
294
295             if(!w) w = n;
296         }
297
298         if(w) {
299             int tdebug = 0, thidden = 0;
300
301             while(t[0] == '-' || t[0] == '+') {
302                 if(t[0] == '-') thidden = 1;
303                 if(t[0] == '+') tdebug = 1;
304                 t++;
305             }
306
307             strncpyz(w->name, t, MAX_NAME);
308             w->hidden = thidden;
309             w->debug = tdebug;
310
311             if(unlikely(debug))
312                 fprintf(stderr, "apps.plugin: AGGREGATION TARGET NAME '%s' on ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n"
313                         , w->name
314                         , w->id
315                         , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
316                         , w->target?w->target->id:w->id
317                         , (w->hidden)?"hidden":"-"
318                         , (w->debug)?"debug":"-"
319                 );
320         }
321     }
322
323     procfile_close(ff);
324
325     apps_groups_default_target = get_apps_groups_target("p+!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing
326     if(!apps_groups_default_target)
327         error("Cannot create default target");
328     else
329         strncpyz(apps_groups_default_target->name, "other", MAX_NAME);
330
331     return 0;
332 }
333
334
335 // ----------------------------------------------------------------------------
336 // data to store for each pid
337 // see: man proc
338
339 #define PID_LOG_IO      0x00000001
340 #define PID_LOG_STATM   0x00000002
341 #define PID_LOG_CMDLINE 0x00000004
342 #define PID_LOG_FDS     0x00000008
343 #define PID_LOG_STAT    0x00000010
344
345 struct pid_stat {
346     int32_t pid;
347     char comm[MAX_COMPARE_NAME + 1];
348     char cmdline[MAX_CMDLINE + 1];
349
350     uint32_t log_thrown;
351
352     // char state;
353     int32_t ppid;
354     // int32_t pgrp;
355     // int32_t session;
356     // int32_t tty_nr;
357     // int32_t tpgid;
358     // uint64_t flags;
359
360     // these are raw values collected
361     unsigned long long minflt_raw;
362     unsigned long long cminflt_raw;
363     unsigned long long majflt_raw;
364     unsigned long long cmajflt_raw;
365     unsigned long long utime_raw;
366     unsigned long long stime_raw;
367     unsigned long long gtime_raw; // guest_time
368     unsigned long long cutime_raw;
369     unsigned long long cstime_raw;
370     unsigned long long cgtime_raw; // cguest_time
371
372     // these are rates
373     unsigned long long minflt;
374     unsigned long long cminflt;
375     unsigned long long majflt;
376     unsigned long long cmajflt;
377     unsigned long long utime;
378     unsigned long long stime;
379     unsigned long long gtime;
380     unsigned long long cutime;
381     unsigned long long cstime;
382     unsigned long long cgtime;
383
384     // int64_t priority;
385     // int64_t nice;
386     int32_t num_threads;
387     // int64_t itrealvalue;
388     // unsigned long long starttime;
389     // unsigned long long vsize;
390     // unsigned long long rss;
391     // unsigned long long rsslim;
392     // unsigned long long starcode;
393     // unsigned long long endcode;
394     // unsigned long long startstack;
395     // unsigned long long kstkesp;
396     // unsigned long long kstkeip;
397     // uint64_t signal;
398     // uint64_t blocked;
399     // uint64_t sigignore;
400     // uint64_t sigcatch;
401     // uint64_t wchan;
402     // uint64_t nswap;
403     // uint64_t cnswap;
404     // int32_t exit_signal;
405     // int32_t processor;
406     // uint32_t rt_priority;
407     // uint32_t policy;
408     // unsigned long long delayacct_blkio_ticks;
409
410     uid_t uid;
411     gid_t gid;
412
413     unsigned long long statm_size;
414     unsigned long long statm_resident;
415     unsigned long long statm_share;
416     // unsigned long long statm_text;
417     // unsigned long long statm_lib;
418     // unsigned long long statm_data;
419     // unsigned long long statm_dirty;
420
421     unsigned long long io_logical_bytes_read_raw;
422     unsigned long long io_logical_bytes_written_raw;
423     // unsigned long long io_read_calls_raw;
424     // unsigned long long io_write_calls_raw;
425     unsigned long long io_storage_bytes_read_raw;
426     unsigned long long io_storage_bytes_written_raw;
427     // unsigned long long io_cancelled_write_bytes_raw;
428
429     unsigned long long io_logical_bytes_read;
430     unsigned long long io_logical_bytes_written;
431     // unsigned long long io_read_calls;
432     // unsigned long long io_write_calls;
433     unsigned long long io_storage_bytes_read;
434     unsigned long long io_storage_bytes_written;
435     // unsigned long long io_cancelled_write_bytes;
436
437     int *fds;                       // array of fds it uses
438     int fds_size;                   // the size of the fds array
439
440     int children_count;             // number of processes directly referencing this
441     int keep;                       // 1 when we need to keep this process in memory even after it exited
442     int keeploops;                  // increases by 1 every time keep is 1 and updated 0
443     int updated;                    // 1 when the process is currently running
444     int merged;                     // 1 when it has been merged to its parent
445     int new_entry;                  // 1 when this is a new process, just saw for the first time
446     int read;                       // 1 when we have already read this process for this iteration
447     int sortlist;                   // higher numbers = top on the process tree
448                                     // each process gets a unique number
449
450     struct target *target;          // app_groups.conf targets
451     struct target *user_target;     // uid based targets
452     struct target *group_target;    // gid based targets
453
454     unsigned long long stat_collected_usec;
455     unsigned long long last_stat_collected_usec;
456
457     unsigned long long io_collected_usec;
458     unsigned long long last_io_collected_usec;
459
460     char *stat_filename;
461     char *statm_filename;
462     char *io_filename;
463     char *cmdline_filename;
464
465     struct pid_stat *parent;
466     struct pid_stat *prev;
467     struct pid_stat *next;
468 } *root_of_pids = NULL, **all_pids;
469
470 long all_pids_count = 0;
471
472 struct pid_stat *get_pid_entry(pid_t pid) {
473     if(all_pids[pid]) {
474         all_pids[pid]->new_entry = 0;
475         return all_pids[pid];
476     }
477
478     all_pids[pid] = callocz(sizeof(struct pid_stat), 1);
479     all_pids[pid]->fds = callocz(sizeof(int), 100);
480     all_pids[pid]->fds_size = 100;
481
482     if(root_of_pids) root_of_pids->prev = all_pids[pid];
483     all_pids[pid]->next = root_of_pids;
484     root_of_pids = all_pids[pid];
485
486     all_pids[pid]->pid = pid;
487     all_pids[pid]->new_entry = 1;
488
489     all_pids_count++;
490
491     return all_pids[pid];
492 }
493
494 void del_pid_entry(pid_t pid) {
495     if(!all_pids[pid]) {
496         error("attempted to free pid %d that is not allocated.", pid);
497         return;
498     }
499
500     if(unlikely(debug))
501         fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm);
502
503     if(root_of_pids == all_pids[pid]) root_of_pids = all_pids[pid]->next;
504     if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
505     if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
506
507     if(all_pids[pid]->fds) freez(all_pids[pid]->fds);
508     if(all_pids[pid]->stat_filename) freez(all_pids[pid]->stat_filename);
509     if(all_pids[pid]->statm_filename) freez(all_pids[pid]->statm_filename);
510     if(all_pids[pid]->io_filename) freez(all_pids[pid]->io_filename);
511     if(all_pids[pid]->cmdline_filename) freez(all_pids[pid]->cmdline_filename);
512     freez(all_pids[pid]);
513
514     all_pids[pid] = NULL;
515     all_pids_count--;
516 }
517
518
519 // ----------------------------------------------------------------------------
520 // update pids from proc
521
522 int read_proc_pid_cmdline(struct pid_stat *p) {
523
524     if(unlikely(!p->cmdline_filename)) {
525         char filename[FILENAME_MAX + 1];
526         snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", global_host_prefix, p->pid);
527         p->cmdline_filename = strdupz(filename);
528     }
529
530     int fd = open(p->cmdline_filename, O_RDONLY, 0666);
531     if(unlikely(fd == -1)) goto cleanup;
532
533     ssize_t i, bytes = read(fd, p->cmdline, MAX_CMDLINE);
534     close(fd);
535
536     if(unlikely(bytes < 0)) goto cleanup;
537
538     p->cmdline[bytes] = '\0';
539     for(i = 0; i < bytes ; i++)
540         if(unlikely(!p->cmdline[i])) p->cmdline[i] = ' ';
541
542     if(unlikely(debug))
543         fprintf(stderr, "Read file '%s' contents: %s\n", p->cmdline_filename, p->cmdline);
544
545     return 1;
546
547 cleanup:
548     // copy the command to the command line
549     strncpyz(p->cmdline, p->comm, MAX_CMDLINE);
550     return 0;
551 }
552
553 int read_proc_pid_ownership(struct pid_stat *p) {
554     if(unlikely(!p->stat_filename)) {
555         error("pid %d does not have a stat_filename", p->pid);
556         return 0;
557     }
558
559     // ----------------------------------------
560     // read uid and gid
561
562     struct stat st;
563     if(stat(p->stat_filename, &st) != 0) {
564         error("Cannot stat file '%s'", p->stat_filename);
565         return 1;
566     }
567
568     p->uid = st.st_uid;
569     p->gid = st.st_gid;
570
571     return 1;
572 }
573
574 int read_proc_pid_stat(struct pid_stat *p) {
575     static procfile *ff = NULL;
576
577     if(unlikely(!p->stat_filename)) {
578         char filename[FILENAME_MAX + 1];
579         snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", global_host_prefix, p->pid);
580         p->stat_filename = strdupz(filename);
581     }
582
583     int set_quotes = (!ff)?1:0;
584
585     ff = procfile_reopen(ff, p->stat_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
586     if(unlikely(!ff)) goto cleanup;
587
588     // if(set_quotes) procfile_set_quotes(ff, "()");
589     if(set_quotes) procfile_set_open_close(ff, "(", ")");
590
591     ff = procfile_readall(ff);
592     if(unlikely(!ff)) goto cleanup;
593
594     p->last_stat_collected_usec = p->stat_collected_usec;
595     p->stat_collected_usec = now_realtime_usec();
596     file_counter++;
597
598     // p->pid           = atol(procfile_lineword(ff, 0, 0+i));
599
600     strncpyz(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME);
601
602     // p->state         = *(procfile_lineword(ff, 0, 2));
603     p->ppid             = (int32_t) atol(procfile_lineword(ff, 0, 3));
604     // p->pgrp          = atol(procfile_lineword(ff, 0, 4));
605     // p->session       = atol(procfile_lineword(ff, 0, 5));
606     // p->tty_nr        = atol(procfile_lineword(ff, 0, 6));
607     // p->tpgid         = atol(procfile_lineword(ff, 0, 7));
608     // p->flags         = strtoull(procfile_lineword(ff, 0, 8), NULL, 10);
609
610     unsigned long long last;
611
612     last = p->minflt_raw;
613     p->minflt_raw       = strtoull(procfile_lineword(ff, 0, 9), NULL, 10);
614     p->minflt = (p->minflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
615
616     last = p->cminflt_raw;
617     p->cminflt_raw      = strtoull(procfile_lineword(ff, 0, 10), NULL, 10);
618     p->cminflt = (p->cminflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
619
620     last = p->majflt_raw;
621     p->majflt_raw       = strtoull(procfile_lineword(ff, 0, 11), NULL, 10);
622     p->majflt = (p->majflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
623
624     last = p->cmajflt_raw;
625     p->cmajflt_raw      = strtoull(procfile_lineword(ff, 0, 12), NULL, 10);
626     p->cmajflt = (p->cmajflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
627
628     last = p->utime_raw;
629     p->utime_raw        = strtoull(procfile_lineword(ff, 0, 13), NULL, 10);
630     p->utime = (p->utime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
631
632     last = p->stime_raw;
633     p->stime_raw        = strtoull(procfile_lineword(ff, 0, 14), NULL, 10);
634     p->stime = (p->stime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
635
636     last = p->cutime_raw;
637     p->cutime_raw       = strtoull(procfile_lineword(ff, 0, 15), NULL, 10);
638     p->cutime = (p->cutime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
639
640     last = p->cstime_raw;
641     p->cstime_raw       = strtoull(procfile_lineword(ff, 0, 16), NULL, 10);
642     p->cstime = (p->cstime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
643
644     // p->priority      = strtoull(procfile_lineword(ff, 0, 17), NULL, 10);
645     // p->nice          = strtoull(procfile_lineword(ff, 0, 18), NULL, 10);
646     p->num_threads      = (int32_t) atol(procfile_lineword(ff, 0, 19));
647     // p->itrealvalue   = strtoull(procfile_lineword(ff, 0, 20), NULL, 10);
648     // p->starttime     = strtoull(procfile_lineword(ff, 0, 21), NULL, 10);
649     // p->vsize         = strtoull(procfile_lineword(ff, 0, 22), NULL, 10);
650     // p->rss           = strtoull(procfile_lineword(ff, 0, 23), NULL, 10);
651     // p->rsslim        = strtoull(procfile_lineword(ff, 0, 24), NULL, 10);
652     // p->starcode      = strtoull(procfile_lineword(ff, 0, 25), NULL, 10);
653     // p->endcode       = strtoull(procfile_lineword(ff, 0, 26), NULL, 10);
654     // p->startstack    = strtoull(procfile_lineword(ff, 0, 27), NULL, 10);
655     // p->kstkesp       = strtoull(procfile_lineword(ff, 0, 28), NULL, 10);
656     // p->kstkeip       = strtoull(procfile_lineword(ff, 0, 29), NULL, 10);
657     // p->signal        = strtoull(procfile_lineword(ff, 0, 30), NULL, 10);
658     // p->blocked       = strtoull(procfile_lineword(ff, 0, 31), NULL, 10);
659     // p->sigignore     = strtoull(procfile_lineword(ff, 0, 32), NULL, 10);
660     // p->sigcatch      = strtoull(procfile_lineword(ff, 0, 33), NULL, 10);
661     // p->wchan         = strtoull(procfile_lineword(ff, 0, 34), NULL, 10);
662     // p->nswap         = strtoull(procfile_lineword(ff, 0, 35), NULL, 10);
663     // p->cnswap        = strtoull(procfile_lineword(ff, 0, 36), NULL, 10);
664     // p->exit_signal   = atol(procfile_lineword(ff, 0, 37));
665     // p->processor     = atol(procfile_lineword(ff, 0, 38));
666     // p->rt_priority   = strtoul(procfile_lineword(ff, 0, 39), NULL, 10);
667     // p->policy        = strtoul(procfile_lineword(ff, 0, 40), NULL, 10);
668     // p->delayacct_blkio_ticks = strtoull(procfile_lineword(ff, 0, 41), NULL, 10);
669
670     if(enable_guest_charts) {
671         last = p->gtime_raw;
672         p->gtime_raw        = strtoull(procfile_lineword(ff, 0, 42), NULL, 10);
673         p->gtime = (p->gtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
674
675         last = p->cgtime_raw;
676         p->cgtime_raw       = strtoull(procfile_lineword(ff, 0, 43), NULL, 10);
677         p->cgtime = (p->cgtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
678
679         if (show_guest_time || p->gtime || p->cgtime) {
680             p->utime -= (p->utime >= p->gtime) ? p->gtime : p->utime;
681             p->cutime -= (p->cutime >= p->cgtime) ? p->cgtime : p->cutime;
682             show_guest_time = 1;
683         }
684     }
685
686     if(unlikely(debug || (p->target && p->target->debug)))
687         fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu, threads=%d\n", global_host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
688
689     if(unlikely(global_iterations_counter == 1)) {
690         p->minflt           = 0;
691         p->cminflt          = 0;
692         p->majflt           = 0;
693         p->cmajflt          = 0;
694         p->utime            = 0;
695         p->stime            = 0;
696         p->gtime            = 0;
697         p->cutime           = 0;
698         p->cstime           = 0;
699         p->cgtime           = 0;
700     }
701
702     return 1;
703
704 cleanup:
705     p->minflt           = 0;
706     p->cminflt          = 0;
707     p->majflt           = 0;
708     p->cmajflt          = 0;
709     p->utime            = 0;
710     p->stime            = 0;
711     p->gtime            = 0;
712     p->cutime           = 0;
713     p->cstime           = 0;
714     p->cgtime           = 0;
715     p->num_threads      = 0;
716     // p->rss              = 0;
717     return 0;
718 }
719
720 int read_proc_pid_statm(struct pid_stat *p) {
721     static procfile *ff = NULL;
722
723     if(unlikely(!p->statm_filename)) {
724         char filename[FILENAME_MAX + 1];
725         snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", global_host_prefix, p->pid);
726         p->statm_filename = strdupz(filename);
727     }
728
729     ff = procfile_reopen(ff, p->statm_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
730     if(unlikely(!ff)) goto cleanup;
731
732     ff = procfile_readall(ff);
733     if(unlikely(!ff)) goto cleanup;
734
735     file_counter++;
736
737     p->statm_size           = strtoull(procfile_lineword(ff, 0, 0), NULL, 10);
738     p->statm_resident       = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
739     p->statm_share          = strtoull(procfile_lineword(ff, 0, 2), NULL, 10);
740     // p->statm_text           = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
741     // p->statm_lib            = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
742     // p->statm_data           = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
743     // p->statm_dirty          = strtoull(procfile_lineword(ff, 0, 6), NULL, 10);
744
745     return 1;
746
747 cleanup:
748     p->statm_size           = 0;
749     p->statm_resident       = 0;
750     p->statm_share          = 0;
751     // p->statm_text           = 0;
752     // p->statm_lib            = 0;
753     // p->statm_data           = 0;
754     // p->statm_dirty          = 0;
755     return 0;
756 }
757
758 int read_proc_pid_io(struct pid_stat *p) {
759     static procfile *ff = NULL;
760
761     if(unlikely(!p->io_filename)) {
762         char filename[FILENAME_MAX + 1];
763         snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", global_host_prefix, p->pid);
764         p->io_filename = strdupz(filename);
765     }
766
767     // open the file
768     ff = procfile_reopen(ff, p->io_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
769     if(unlikely(!ff)) goto cleanup;
770
771     ff = procfile_readall(ff);
772     if(unlikely(!ff)) goto cleanup;
773
774     file_counter++;
775
776     p->last_io_collected_usec = p->io_collected_usec;
777     p->io_collected_usec = now_realtime_usec();
778
779     unsigned long long last;
780
781     last = p->io_logical_bytes_read_raw;
782     p->io_logical_bytes_read_raw = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
783     p->io_logical_bytes_read = (p->io_logical_bytes_read_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
784
785     last = p->io_logical_bytes_written_raw;
786     p->io_logical_bytes_written_raw = strtoull(procfile_lineword(ff, 1, 1), NULL, 10);
787     p->io_logical_bytes_written = (p->io_logical_bytes_written_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
788
789     // last = p->io_read_calls_raw;
790     // p->io_read_calls_raw = strtoull(procfile_lineword(ff, 2, 1), NULL, 10);
791     // p->io_read_calls = (p->io_read_calls_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
792
793     // last = p->io_write_calls_raw;
794     // p->io_write_calls_raw = strtoull(procfile_lineword(ff, 3, 1), NULL, 10);
795     // p->io_write_calls = (p->io_write_calls_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
796
797     last = p->io_storage_bytes_read_raw;
798     p->io_storage_bytes_read_raw = strtoull(procfile_lineword(ff, 4, 1), NULL, 10);
799     p->io_storage_bytes_read = (p->io_storage_bytes_read_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
800
801     last = p->io_storage_bytes_written_raw;
802     p->io_storage_bytes_written_raw = strtoull(procfile_lineword(ff, 5, 1), NULL, 10);
803     p->io_storage_bytes_written = (p->io_storage_bytes_written_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
804
805     // last = p->io_cancelled_write_bytes_raw;
806     // p->io_cancelled_write_bytes_raw = strtoull(procfile_lineword(ff, 6, 1), NULL, 10);
807     // p->io_cancelled_write_bytes = (p->io_cancelled_write_bytes_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
808
809     if(unlikely(global_iterations_counter == 1)) {
810         p->io_logical_bytes_read        = 0;
811         p->io_logical_bytes_written     = 0;
812         // p->io_read_calls             = 0;
813         // p->io_write_calls            = 0;
814         p->io_storage_bytes_read        = 0;
815         p->io_storage_bytes_written     = 0;
816         // p->io_cancelled_write_bytes  = 0;
817     }
818
819     return 1;
820
821 cleanup:
822     p->io_logical_bytes_read        = 0;
823     p->io_logical_bytes_written     = 0;
824     // p->io_read_calls             = 0;
825     // p->io_write_calls            = 0;
826     p->io_storage_bytes_read        = 0;
827     p->io_storage_bytes_written     = 0;
828     // p->io_cancelled_write_bytes  = 0;
829     return 0;
830 }
831
832 unsigned long long global_utime = 0;
833 unsigned long long global_stime = 0;
834 unsigned long long global_gtime = 0;
835
836 int read_proc_stat() {
837     static char filename[FILENAME_MAX + 1] = "";
838     static procfile *ff = NULL;
839     static unsigned long long utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0;
840     static usec_t collected_usec = 0, last_collected_usec = 0;
841
842     if(unlikely(!ff)) {
843         snprintfz(filename, FILENAME_MAX, "%s/proc/stat", global_host_prefix);
844         ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
845         if(unlikely(!ff)) goto cleanup;
846     }
847
848     ff = procfile_readall(ff);
849     if(unlikely(!ff)) goto cleanup;
850
851     last_collected_usec = collected_usec;
852     collected_usec = now_realtime_usec();
853
854     file_counter++;
855
856     unsigned long long last;
857
858     last = utime_raw;
859     utime_raw = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
860     global_utime = (utime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec);
861
862     // nice time, on user time
863     last = ntime_raw;
864     ntime_raw = strtoull(procfile_lineword(ff, 0, 2), NULL, 10);
865     global_utime += (ntime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec);
866
867     last = stime_raw;
868     stime_raw = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
869     global_stime = (stime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec);
870
871     last = gtime_raw;
872     gtime_raw = strtoull(procfile_lineword(ff, 0, 10), NULL, 10);
873     global_gtime = (gtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec);
874
875     if(enable_guest_charts) {
876         // guest nice time, on guest time
877         last = gntime_raw;
878         gntime_raw = strtoull(procfile_lineword(ff, 0, 11), NULL, 10);
879         global_gtime += (gntime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec);
880
881         // remove guest time from user time
882         global_utime -= (global_utime > global_gtime) ? global_gtime : global_utime;
883     }
884
885     if(unlikely(global_iterations_counter == 1)) {
886         global_utime = 0;
887         global_stime = 0;
888         global_gtime = 0;
889     }
890
891     return 1;
892
893 cleanup:
894     global_utime = 0;
895     global_stime = 0;
896     global_gtime = 0;
897     return 0;
898 }
899
900
901 // ----------------------------------------------------------------------------
902 // file descriptor
903 // this is used to keep a global list of all open files of the system
904 // it is needed in order to calculate the unique files processes have open
905
906 #define FILE_DESCRIPTORS_INCREASE_STEP 100
907
908 struct file_descriptor {
909     avl avl;
910 #ifdef NETDATA_INTERNAL_CHECKS
911     uint32_t magic;
912 #endif /* NETDATA_INTERNAL_CHECKS */
913     uint32_t hash;
914     const char *name;
915     int type;
916     int count;
917     int pos;
918 } *all_files = NULL;
919
920 int all_files_len = 0;
921 int all_files_size = 0;
922
923 int file_descriptor_compare(void* a, void* b) {
924 #ifdef NETDATA_INTERNAL_CHECKS
925     if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
926         error("Corrupted index data detected. Please report this.");
927 #endif /* NETDATA_INTERNAL_CHECKS */
928
929     if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
930         return -1;
931
932     else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
933         return 1;
934
935     else
936         return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
937 }
938
939 int file_descriptor_iterator(avl *a) { if(a) {}; return 0; }
940
941 avl_tree all_files_index = {
942         NULL,
943         file_descriptor_compare
944 };
945
946 static struct file_descriptor *file_descriptor_find(const char *name, uint32_t hash) {
947     struct file_descriptor tmp;
948     tmp.hash = (hash)?hash:simple_hash(name);
949     tmp.name = name;
950     tmp.count = 0;
951     tmp.pos = 0;
952 #ifdef NETDATA_INTERNAL_CHECKS
953     tmp.magic = 0x0BADCAFE;
954 #endif /* NETDATA_INTERNAL_CHECKS */
955
956     return (struct file_descriptor *)avl_search(&all_files_index, (avl *) &tmp);
957 }
958
959 #define file_descriptor_add(fd) avl_insert(&all_files_index, (avl *)(fd))
960 #define file_descriptor_remove(fd) avl_remove(&all_files_index, (avl *)(fd))
961
962 #define FILETYPE_OTHER 0
963 #define FILETYPE_FILE 1
964 #define FILETYPE_PIPE 2
965 #define FILETYPE_SOCKET 3
966 #define FILETYPE_INOTIFY 4
967 #define FILETYPE_EVENTFD 5
968 #define FILETYPE_EVENTPOLL 6
969 #define FILETYPE_TIMERFD 7
970 #define FILETYPE_SIGNALFD 8
971
972 void file_descriptor_not_used(int id)
973 {
974     if(id > 0 && id < all_files_size) {
975
976 #ifdef NETDATA_INTERNAL_CHECKS
977         if(all_files[id].magic != 0x0BADCAFE) {
978             error("Ignoring request to remove empty file id %d.", id);
979             return;
980         }
981 #endif /* NETDATA_INTERNAL_CHECKS */
982
983         if(unlikely(debug))
984             fprintf(stderr, "apps.plugin: decreasing slot %d (count = %d).\n", id, all_files[id].count);
985
986         if(all_files[id].count > 0) {
987             all_files[id].count--;
988
989             if(!all_files[id].count) {
990                 if(unlikely(debug))
991                     fprintf(stderr, "apps.plugin:   >> slot %d is empty.\n", id);
992
993                 file_descriptor_remove(&all_files[id]);
994 #ifdef NETDATA_INTERNAL_CHECKS
995                 all_files[id].magic = 0x00000000;
996 #endif /* NETDATA_INTERNAL_CHECKS */
997                 all_files_len--;
998             }
999         }
1000         else
1001             error("Request to decrease counter of fd %d (%s), while the use counter is 0", id, all_files[id].name);
1002     }
1003     else    error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)", id, all_files_size);
1004 }
1005
1006 int file_descriptor_find_or_add(const char *name)
1007 {
1008     static int last_pos = 0;
1009     uint32_t hash = simple_hash(name);
1010
1011     if(unlikely(debug))
1012         fprintf(stderr, "apps.plugin: adding or finding name '%s' with hash %u\n", name, hash);
1013
1014     struct file_descriptor *fd = file_descriptor_find(name, hash);
1015     if(fd) {
1016         // found
1017         if(unlikely(debug))
1018             fprintf(stderr, "apps.plugin:   >> found on slot %d\n", fd->pos);
1019
1020         fd->count++;
1021         return fd->pos;
1022     }
1023     // not found
1024
1025     // check we have enough memory to add it
1026     if(!all_files || all_files_len == all_files_size) {
1027         void *old = all_files;
1028         int i;
1029
1030         // there is no empty slot
1031         if(unlikely(debug))
1032             fprintf(stderr, "apps.plugin: extending fd array to %d entries\n", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP);
1033
1034         all_files = reallocz(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
1035
1036         // if the address changed, we have to rebuild the index
1037         // since all pointers are now invalid
1038         if(old && old != (void *)all_files) {
1039             if(unlikely(debug))
1040                 fprintf(stderr, "apps.plugin:   >> re-indexing.\n");
1041
1042             all_files_index.root = NULL;
1043             for(i = 0; i < all_files_size; i++) {
1044                 if(!all_files[i].count) continue;
1045                 file_descriptor_add(&all_files[i]);
1046             }
1047
1048             if(unlikely(debug))
1049                 fprintf(stderr, "apps.plugin:   >> re-indexing done.\n");
1050         }
1051
1052         for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) {
1053             all_files[i].count = 0;
1054             all_files[i].name = NULL;
1055 #ifdef NETDATA_INTERNAL_CHECKS
1056             all_files[i].magic = 0x00000000;
1057 #endif /* NETDATA_INTERNAL_CHECKS */
1058             all_files[i].pos = i;
1059         }
1060
1061         if(!all_files_size) all_files_len = 1;
1062         all_files_size += FILE_DESCRIPTORS_INCREASE_STEP;
1063     }
1064
1065     if(unlikely(debug))
1066         fprintf(stderr, "apps.plugin:   >> searching for empty slot.\n");
1067
1068     // search for an empty slot
1069     int i, c;
1070     for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
1071         if(c >= all_files_size) c = 0;
1072         if(c == 0) continue;
1073
1074         if(!all_files[c].count) {
1075             if(unlikely(debug))
1076                 fprintf(stderr, "apps.plugin:   >> Examining slot %d.\n", c);
1077
1078 #ifdef NETDATA_INTERNAL_CHECKS
1079             if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
1080                 error("fd on position %d is not cleared properly. It still has %s in it.\n", c, all_files[c].name);
1081 #endif /* NETDATA_INTERNAL_CHECKS */
1082
1083             if(unlikely(debug))
1084                 fprintf(stderr, "apps.plugin:   >> %s fd position %d for %s (last name: %s)\n", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
1085
1086             if(all_files[c].name) freez((void *)all_files[c].name);
1087             all_files[c].name = NULL;
1088             last_pos = c;
1089             break;
1090         }
1091     }
1092     if(i == all_files_size) {
1093         fatal("We should find an empty slot, but there isn't any");
1094         exit(1);
1095     }
1096
1097     if(unlikely(debug))
1098         fprintf(stderr, "apps.plugin:   >> updating slot %d.\n", c);
1099
1100     all_files_len++;
1101
1102     // else we have an empty slot in 'c'
1103
1104     int type;
1105     if(name[0] == '/') type = FILETYPE_FILE;
1106     else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE;
1107     else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET;
1108     else if(strcmp(name, "anon_inode:inotify") == 0 || strcmp(name, "inotify") == 0) type = FILETYPE_INOTIFY;
1109     else if(strcmp(name, "anon_inode:[eventfd]") == 0) type = FILETYPE_EVENTFD;
1110     else if(strcmp(name, "anon_inode:[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
1111     else if(strcmp(name, "anon_inode:[timerfd]") == 0) type = FILETYPE_TIMERFD;
1112     else if(strcmp(name, "anon_inode:[signalfd]") == 0) type = FILETYPE_SIGNALFD;
1113     else if(strncmp(name, "anon_inode:", 11) == 0) {
1114         if(unlikely(debug))
1115             fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
1116
1117         type = FILETYPE_OTHER;
1118     }
1119     else {
1120         if(unlikely(debug))
1121             fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name);
1122
1123         type = FILETYPE_OTHER;
1124     }
1125
1126     all_files[c].name = strdupz(name);
1127     all_files[c].hash = hash;
1128     all_files[c].type = type;
1129     all_files[c].pos  = c;
1130     all_files[c].count = 1;
1131 #ifdef NETDATA_INTERNAL_CHECKS
1132     all_files[c].magic = 0x0BADCAFE;
1133 #endif /* NETDATA_INTERNAL_CHECKS */
1134     file_descriptor_add(&all_files[c]);
1135
1136     if(unlikely(debug))
1137         fprintf(stderr, "apps.plugin: using fd position %d (name: %s)\n", c, all_files[c].name);
1138
1139     return c;
1140 }
1141
1142 int read_pid_file_descriptors(struct pid_stat *p) {
1143     char dirname[FILENAME_MAX+1];
1144
1145     snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", global_host_prefix, p->pid);
1146     DIR *fds = opendir(dirname);
1147     if(fds) {
1148         int c;
1149         struct dirent *de;
1150         char fdname[FILENAME_MAX + 1];
1151         char linkname[FILENAME_MAX + 1];
1152
1153         // make the array negative
1154         for(c = 0 ; c < p->fds_size ; c++)
1155             p->fds[c] = -p->fds[c];
1156
1157         while((de = readdir(fds))) {
1158             if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
1159                 continue;
1160
1161             // check if the fds array is small
1162             int fdid = atoi(de->d_name);
1163             if(fdid < 0) continue;
1164             if(fdid >= p->fds_size) {
1165                 // it is small, extend it
1166                 if(unlikely(debug))
1167                     fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100);
1168
1169                 p->fds = reallocz(p->fds, (fdid + 100) * sizeof(int));
1170                 if(!p->fds) {
1171                     fatal("Cannot re-allocate fds for %s", p->comm);
1172                     break;
1173                 }
1174
1175                 // and initialize it
1176                 for(c = p->fds_size ; c < (fdid + 100) ; c++) p->fds[c] = 0;
1177                 p->fds_size = fdid + 100;
1178             }
1179
1180             if(p->fds[fdid] == 0) {
1181                 // we don't know this fd, get it
1182
1183                 sprintf(fdname, "%s/proc/%d/fd/%s", global_host_prefix, p->pid, de->d_name);
1184                 ssize_t l = readlink(fdname, linkname, FILENAME_MAX);
1185                 if(l == -1) {
1186                     if(debug || (p->target && p->target->debug)) {
1187                         if(debug || (p->target && p->target->debug))
1188                             error("Cannot read link %s", fdname);
1189                     }
1190                     continue;
1191                 }
1192                 linkname[l] = '\0';
1193                 file_counter++;
1194
1195                 // if another process already has this, we will get
1196                 // the same id
1197                 p->fds[fdid] = file_descriptor_find_or_add(linkname);
1198             }
1199
1200             // else make it positive again, we need it
1201             // of course, the actual file may have changed, but we don't care so much
1202             // FIXME: we could compare the inode as returned by readdir dirent structure
1203             else p->fds[fdid] = -p->fds[fdid];
1204         }
1205         closedir(fds);
1206
1207         // remove all the negative file descriptors
1208         for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] < 0) {
1209             file_descriptor_not_used(-p->fds[c]);
1210             p->fds[c] = 0;
1211         }
1212     }
1213     else return 0;
1214
1215     return 1;
1216 }
1217
1218 // ----------------------------------------------------------------------------
1219
1220 int print_process_and_parents(struct pid_stat *p, unsigned long long time) {
1221     char *prefix = "\\_ ";
1222     int indent = 0;
1223
1224     if(p->parent)
1225         indent = print_process_and_parents(p->parent, p->stat_collected_usec);
1226     else
1227         prefix = " > ";
1228
1229     char buffer[indent + 1];
1230     int i;
1231
1232     for(i = 0; i < indent ;i++) buffer[i] = ' ';
1233     buffer[i] = '\0';
1234
1235     fprintf(stderr, "  %s %s%s (%d %s %lld"
1236         , buffer
1237         , prefix
1238         , p->comm
1239         , p->pid
1240         , p->updated?"running":"exited"
1241         , (long long)p->stat_collected_usec - (long long)time
1242         );
1243
1244     if(p->utime)   fprintf(stderr, " utime=%llu",   p->utime);
1245     if(p->stime)   fprintf(stderr, " stime=%llu",   p->stime);
1246     if(p->gtime)   fprintf(stderr, " gtime=%llu",   p->gtime);
1247     if(p->cutime)  fprintf(stderr, " cutime=%llu",  p->cutime);
1248     if(p->cstime)  fprintf(stderr, " cstime=%llu",  p->cstime);
1249     if(p->cgtime)  fprintf(stderr, " cgtime=%llu",  p->cgtime);
1250     if(p->minflt)  fprintf(stderr, " minflt=%llu",  p->minflt);
1251     if(p->cminflt) fprintf(stderr, " cminflt=%llu", p->cminflt);
1252     if(p->majflt)  fprintf(stderr, " majflt=%llu",  p->majflt);
1253     if(p->cmajflt) fprintf(stderr, " cmajflt=%llu", p->cmajflt);
1254     fprintf(stderr, ")\n");
1255
1256     return indent + 1;
1257 }
1258
1259 void print_process_tree(struct pid_stat *p, char *msg) {
1260     log_date(stderr);
1261     fprintf(stderr, "%s: process %s (%d, %s) with parents:\n", msg, p->comm, p->pid, p->updated?"running":"exited");
1262     print_process_and_parents(p, p->stat_collected_usec);
1263 }
1264
1265 void find_lost_child_debug(struct pid_stat *pe, unsigned long long lost, int type) {
1266     int found = 0;
1267     struct pid_stat *p = NULL;
1268
1269     for(p = root_of_pids; p ; p = p->next) {
1270         if(p == pe) continue;
1271
1272         switch(type) {
1273             case 1:
1274                 if(p->cminflt > lost) {
1275                     fprintf(stderr, " > process %d (%s) could use the lost exited child minflt %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
1276                     found++;
1277                 }
1278                 break;
1279
1280             case 2:
1281                 if(p->cmajflt > lost) {
1282                     fprintf(stderr, " > process %d (%s) could use the lost exited child majflt %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
1283                     found++;
1284                 }
1285                 break;
1286
1287             case 3:
1288                 if(p->cutime > lost) {
1289                     fprintf(stderr, " > process %d (%s) could use the lost exited child utime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
1290                     found++;
1291                 }
1292                 break;
1293
1294             case 4:
1295                 if(p->cstime > lost) {
1296                     fprintf(stderr, " > process %d (%s) could use the lost exited child stime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
1297                     found++;
1298                 }
1299                 break;
1300
1301             case 5:
1302                 if(p->cgtime > lost) {
1303                     fprintf(stderr, " > process %d (%s) could use the lost exited child gtime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
1304                     found++;
1305                 }
1306                 break;
1307         }
1308     }
1309
1310     if(!found) {
1311         switch(type) {
1312             case 1:
1313                 fprintf(stderr, " > cannot find any process to use the lost exited child minflt %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
1314                 break;
1315
1316             case 2:
1317                 fprintf(stderr, " > cannot find any process to use the lost exited child majflt %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
1318                 break;
1319
1320             case 3:
1321                 fprintf(stderr, " > cannot find any process to use the lost exited child utime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
1322                 break;
1323
1324             case 4:
1325                 fprintf(stderr, " > cannot find any process to use the lost exited child stime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
1326                 break;
1327
1328             case 5:
1329                 fprintf(stderr, " > cannot find any process to use the lost exited child gtime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
1330                 break;
1331         }
1332     }
1333 }
1334
1335 unsigned long long remove_exited_child_from_parent(unsigned long long *field, unsigned long long *pfield) {
1336     unsigned long long absorbed = 0;
1337
1338     if(*field > *pfield) {
1339         absorbed += *pfield;
1340         *field -= *pfield;
1341         *pfield = 0;
1342     }
1343     else {
1344         absorbed += *field;
1345         *pfield -= *field;
1346         *field = 0;
1347     }
1348
1349     return absorbed;
1350 }
1351
1352 void process_exited_processes() {
1353     struct pid_stat *p;
1354
1355     for(p = root_of_pids; p ; p = p->next) {
1356         if(p->updated || !p->stat_collected_usec)
1357             continue;
1358
1359         struct pid_stat *pp = p->parent;
1360
1361         unsigned long long utime  = (p->utime_raw + p->cutime_raw)   * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
1362         unsigned long long stime  = (p->stime_raw + p->cstime_raw)   * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
1363         unsigned long long gtime  = (p->gtime_raw + p->cgtime_raw)   * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
1364         unsigned long long minflt = (p->minflt_raw + p->cminflt_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
1365         unsigned long long majflt = (p->majflt_raw + p->cmajflt_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
1366
1367         if(utime + stime + gtime + minflt + majflt == 0)
1368             continue;
1369
1370         if(unlikely(debug)) {
1371             log_date(stderr);
1372             fprintf(stderr, "Absorb %s (%d %s total resources: utime=%llu stime=%llu gtime=%llu minflt=%llu majflt=%llu)\n"
1373                 , p->comm
1374                 , p->pid
1375                 , p->updated?"running":"exited"
1376                 , utime
1377                 , stime
1378                 , gtime
1379                 , minflt
1380                 , majflt
1381                 );
1382             print_process_tree(p, "Searching parents");
1383         }
1384
1385         for(pp = p->parent; pp ; pp = pp->parent) {
1386             if(!pp->updated) continue;
1387
1388             unsigned long long absorbed;
1389             absorbed = remove_exited_child_from_parent(&utime,  &pp->cutime);
1390             if(unlikely(debug && absorbed))
1391                 fprintf(stderr, " > process %s (%d %s) absorbed %llu utime (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, utime);
1392
1393             absorbed = remove_exited_child_from_parent(&stime,  &pp->cstime);
1394             if(unlikely(debug && absorbed))
1395                 fprintf(stderr, " > process %s (%d %s) absorbed %llu stime (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, stime);
1396
1397             absorbed = remove_exited_child_from_parent(&gtime,  &pp->cgtime);
1398             if(unlikely(debug && absorbed))
1399                 fprintf(stderr, " > process %s (%d %s) absorbed %llu gtime (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, gtime);
1400
1401             absorbed = remove_exited_child_from_parent(&minflt, &pp->cminflt);
1402             if(unlikely(debug && absorbed))
1403                 fprintf(stderr, " > process %s (%d %s) absorbed %llu minflt (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, minflt);
1404
1405             absorbed = remove_exited_child_from_parent(&majflt, &pp->cmajflt);
1406             if(unlikely(debug && absorbed))
1407                 fprintf(stderr, " > process %s (%d %s) absorbed %llu majflt (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, majflt);
1408         }
1409
1410         if(unlikely(utime + stime + gtime + minflt + majflt > 0)) {
1411             if(unlikely(debug)) {
1412                 if(utime)  find_lost_child_debug(p, utime,  3);
1413                 if(stime)  find_lost_child_debug(p, stime,  4);
1414                 if(gtime)  find_lost_child_debug(p, gtime,  5);
1415                 if(minflt) find_lost_child_debug(p, minflt, 1);
1416                 if(majflt) find_lost_child_debug(p, majflt, 2);
1417             }
1418
1419             p->keep = 1;
1420
1421             if(unlikely(debug))
1422                 fprintf(stderr, " > remaining resources - KEEP - for another loop: %s (%d %s total resources: utime=%llu stime=%llu gtime=%llu minflt=%llu majflt=%llu)\n"
1423                     , p->comm
1424                     , p->pid
1425                     , p->updated?"running":"exited"
1426                     , utime
1427                     , stime
1428                     , gtime
1429                     , minflt
1430                     , majflt
1431                     );
1432
1433             for(pp = p->parent; pp ; pp = pp->parent) {
1434                 if(pp->updated) break;
1435                 pp->keep = 1;
1436
1437                 if(unlikely(debug))
1438                     fprintf(stderr, " > - KEEP - parent for another loop: %s (%d %s)\n"
1439                         , pp->comm
1440                         , pp->pid
1441                         , pp->updated?"running":"exited"
1442                         );
1443             }
1444
1445             p->utime_raw   = utime  * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
1446             p->stime_raw   = stime  * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
1447             p->gtime_raw   = gtime  * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
1448             p->minflt_raw  = minflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
1449             p->majflt_raw  = majflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
1450             p->cutime_raw = p->cstime_raw = p->cgtime_raw = p->cminflt_raw = p->cmajflt_raw = 0;
1451
1452             if(unlikely(debug))
1453                 fprintf(stderr, "\n");
1454         }
1455         else if(unlikely(debug)) {
1456             fprintf(stderr, " > totally absorbed - DONE - %s (%d %s)\n"
1457                 , p->comm
1458                 , p->pid
1459                 , p->updated?"running":"exited"
1460                 );
1461         }
1462     }
1463 }
1464
1465 void link_all_processes_to_their_parents(void) {
1466     struct pid_stat *p, *pp;
1467
1468     // link all children to their parents
1469     // and update children count on parents
1470     for(p = root_of_pids; p ; p = p->next) {
1471         // for each process found
1472
1473         p->sortlist = 0;
1474         p->parent = NULL;
1475
1476         if(unlikely(!p->ppid)) {
1477             p->parent = NULL;
1478             continue;
1479         }
1480
1481         pp = all_pids[p->ppid];
1482         if(likely(pp)) {
1483             p->parent = pp;
1484             pp->children_count++;
1485
1486             if(unlikely(debug || (p->target && p->target->debug)))
1487                 fprintf(stderr, "apps.plugin: \tchild %d (%s, %s) on target '%s' has parent %d (%s, %s). Parent: utime=%llu, stime=%llu, gtime=%llu, minflt=%llu, majflt=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->updated?"running":"exited", (p->target)?p->target->name:"UNSET", pp->pid, pp->comm, pp->updated?"running":"exited", pp->utime, pp->stime, pp->gtime, pp->minflt, pp->majflt, pp->cutime, pp->cstime, pp->cgtime, pp->cminflt, pp->cmajflt);
1488         }
1489         else {
1490             p->parent = NULL;
1491             error("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
1492         }
1493     }
1494 }
1495
1496 // ----------------------------------------------------------------------------
1497
1498 // 1. read all files in /proc
1499 // 2. for each numeric directory:
1500 //    i.   read /proc/pid/stat
1501 //    ii.  read /proc/pid/statm
1502 //    iii. read /proc/pid/io (requires root access)
1503 //    iii. read the entries in directory /proc/pid/fd (requires root access)
1504 //         for each entry:
1505 //         a. find or create a struct file_descriptor
1506 //         b. cleanup any old/unused file_descriptors
1507
1508 // after all these, some pids may be linked to targets, while others may not
1509
1510 // in case of errors, only 1 every 1000 errors is printed
1511 // to avoid filling up all disk space
1512 // if debug is enabled, all errors are printed
1513
1514 static int compar_pid(const void *pid1, const void *pid2) {
1515
1516     struct pid_stat *p1 = all_pids[*((pid_t *)pid1)];
1517     struct pid_stat *p2 = all_pids[*((pid_t *)pid2)];
1518
1519     if(p1->sortlist > p2->sortlist)
1520         return -1;
1521     else
1522         return 1;
1523 }
1524
1525 static inline int managed_log(struct pid_stat *p, uint32_t log, int status) {
1526     if(unlikely(!status)) {
1527         // error("command failed log %u, errno %d", log, errno);
1528
1529         if(unlikely(debug || errno != ENOENT)) {
1530             if(unlikely(debug || !(p->log_thrown & log))) {
1531                 p->log_thrown |= log;
1532                 switch(log) {
1533                     case PID_LOG_IO:
1534                         error("Cannot process %s/proc/%d/io (command '%s')", global_host_prefix, p->pid, p->comm);
1535                         break;
1536
1537                     case PID_LOG_STATM:
1538                         error("Cannot process %s/proc/%d/statm (command '%s')", global_host_prefix, p->pid, p->comm);
1539                         break;
1540
1541                     case PID_LOG_CMDLINE:
1542                         error("Cannot process %s/proc/%d/cmdline (command '%s')", global_host_prefix, p->pid, p->comm);
1543                         break;
1544
1545                     case PID_LOG_FDS:
1546                         error("Cannot process entries in %s/proc/%d/fd (command '%s')", global_host_prefix, p->pid, p->comm);
1547                         break;
1548
1549                     case PID_LOG_STAT:
1550                         break;
1551
1552                     default:
1553                         error("unhandled error for pid %d, command '%s'", p->pid, p->comm);
1554                         break;
1555                 }
1556             }
1557         }
1558         errno = 0;
1559     }
1560     else if(unlikely(p->log_thrown & log)) {
1561         // error("unsetting log %u on pid %d", log, p->pid);
1562         p->log_thrown &= ~log;
1563     }
1564
1565     return status;
1566 }
1567
1568 void collect_data_for_pid(pid_t pid) {
1569     if(unlikely(pid <= 0 || pid > pid_max)) {
1570         error("Invalid pid %d read (expected 1 to %d). Ignoring process.", pid, pid_max);
1571         return;
1572     }
1573
1574     struct pid_stat *p = get_pid_entry(pid);
1575     if(unlikely(!p || p->read)) return;
1576     p->read             = 1;
1577
1578     // fprintf(stderr, "Reading process %d (%s), sortlist %d\n", p->pid, p->comm, p->sortlist);
1579
1580     // --------------------------------------------------------------------
1581     // /proc/<pid>/stat
1582
1583     if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p))))
1584         // there is no reason to proceed if we cannot get its status
1585         return;
1586
1587     read_proc_pid_ownership(p);
1588
1589     // check its parent pid
1590     if(unlikely(p->ppid < 0 || p->ppid > pid_max)) {
1591         error("Pid %d (command '%s') states invalid parent pid %d. Using 0.", pid, p->comm, p->ppid);
1592         p->ppid = 0;
1593     }
1594
1595     // --------------------------------------------------------------------
1596     // /proc/<pid>/io
1597
1598     managed_log(p, PID_LOG_IO, read_proc_pid_io(p));
1599
1600     // --------------------------------------------------------------------
1601     // /proc/<pid>/statm
1602
1603     if(unlikely(!managed_log(p, PID_LOG_STATM, read_proc_pid_statm(p))))
1604         // there is no reason to proceed if we cannot get its memory status
1605         return;
1606
1607     // --------------------------------------------------------------------
1608     // link it
1609
1610     // check if it is target
1611     // we do this only once, the first time this pid is loaded
1612     if(unlikely(p->new_entry)) {
1613         // /proc/<pid>/cmdline
1614         if(likely(proc_pid_cmdline_is_needed))
1615             managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
1616
1617         if(unlikely(debug))
1618             fprintf(stderr, "apps.plugin: \tJust added %d (%s)\n", pid, p->comm);
1619
1620         uint32_t hash = simple_hash(p->comm);
1621         size_t pclen  = strlen(p->comm);
1622
1623         struct target *w;
1624         for(w = apps_groups_root_target; w ; w = w->next) {
1625             // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
1626
1627             // find it - 4 cases:
1628             // 1. the target is not a pattern
1629             // 2. the target has the prefix
1630             // 3. the target has the suffix
1631             // 4. the target is something inside cmdline
1632             if( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
1633                    || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
1634                    || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
1635                    || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare))
1636                     ) {
1637                 if(w->target) p->target = w->target;
1638                 else p->target = w;
1639
1640                 if(debug || (p->target && p->target->debug))
1641                     fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
1642
1643                 break;
1644             }
1645         }
1646     }
1647
1648     // --------------------------------------------------------------------
1649     // /proc/<pid>/fd
1650
1651     if(enable_file_charts)
1652             managed_log(p, PID_LOG_FDS, read_pid_file_descriptors(p));
1653
1654     // --------------------------------------------------------------------
1655     // done!
1656
1657     if(unlikely(debug && include_exited_childs && all_pids_count && p->ppid && all_pids[p->ppid] && !all_pids[p->ppid]->read))
1658         fprintf(stderr, "Read process %d (%s) sortlisted %d, but its parent %d (%s) sortlisted %d, is not read\n", p->pid, p->comm, p->sortlist, all_pids[p->ppid]->pid, all_pids[p->ppid]->comm, all_pids[p->ppid]->sortlist);
1659
1660     // mark it as updated
1661     p->updated = 1;
1662     p->keep = 0;
1663     p->keeploops = 0;
1664 }
1665
1666 int collect_data_for_all_processes_from_proc(void) {
1667     struct pid_stat *p = NULL;
1668
1669     if(all_pids_count) {
1670         // read parents before childs
1671         // this is needed to prevent a situation where
1672         // a child is found running, but until we read
1673         // its parent, it has exited and its parent
1674         // has accumulated its resources
1675
1676         long slc = 0;
1677         for(p = root_of_pids; p ; p = p->next) {
1678             p->read             = 0;
1679             p->updated          = 0;
1680             p->new_entry        = 0;
1681             p->merged           = 0;
1682             p->children_count   = 0;
1683             p->parent           = NULL;
1684
1685             all_pids_sortlist[slc++] = p->pid;
1686         }
1687
1688         if(unlikely(slc != all_pids_count)) {
1689             error("Internal error: I was thinking I had %ld processes in my arrays, but it seems there are more.", all_pids_count);
1690             all_pids_count = slc;
1691         }
1692
1693         if(include_exited_childs) {
1694             qsort((void *)all_pids_sortlist, all_pids_count, sizeof(pid_t), compar_pid);
1695             for(slc = 0; slc < all_pids_count; slc++)
1696                 collect_data_for_pid(all_pids_sortlist[slc]);
1697         }
1698     }
1699
1700     char dirname[FILENAME_MAX + 1];
1701
1702     snprintfz(dirname, FILENAME_MAX, "%s/proc", global_host_prefix);
1703     DIR *dir = opendir(dirname);
1704     if(!dir) return 0;
1705
1706     struct dirent *file = NULL;
1707
1708     while((file = readdir(dir))) {
1709         char *endptr = file->d_name;
1710         pid_t pid = (pid_t) strtoul(file->d_name, &endptr, 10);
1711
1712         // make sure we read a valid number
1713         if(unlikely(endptr == file->d_name || *endptr != '\0'))
1714             continue;
1715
1716         collect_data_for_pid(pid);
1717     }
1718     closedir(dir);
1719
1720     // normally this is done
1721     // however we may have processes exited while we collected values
1722     // so let's find the exited ones
1723     // we do this by collecting the ownership of process
1724     // if we manage to get the ownership, the process still runs
1725
1726     read_proc_stat();
1727     link_all_processes_to_their_parents();
1728     process_exited_processes();
1729
1730     return 1;
1731 }
1732
1733 // ----------------------------------------------------------------------------
1734 // update statistics on the targets
1735
1736 // 1. link all childs to their parents
1737 // 2. go from bottom to top, marking as merged all childs to their parents
1738 //    this step links all parents without a target to the child target, if any
1739 // 3. link all top level processes (the ones not merged) to the default target
1740 // 4. go from top to bottom, linking all childs without a target, to their parent target
1741 //    after this step, all processes have a target
1742 // [5. for each killed pid (updated = 0), remove its usage from its target]
1743 // 6. zero all apps_groups_targets
1744 // 7. concentrate all values on the apps_groups_targets
1745 // 8. remove all killed processes
1746 // 9. find the unique file count for each target
1747 // check: update_apps_groups_statistics()
1748
1749 void cleanup_exited_pids(void) {
1750     int c;
1751     struct pid_stat *p = NULL;
1752
1753     for(p = root_of_pids; p ;) {
1754         if(!p->updated && (!p->keep || p->keeploops > 0)) {
1755 //          fprintf(stderr, "\tEXITED %d %s [parent %d %s, target %s] utime=%llu, stime=%llu, gtime=%llu, cutime=%llu, cstime=%llu, cgtime=%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->gtime, p->cutime, p->cstime, p->cgtime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
1756
1757             if(unlikely(debug && (p->keep || p->keeploops)))
1758                 fprintf(stderr, " > CLEANUP cannot keep exited process %d (%s) anymore - removing it.\n", p->pid, p->comm);
1759
1760             for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
1761                 file_descriptor_not_used(p->fds[c]);
1762                 p->fds[c] = 0;
1763             }
1764
1765             pid_t r = p->pid;
1766             p = p->next;
1767             del_pid_entry(r);
1768         }
1769         else {
1770             if(unlikely(p->keep)) p->keeploops++;
1771             p->keep = 0;
1772             p = p->next;
1773         }
1774     }
1775 }
1776
1777 void apply_apps_groups_targets_inheritance(void) {
1778     struct pid_stat *p = NULL;
1779
1780     // children that do not have a target
1781     // inherit their target from their parent
1782     int found = 1, loops = 0;
1783     while(found) {
1784         if(unlikely(debug)) loops++;
1785         found = 0;
1786         for(p = root_of_pids; p ; p = p->next) {
1787             // if this process does not have a target
1788             // and it has a parent
1789             // and its parent has a target
1790             // then, set the parent's target to this process
1791             if(unlikely(!p->target && p->parent && p->parent->target)) {
1792                 p->target = p->parent->target;
1793                 found++;
1794
1795                 if(debug || (p->target && p->target->debug))
1796                     fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s).\n", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
1797             }
1798         }
1799     }
1800
1801     // find all the procs with 0 childs and merge them to their parents
1802     // repeat, until nothing more can be done.
1803     int sortlist = 1;
1804     found = 1;
1805     while(found) {
1806         if(unlikely(debug)) loops++;
1807         found = 0;
1808
1809         for(p = root_of_pids; p ; p = p->next) {
1810             if(unlikely(!p->sortlist && !p->children_count))
1811                 p->sortlist = sortlist++;
1812
1813             // if this process does not have any children
1814             // and is not already merged
1815             // and has a parent
1816             // and its parent has children
1817             // and the target of this process and its parent is the same, or the parent does not have a target
1818             // and its parent is not init
1819             // then, mark them as merged.
1820             if(unlikely(
1821                     !p->children_count
1822                     && !p->merged
1823                     && p->parent
1824                     && p->parent->children_count
1825                     && (p->target == p->parent->target || !p->parent->target)
1826                     && p->ppid != 1
1827                 )) {
1828                 p->parent->children_count--;
1829                 p->merged = 1;
1830
1831                 // the parent inherits the child's target, if it does not have a target itself
1832                 if(unlikely(p->target && !p->parent->target)) {
1833                     p->parent->target = p->target;
1834
1835                     if(debug || (p->target && p->target->debug))
1836                         fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %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);
1837                 }
1838
1839                 found++;
1840             }
1841         }
1842
1843         if(unlikely(debug))
1844             fprintf(stderr, "apps.plugin: TARGET INHERITANCE: merged %d processes\n", found);
1845     }
1846
1847     // init goes always to default target
1848     if(all_pids[1])
1849         all_pids[1]->target = apps_groups_default_target;
1850
1851     // give a default target on all top level processes
1852     if(unlikely(debug)) loops++;
1853     for(p = root_of_pids; p ; p = p->next) {
1854         // if the process is not merged itself
1855         // then is is a top level process
1856         if(unlikely(!p->merged && !p->target))
1857             p->target = apps_groups_default_target;
1858
1859         // make sure all processes have a sortlist
1860         if(unlikely(!p->sortlist))
1861             p->sortlist = sortlist++;
1862     }
1863
1864     if(all_pids[1])
1865         all_pids[1]->sortlist = sortlist++;
1866
1867     // give a target to all merged child processes
1868     found = 1;
1869     while(found) {
1870         if(unlikely(debug)) loops++;
1871         found = 0;
1872         for(p = root_of_pids; p ; p = p->next) {
1873             if(unlikely(!p->target && p->merged && p->parent && p->parent->target)) {
1874                 p->target = p->parent->target;
1875                 found++;
1876
1877                 if(debug || (p->target && p->target->debug))
1878                     fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s) at phase 2.\n", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
1879             }
1880         }
1881     }
1882
1883     if(unlikely(debug))
1884         fprintf(stderr, "apps.plugin: apply_apps_groups_targets_inheritance() made %d loops on the process tree\n", loops);
1885 }
1886
1887 long zero_all_targets(struct target *root) {
1888     struct target *w;
1889     long count = 0;
1890
1891     for (w = root; w ; w = w->next) {
1892         count++;
1893
1894         if(w->fds) freez(w->fds);
1895         w->fds = NULL;
1896
1897         w->minflt = 0;
1898         w->majflt = 0;
1899         w->utime = 0;
1900         w->stime = 0;
1901         w->gtime = 0;
1902         w->cminflt = 0;
1903         w->cmajflt = 0;
1904         w->cutime = 0;
1905         w->cstime = 0;
1906         w->cgtime = 0;
1907         w->num_threads = 0;
1908         // w->rss = 0;
1909         w->processes = 0;
1910
1911         w->statm_size = 0;
1912         w->statm_resident = 0;
1913         w->statm_share = 0;
1914         // w->statm_text = 0;
1915         // w->statm_lib = 0;
1916         // w->statm_data = 0;
1917         // w->statm_dirty = 0;
1918
1919         w->io_logical_bytes_read = 0;
1920         w->io_logical_bytes_written = 0;
1921         // w->io_read_calls = 0;
1922         // w->io_write_calls = 0;
1923         w->io_storage_bytes_read = 0;
1924         w->io_storage_bytes_written = 0;
1925         // w->io_cancelled_write_bytes = 0;
1926     }
1927
1928     return count;
1929 }
1930
1931 void aggregate_pid_on_target(struct target *w, struct pid_stat *p, struct target *o) {
1932     (void)o;
1933
1934     if(unlikely(!w->fds))
1935         w->fds = callocz(sizeof(int), (size_t) all_files_size);
1936
1937     if(likely(p->updated)) {
1938         w->cutime  += p->cutime;
1939         w->cstime  += p->cstime;
1940         w->cgtime  += p->cgtime;
1941         w->cminflt += p->cminflt;
1942         w->cmajflt += p->cmajflt;
1943
1944         w->utime  += p->utime;
1945         w->stime  += p->stime;
1946         w->gtime  += p->gtime;
1947         w->minflt += p->minflt;
1948         w->majflt += p->majflt;
1949
1950         // w->rss += p->rss;
1951
1952         w->statm_size += p->statm_size;
1953         w->statm_resident += p->statm_resident;
1954         w->statm_share += p->statm_share;
1955         // w->statm_text += p->statm_text;
1956         // w->statm_lib += p->statm_lib;
1957         // w->statm_data += p->statm_data;
1958         // w->statm_dirty += p->statm_dirty;
1959
1960         w->io_logical_bytes_read    += p->io_logical_bytes_read;
1961         w->io_logical_bytes_written += p->io_logical_bytes_written;
1962         // w->io_read_calls            += p->io_read_calls;
1963         // w->io_write_calls           += p->io_write_calls;
1964         w->io_storage_bytes_read    += p->io_storage_bytes_read;
1965         w->io_storage_bytes_written += p->io_storage_bytes_written;
1966         // w->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
1967
1968         w->processes++;
1969         w->num_threads += p->num_threads;
1970
1971         if(likely(w->fds)) {
1972             int c;
1973             for(c = 0; c < p->fds_size ;c++) {
1974                 if(p->fds[c] == 0) continue;
1975
1976                 if(likely(p->fds[c] < all_files_size)) {
1977                     if(w->fds) w->fds[p->fds[c]]++;
1978                 }
1979                 else
1980                     error("Invalid fd number %d", p->fds[c]);
1981             }
1982         }
1983
1984         if(unlikely(debug || w->debug))
1985             fprintf(stderr, "apps.plugin: \taggregating '%s' pid %d on target '%s' utime=%llu, stime=%llu, gtime=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->pid, w->name, p->utime, p->stime, p->gtime, p->cutime, p->cstime, p->cgtime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
1986     }
1987 }
1988
1989 void count_targets_fds(struct target *root) {
1990     int c;
1991     struct target *w;
1992
1993     for (w = root; w ; w = w->next) {
1994         if(!w->fds) continue;
1995
1996         w->openfiles = 0;
1997         w->openpipes = 0;
1998         w->opensockets = 0;
1999         w->openinotifies = 0;
2000         w->openeventfds = 0;
2001         w->opentimerfds = 0;
2002         w->opensignalfds = 0;
2003         w->openeventpolls = 0;
2004         w->openother = 0;
2005
2006         for(c = 1; c < all_files_size ;c++) {
2007             if(w->fds[c] > 0)
2008                 switch(all_files[c].type) {
2009                 case FILETYPE_FILE:
2010                     w->openfiles++;
2011                     break;
2012
2013                 case FILETYPE_PIPE:
2014                     w->openpipes++;
2015                     break;
2016
2017                 case FILETYPE_SOCKET:
2018                     w->opensockets++;
2019                     break;
2020
2021                 case FILETYPE_INOTIFY:
2022                     w->openinotifies++;
2023                     break;
2024
2025                 case FILETYPE_EVENTFD:
2026                     w->openeventfds++;
2027                     break;
2028
2029                 case FILETYPE_TIMERFD:
2030                     w->opentimerfds++;
2031                     break;
2032
2033                 case FILETYPE_SIGNALFD:
2034                     w->opensignalfds++;
2035                     break;
2036
2037                 case FILETYPE_EVENTPOLL:
2038                     w->openeventpolls++;
2039                     break;
2040
2041                 default:
2042                     w->openother++;
2043             }
2044         }
2045
2046         freez(w->fds);
2047         w->fds = NULL;
2048     }
2049 }
2050
2051 void calculate_netdata_statistics(void) {
2052     apply_apps_groups_targets_inheritance();
2053
2054     zero_all_targets(users_root_target);
2055     zero_all_targets(groups_root_target);
2056     apps_groups_targets = zero_all_targets(apps_groups_root_target);
2057
2058     // this has to be done, before the cleanup
2059     struct pid_stat *p = NULL;
2060     struct target *w = NULL, *o = NULL;
2061
2062     // concentrate everything on the apps_groups_targets
2063     for(p = root_of_pids; p ; p = p->next) {
2064
2065         // --------------------------------------------------------------------
2066         // apps_groups targets
2067         if(likely(p->target))
2068             aggregate_pid_on_target(p->target, p, NULL);
2069         else
2070             error("pid %d %s was left without a target!", p->pid, p->comm);
2071
2072
2073         // --------------------------------------------------------------------
2074         // user targets
2075         o = p->user_target;
2076         if(likely(p->user_target && p->user_target->uid == p->uid))
2077             w = p->user_target;
2078         else {
2079             if(unlikely(debug && p->user_target))
2080                     fprintf(stderr, "apps.plugin: \t\tpid %d (%s) switched user from %u (%s) to %u.\n", p->pid, p->comm, p->user_target->uid, p->user_target->name, p->uid);
2081
2082             w = p->user_target = get_users_target(p->uid);
2083         }
2084
2085         if(likely(w))
2086             aggregate_pid_on_target(w, p, o);
2087         else
2088             error("pid %d %s was left without a user target!", p->pid, p->comm);
2089
2090
2091         // --------------------------------------------------------------------
2092         // group targets
2093         o = p->group_target;
2094         if(likely(p->group_target && p->group_target->gid == p->gid))
2095             w = p->group_target;
2096         else {
2097             if(unlikely(debug && p->group_target))
2098                     fprintf(stderr, "apps.plugin: \t\tpid %d (%s) switched group from %u (%s) to %u.\n", p->pid, p->comm, p->group_target->gid, p->group_target->name, p->gid);
2099
2100             w = p->group_target = get_groups_target(p->gid);
2101         }
2102
2103         if(likely(w))
2104             aggregate_pid_on_target(w, p, o);
2105         else
2106             error("pid %d %s was left without a group target!", p->pid, p->comm);
2107
2108     }
2109
2110     count_targets_fds(apps_groups_root_target);
2111     count_targets_fds(users_root_target);
2112     count_targets_fds(groups_root_target);
2113
2114     cleanup_exited_pids();
2115 }
2116
2117 // ----------------------------------------------------------------------------
2118 // update chart dimensions
2119
2120 BUFFER *output = NULL;
2121 int print_calculated_number(char *str, calculated_number value) { (void)str; (void)value; return 0; }
2122
2123 static inline void send_BEGIN(const char *type, const char *id, unsigned long long usec) {
2124     // fprintf(stdout, "BEGIN %s.%s %llu\n", type, id, usec);
2125     buffer_strcat(output, "BEGIN ");
2126     buffer_strcat(output, type);
2127     buffer_strcat(output, ".");
2128     buffer_strcat(output, id);
2129     buffer_strcat(output, " ");
2130     buffer_print_llu(output, usec);
2131     buffer_strcat(output, "\n");
2132 }
2133
2134 static inline void send_SET(const char *name, unsigned long long value) {
2135     // fprintf(stdout, "SET %s = %llu\n", name, value);
2136     buffer_strcat(output, "SET ");
2137     buffer_strcat(output, name);
2138     buffer_strcat(output, " = ");
2139     buffer_print_llu(output, value);
2140     buffer_strcat(output, "\n");
2141 }
2142
2143 static inline void send_END(void) {
2144     // fprintf(stdout, "END\n");
2145     buffer_strcat(output, "END\n");
2146 }
2147
2148 double utime_fix_ratio = 1.0, stime_fix_ratio = 1.0, gtime_fix_ratio = 1.0, cutime_fix_ratio = 1.0, cstime_fix_ratio = 1.0, cgtime_fix_ratio = 1.0;
2149 double minflt_fix_ratio = 1.0, majflt_fix_ratio = 1.0, cminflt_fix_ratio = 1.0, cmajflt_fix_ratio = 1.0;
2150
2151 usec_t send_resource_usage_to_netdata() {
2152     static struct timeval last = { 0, 0 };
2153     static struct rusage me_last;
2154
2155     struct timeval now;
2156     struct rusage me;
2157
2158     usec_t usec;
2159     usec_t cpuuser;
2160     usec_t cpusyst;
2161
2162     if(!last.tv_sec) {
2163         now_realtime_timeval(&last);
2164         getrusage(RUSAGE_SELF, &me_last);
2165
2166         // the first time, give a zero to allow
2167         // netdata calibrate to the current time
2168         // usec = update_every * USEC_PER_SEC;
2169         usec = 0ULL;
2170         cpuuser = 0;
2171         cpusyst = 0;
2172     }
2173     else {
2174         now_realtime_timeval(&now);
2175         getrusage(RUSAGE_SELF, &me);
2176
2177         usec = dt_usec(&now, &last);
2178         cpuuser = me.ru_utime.tv_sec * USEC_PER_SEC + me.ru_utime.tv_usec;
2179         cpusyst = me.ru_stime.tv_sec * USEC_PER_SEC + me.ru_stime.tv_usec;
2180
2181         memmove(&last, &now, sizeof(struct timeval));
2182         memmove(&me_last, &me, sizeof(struct rusage));
2183     }
2184
2185     buffer_sprintf(output,
2186         "BEGIN netdata.apps_cpu %llu\n"
2187         "SET user = %llu\n"
2188         "SET system = %llu\n"
2189         "END\n"
2190         "BEGIN netdata.apps_files %llu\n"
2191         "SET files = %llu\n"
2192         "SET pids = %ld\n"
2193         "SET fds = %d\n"
2194         "SET targets = %ld\n"
2195         "END\n"
2196         "BEGIN netdata.apps_fix %llu\n"
2197         "SET utime = %llu\n"
2198         "SET stime = %llu\n"
2199         "SET gtime = %llu\n"
2200         "SET minflt = %llu\n"
2201         "SET majflt = %llu\n"
2202         "END\n"
2203         , usec
2204         , cpuuser
2205         , cpusyst
2206         , usec
2207         , file_counter
2208         , all_pids_count
2209         , all_files_len
2210         , apps_groups_targets
2211         , usec
2212         , (unsigned long long)(utime_fix_ratio   * 100 * RATES_DETAIL)
2213         , (unsigned long long)(stime_fix_ratio   * 100 * RATES_DETAIL)
2214         , (unsigned long long)(gtime_fix_ratio   * 100 * RATES_DETAIL)
2215         , (unsigned long long)(minflt_fix_ratio  * 100 * RATES_DETAIL)
2216         , (unsigned long long)(majflt_fix_ratio  * 100 * RATES_DETAIL)
2217         );
2218
2219     if(include_exited_childs)
2220         buffer_sprintf(output,
2221             "BEGIN netdata.apps_children_fix %llu\n"
2222             "SET cutime = %llu\n"
2223             "SET cstime = %llu\n"
2224             "SET cgtime = %llu\n"
2225             "SET cminflt = %llu\n"
2226             "SET cmajflt = %llu\n"
2227             "END\n"
2228             , usec
2229             , (unsigned long long)(cutime_fix_ratio  * 100 * RATES_DETAIL)
2230             , (unsigned long long)(cstime_fix_ratio  * 100 * RATES_DETAIL)
2231             , (unsigned long long)(cgtime_fix_ratio  * 100 * RATES_DETAIL)
2232             , (unsigned long long)(cminflt_fix_ratio * 100 * RATES_DETAIL)
2233             , (unsigned long long)(cmajflt_fix_ratio * 100 * RATES_DETAIL)
2234             );
2235
2236     return usec;
2237 }
2238
2239 void normalize_data(struct target *root) {
2240     struct target *w;
2241
2242     // childs processing introduces spikes
2243     // here we try to eliminate them by disabling childs processing either for specific dimensions
2244     // or entirely. Of course, either way, we disable it just a single iteration.
2245
2246     unsigned long long max = processors * hz * RATES_DETAIL;
2247     unsigned long long utime = 0, cutime = 0, stime = 0, cstime = 0, gtime = 0, cgtime = 0, minflt = 0, cminflt = 0, majflt = 0, cmajflt = 0;
2248
2249     if(global_utime > max) global_utime = max;
2250     if(global_stime > max) global_stime = max;
2251     if(global_gtime > max) global_gtime = max;
2252
2253     for(w = root; w ; w = w->next) {
2254         if(w->target || (!w->processes && !w->exposed)) continue;
2255
2256         utime   += w->utime;
2257         stime   += w->stime;
2258         gtime   += w->gtime;
2259         cutime  += w->cutime;
2260         cstime  += w->cstime;
2261         cgtime  += w->cgtime;
2262
2263         minflt  += w->minflt;
2264         majflt  += w->majflt;
2265         cminflt += w->cminflt;
2266         cmajflt += w->cmajflt;
2267     }
2268
2269     if((global_utime || global_stime || global_gtime) && (utime || stime || gtime)) {
2270         if(global_utime + global_stime + global_gtime > utime + cutime + stime + cstime + gtime + cgtime) {
2271             // everything we collected fits
2272             utime_fix_ratio  =
2273             stime_fix_ratio  =
2274             gtime_fix_ratio  =
2275             cutime_fix_ratio =
2276             cstime_fix_ratio =
2277             cgtime_fix_ratio = 1.0; //(double)(global_utime + global_stime) / (double)(utime + cutime + stime + cstime);
2278         }
2279         else if(global_utime + global_stime > utime + stime) {
2280             // childrens resources are too high
2281             // lower only the children resources
2282             utime_fix_ratio  =
2283             stime_fix_ratio  =
2284             gtime_fix_ratio  = 1.0;
2285             cutime_fix_ratio =
2286             cstime_fix_ratio =
2287             cgtime_fix_ratio = (double)((global_utime + global_stime) - (utime + stime)) / (double)(cutime + cstime);
2288         }
2289         else {
2290             // even running processes are unrealistic
2291             // zero the children resources
2292             // lower the running processes resources
2293             utime_fix_ratio  =
2294             stime_fix_ratio  =
2295             gtime_fix_ratio  = (double)(global_utime + global_stime) / (double)(utime + stime);
2296             cutime_fix_ratio =
2297             cstime_fix_ratio =
2298             cgtime_fix_ratio = 0.0;
2299         }
2300     }
2301     else {
2302         utime_fix_ratio  =
2303         stime_fix_ratio  =
2304         gtime_fix_ratio  =
2305         cutime_fix_ratio =
2306         cstime_fix_ratio =
2307         cgtime_fix_ratio = 0.0;
2308     }
2309
2310     if(utime_fix_ratio  > 1.0) utime_fix_ratio  = 1.0;
2311     if(cutime_fix_ratio > 1.0) cutime_fix_ratio = 1.0;
2312     if(stime_fix_ratio  > 1.0) stime_fix_ratio  = 1.0;
2313     if(cstime_fix_ratio > 1.0) cstime_fix_ratio = 1.0;
2314     if(gtime_fix_ratio  > 1.0) gtime_fix_ratio  = 1.0;
2315     if(cgtime_fix_ratio > 1.0) cgtime_fix_ratio = 1.0;
2316
2317     // if(utime_fix_ratio  < 0.0) utime_fix_ratio  = 0.0;
2318     // if(cutime_fix_ratio < 0.0) cutime_fix_ratio = 0.0;
2319     // if(stime_fix_ratio  < 0.0) stime_fix_ratio  = 0.0;
2320     // if(cstime_fix_ratio < 0.0) cstime_fix_ratio = 0.0;
2321     // if(gtime_fix_ratio  < 0.0) gtime_fix_ratio  = 0.0;
2322     // if(cgtime_fix_ratio < 0.0) cgtime_fix_ratio = 0.0;
2323
2324     // FIXME
2325     // we use cpu time to normalize page faults
2326     // the problem is that to find the proper max values
2327     // for page faults we have to parse /proc/vmstat
2328     // which is quite big to do it again (netdata does it already)
2329     //
2330     // a better solution could be to somehow have netdata
2331     // do this normalization for us
2332
2333     if(utime || stime || gtime)
2334         majflt_fix_ratio =
2335         minflt_fix_ratio = (double)(utime * utime_fix_ratio + stime * stime_fix_ratio + gtime * gtime_fix_ratio) / (double)(utime + stime + gtime);
2336     else
2337         minflt_fix_ratio =
2338         majflt_fix_ratio = 1.0;
2339
2340     if(cutime || cstime || cgtime)
2341         cmajflt_fix_ratio =
2342         cminflt_fix_ratio = (double)(cutime * cutime_fix_ratio + cstime * cstime_fix_ratio + cgtime * cgtime_fix_ratio) / (double)(cutime + cstime + cgtime);
2343     else
2344         cminflt_fix_ratio =
2345         cmajflt_fix_ratio = 1.0;
2346
2347     // the report
2348
2349     if(unlikely(debug)) {
2350         fprintf(stderr,
2351             "SYSTEM: u=%llu s=%llu g=%llu "
2352             "COLLECTED: u=%llu s=%llu g=%llu cu=%llu cs=%llu cg=%llu "
2353             "DELTA: u=%lld s=%lld g=%lld "
2354             "FIX: u=%0.2f s=%0.2f g=%0.2f cu=%0.2f cs=%0.2f cg=%0.2f "
2355             "FINALLY: u=%llu s=%llu g=%llu cu=%llu cs=%llu cg=%llu "
2356             "\n"
2357             , global_utime
2358             , global_stime
2359             , global_gtime
2360             , utime
2361             , stime
2362             , gtime
2363             , cutime
2364             , cstime
2365             , cgtime
2366             , (long long)utime + (long long)cutime - (long long)global_utime
2367             , (long long)stime + (long long)cstime - (long long)global_stime
2368             , (long long)gtime + (long long)cgtime - (long long)global_gtime
2369             , utime_fix_ratio
2370             , stime_fix_ratio
2371             , gtime_fix_ratio
2372             , cutime_fix_ratio
2373             , cstime_fix_ratio
2374             , cgtime_fix_ratio
2375             , (unsigned long long)(utime * utime_fix_ratio)
2376             , (unsigned long long)(stime * stime_fix_ratio)
2377             , (unsigned long long)(gtime * gtime_fix_ratio)
2378             , (unsigned long long)(cutime * cutime_fix_ratio)
2379             , (unsigned long long)(cstime * cstime_fix_ratio)
2380             , (unsigned long long)(cgtime * cgtime_fix_ratio)
2381             );
2382     }
2383 }
2384
2385 void send_collected_data_to_netdata(struct target *root, const char *type, usec_t usec) {
2386     struct target *w;
2387
2388     send_BEGIN(type, "cpu", usec);
2389     for (w = root; w ; w = w->next) {
2390         if(unlikely(w->exposed))
2391             send_SET(w->name, (unsigned long long)(w->utime * utime_fix_ratio) + (unsigned long long)(w->stime * stime_fix_ratio) + (unsigned long long)(w->gtime * gtime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cutime * cutime_fix_ratio) + (unsigned long long)(w->cstime * cstime_fix_ratio) + (unsigned long long)(w->cgtime * cgtime_fix_ratio)):0ULL));
2392     }
2393     send_END();
2394
2395     send_BEGIN(type, "cpu_user", usec);
2396     for (w = root; w ; w = w->next) {
2397         if(unlikely(w->exposed))
2398             send_SET(w->name, (unsigned long long)(w->utime * utime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cutime * cutime_fix_ratio)):0ULL));
2399     }
2400     send_END();
2401
2402     send_BEGIN(type, "cpu_system", usec);
2403     for (w = root; w ; w = w->next) {
2404         if(unlikely(w->exposed))
2405             send_SET(w->name, (unsigned long long)(w->stime * stime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cstime * cstime_fix_ratio)):0ULL));
2406     }
2407     send_END();
2408
2409     if(show_guest_time) {
2410         send_BEGIN(type, "cpu_guest", usec);
2411         for (w = root; w ; w = w->next) {
2412             if(unlikely(w->exposed))
2413                 send_SET(w->name, (unsigned long long)(w->gtime * gtime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cgtime * cgtime_fix_ratio)):0ULL));
2414         }
2415         send_END();
2416     }
2417
2418     send_BEGIN(type, "threads", usec);
2419     for (w = root; w ; w = w->next) {
2420         if(unlikely(w->exposed))
2421             send_SET(w->name, w->num_threads);
2422     }
2423     send_END();
2424
2425     send_BEGIN(type, "processes", usec);
2426     for (w = root; w ; w = w->next) {
2427         if(unlikely(w->exposed))
2428             send_SET(w->name, w->processes);
2429     }
2430     send_END();
2431
2432     send_BEGIN(type, "mem", usec);
2433     for (w = root; w ; w = w->next) {
2434         if(unlikely(w->exposed))
2435             send_SET(w->name, (w->statm_resident > w->statm_share)?(w->statm_resident - w->statm_share):0ULL);
2436     }
2437     send_END();
2438
2439     send_BEGIN(type, "vmem", usec);
2440     for (w = root; w ; w = w->next) {
2441         if(unlikely(w->exposed))
2442             send_SET(w->name, w->statm_size);
2443     }
2444     send_END();
2445
2446     send_BEGIN(type, "minor_faults", usec);
2447     for (w = root; w ; w = w->next) {
2448         if(unlikely(w->exposed))
2449             send_SET(w->name, (unsigned long long)(w->minflt * minflt_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cminflt * cminflt_fix_ratio)):0ULL));
2450     }
2451     send_END();
2452
2453     send_BEGIN(type, "major_faults", usec);
2454     for (w = root; w ; w = w->next) {
2455         if(unlikely(w->exposed))
2456             send_SET(w->name, (unsigned long long)(w->majflt * majflt_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cmajflt * cmajflt_fix_ratio)):0ULL));
2457     }
2458     send_END();
2459
2460     send_BEGIN(type, "lreads", usec);
2461     for (w = root; w ; w = w->next) {
2462         if(unlikely(w->exposed))
2463             send_SET(w->name, w->io_logical_bytes_read);
2464     }
2465     send_END();
2466
2467     send_BEGIN(type, "lwrites", usec);
2468     for (w = root; w ; w = w->next) {
2469         if(unlikely(w->exposed))
2470             send_SET(w->name, w->io_logical_bytes_written);
2471     }
2472     send_END();
2473
2474     send_BEGIN(type, "preads", usec);
2475     for (w = root; w ; w = w->next) {
2476         if(unlikely(w->exposed))
2477             send_SET(w->name, w->io_storage_bytes_read);
2478     }
2479     send_END();
2480
2481     send_BEGIN(type, "pwrites", usec);
2482     for (w = root; w ; w = w->next) {
2483         if(unlikely(w->exposed))
2484             send_SET(w->name, w->io_storage_bytes_written);
2485     }
2486     send_END();
2487
2488     if(enable_file_charts) {
2489         send_BEGIN(type, "files", usec);
2490         for (w = root; w; w = w->next) {
2491             if (unlikely(w->exposed))
2492                 send_SET(w->name, w->openfiles);
2493         }
2494         send_END();
2495
2496         send_BEGIN(type, "sockets", usec);
2497         for (w = root; w; w = w->next) {
2498             if (unlikely(w->exposed))
2499                 send_SET(w->name, w->opensockets);
2500         }
2501         send_END();
2502
2503         send_BEGIN(type, "pipes", usec);
2504         for (w = root; w; w = w->next) {
2505             if (unlikely(w->exposed))
2506                 send_SET(w->name, w->openpipes);
2507         }
2508         send_END();
2509     }
2510 }
2511
2512
2513 // ----------------------------------------------------------------------------
2514 // generate the charts
2515
2516 void send_charts_updates_to_netdata(struct target *root, const char *type, const char *title)
2517 {
2518     struct target *w;
2519     int newly_added = 0;
2520
2521     for(w = root ; w ; w = w->next) {
2522         if (w->target) continue;
2523
2524         if (!w->exposed && w->processes) {
2525             newly_added++;
2526             w->exposed = 1;
2527             if (debug || w->debug) fprintf(stderr, "apps.plugin: %s just added - regenerating charts.\n", w->name);
2528         }
2529     }
2530
2531     // nothing more to show
2532     if(!newly_added && show_guest_time == show_guest_time_old) return;
2533
2534     // we have something new to show
2535     // update the charts
2536     buffer_sprintf(output, "CHART %s.cpu '' '%s CPU Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu stacked 20001 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
2537     for (w = root; w ; w = w->next) {
2538         if(unlikely(w->exposed))
2539             buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu %s\n", w->name, hz * RATES_DETAIL / 100, w->hidden ? "hidden" : "");
2540     }
2541
2542     buffer_sprintf(output, "CHART %s.mem '' '%s Real Memory (w/o shared)' 'MB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every);
2543     for (w = root; w ; w = w->next) {
2544         if(unlikely(w->exposed))
2545             buffer_sprintf(output, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
2546     }
2547
2548     buffer_sprintf(output, "CHART %s.vmem '' '%s Virtual Memory Size' 'MB' mem %s.vmem stacked 20004 %d\n", type, title, type, update_every);
2549     for (w = root; w ; w = w->next) {
2550         if(unlikely(w->exposed))
2551             buffer_sprintf(output, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
2552     }
2553
2554     buffer_sprintf(output, "CHART %s.threads '' '%s Threads' 'threads' processes %s.threads stacked 20005 %d\n", type, title, type, update_every);
2555     for (w = root; w ; w = w->next) {
2556         if(unlikely(w->exposed))
2557             buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
2558     }
2559
2560     buffer_sprintf(output, "CHART %s.processes '' '%s Processes' 'processes' processes %s.processes stacked 20004 %d\n", type, title, type, update_every);
2561     for (w = root; w ; w = w->next) {
2562         if(unlikely(w->exposed))
2563             buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
2564     }
2565
2566     buffer_sprintf(output, "CHART %s.cpu_user '' '%s CPU User Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_user stacked 20020 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
2567     for (w = root; w ; w = w->next) {
2568         if(unlikely(w->exposed))
2569             buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU);
2570     }
2571
2572     buffer_sprintf(output, "CHART %s.cpu_system '' '%s CPU System Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20021 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
2573     for (w = root; w ; w = w->next) {
2574         if(unlikely(w->exposed))
2575             buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU);
2576     }
2577
2578     if(show_guest_time) {
2579         buffer_sprintf(output, "CHART %s.cpu_guest '' '%s CPU Guest Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20022 %d\n", type, title, (processors * 100), processors, (processors > 1) ? "s" : "", type, update_every);
2580         for (w = root; w; w = w->next) {
2581             if(unlikely(w->exposed))
2582                 buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU);
2583         }
2584     }
2585
2586     buffer_sprintf(output, "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);
2587     for (w = root; w ; w = w->next) {
2588         if(unlikely(w->exposed))
2589             buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL);
2590     }
2591
2592     buffer_sprintf(output, "CHART %s.minor_faults '' '%s Minor Page Faults' 'page faults/s' mem %s.minor_faults stacked 20011 %d\n", type, title, type, update_every);
2593     for (w = root; w ; w = w->next) {
2594         if(unlikely(w->exposed))
2595             buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL);
2596     }
2597
2598     buffer_sprintf(output, "CHART %s.lreads '' '%s Disk Logical Reads' 'kilobytes/s' disk %s.lreads stacked 20042 %d\n", type, title, type, update_every);
2599     for (w = root; w ; w = w->next) {
2600         if(unlikely(w->exposed))
2601             buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
2602     }
2603
2604     buffer_sprintf(output, "CHART %s.lwrites '' '%s I/O Logical Writes' 'kilobytes/s' disk %s.lwrites stacked 20042 %d\n", type, title, type, update_every);
2605     for (w = root; w ; w = w->next) {
2606         if(unlikely(w->exposed))
2607             buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
2608     }
2609
2610     buffer_sprintf(output, "CHART %s.preads '' '%s Disk Reads' 'kilobytes/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every);
2611     for (w = root; w ; w = w->next) {
2612         if(unlikely(w->exposed))
2613             buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
2614     }
2615
2616     buffer_sprintf(output, "CHART %s.pwrites '' '%s Disk Writes' 'kilobytes/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every);
2617     for (w = root; w ; w = w->next) {
2618         if(unlikely(w->exposed))
2619             buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
2620     }
2621
2622     if(enable_file_charts) {
2623         buffer_sprintf(output, "CHART %s.files '' '%s Open Files' 'open files' disk %s.files stacked 20050 %d\n", type,
2624                        title, type, update_every);
2625         for (w = root; w; w = w->next) {
2626             if (unlikely(w->exposed))
2627                 buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
2628         }
2629
2630         buffer_sprintf(output, "CHART %s.sockets '' '%s Open Sockets' 'open sockets' net %s.sockets stacked 20051 %d\n",
2631                        type, title, type, update_every);
2632         for (w = root; w; w = w->next) {
2633             if (unlikely(w->exposed))
2634                 buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
2635         }
2636
2637         buffer_sprintf(output, "CHART %s.pipes '' '%s Pipes' 'open pipes' processes %s.pipes stacked 20053 %d\n", type,
2638                        title, type, update_every);
2639         for (w = root; w; w = w->next) {
2640             if (unlikely(w->exposed))
2641                 buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
2642         }
2643     }
2644 }
2645
2646
2647 // ----------------------------------------------------------------------------
2648 // parse command line arguments
2649
2650 void parse_args(int argc, char **argv)
2651 {
2652     int i, freq = 0;
2653     char *name = NULL;
2654
2655     for(i = 1; i < argc; i++) {
2656         if(!freq) {
2657             int n = atoi(argv[i]);
2658             if(n > 0) {
2659                 freq = n;
2660                 continue;
2661             }
2662         }
2663
2664         if(strcmp("debug", argv[i]) == 0) {
2665             debug = 1;
2666             // debug_flags = 0xffffffff;
2667             continue;
2668         }
2669
2670         if(strcmp("no-childs", argv[i]) == 0 || strcmp("without-childs", argv[i]) == 0) {
2671             include_exited_childs = 0;
2672             continue;
2673         }
2674
2675         if(strcmp("with-childs", argv[i]) == 0) {
2676             include_exited_childs = 1;
2677             continue;
2678         }
2679
2680         if(strcmp("with-guest", argv[i]) == 0) {
2681             enable_guest_charts = 1;
2682             continue;
2683         }
2684
2685         if(strcmp("no-guest", argv[i]) == 0 || strcmp("without-guest", argv[i]) == 0) {
2686             enable_guest_charts = 0;
2687             continue;
2688         }
2689
2690         if(strcmp("with-files", argv[i]) == 0) {
2691             enable_file_charts = 1;
2692             continue;
2693         }
2694
2695         if(strcmp("no-files", argv[i]) == 0 || strcmp("without-files", argv[i]) == 0) {
2696             enable_file_charts = 0;
2697             continue;
2698         }
2699
2700         if(strcmp("no-users", argv[i]) == 0 || strcmp("without-users", argv[i]) == 0) {
2701             enable_users_charts = 0;
2702             continue;
2703         }
2704
2705         if(strcmp("no-groups", argv[i]) == 0 || strcmp("without-groups", argv[i]) == 0) {
2706             enable_groups_charts = 0;
2707             continue;
2708         }
2709
2710         if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
2711             fprintf(stderr,
2712                     "apps.plugin\n"
2713                     "(C) 2016 Costa Tsaousis"
2714                     "GPL v3+\n"
2715                     "This program is a data collector plugin for netdata.\n"
2716                     "\n"
2717                     "Valid command line options:\n"
2718                     "\n"
2719                     "SECONDS           set the data collection frequency\n"
2720                     "\n"
2721                     "debug             enable debugging (lot of output)\n"
2722                     "\n"
2723                     "with-childs\n"
2724                     "without-childs    enable / disable aggregating exited\n"
2725                     "                  children resources into parents\n"
2726                     "                  (default is enabled)\n"
2727                     "\n"
2728                     "with-guest\n"
2729                     "without-guest     enable / disable reporting guest charts\n"
2730                     "                  (default is disabled)\n"
2731                     "\n"
2732                     "with-files\n"
2733                     "without-files     enable / disable reporting files, sockets, pipes\n"
2734                     "                  (default is enabled)\n"
2735                     "\n"
2736                     "NAME              read apps_NAME.conf instead of\n"
2737                     "                  apps_groups.conf\n"
2738                     "                  (default NAME=groups)\n"
2739             );
2740             exit(1);
2741         }
2742
2743         if(!name) {
2744             name = argv[i];
2745             continue;
2746         }
2747
2748         error("Cannot understand option %s", argv[i]);
2749         exit(1);
2750     }
2751
2752     if(freq > 0) update_every = freq;
2753     if(!name) name = "groups";
2754
2755     if(read_apps_groups_conf(name)) {
2756         error("Cannot read process groups %s", name);
2757         exit(1);
2758     }
2759 }
2760
2761 int main(int argc, char **argv)
2762 {
2763     // debug_flags = D_PROCFILE;
2764
2765     // set the name for logging
2766     program_name = "apps.plugin";
2767
2768     info("started on pid %d", getpid());
2769
2770     // disable syslog for apps.plugin
2771     error_log_syslog = 0;
2772
2773     // set errors flood protection to 100 logs per hour
2774     error_log_errors_per_period = 100;
2775     error_log_throttle_period = 3600;
2776
2777     global_host_prefix = getenv("NETDATA_HOST_PREFIX");
2778     if(global_host_prefix == NULL) {
2779         // info("NETDATA_HOST_PREFIX is not passed from netdata");
2780         global_host_prefix = "";
2781     }
2782     // else info("Found NETDATA_HOST_PREFIX='%s'", global_host_prefix);
2783
2784     config_dir = getenv("NETDATA_CONFIG_DIR");
2785     if(config_dir == NULL) {
2786         // info("NETDATA_CONFIG_DIR is not passed from netdata");
2787         config_dir = CONFIG_DIR;
2788     }
2789     // else info("Found NETDATA_CONFIG_DIR='%s'", config_dir);
2790
2791 #ifdef NETDATA_INTERNAL_CHECKS
2792     if(debug_flags != 0) {
2793         struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
2794         if(setrlimit(RLIMIT_CORE, &rl) != 0)
2795             info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
2796         prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
2797     }
2798 #endif /* NETDATA_INTERNAL_CHECKS */
2799
2800     procfile_adaptive_initial_allocation = 1;
2801
2802     time_t started_t = now_realtime_sec();
2803     get_system_HZ();
2804     get_system_pid_max();
2805     get_system_cpus();
2806
2807     parse_args(argc, argv);
2808
2809     all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max);
2810     all_pids = callocz(sizeof(struct pid_stat *), (size_t) pid_max);
2811
2812     output = buffer_create(1024);
2813     buffer_sprintf(output,
2814         "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %1$d\n"
2815         "DIMENSION user '' incremental 1 1000\n"
2816         "DIMENSION system '' incremental 1 1000\n"
2817         "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_files line 140001 %1$d\n"
2818         "DIMENSION files '' incremental 1 1\n"
2819         "DIMENSION pids '' absolute 1 1\n"
2820         "DIMENSION fds '' absolute 1 1\n"
2821         "DIMENSION targets '' absolute 1 1\n"
2822         "CHART netdata.apps_fix '' 'Apps Plugin Normalization Ratios' 'percentage' apps.plugin netdata.apps_fix line 140002 %1$d\n"
2823         "DIMENSION utime '' absolute 1 %2$llu\n"
2824         "DIMENSION stime '' absolute 1 %2$llu\n"
2825         "DIMENSION gtime '' absolute 1 %2$llu\n"
2826         "DIMENSION minflt '' absolute 1 %2$llu\n"
2827         "DIMENSION majflt '' absolute 1 %2$llu\n"
2828         , update_every
2829         , RATES_DETAIL
2830         );
2831
2832     if(include_exited_childs)
2833         buffer_sprintf(output,
2834             "CHART netdata.apps_children_fix '' 'Apps Plugin Exited Children Normalization Ratios' 'percentage' apps.plugin netdata.apps_children_fix line 140003 %1$d\n"
2835             "DIMENSION cutime '' absolute 1 %2$llu\n"
2836             "DIMENSION cstime '' absolute 1 %2$llu\n"
2837             "DIMENSION cgtime '' absolute 1 %2$llu\n"
2838             "DIMENSION cminflt '' absolute 1 %2$llu\n"
2839             "DIMENSION cmajflt '' absolute 1 %2$llu\n"
2840             , update_every
2841             , RATES_DETAIL
2842             );
2843
2844     usec_t step = update_every * USEC_PER_SEC;
2845     global_iterations_counter = 1;
2846     for(;1; global_iterations_counter++) {
2847         usec_t now = now_realtime_usec();
2848         usec_t next = now - (now % step) + step;
2849
2850         while(now < next) {
2851             sleep_usec(next - now);
2852             now = now_realtime_usec();
2853         }
2854
2855         if(!collect_data_for_all_processes_from_proc()) {
2856             error("Cannot collect /proc data for running processes. Disabling apps.plugin...");
2857             printf("DISABLE\n");
2858             exit(1);
2859         }
2860
2861         calculate_netdata_statistics();
2862         normalize_data(apps_groups_root_target);
2863
2864         usec_t dt = send_resource_usage_to_netdata();
2865
2866         // this is smart enough to show only newly added apps, when needed
2867         send_charts_updates_to_netdata(apps_groups_root_target, "apps", "Apps");
2868
2869         if(likely(enable_users_charts))
2870             send_charts_updates_to_netdata(users_root_target, "users", "Users");
2871
2872         if(likely(enable_groups_charts))
2873             send_charts_updates_to_netdata(groups_root_target, "groups", "User Groups");
2874
2875         send_collected_data_to_netdata(apps_groups_root_target, "apps", dt);
2876
2877         if(likely(enable_users_charts))
2878             send_collected_data_to_netdata(users_root_target, "users", dt);
2879
2880         if(likely(enable_groups_charts))
2881             send_collected_data_to_netdata(groups_root_target, "groups", dt);
2882
2883         show_guest_time_old = show_guest_time;
2884
2885         if(write(STDOUT_FILENO, buffer_tostring(output), buffer_strlen(output)) == -1)
2886             fatal("Cannot send chart values to netdata.");
2887
2888         // fflush(stdout);
2889         buffer_flush(output);
2890
2891         if(unlikely(debug))
2892             fprintf(stderr, "apps.plugin: done Loop No %llu\n", global_iterations_counter);
2893
2894         time_t current_t = now_realtime_sec();
2895
2896         // restart check (14400 seconds)
2897         if(current_t - started_t > 14400) exit(0);
2898     }
2899 }