]> arthur.barton.de Git - netdata.git/blobdiff - src/apps_plugin.c
Merge pull request #1889 from l2isbad/php_fpm_bugfix
[netdata.git] / src / apps_plugin.c
index 103bc143293d26c494047ffcfbd890be4c92fc1c..bedd58a5cf71bb6ae8547e9167f0320ad154ba88 100644 (file)
@@ -7,6 +7,20 @@
 
 #include "common.h"
 
+// ----------------------------------------------------------------------------
+// per O/S configuration
+
+// the minimum PID of the system
+// this is also the pid of the init process
+#define INIT_PID 1
+
+// if the way apps.plugin will work, will read the entire process list,
+// including the resource utilization of each process, instantly
+// set this to 1
+// when set to 0, apps.plugin builds a sort list of processes, in order
+// to process children processes, before parent processes
+#define ALL_PIDS_ARE_READ_INSTANTLY 0
+
 
 // ----------------------------------------------------------------------------
 // string lengths
@@ -64,6 +78,7 @@ static int
 
 static size_t
         global_iterations_counter = 1,
+        calls_counter = 0,
         file_counter = 0;
 
 
@@ -336,6 +351,7 @@ static struct pid_stat
 static size_t
         all_pids_count = 0;     // the number of processes running
 
+#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
 // Another pre-allocated list of all possible pids.
 // We need it to pids and assign them a unique sortlist id, so that we
 // read parents before children. This is needed to prevent a situation where
@@ -343,7 +359,7 @@ static size_t
 // its parent has accumulated its resources.
 static pid_t
         *all_pids_sortlist = NULL;
-
+#endif
 
 // ----------------------------------------------------------------------------
 // file descriptor
@@ -787,7 +803,9 @@ cleanup:
     return 0;
 }
 
