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