-static inline int read_proc_pid_ownership(struct pid_stat *p) {
+static inline int read_proc_pid_ownership(struct pid_stat *p, void *ptr) {
+    (void)ptr;
+
     if(unlikely(!p->stat_filename)) {
         error("pid %d does not have a stat_filename", p->pid);
         return 0;
@@ -808,7 +826,27 @@ static inline int read_proc_pid_ownership(struct pid_stat *p) {
     return 1;
 }
 
-static inline int read_proc_pid_stat(struct pid_stat *p) {
+// ----------------------------------------------------------------------------
+// macro to calculate the incremental rate of a value
+// each parameter is accessed only ONCE - so it is safe to pass function calls
+// or other macros as parameters
+
+#define incremental_rate(rate_variable, last_kernel_variable, new_kernel_value, collected_usec, last_collected_usec) { \
+        kernel_uint_t _new_tmp = new_kernel_value; \
+        rate_variable = (_new_tmp - last_kernel_variable) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); \
+        last_kernel_variable = _new_tmp; \
+    }
+
+// the same macro for struct pid members
+#define pid_incremental_rate(type, var, value) \
+    incremental_rate(var, var##_raw, value, p->type##_collected_usec, p->last_##type##_collected_usec)
+
+
+// ----------------------------------------------------------------------------
+
+static inline int read_proc_pid_stat(struct pid_stat *p, void *ptr) {
+    (void)ptr;
+
     static procfile *ff = NULL;
 
     if(unlikely(!p->stat_filename)) {
@@ -831,7 +869,7 @@ static inline int read_proc_pid_stat(struct pid_stat *p) {
 
     p->last_stat_collected_usec = p->stat_collected_usec;
     p->stat_collected_usec = now_monotonic_usec();
-    file_counter++;
+    calls_counter++;
 
     // p->pid           = str2pid_t(procfile_lineword(ff, 0, 0));
     char *comm          = procfile_lineword(ff, 0, 1);
@@ -860,37 +898,14 @@ static inline int read_proc_pid_stat(struct pid_stat *p) {
         assign_target_to_pid(p);
     }
 
-    kernel_uint_t last = p->minflt_raw;
-    p->minflt_raw       = str2kernel_uint_t(procfile_lineword(ff, 0, 9));
-    p->minflt = (p->minflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-    last = p->cminflt_raw;
-    p->cminflt_raw      = str2kernel_uint_t(procfile_lineword(ff, 0, 10));
-    p->cminflt = (p->cminflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-    last = p->majflt_raw;
-    p->majflt_raw       = str2kernel_uint_t(procfile_lineword(ff, 0, 11));
-    p->majflt = (p->majflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-    last = p->cmajflt_raw;
-    p->cmajflt_raw      = str2kernel_uint_t(procfile_lineword(ff, 0, 12));
-    p->cmajflt = (p->cmajflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-    last = p->utime_raw;
-    p->utime_raw        = str2kernel_uint_t(procfile_lineword(ff, 0, 13));
-    p->utime = (p->utime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-    last = p->stime_raw;
-    p->stime_raw        = str2kernel_uint_t(procfile_lineword(ff, 0, 14));
-    p->stime = (p->stime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-    last = p->cutime_raw;
-    p->cutime_raw       = str2kernel_uint_t(procfile_lineword(ff, 0, 15));
-    p->cutime = (p->cutime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-    last = p->cstime_raw;
-    p->cstime_raw       = str2kernel_uint_t(procfile_lineword(ff, 0, 16));
-    p->cstime = (p->cstime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+    pid_incremental_rate(stat, p->minflt,  str2kernel_uint_t(procfile_lineword(ff, 0,  9)));
+    pid_incremental_rate(stat, p->cminflt, str2kernel_uint_t(procfile_lineword(ff, 0, 10)));
+    pid_incremental_rate(stat, p->majflt,  str2kernel_uint_t(procfile_lineword(ff, 0, 11)));
+    pid_incremental_rate(stat, p->cmajflt, str2kernel_uint_t(procfile_lineword(ff, 0, 12)));
+    pid_incremental_rate(stat, p->utime,   str2kernel_uint_t(procfile_lineword(ff, 0, 13)));
+    pid_incremental_rate(stat, p->stime,   str2kernel_uint_t(procfile_lineword(ff, 0, 14)));
+    pid_incremental_rate(stat, p->cutime,  str2kernel_uint_t(procfile_lineword(ff, 0, 15)));
+    pid_incremental_rate(stat, p->cstime,  str2kernel_uint_t(procfile_lineword(ff, 0, 16)));
 
     // p->priority      = str2kernel_uint_t(procfile_lineword(ff, 0, 17));
     // p->nice          = str2kernel_uint_t(procfile_lineword(ff, 0, 18));
@@ -919,13 +934,9 @@ static inline int read_proc_pid_stat(struct pid_stat *p) {
     // p->delayacct_blkio_ticks = str2kernel_uint_t(procfile_lineword(ff, 0, 41));
 
     if(enable_guest_charts) {
-        last = p->gtime_raw;
-        p->gtime_raw        = str2kernel_uint_t(procfile_lineword(ff, 0, 42));
-        p->gtime = (p->gtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
 
-        last = p->cgtime_raw;
-        p->cgtime_raw       = str2kernel_uint_t(procfile_lineword(ff, 0, 43));
-        p->cgtime = (p->cgtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+        pid_incremental_rate(stat, p->gtime,  str2kernel_uint_t(procfile_lineword(ff, 0, 42)));
+        pid_incremental_rate(stat, p->cgtime, str2kernel_uint_t(procfile_lineword(ff, 0, 43)));
 
         if (show_guest_time || p->gtime || p->cgtime) {
             p->utime -= (p->utime >= p->gtime) ? p->gtime : p->utime;
@@ -968,7 +979,9 @@ cleanup:
     return 0;
 }
 
-static inline int read_proc_pid_statm(struct pid_stat *p) {
+static inline int read_proc_pid_statm(struct pid_stat *p, void *ptr) {
+    (void)ptr;
+
     static procfile *ff = NULL;
 
     if(unlikely(!p->statm_filename)) {
@@ -983,7 +996,7 @@ static inline int read_proc_pid_statm(struct pid_stat *p) {
     ff = procfile_readall(ff);
     if(unlikely(!ff)) goto cleanup;
 
-    file_counter++;
+    calls_counter++;
 
     p->statm_size           = str2kernel_uint_t(procfile_lineword(ff, 0, 0));
     p->statm_resident       = str2kernel_uint_t(procfile_lineword(ff, 0, 1));
@@ -1006,7 +1019,9 @@ cleanup:
     return 0;
 }
 
-static inline int read_proc_pid_io(struct pid_stat *p) {
+static inline int read_proc_pid_io(struct pid_stat *p, void *ptr) {
+    (void)ptr;
+
     static procfile *ff = NULL;
 
     if(unlikely(!p->io_filename)) {
@@ -1022,40 +1037,18 @@ static inline int read_proc_pid_io(struct pid_stat *p) {
     ff = procfile_readall(ff);
     if(unlikely(!ff)) goto cleanup;
 
-    file_counter++;
+    calls_counter++;
 
     p->last_io_collected_usec = p->io_collected_usec;
     p->io_collected_usec = now_monotonic_usec();
 
-    kernel_uint_t last;
-
-    last = p->io_logical_bytes_read_raw;
-    p->io_logical_bytes_read_raw = str2kernel_uint_t(procfile_lineword(ff, 0, 1));
-    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);
-
-    last = p->io_logical_bytes_written_raw;
-    p->io_logical_bytes_written_raw = str2kernel_uint_t(procfile_lineword(ff, 1, 1));
-    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);
-
-    // last = p->io_read_calls_raw;
-    // p->io_read_calls_raw = str2kernel_uint_t(procfile_lineword(ff, 2, 1));
-    // p->io_read_calls = (p->io_read_calls_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
-
-    // last = p->io_write_calls_raw;
-    // p->io_write_calls_raw = str2kernel_uint_t(procfile_lineword(ff, 3, 1));
-    // p->io_write_calls = (p->io_write_calls_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
-
-    last = p->io_storage_bytes_read_raw;
-    p->io_storage_bytes_read_raw = str2kernel_uint_t(procfile_lineword(ff, 4, 1));
-    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);
-
-    last = p->io_storage_bytes_written_raw;
-    p->io_storage_bytes_written_raw = str2kernel_uint_t(procfile_lineword(ff, 5, 1));
-    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);
-
-    // last = p->io_cancelled_write_bytes_raw;
-    // p->io_cancelled_write_bytes_raw = str2kernel_uint_t(procfile_lineword(ff, 6, 1));
-    // 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);
+    pid_incremental_rate(io, p->io_logical_bytes_read,       str2kernel_uint_t(procfile_lineword(ff, 0,  1)));
+    pid_incremental_rate(io, p->io_logical_bytes_written,    str2kernel_uint_t(procfile_lineword(ff, 1,  1)));
+    // pid_incremental_rate(io, p->io_read_calls,               str2kernel_uint_t(procfile_lineword(ff, 2,  1)));
+    // pid_incremental_rate(io, p->io_write_calls,              str2kernel_uint_t(procfile_lineword(ff, 3,  1)));
+    pid_incremental_rate(io, p->io_storage_bytes_read,       str2kernel_uint_t(procfile_lineword(ff, 4,  1)));
+    pid_incremental_rate(io, p->io_storage_bytes_written,    str2kernel_uint_t(procfile_lineword(ff, 5,  1)));
+    // pid_incremental_rate(io, p->io_cancelled_write_bytes,    str2kernel_uint_t(procfile_lineword(ff, 6,  1)));
 
     if(unlikely(global_iterations_counter == 1)) {
         p->io_logical_bytes_read        = 0;
@@ -1098,32 +1091,26 @@ static inline int read_proc_stat() {
     last_collected_usec = collected_usec;
     collected_usec = now_monotonic_usec();
 
-    file_counter++;
+    calls_counter++;
 
-    kernel_uint_t last;
+    // temporary - it is added global_ntime;
+    kernel_uint_t global_ntime = 0;
 
-    last = utime_raw;
-    utime_raw = str2kernel_uint_t(procfile_lineword(ff, 0, 1));
-    global_utime = (utime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec);
+    incremental_rate(global_utime, utime_raw, str2kernel_uint_t(procfile_lineword(ff, 0,  1)), collected_usec, last_collected_usec);
+    incremental_rate(global_ntime, ntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0,  2)), collected_usec, last_collected_usec);
+    incremental_rate(global_stime, stime_raw, str2kernel_uint_t(procfile_lineword(ff, 0,  3)), collected_usec, last_collected_usec);
+    incremental_rate(global_gtime, gtime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 10)), collected_usec, last_collected_usec);
 
-    // nice time, on user time
-    last = ntime_raw;
-    ntime_raw = str2kernel_uint_t(procfile_lineword(ff, 0, 2));
-    global_utime += (ntime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec);
-
-    last = stime_raw;
-    stime_raw = str2kernel_uint_t(procfile_lineword(ff, 0, 3));
-    global_stime = (stime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec);
-
-    last = gtime_raw;
-    gtime_raw = str2kernel_uint_t(procfile_lineword(ff, 0, 10));
-    global_gtime = (gtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec);
+    global_utime += global_ntime;
 
     if(enable_guest_charts) {
+        // temporary - it is added global_ntime;
+        kernel_uint_t global_gntime = 0;
+
         // guest nice time, on guest time
-        last = gntime_raw;
-        gntime_raw = str2kernel_uint_t(procfile_lineword(ff, 0, 11));
-        global_gtime += (gntime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec);
+        incremental_rate(global_gntime, gntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 11)), collected_usec, last_collected_usec);
+
+        global_gtime += global_gntime;
 
         // remove guest time from user time
         global_utime -= (global_utime > global_gtime) ? global_gtime : global_utime;
@@ -1387,8 +1374,9 @@ static inline void make_all_pid_fds_negative(struct pid_stat *p) {
 }
 
 static inline void cleanup_negative_pid_fds(struct pid_stat *p) {
-    int *fd = p->fds, *end = &p->fds[p->fds_size];
-    while(fd < end) {
+    int *fd = p->fds, *fdend = &p->fds[p->fds_size];
+
+    while(fd < fdend) {
         if(unlikely(*fd < 0)) {
             file_descriptor_not_used(-(*fd));
             *fd++ = 0;
@@ -1403,7 +1391,9 @@ static inline void zero_pid_fds(struct pid_stat *p, int first, int size) {
     while(fd < end) *fd++ = 0;
 }
 
-static inline int read_pid_file_descriptors(struct pid_stat *p) {
+static inline int read_pid_file_descriptors(struct pid_stat *p, void *ptr) {
+    (void)ptr;
+
     if(unlikely(!p->fds_dirname)) {
         char dirname[FILENAME_MAX+1];
         snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", netdata_configured_host_prefix, p->pid);
@@ -1429,7 +1419,7 @@ static inline int read_pid_file_descriptors(struct pid_stat *p) {
             continue;
 
         // get its number
-        int fdid = (int)str2l(de->d_name);
+        int fdid = (int) str2l(de->d_name);
         if(unlikely(fdid < 0)) continue;
 
         // check if the fds array is small
@@ -1437,7 +1427,12 @@ static inline int read_pid_file_descriptors(struct pid_stat *p) {
             // it is small, extend it
 
             if(unlikely(debug))
-                fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + MAX_SPARE_FDS);
+                fprintf(stderr
+                        , "apps.plugin: extending fd memory slots for %s from %d to %d\n"
+                        , p->comm
+                        , p->fds_size
+                        , fdid + MAX_SPARE_FDS
+                );
 
             p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(int));
 
@@ -1468,9 +1463,10 @@ static inline int read_pid_file_descriptors(struct pid_stat *p) {
             p->fds[fdid] = file_descriptor_find_or_add(linkname);
         }
 
-        // else make it positive again, we need it
-        // of course, the actual file may have changed, but we don't care so much
-        // FIXME: we could compare the inode as returned by readdir dirent structure
+            // else make it positive again, we need it
+            // of course, the actual file may have changed, but we don't care so much
+            // FIXME: we could compare the inode as returned by readdir dirent structure
+            // UPDATE: no we cannot use inodes - under /proc inodes don't change when the link is changed
 
         else
             p->fds[fdid] = -p->fds[fdid];
@@ -1623,11 +1619,11 @@ static inline void process_exited_processes() {
         if(p->updated || !p->stat_collected_usec)
             continue;
 
-        kernel_uint_t utime  = (p->utime_raw + p->cutime_raw)   * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-        kernel_uint_t stime  = (p->stime_raw + p->cstime_raw)   * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-        kernel_uint_t gtime  = (p->gtime_raw + p->cgtime_raw)   * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-        kernel_uint_t minflt = (p->minflt_raw + p->cminflt_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-        kernel_uint_t majflt = (p->majflt_raw + p->cmajflt_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+        kernel_uint_t utime  = (p->utime_raw + p->cutime_raw)   * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+        kernel_uint_t stime  = (p->stime_raw + p->cstime_raw)   * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+        kernel_uint_t gtime  = (p->gtime_raw + p->cgtime_raw)   * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+        kernel_uint_t minflt = (p->minflt_raw + p->cminflt_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+        kernel_uint_t majflt = (p->majflt_raw + p->cmajflt_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
 
         if(utime + stime + gtime + minflt + majflt == 0)
             continue;
@@ -1788,9 +1784,9 @@ static int compar_pid(const void *pid1, const void *pid2) {
         return 1;
 }
 
-static inline int collect_data_for_pid(pid_t pid) {
-    if(unlikely(pid <= 0 || pid > pid_max)) {
-        error("Invalid pid %d read (expected 1 to %d). Ignoring process.", pid, pid_max);
+static inline int collect_data_for_pid(pid_t pid, void *ptr) {
+    if(unlikely(pid < INIT_PID || pid > pid_max)) {
+        error("Invalid pid %d read (expected %d to %d). Ignoring process.", pid, INIT_PID, pid_max);
         return 0;
     }
 
@@ -1803,11 +1799,11 @@ static inline int collect_data_for_pid(pid_t pid) {
     // --------------------------------------------------------------------
     // /proc/<pid>/stat
 
-    if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p))))
+    if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p, ptr))))
         // there is no reason to proceed if we cannot get its status
         return 0;
 
-    read_proc_pid_ownership(p);
+    read_proc_pid_ownership(p, ptr);
 
     // check its parent pid
     if(unlikely(p->ppid < 0 || p->ppid > pid_max)) {
@@ -1818,12 +1814,12 @@ static inline int collect_data_for_pid(pid_t pid) {
     // --------------------------------------------------------------------
     // /proc/<pid>/io
 
-    managed_log(p, PID_LOG_IO, read_proc_pid_io(p));
+    managed_log(p, PID_LOG_IO, read_proc_pid_io(p, ptr));
 
     // --------------------------------------------------------------------
     // /proc/<pid>/statm
 
-    if(unlikely(!managed_log(p, PID_LOG_STATM, read_proc_pid_statm(p))))
+    if(unlikely(!managed_log(p, PID_LOG_STATM, read_proc_pid_statm(p, ptr))))
         // there is no reason to proceed if we cannot get its memory status
         return 0;
 
@@ -1831,7 +1827,7 @@ static inline int collect_data_for_pid(pid_t pid) {
     // /proc/<pid>/fd
 
     if(enable_file_charts)
-            managed_log(p, PID_LOG_FDS, read_pid_file_descriptors(p));
+            managed_log(p, PID_LOG_FDS, read_pid_file_descriptors(p, ptr));
 
     // --------------------------------------------------------------------
     // done!
@@ -1859,9 +1855,12 @@ static int collect_data_for_all_processes(void) {
             p->children_count   = 0;
             p->parent           = NULL;
 
+#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
             all_pids_sortlist[slc++] = p->pid;
+#endif
         }
 
+#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
         if(unlikely(slc != all_pids_count)) {
             error("Internal error: I was thinking I had %zu processes in my arrays, but it seems there are more.", all_pids_count);
             all_pids_count = slc;
@@ -1878,10 +1877,11 @@ static int collect_data_for_all_processes(void) {
 
             // we forward read all running processes
             // collect_data_for_pid() is smart enough,
-            // not to read the same pid twice per iterations
+            // not to read the same pid twice per iteration
             for(slc = 0; slc < all_pids_count; slc++)
-                collect_data_for_pid(all_pids_sortlist[slc]);
+                collect_data_for_pid(all_pids_sortlist[slc], NULL);
         }
+#endif
     }
 
     char dirname[FILENAME_MAX + 1];
@@ -1904,7 +1904,7 @@ static int collect_data_for_all_processes(void) {
         if(unlikely(endptr == de->d_name || *endptr != '\0'))
             continue;
 
-        collect_data_for_pid(pid);
+        collect_data_for_pid(pid, NULL);
     }
     closedir(dir);
 
@@ -1952,10 +1952,11 @@ static void cleanup_exited_pids(void) {
             if(unlikely(debug && (p->keep || p->keeploops)))
                 fprintf(stderr, " > CLEANUP cannot keep exited process %d (%s) anymore - removing it.\n", p->pid, p->comm);
 
-            for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
-                file_descriptor_not_used(p->fds[c]);
-                p->fds[c] = 0;
-            }
+            for(c = 0; c < p->fds_size; c++)
+                if(p->fds[c] > 0) {
+                    file_descriptor_not_used(p->fds[c]);
+                    p->fds[c] = 0;
+                }
 
             pid_t r = p->pid;
             p = p->next;
@@ -2018,7 +2019,7 @@ static void apply_apps_groups_targets_inheritance(void) {
                     && p->parent
                     && p->parent->children_count
                     && (p->target == p->parent->target || !p->parent->target)
-                    && p->ppid != 1
+                    && p->ppid != INIT_PID
                 )) {
                 p->parent->children_count--;
                 p->merged = 1;
@@ -2040,8 +2041,8 @@ static void apply_apps_groups_targets_inheritance(void) {
     }
 
     // init goes always to default target
-    if(all_pids[1])
-        all_pids[1]->target = apps_groups_default_target;
+    if(all_pids[INIT_PID])
+        all_pids[INIT_PID]->target = apps_groups_default_target;
 
     // give a default target on all top level processes
     if(unlikely(debug)) loops++;
@@ -2398,6 +2399,7 @@ static usec_t send_resource_usage_to_netdata() {
                         "DIMENSION user '' incremental 1 1000\n"
                         "DIMENSION system '' incremental 1 1000\n"
                         "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_files line 140001 %1$d\n"
+                        "DIMENSION calls '' incremental 1 1\n"
                         "DIMENSION files '' incremental 1 1\n"
                         "DIMENSION pids '' absolute 1 1\n"
                         "DIMENSION fds '' absolute 1 1\n"
@@ -2431,6 +2433,7 @@ static usec_t send_resource_usage_to_netdata() {
         "SET system = %llu\n"
         "END\n"
         "BEGIN netdata.apps_files %llu\n"
+        "SET calls = %zu\n"
         "SET files = %zu\n"
         "SET pids = %zu\n"
         "SET fds = %d\n"
@@ -2447,6 +2450,7 @@ static usec_t send_resource_usage_to_netdata() {
         , cpuuser
         , cpusyst
         , usec
+        , calls_counter
         , file_counter
         , all_pids_count
         , all_files_len
@@ -2890,6 +2894,22 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
 // ----------------------------------------------------------------------------
 // parse command line arguments
 
+int check_proc_1_io() {
+    int ret = 0;
+
+    procfile *ff = procfile_open("/proc/1/io", NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
+    if(!ff) goto cleanup;
+
+    ff = procfile_readall(ff);
+    if(!ff) goto cleanup;
+
+    ret = 1;
+
+cleanup:
+    procfile_close(ff);
+    return ret;
+}
+
 static void parse_args(int argc, char **argv)
 {
     int i, freq = 0;
@@ -2909,6 +2929,15 @@ static void parse_args(int argc, char **argv)
             exit(0);
         }
 
+        if(strcmp("test-permissions", argv[i]) == 0 || strcmp("-t", argv[i]) == 0) {
+            if(!check_proc_1_io()) {
+                perror("Tried to read /proc/1/io and it failed");
+                exit(1);
+            }
+            printf("OK\n");
+            exit(0);
+        }
+
         if(strcmp("debug", argv[i]) == 0) {
             debug = 1;
             // debug_flags = 0xffffffff;
@@ -3081,8 +3110,6 @@ int main(int argc, char **argv) {
     // set the name for logging
     program_name = "apps.plugin";
 
-    info("started on pid %d", getpid());
-
     // disable syslog for apps.plugin
     error_log_syslog = 0;
 
@@ -3124,28 +3151,31 @@ int main(int argc, char **argv) {
 
     parse_args(argc, argv);
 
-    if(!check_capabilities()) {
-        if(!am_i_running_as_root()) {
-            uid_t uid = getuid(), euid = geteuid();
+    if(!check_capabilities() && !am_i_running_as_root() && !check_proc_1_io()) {
+        uid_t uid = getuid(), euid = geteuid();
 #ifdef HAVE_CAPABILITY
-            error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
-                          "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
-                          "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %s; "
-                          "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; "
-                  , uid, euid, argv[0], argv[0], argv[0]
-            );
+        error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
+                      "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
+                      "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %s; "
+                      "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; "
+              , uid, euid, argv[0], argv[0], argv[0]
+        );
 #else
-            error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
-                          "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
-                          "Your system does not support capabilities. "
-                          "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; "
-                  , uid, euid, argv[0], argv[0]
-            );
+        error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
+                      "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
+                      "Your system does not support capabilities. "
+                      "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; "
+              , uid, euid, argv[0], argv[0]
+        );
 #endif
-        }
     }
 
+    info("started on pid %d", getpid());
+
+#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
     all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max);
+#endif
+
     all_pids          = callocz(sizeof(struct pid_stat *), (size_t) pid_max);
 
     usec_t step = update_every * USEC_PER_SEC;